diff --git a/Core/Code/Algorithms/mitkExtractSliceFilter.cpp b/Core/Code/Algorithms/mitkExtractSliceFilter.cpp index 0fe46c9a38..ca0f0c0689 100644 --- a/Core/Code/Algorithms/mitkExtractSliceFilter.cpp +++ b/Core/Code/Algorithms/mitkExtractSliceFilter.cpp @@ -1,486 +1,486 @@ /*=================================================================== 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 "mitkExtractSliceFilter.h" #include #include #include #include #include #include #include mitk::ExtractSliceFilter::ExtractSliceFilter(vtkImageReslice* reslicer ){ if(reslicer == NULL){ m_Reslicer = vtkSmartPointer::New(); } else { m_Reslicer = reslicer; } m_TimeStep = 0; m_Reslicer->ReleaseDataFlagOn(); m_InterpolationMode = ExtractSliceFilter::RESLICE_NEAREST; m_ResliceTransform = NULL; m_InPlaneResampleExtentByGeometry = false; m_OutPutSpacing = new mitk::ScalarType[2]; m_OutputDimension = 2; m_ZSpacing = 1.0; m_ZMin = 0; m_ZMax = 0; m_VtkOutputRequested = false; } mitk::ExtractSliceFilter::~ExtractSliceFilter(){ m_ResliceTransform = NULL; m_WorldGeometry = NULL; delete [] m_OutPutSpacing; } void mitk::ExtractSliceFilter::GenerateOutputInformation(){ Superclass::GenerateOutputInformation(); //TODO try figure out how to set the specs of the slice before it is actually extracted /*Image::Pointer output = this->GetOutput(); Image::ConstPointer input = this->GetInput(); if (input.IsNull()) return; unsigned int dimensions[2]; dimensions[0] = m_WorldGeometry->GetExtent(0); dimensions[1] = m_WorldGeometry->GetExtent(1); output->Initialize(input->GetPixelType(), 2, dimensions, 1);*/ } void mitk::ExtractSliceFilter::GenerateInputRequestedRegion(){ //As we want all pixel information fo the image in our plane, the requested region //is set to the largest possible region in the image. //This is needed because an oblique plane has a larger extent then the image //and the in pipeline it is checked via PropagateResquestedRegion(). But the //extent of the slice is actually fitting because it is oblique within the image. ImageToImageFilter::InputImagePointer input = const_cast< ImageToImageFilter::InputImageType* > ( this->GetInput() ); input->SetRequestedRegionToLargestPossibleRegion(); } mitk::ScalarType* mitk::ExtractSliceFilter::GetOutputSpacing(){ return m_OutPutSpacing; } void mitk::ExtractSliceFilter::GenerateData(){ mitk::Image *input = const_cast< mitk::Image * >( this->GetInput() ); if (!input) { MITK_ERROR << "mitk::ExtractSliceFilter: No input image available. Please set the input!" << std::endl; itkExceptionMacro("mitk::ExtractSliceFilter: No input image available. Please set the input!"); return; } if(!m_WorldGeometry) { MITK_ERROR << "mitk::ExtractSliceFilter: No Geometry for reslicing available." << std::endl; itkExceptionMacro("mitk::ExtractSliceFilter: No Geometry for reslicing available."); return; } const TimeGeometry* inputTimeGeometry = this->GetInput()->GetTimeGeometry(); if ( ( inputTimeGeometry == NULL ) || ( inputTimeGeometry->CountTimeSteps() <= 0 ) ) { itkWarningMacro(<<"Error reading input image TimeGeometry."); return; } // is it a valid timeStep? if ( inputTimeGeometry->IsValidTimeStep( m_TimeStep ) == false ) { itkWarningMacro(<<"This is not a valid timestep: "<< m_TimeStep ); return; } // check if there is something to display. if ( ! input->IsVolumeSet( m_TimeStep ) ) { itkWarningMacro(<<"No volume data existent at given timestep "<< m_TimeStep ); return; } /*================#BEGIN setup vtkImageRslice properties================*/ Point3D origin; Vector3D right, bottom, normal; double widthInMM, heightInMM; Vector2D extent; const PlaneGeometry* planeGeometry = dynamic_cast(m_WorldGeometry); if ( planeGeometry != NULL ) { //if the worldGeomatry is a PlaneGeometry everthing is straight forward origin = planeGeometry->GetOrigin(); right = planeGeometry->GetAxisVector( 0 ); bottom = planeGeometry->GetAxisVector( 1 ); normal = planeGeometry->GetNormal(); if ( m_InPlaneResampleExtentByGeometry ) { // Resampling grid corresponds to the current world geometry. This // means that the spacing of the output 2D image depends on the // currently selected world geometry, and *not* on the image itself. extent[0] = m_WorldGeometry->GetExtent( 0 ); extent[1] = m_WorldGeometry->GetExtent( 1 ); } else { // Resampling grid corresponds to the input geometry. This means that // the spacing of the output 2D image is directly derived from the // associated input image, regardless of the currently selected world // geometry. Vector3D rightInIndex, bottomInIndex; inputTimeGeometry->GetGeometryForTimeStep( m_TimeStep )->WorldToIndex( right, rightInIndex ); inputTimeGeometry->GetGeometryForTimeStep( m_TimeStep )->WorldToIndex( bottom, bottomInIndex ); extent[0] = rightInIndex.GetNorm(); extent[1] = bottomInIndex.GetNorm(); } // Get the extent of the current world geometry and calculate resampling // spacing therefrom. widthInMM = m_WorldGeometry->GetExtentInMM( 0 ); heightInMM = m_WorldGeometry->GetExtentInMM( 1 ); m_OutPutSpacing[0] = widthInMM / extent[0]; m_OutPutSpacing[1] = heightInMM / extent[1]; right.Normalize(); bottom.Normalize(); normal.Normalize(); /* * Transform the origin to center based coordinates. * Note: * This is needed besause vtk's origin is center based too (!!!) ( see 'The VTK book' page 88 ) * and the worldGeometry surrouding the image is no imageGeometry. So the worldGeometry * has its origin at the corner of the voxel and needs to be transformed. */ origin += right * ( m_OutPutSpacing[0] * 0.5 ); origin += bottom * ( m_OutPutSpacing[1] * 0.5 ); //set the tranform for reslicing. // Use inverse transform of the input geometry for reslicing the 3D image. // This is needed if the image volume already transformed if(m_ResliceTransform.IsNotNull()) m_Reslicer->SetResliceTransform(m_ResliceTransform->GetVtkTransform()->GetLinearInverse()); // Set background level to TRANSLUCENT (see Geometry2DDataVtkMapper3D), // else the background of the image turns out gray m_Reslicer->SetBackgroundLevel( -32768 ); } else{ //Code for curved planes, mostly taken 1:1 from imageVtkMapper2D and not tested yet. // Do we have an AbstractTransformGeometry? // This is the case for AbstractTransformGeometry's (e.g. a ThinPlateSplineCurvedGeometry ) const mitk::AbstractTransformGeometry* abstractGeometry = dynamic_cast< const AbstractTransformGeometry * >(m_WorldGeometry); if(abstractGeometry != NULL) { m_ResliceTransform = abstractGeometry; extent[0] = abstractGeometry->GetParametricExtent(0); extent[1] = abstractGeometry->GetParametricExtent(1); widthInMM = abstractGeometry->GetParametricExtentInMM(0); heightInMM = abstractGeometry->GetParametricExtentInMM(1); m_OutPutSpacing[0] = widthInMM / extent[0]; m_OutPutSpacing[1] = heightInMM / extent[1]; origin = abstractGeometry->GetPlane()->GetOrigin(); right = abstractGeometry->GetPlane()->GetAxisVector(0); right.Normalize(); bottom = abstractGeometry->GetPlane()->GetAxisVector(1); bottom.Normalize(); normal = abstractGeometry->GetPlane()->GetNormal(); normal.Normalize(); // Use a combination of the InputGeometry *and* the possible non-rigid // AbstractTransformGeometry for reslicing the 3D Image vtkSmartPointer composedResliceTransform = vtkSmartPointer::New(); composedResliceTransform->Identity(); composedResliceTransform->Concatenate( inputTimeGeometry->GetGeometryForTimeStep( m_TimeStep )->GetVtkTransform()->GetLinearInverse() ); composedResliceTransform->Concatenate( abstractGeometry->GetVtkAbstractTransform() ); m_Reslicer->SetResliceTransform( composedResliceTransform ); // Set background level to BLACK instead of translucent, to avoid // boundary artifacts (see Geometry2DDataVtkMapper3D) m_Reslicer->SetBackgroundLevel( -1023 ); } else { itkExceptionMacro("mitk::ExtractSliceFilter: No fitting geometry for reslice axis!"); return; } } if(m_ResliceTransform.IsNotNull()){ //if the resliceTransform is set the reslice axis are recalculated. //Thus the geometry information is not fitting. Therefor a unitSpacingFilter //is used to set up a global spacing of 1 and compensate the transform. vtkSmartPointer unitSpacingImageFilter = vtkSmartPointer::New() ; unitSpacingImageFilter->ReleaseDataFlagOn(); unitSpacingImageFilter->SetOutputSpacing( 1.0, 1.0, 1.0 ); unitSpacingImageFilter->SetInputData( input->GetVtkImageData(m_TimeStep) ); m_Reslicer->SetInputConnection(unitSpacingImageFilter->GetOutputPort() ); } else { //if no tranform is set the image can be used directly m_Reslicer->SetInputData(input->GetVtkImageData(m_TimeStep)); } /*setup the plane where vktImageReslice extracts the slice*/ //ResliceAxesOrigin is the ancor point of the plane double originInVtk[3]; itk2vtk(origin, originInVtk); m_Reslicer->SetResliceAxesOrigin(originInVtk); //the cosines define the plane: x and y are the direction vectors, n is the planes normal //this specifies a matrix 3x3 // x1 y1 n1 // x2 y2 n2 // x3 y3 n3 double cosines[9]; vnl2vtk(right.GetVnlVector(), cosines);//x vnl2vtk(bottom.GetVnlVector(), cosines + 3);//y vnl2vtk(normal.GetVnlVector(), cosines + 6);//n m_Reslicer->SetResliceAxesDirectionCosines(cosines); //we only have one slice, not a volume m_Reslicer->SetOutputDimensionality(m_OutputDimension); //set the interpolation mode for slicing switch(this->m_InterpolationMode){ case RESLICE_NEAREST: m_Reslicer->SetInterpolationModeToNearestNeighbor(); break; case RESLICE_LINEAR: m_Reslicer->SetInterpolationModeToLinear(); break; case RESLICE_CUBIC: m_Reslicer->SetInterpolationModeToCubic(); break; default: //the default interpolation used by mitk m_Reslicer->SetInterpolationModeToNearestNeighbor(); } /*========== BEGIN setup extent of the slice ==========*/ int xMin, xMax, yMin, yMax; xMin = yMin = 0; xMax = static_cast< int >( extent[0]); yMax = static_cast< int >( extent[1]); double sliceBounds[6]; if (m_WorldGeometry->GetReferenceGeometry()) { for ( int i = 0; i < 6; ++i ) { sliceBounds[i] = 0.0; } if (this->GetClippedPlaneBounds( m_WorldGeometry->GetReferenceGeometry(), planeGeometry, sliceBounds )) { // Calculate output extent (integer values) xMin = static_cast< int >( sliceBounds[0] / m_OutPutSpacing[0] + 0.5 ); xMax = static_cast< int >( sliceBounds[1] / m_OutPutSpacing[0] + 0.5 ); yMin = static_cast< int >( sliceBounds[2] / m_OutPutSpacing[1] + 0.5 ); yMax = static_cast< int >( sliceBounds[3] / m_OutPutSpacing[1] + 0.5 ); } // ELSE we use the default values } // Set the output extents! First included pixel index and last included pixel index // xMax and yMax are one after the last pixel. so they have to be decremented by 1. // In case we have a 2D image, xMax or yMax might be 0. in this case, do not decrement, but take 0. m_Reslicer->SetOutputExtent(xMin, std::max(0, xMax-1), yMin, std::max(0, yMax-1), m_ZMin, m_ZMax ); /*========== END setup extent of the slice ==========*/ m_Reslicer->SetOutputOrigin( 0.0, 0.0, 0.0 ); m_Reslicer->SetOutputSpacing( m_OutPutSpacing[0], m_OutPutSpacing[1], m_ZSpacing ); //TODO check the following lines, they are responsible wether vtk error outputs appear or not m_Reslicer->UpdateWholeExtent(); //this produces a bad allocation error for 2D images //m_Reslicer->GetOutput()->UpdateInformation(); //m_Reslicer->GetOutput()->SetUpdateExtentToWholeExtent(); //start the pipeline m_Reslicer->Update(); /*================ #END setup vtkImageRslice properties================*/ if(m_VtkOutputRequested){ return; //no converting to mitk //no mitk geometry will be set, as the output is vtkImageData only!!! } else { /*================ #BEGIN Get the slice from vtkImageReslice and convert it to mit::Image================*/ vtkImageData* reslicedImage; reslicedImage = m_Reslicer->GetOutput(); if(!reslicedImage) { itkWarningMacro(<<"Reslicer returned empty image"); return; } mitk::Image::Pointer resultImage = this->GetOutput(); //initialize resultimage with the specs of the vtkImageData object returned from vtkImageReslice if (reslicedImage->GetDataDimension() == 1) { // If original image was 2D, the slice might have an y extent of 0. // Still i want to ensure here that Image is 2D resultImage->Initialize(reslicedImage,1,-1,-1,1); } else { resultImage->Initialize(reslicedImage); } //transfer the voxel data resultImage->SetVolume(reslicedImage->GetScalarPointer()); //set the geometry from current worldgeometry for the reusultimage //this is needed that the image has the correct mitk geometry //the originalGeometry is the Geometry of the result slice // mitk::AffineGeometryFrame3D::Pointer originalGeometryAGF = m_WorldGeometry->Clone(); -// Geometry2D::Pointer originalGeometry = dynamic_cast( originalGeometryAGF.GetPointer() ); - Geometry2D::Pointer originalGeometry = m_WorldGeometry->Clone(); +// PlaneGeometry::Pointer originalGeometry = dynamic_cast( originalGeometryAGF.GetPointer() ); + PlaneGeometry::Pointer originalGeometry = m_WorldGeometry->Clone(); originalGeometry->GetIndexToWorldTransform()->SetMatrix(m_WorldGeometry->GetIndexToWorldTransform()->GetMatrix()); //the origin of the worldGeometry is transformed to center based coordinates to be an imageGeometry Point3D sliceOrigin = originalGeometry->GetOrigin(); sliceOrigin += right * ( m_OutPutSpacing[0] * 0.5 ); sliceOrigin += bottom * ( m_OutPutSpacing[1] * 0.5 ); //a worldGeometry is no imageGeometry, thus it is manually set to true originalGeometry->ImageGeometryOn(); /*At this point we have to adjust the geometry because the origin isn't correct. The wrong origin is related to the rotation of the current world geometry plane. This causes errors on transfering world to index coordinates. We just shift the origin in each direction about the amount of the expanding (needed while rotating the plane). */ Vector3D axis0 = originalGeometry->GetAxisVector(0); Vector3D axis1 = originalGeometry->GetAxisVector(1); axis0.Normalize(); axis1.Normalize(); //adapt the origin. Note that for orthogonal planes the minima are '0' and thus the origin stays the same. sliceOrigin += (axis0 * (xMin * m_OutPutSpacing[0])) + (axis1 * (yMin * m_OutPutSpacing[1])); originalGeometry->SetOrigin(sliceOrigin); originalGeometry->Modified(); resultImage->SetGeometry( originalGeometry ); /*the bounds as well as the extent of the worldGeometry are not adapted correctly during crosshair rotation. This is only a quick fix and has to be evaluated. The new bounds are set via the max values of the calcuted slice extent. It will look like [ 0, x, 0, y, 0, 1]. */ mitk::BoundingBox::BoundsArrayType boundsCopy; boundsCopy[0] = boundsCopy[2] = boundsCopy[4] = 0; boundsCopy[5] = 1; boundsCopy[1] = xMax - xMin; boundsCopy[3] = yMax - yMin; resultImage->GetGeometry()->SetBounds(boundsCopy); /*================ #END Get the slice from vtkImageReslice and convert it to mitk Image================*/ } } bool mitk::ExtractSliceFilter::GetClippedPlaneBounds(double bounds[6]){ if(!m_WorldGeometry || !this->GetInput()) return false; return this->GetClippedPlaneBounds(m_WorldGeometry->GetReferenceGeometry(), dynamic_cast< const PlaneGeometry * >( m_WorldGeometry ), bounds); } bool mitk::ExtractSliceFilter::GetClippedPlaneBounds( const BaseGeometry *boundingGeometry, const PlaneGeometry *planeGeometry, double *bounds ) { bool b = mitk::PlaneClipping::CalculateClippedPlaneBounds(boundingGeometry, planeGeometry, bounds); return b; } diff --git a/Core/Code/Algorithms/mitkExtractSliceFilter.h b/Core/Code/Algorithms/mitkExtractSliceFilter.h index f00a3a4f42..c563af5e3c 100644 --- a/Core/Code/Algorithms/mitkExtractSliceFilter.h +++ b/Core/Code/Algorithms/mitkExtractSliceFilter.h @@ -1,174 +1,174 @@ /*=================================================================== 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 mitkExtractSliceFilter_h_Included #define mitkExtractSliceFilter_h_Included #include "MitkExports.h" #include "mitkImageToImageFilter.h" #include #include #include #include #include #include #include namespace mitk { /** \brief ExtractSliceFilter extracts a 2D abitrary oriented slice from a 3D volume. The filter can reslice in all orthogonal planes such as sagittal, coronal and axial, and is also able to reslice a abitrary oriented oblique plane. Curved planes are specified via an AbstractTransformGeometry as the input worldgeometry. The convinient workflow is: 1. Set an image as input. 2. Set the worldGeometry2D. This defines a grid where the slice is being extracted 3. And then start the pipeline. There are a few more properties that can be set to modify the behavior of the slicing. The properties are: - interpolation mode either Nearestneighbor, Linear or Cubic. - a transform this is a convinient way to adapt the reslice axis for the case that the image is transformed e.g. rotated. - time step the time step in a times volume. - resample by geometry wether the resampling grid corresponds to the specs of the worldgeometry or is directly derived from the input image By default the properties are set to: - interpolation mode Nearestneighbor. - a transform NULL (No transform is set). - time step 0. - resample by geometry false (Corresponds to input image). */ class MITK_CORE_EXPORT ExtractSliceFilter : public ImageToImageFilter { public: mitkClassMacro(ExtractSliceFilter, ImageToImageFilter); itkNewMacro(ExtractSliceFilter); mitkNewMacro1Param(Self, vtkImageReslice*); /** \brief Set the axis where to reslice at.*/ - void SetWorldGeometry(const Geometry2D* geometry ){ + void SetWorldGeometry(const PlaneGeometry* geometry ){ this->m_WorldGeometry = geometry; this->Modified(); } /** \brief Set the time step in the 4D volume */ void SetTimeStep( unsigned int timestep){ this->m_TimeStep = timestep; } unsigned int GetTimeStep(){ return this->m_TimeStep; } /** \brief Set a transform for the reslice axes. * This transform is needed if the image volume itself is transformed. (Effects the reslice axis) */ void SetResliceTransformByGeometry(const BaseGeometry* transform){ this->m_ResliceTransform = transform; } /** \brief Resampling grid corresponds to: false->image true->worldgeometry*/ void SetInPlaneResampleExtentByGeometry(bool inPlaneResampleExtentByGeometry){ this->m_InPlaneResampleExtentByGeometry = inPlaneResampleExtentByGeometry; } /** \brief Sets the output dimension of the slice*/ void SetOutputDimensionality(unsigned int dimension){ this->m_OutputDimension = dimension; } /** \brief Set the spacing in z direction manually. * Required if the outputDimension is > 2. */ void SetOutputSpacingZDirection(double zSpacing){ this->m_ZSpacing = zSpacing; } /** \brief Set the extent in pixel for direction z manualy. Required if the output dimension is > 2. */ void SetOutputExtentZDirection(int zMin, int zMax) { this->m_ZMin = zMin; this->m_ZMax = zMax; } /** \brief Get the bounding box of the slice [xMin, xMax, yMin, yMax, zMin, zMax] * The method uses the input of the filter to calculate the bounds. * It is recommended to use * GetClippedPlaneBounds(const Geometry3D*, const PlaneGeometry*, double*) * if you are not sure about the input. */ bool GetClippedPlaneBounds(double bounds[6]); /** \brief Get the bounding box of the slice [xMin, xMax, yMin, yMax, zMin, zMax]*/ bool GetClippedPlaneBounds( const BaseGeometry *boundingGeometry, const PlaneGeometry *planeGeometry, double *bounds ); /** \brief Get the spacing of the slice. returns mitk::ScalarType[2] */ mitk::ScalarType* GetOutputSpacing(); /** \brief Get Output as vtkImageData. * Note: * SetVtkOutputRequest(true) has to be called at least once before * GetVtkOutput(). Otherwise the output is empty for the first update step. */ vtkImageData* GetVtkOutput(){ m_VtkOutputRequested = true; return m_Reslicer->GetOutput(); } /** Set VtkOutPutRequest to suppress the convertion of the image. * It is suggested to use this with GetVtkOutput(). * Note: * SetVtkOutputRequest(true) has to be called at least once before * GetVtkOutput(). Otherwise the output is empty for the first update step. */ void SetVtkOutputRequest(bool isRequested){ m_VtkOutputRequested = isRequested; } /** \brief Get the reslices axis matrix. * Note: the axis are recalculated when calling SetResliceTransformByGeometry. */ vtkMatrix4x4* GetResliceAxes(){ return this->m_Reslicer->GetResliceAxes(); } enum ResliceInterpolation { RESLICE_NEAREST=0, RESLICE_LINEAR=1, RESLICE_CUBIC=3 }; void SetInterpolationMode( ExtractSliceFilter::ResliceInterpolation interpolation){ this->m_InterpolationMode = interpolation; } protected: ExtractSliceFilter(vtkImageReslice* reslicer = NULL); virtual ~ExtractSliceFilter(); virtual void GenerateData(); virtual void GenerateOutputInformation(); virtual void GenerateInputRequestedRegion(); - const Geometry2D* m_WorldGeometry; + const PlaneGeometry* m_WorldGeometry; vtkSmartPointer m_Reslicer; unsigned int m_TimeStep; unsigned int m_OutputDimension; double m_ZSpacing; int m_ZMin; int m_ZMax; ResliceInterpolation m_InterpolationMode; BaseGeometry::ConstPointer m_ResliceTransform; bool m_InPlaneResampleExtentByGeometry;//Resampling grid corresponds to: false->image true->worldgeometry mitk::ScalarType* m_OutPutSpacing; bool m_VtkOutputRequested; }; } #endif // mitkExtractSliceFilter_h_Included diff --git a/Core/Code/Algorithms/mitkGeometry2DDataToSurfaceFilter.h b/Core/Code/Algorithms/mitkGeometry2DDataToSurfaceFilter.h index 4b64927f57..440a895151 100644 --- a/Core/Code/Algorithms/mitkGeometry2DDataToSurfaceFilter.h +++ b/Core/Code/Algorithms/mitkGeometry2DDataToSurfaceFilter.h @@ -1,227 +1,227 @@ /*=================================================================== 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 MITKGEOMETRY2DDATATOSURFACEDATAFILTER_H_HEADER_INCLUDED_C10B22CD #define MITKGEOMETRY2DDATATOSURFACEDATAFILTER_H_HEADER_INCLUDED_C10B22CD #include "mitkSurfaceSource.h" #include "mitkGeometry3D.h" #include "vtkSystemIncludes.h" class vtkPlaneSource; class vtkTransformPolyDataFilter; class vtkCubeSource; class vtkTransform; class vtkPlane; class vtkCutter; class vtkStripper; class vtkPolyData; class vtkPPolyDataNormals; class vtkTriangleFilter; class vtkTextureMapToPlane; class vtkBox; class vtkClipPolyData; namespace mitk { class Geometry2DData; /** \brief Superclass of all classes having a Geometry2DData as input and * generating Images as output * * Currently implemented for PlaneGeometry and AbstractTransformGeometry. * Currently, this class does not really have subclasses, but does the job - * for itself. It checks which kind of Geometry2D is stored in the + * for itself. It checks which kind of PlaneGeometry is stored in the * Geometry2DData and - if it knows how - it generates the respective * Surface. Of course, this has the disadvantage that for any new type of - * Geometry2D this class (Geometry2DDataToSurfaceFilter) has to be + * PlaneGeometry this class (Geometry2DDataToSurfaceFilter) has to be * changed/extended. The idea is to move the type specific generation code in * subclasses, and internally (within this class) use a factory to create an * instance of the required subclass and delegate the surface generation to * it. * * \sa mitk::DeformablePlane * \todo make extension easier * \ingroup Process */ class MITK_CORE_EXPORT Geometry2DDataToSurfaceFilter : public SurfaceSource { public: mitkClassMacro(Geometry2DDataToSurfaceFilter, SurfaceSource); itkNewMacro(Self); virtual void GenerateOutputInformation(); virtual void GenerateData(); const Geometry2DData *GetInput(void); const Geometry2DData *GetInput(unsigned int idx); virtual void SetInput(const Geometry2DData *image); using itk::ProcessObject::SetInput; virtual void SetInput(unsigned int index, const Geometry2DData *image); /** \brief If \a true (default), use Geometry3D::GetParametricBounds() to define the resolution in parameter space, * otherwise use m_XResolution and m_YResolution */ itkGetMacro(UseGeometryParametricBounds, bool); /** \brief If \a true (default), use Geometry3D::GetParametricBounds() to define the resolution in parameter space, * otherwise use m_XResolution and m_YResolution */ itkSetMacro(UseGeometryParametricBounds, bool); /** \brief Get x-resolution in parameter space * * The m_PlaneSource will create this many sub-rectangles * in x-direction (see vtkPlaneSource::SetXResolution) * \note Only used, when GetUseGeometryParametricBounds() is \a false, otherwise the * the x-bounds of Geometry3D::GetParametricBounds() are used. * \sa m_XResolution */ itkGetMacro(XResolution, int); /** \brief Set x-resolution in parameter space * * The m_PlaneSource will create this many sub-rectangles * in x-direction (see vtkPlaneSource::SetXResolution) * \note Only used, when GetUseGeometryParametricBounds() is \a false, otherwise the * the x-bounds of Geometry3D::GetParametricBounds() are used. * \sa m_XResolution */ itkSetMacro(XResolution, int); /** \brief Get y-resolution in parameter space * * The m_PlaneSource will create this many sub-rectangles * in y-direction (see vtkPlaneSource::SetYResolution) * \note Only used, when GetUseGeometryParametricBounds() is \a false, otherwise the * the y-bounds of Geometry3D::GetParametricBounds() are used. * \sa m_YResolution */ itkGetMacro(YResolution, int); /** \brief Set y-resolution in parameter space * * The m_PlaneSource will create this many sub-rectangles * in y-direction (see vtkPlaneSource::SetYResolution) * \note Only used, when GetUseGeometryParametricBounds() is \a false, otherwise the * the y-bounds of Geometry3D::GetParametricBounds() are used. * \sa m_YResolution */ itkSetMacro(YResolution, int); /** \brief Get whether the Surface is at the origin and placed using the Geometry * * Default is \a false, i.e., the transform of the Geometry is the identity, thus * the points within the Surface are at their final position. Otherwise * (m_PlaceByGeometry==\a true), the first cornerpoint of the created Surface is * at the origin and the actual position is determined by the transform of the * Geometry. * \sa m_PlaceByGeometry */ itkGetConstMacro(PlaceByGeometry, bool); /** \brief Set whether the Surface is at the origin and placed using the Geometry * * Default is \a false, i.e., the transform of the Geometry is the identity, thus * the points within the Surface are at their final position. Otherwise * (m_PlaceByGeometry==\a true), the first cornerpoint of the created Surface is * at the origin and the actual position is determined by the transform of the * Geometry. * \sa m_PlaceByGeometry */ itkSetMacro(PlaceByGeometry, bool); itkBooleanMacro(PlaceByGeometry); itkGetConstMacro( UseBoundingBox, bool ); itkSetMacro( UseBoundingBox, bool ); itkBooleanMacro( UseBoundingBox ); void SetBoundingBox( const BoundingBox *boundingBox ); const BoundingBox *GetBoundingBox() const; protected: Geometry2DDataToSurfaceFilter(); virtual ~Geometry2DDataToSurfaceFilter(); - /** \brief Source to create the vtk-representation of the parameter space rectangle of the Geometry2D + /** \brief Source to create the vtk-representation of the parameter space rectangle of the PlaneGeometry */ vtkPlaneSource* m_PlaneSource; - /** \brief Filter to create the vtk-representation of the Geometry2D, which is a + /** \brief Filter to create the vtk-representation of the PlaneGeometry, which is a * transformation of the m_PlaneSource */ vtkTransformPolyDataFilter* m_VtkTransformPlaneFilter; /** \brief If \a true, use Geometry3D::GetParametricBounds() to define the resolution in parameter space, * otherwise use m_XResolution and m_YResolution */ bool m_UseGeometryParametricBounds; /** \brief X-resolution in parameter space * * The m_PlaneSource will create this many sub-rectangles * in x-direction (see vtkPlaneSource::SetXResolution) * \note Only used, when GetUseGeometryParametricBounds() is \a false, otherwise the * the x-bounds of Geometry3D::GetParametricBounds() are used. * \sa m_XResolution */ int m_XResolution; /** \brief Y-resolution in parameter space * * The m_PlaneSource will create this many sub-rectangles * in y-direction (see vtkPlaneSource::SetYResolution) * \note Only used, when GetUseGeometryParametricBounds() is \a false, otherwise the * the y-bounds of Geometry3D::GetParametricBounds() are used. */ int m_YResolution; /** \brief Define whether the Surface is at the origin and placed using the Geometry * * Default is \a false, i.e., the transform of the Geometry is the identity, thus * the points within the Surface are at their final position. Otherwise * (m_PlaceByGeometry==\a true), the first cornerpoint of the created Surface is * at the origin and the actual position is determined by the transform of the * Geometry. */ bool m_PlaceByGeometry; bool m_UseBoundingBox; BoundingBox::ConstPointer m_BoundingBox; vtkCubeSource *m_CubeSource; vtkTransform *m_Transform; vtkTransformPolyDataFilter *m_PolyDataTransformer; vtkPlane *m_Plane; vtkCutter *m_PlaneCutter; vtkStripper *m_PlaneStripper; vtkPolyData *m_PlanePolyData; vtkPPolyDataNormals * m_NormalsUpdater; vtkTriangleFilter *m_PlaneTriangler; vtkTextureMapToPlane *m_TextureMapToPlane; vtkBox *m_Box; vtkClipPolyData *m_PlaneClipper; }; } // namespace mitk #endif /* MITKGEOMETRY2DDATATOSURFACEDATAFILTER_H_HEADER_INCLUDED_C10B22CD */ diff --git a/Core/Code/Controllers/mitkPlanePositionManager.cpp b/Core/Code/Controllers/mitkPlanePositionManager.cpp index e22b217498..2587d0fd0c 100644 --- a/Core/Code/Controllers/mitkPlanePositionManager.cpp +++ b/Core/Code/Controllers/mitkPlanePositionManager.cpp @@ -1,106 +1,106 @@ /*=================================================================== 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 "mitkPlanePositionManager.h" #include "mitkInteractionConst.h" mitk::PlanePositionManagerService::PlanePositionManagerService() { } mitk::PlanePositionManagerService::~PlanePositionManagerService() { for (unsigned int i = 0; i < m_PositionList.size(); ++i) delete m_PositionList[i]; } -unsigned int mitk::PlanePositionManagerService::AddNewPlanePosition ( const Geometry2D* plane, unsigned int sliceIndex ) +unsigned int mitk::PlanePositionManagerService::AddNewPlanePosition ( const PlaneGeometry* plane, unsigned int sliceIndex ) { for (unsigned int i = 0; i < m_PositionList.size(); ++i) { if (m_PositionList[i] != 0) { bool isSameMatrix(true); bool isSameOffset(true); isSameOffset = mitk::Equal(m_PositionList[i]->GetTransform()->GetOffset(), plane->GetIndexToWorldTransform()->GetOffset()); if(!isSameOffset || sliceIndex != m_PositionList[i]->GetPos()) continue; isSameMatrix = mitk::MatrixEqualElementWise(m_PositionList[i]->GetTransform()->GetMatrix(), plane->GetIndexToWorldTransform()->GetMatrix()); if(isSameMatrix) return i; } } AffineTransform3D::Pointer transform = AffineTransform3D::New(); Matrix3D matrix; matrix.GetVnlMatrix().set_column(0, plane->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(0)); matrix.GetVnlMatrix().set_column(1, plane->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(1)); matrix.GetVnlMatrix().set_column(2, plane->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(2)); transform->SetMatrix(matrix); transform->SetOffset(plane->GetIndexToWorldTransform()->GetOffset()); mitk::Vector3D direction; direction[0] = plane->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(2)[0]; direction[1] = plane->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(2)[1]; direction[2] = plane->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(2)[2]; direction.Normalize(); mitk::RestorePlanePositionOperation* newOp = new mitk::RestorePlanePositionOperation (OpRESTOREPLANEPOSITION, plane->GetExtent(0), plane->GetExtent(1), plane->GetSpacing(), sliceIndex, direction, transform); m_PositionList.push_back( newOp ); return GetNumberOfPlanePositions()-1; } bool mitk::PlanePositionManagerService::RemovePlanePosition( unsigned int ID ) { if (m_PositionList.size() > ID) { delete m_PositionList[ID]; m_PositionList.erase(m_PositionList.begin()+ID); return true; } else { return false; } } mitk::RestorePlanePositionOperation* mitk::PlanePositionManagerService::GetPlanePosition ( unsigned int ID ) { if ( ID < m_PositionList.size() ) { return m_PositionList[ID]; } else { MITK_WARN<<"GetPlanePosition returned NULL!"; return 0; } } unsigned int mitk::PlanePositionManagerService::GetNumberOfPlanePositions() { return m_PositionList.size(); } void mitk::PlanePositionManagerService::RemoveAllPlanePositions() { for (unsigned int i = 0; i < m_PositionList.size(); ++i) delete m_PositionList[i]; m_PositionList.clear(); } diff --git a/Core/Code/Controllers/mitkPlanePositionManager.h b/Core/Code/Controllers/mitkPlanePositionManager.h index e9989f9686..e21d216268 100644 --- a/Core/Code/Controllers/mitkPlanePositionManager.h +++ b/Core/Code/Controllers/mitkPlanePositionManager.h @@ -1,93 +1,93 @@ /*=================================================================== 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 mitkPlanePositionManager_h_Included #define mitkPlanePositionManager_h_Included #include "mitkCommon.h" #include "mitkRestorePlanePositionOperation.h" #include "mitkDataStorage.h" #include class MitkCoreActivator; namespace mitk { /** The mitk::PlanePositionManagerService holds and manages a list of certain planepositions. To store a new position you need to specify the first slice of your slicestack and the slicenumber you want to restore in the mitk::PlanePositionManager::AddNewPlanePosition() function. To restore a position call mitk::PlanePositionManagerService::GetPlanePosition(ID) where ID is the position in the plane positionlist (returned by AddNewPlanePostion). This will give a mitk::RestorePlanePositionOperation which can be executed by the SliceNavigationController of the slicestack. \sa QmitkSegmentationView.cpp */ class MITK_CORE_EXPORT PlanePositionManagerService { public: PlanePositionManagerService(); ~PlanePositionManagerService(); /** \brief Adds a new plane position to the list. If this geometry is identical to one of the list nothing will be added \a plane THE FIRST! slice of the slice stack \a sliceIndex the slice number of the selected slice \return returns the ID i.e. the position in the positionlist. If the PlaneGeometry which is to be added already exists the existing ID will be returned. */ - unsigned int AddNewPlanePosition(const mitk::Geometry2D* plane, unsigned int sliceIndex = 0); + unsigned int AddNewPlanePosition(const mitk::PlaneGeometry* plane, unsigned int sliceIndex = 0); /** \brief Removes the plane at the position \a ID from the list. \a ID the plane ID which should be removed, i.e. its position in the list \return true if the plane was removed successfully and false if it is an invalid ID */ bool RemovePlanePosition(unsigned int ID); /// \brief Clears the complete positionlist void RemoveAllPlanePositions(); /** \brief Getter for a specific plane position with a given ID \a ID the ID of the plane position \return Returns a RestorePlanePositionOperation which can be executed by th SliceNavigationController or NULL for an invalid ID */ mitk::RestorePlanePositionOperation* GetPlanePosition( unsigned int ID); /// \brief Getting the number of all stored planes unsigned int GetNumberOfPlanePositions(); private: // Disable copy constructor and assignment operator. PlanePositionManagerService(const PlanePositionManagerService&); PlanePositionManagerService& operator=(const PlanePositionManagerService&); std::vector m_PositionList; }; } US_DECLARE_SERVICE_INTERFACE(mitk::PlanePositionManagerService, "org.mitk.PlanePositionManagerService") #endif diff --git a/Core/Code/Controllers/mitkSliceNavigationController.cpp b/Core/Code/Controllers/mitkSliceNavigationController.cpp index 3af2fc64f9..b1fd535666 100644 --- a/Core/Code/Controllers/mitkSliceNavigationController.cpp +++ b/Core/Code/Controllers/mitkSliceNavigationController.cpp @@ -1,840 +1,840 @@ /*=================================================================== 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 "mitkSliceNavigationController.h" #include "mitkBaseRenderer.h" #include "mitkSlicedGeometry3D.h" #include "mitkPlaneGeometry.h" #include "mitkOperation.h" #include "mitkOperationActor.h" #include "mitkStateEvent.h" #include "mitkCrosshairPositionEvent.h" #include "mitkPositionEvent.h" #include "mitkProportionalTimeGeometry.h" #include "mitkInteractionConst.h" #include "mitkAction.h" #include "mitkGlobalInteraction.h" #include "mitkEventMapper.h" #include "mitkFocusManager.h" #include "mitkVtkPropRenderer.h" #include "mitkRenderingManager.h" #include "mitkInteractionConst.h" #include "mitkPointOperation.h" #include "mitkPlaneOperation.h" #include "mitkUndoController.h" #include "mitkOperationEvent.h" #include "mitkNodePredicateDataType.h" #include "mitkStatusBar.h" #include "mitkApplyTransformMatrixOperation.h" #include "mitkMemoryUtilities.h" #include namespace mitk { SliceNavigationController::SliceNavigationController( const char *type ) : BaseController( type ), m_InputWorldGeometry3D( NULL ), m_InputWorldTimeGeometry( NULL ), m_CreatedWorldGeometry( NULL ), m_ViewDirection( Axial ), m_DefaultViewDirection( Axial ), m_RenderingManager( NULL ), m_Renderer( NULL ), m_Top( false ), m_FrontSide( false ), m_Rotated( false ), m_BlockUpdate( false ), m_SliceLocked( false ), m_SliceRotationLocked( false ), m_OldPos(0) { typedef itk::SimpleMemberCommand< SliceNavigationController > SNCCommandType; SNCCommandType::Pointer sliceStepperChangedCommand, timeStepperChangedCommand; sliceStepperChangedCommand = SNCCommandType::New(); timeStepperChangedCommand = SNCCommandType::New(); sliceStepperChangedCommand->SetCallbackFunction( this, &SliceNavigationController::SendSlice ); timeStepperChangedCommand->SetCallbackFunction( this, &SliceNavigationController::SendTime ); m_Slice->AddObserver( itk::ModifiedEvent(), sliceStepperChangedCommand ); m_Time->AddObserver( itk::ModifiedEvent(), timeStepperChangedCommand ); m_Slice->SetUnitName( "mm" ); m_Time->SetUnitName( "ms" ); m_Top = false; m_FrontSide = false; m_Rotated = false; } SliceNavigationController::~SliceNavigationController() { } void SliceNavigationController::SetInputWorldGeometry3D( const BaseGeometry *geometry ) { if ( geometry != NULL ) { if ( const_cast< BoundingBox * >( geometry->GetBoundingBox()) ->GetDiagonalLength2() < eps ) { itkWarningMacro( "setting an empty bounding-box" ); geometry = NULL; } } if ( m_InputWorldGeometry3D != geometry ) { m_InputWorldGeometry3D = geometry; m_InputWorldTimeGeometry = NULL; this->Modified(); } } void SliceNavigationController::SetInputWorldTimeGeometry( const TimeGeometry *geometry ) { if ( geometry != NULL ) { if ( const_cast< BoundingBox * >( geometry->GetBoundingBoxInWorld()) ->GetDiagonalLength2() < eps ) { itkWarningMacro( "setting an empty bounding-box" ); geometry = NULL; } } if ( m_InputWorldTimeGeometry != geometry ) { m_InputWorldTimeGeometry = geometry; m_InputWorldGeometry3D = NULL; this->Modified(); } } RenderingManager * SliceNavigationController::GetRenderingManager() const { mitk::RenderingManager* renderingManager = m_RenderingManager.GetPointer(); if (renderingManager != NULL) return renderingManager; if ( m_Renderer != NULL ) { renderingManager = m_Renderer->GetRenderingManager(); if (renderingManager != NULL) return renderingManager; } return mitk::RenderingManager::GetInstance(); } void SliceNavigationController::SetViewDirectionToDefault() { m_ViewDirection = m_DefaultViewDirection; } const char* SliceNavigationController::GetViewDirectionAsString() { const char* viewDirectionString; switch(m_ViewDirection) { case 0: viewDirectionString = "Axial"; break; case 1: viewDirectionString = "Sagittal"; break; case 2: viewDirectionString = "Frontal"; break; case 3: viewDirectionString = "Original"; break; default: viewDirectionString = "No View Direction Available"; break; } return viewDirectionString; } void SliceNavigationController::Update() { if ( !m_BlockUpdate ) { if ( m_ViewDirection == Axial ) { this->Update( Axial, false, false, true ); } else { this->Update( m_ViewDirection ); } } } void SliceNavigationController::Update( SliceNavigationController::ViewDirection viewDirection, bool top, bool frontside, bool rotated ) { TimeGeometry::ConstPointer worldTimeGeometry = m_InputWorldTimeGeometry; if( m_BlockUpdate || ( m_InputWorldTimeGeometry.IsNull() && m_InputWorldGeometry3D.IsNull() ) || ( (worldTimeGeometry.IsNotNull()) && (worldTimeGeometry->CountTimeSteps() == 0) ) ) { return; } m_BlockUpdate = true; if ( m_InputWorldTimeGeometry.IsNotNull() && m_LastUpdateTime < m_InputWorldTimeGeometry->GetMTime() ) { Modified(); } if ( m_InputWorldGeometry3D.IsNotNull() && m_LastUpdateTime < m_InputWorldGeometry3D->GetMTime() ) { Modified(); } this->SetViewDirection( viewDirection ); this->SetTop( top ); this->SetFrontSide( frontside ); this->SetRotated( rotated ); if ( m_LastUpdateTime < GetMTime() ) { m_LastUpdateTime = GetMTime(); // initialize the viewplane SlicedGeometry3D::Pointer slicedWorldGeometry = NULL; BaseGeometry::ConstPointer currentGeometry = NULL; if (m_InputWorldTimeGeometry.IsNotNull()) if (m_InputWorldTimeGeometry->IsValidTimeStep(GetTime()->GetPos())) currentGeometry = m_InputWorldTimeGeometry->GetGeometryForTimeStep(GetTime()->GetPos()); else currentGeometry = m_InputWorldTimeGeometry->GetGeometryForTimeStep(0); else currentGeometry = m_InputWorldGeometry3D; m_CreatedWorldGeometry = NULL; switch ( viewDirection ) { case Original: if ( worldTimeGeometry.IsNotNull()) { m_CreatedWorldGeometry = worldTimeGeometry->Clone(); worldTimeGeometry = m_CreatedWorldGeometry.GetPointer(); slicedWorldGeometry = dynamic_cast< SlicedGeometry3D * >( m_CreatedWorldGeometry->GetGeometryForTimeStep( this->GetTime()->GetPos() ).GetPointer() ); if ( slicedWorldGeometry.IsNotNull() ) { break; } } else { const SlicedGeometry3D *worldSlicedGeometry = dynamic_cast< const SlicedGeometry3D * >( currentGeometry.GetPointer()); if ( worldSlicedGeometry != NULL ) { slicedWorldGeometry = static_cast< SlicedGeometry3D * >( currentGeometry->Clone().GetPointer()); break; } } //else: use Axial: no "break" here!! case Axial: slicedWorldGeometry = SlicedGeometry3D::New(); slicedWorldGeometry->InitializePlanes( currentGeometry, PlaneGeometry::Axial, top, frontside, rotated ); slicedWorldGeometry->SetSliceNavigationController( this ); break; case Frontal: slicedWorldGeometry = SlicedGeometry3D::New(); slicedWorldGeometry->InitializePlanes( currentGeometry, PlaneGeometry::Frontal, top, frontside, rotated ); slicedWorldGeometry->SetSliceNavigationController( this ); break; case Sagittal: slicedWorldGeometry = SlicedGeometry3D::New(); slicedWorldGeometry->InitializePlanes( currentGeometry, PlaneGeometry::Sagittal, top, frontside, rotated ); slicedWorldGeometry->SetSliceNavigationController( this ); break; default: itkExceptionMacro("unknown ViewDirection"); } m_Slice->SetPos( 0 ); m_Slice->SetSteps( (int)slicedWorldGeometry->GetSlices() ); if ( m_CreatedWorldGeometry.IsNull() ) { // initialize TimeGeometry m_CreatedWorldGeometry = ProportionalTimeGeometry::New(); } if ( worldTimeGeometry.IsNull()) { m_CreatedWorldGeometry = ProportionalTimeGeometry::New(); dynamic_cast(m_CreatedWorldGeometry.GetPointer())->Initialize(slicedWorldGeometry, 1); m_Time->SetSteps( 0 ); m_Time->SetPos( 0 ); m_Time->InvalidateRange(); } else { m_BlockUpdate = true; m_Time->SetSteps( worldTimeGeometry->CountTimeSteps() ); m_Time->SetPos( 0 ); const TimeBounds &timeBounds = worldTimeGeometry->GetTimeBounds(); m_Time->SetRange( timeBounds[0], timeBounds[1] ); m_BlockUpdate = false; assert( worldTimeGeometry->GetGeometryForTimeStep( this->GetTime()->GetPos() ).IsNotNull() ); slicedWorldGeometry->SetTimeBounds( worldTimeGeometry->GetGeometryForTimeStep( this->GetTime()->GetPos() )->GetTimeBounds() ); //@todo implement for non-evenly-timed geometry! m_CreatedWorldGeometry = ProportionalTimeGeometry::New(); dynamic_cast(m_CreatedWorldGeometry.GetPointer())->Initialize(slicedWorldGeometry, worldTimeGeometry->CountTimeSteps()); } } // unblock update; we may do this now, because if m_BlockUpdate was already // true before this method was entered, then we will never come here. m_BlockUpdate = false; // Send the geometry. Do this even if nothing was changed, because maybe // Update() was only called to re-send the old geometry and time/slice data. this->SendCreatedWorldGeometry(); this->SendSlice(); this->SendTime(); // Adjust the stepper range of slice stepper according to geometry this->AdjustSliceStepperRange(); } void SliceNavigationController::SendCreatedWorldGeometry() { // Send the geometry. Do this even if nothing was changed, because maybe // Update() was only called to re-send the old geometry. if ( !m_BlockUpdate ) { this->InvokeEvent( GeometrySendEvent(m_CreatedWorldGeometry, 0) ); } } void SliceNavigationController::SendCreatedWorldGeometryUpdate() { if ( !m_BlockUpdate ) { this->InvokeEvent( GeometryUpdateEvent(m_CreatedWorldGeometry, m_Slice->GetPos()) ); } } void SliceNavigationController::SendSlice() { if ( !m_BlockUpdate ) { if ( m_CreatedWorldGeometry.IsNotNull() ) { this->InvokeEvent( GeometrySliceEvent(m_CreatedWorldGeometry, m_Slice->GetPos()) ); // send crosshair event crosshairPositionEvent.Send(); // Request rendering update for all views this->GetRenderingManager()->RequestUpdateAll(); } } } void SliceNavigationController::SendTime() { if ( !m_BlockUpdate ) { if ( m_CreatedWorldGeometry.IsNotNull() ) { this->InvokeEvent( GeometryTimeEvent(m_CreatedWorldGeometry, m_Time->GetPos()) ); // Request rendering update for all views this->GetRenderingManager()->RequestUpdateAll(); } } } void SliceNavigationController::SetGeometry( const itk::EventObject & ) { } void SliceNavigationController ::SetGeometryTime( const itk::EventObject &geometryTimeEvent ) { const SliceNavigationController::GeometryTimeEvent *timeEvent = dynamic_cast< const SliceNavigationController::GeometryTimeEvent * >( &geometryTimeEvent); assert( timeEvent != NULL ); TimeGeometry *timeGeometry = timeEvent->GetTimeGeometry(); assert( timeGeometry != NULL ); if ( m_CreatedWorldGeometry.IsNotNull() ) { int timeStep = (int) timeEvent->GetPos(); ScalarType timeInMS; timeInMS = timeGeometry->TimeStepToTimePoint( timeStep ); timeStep = m_CreatedWorldGeometry->TimePointToTimeStep( timeInMS ); this->GetTime()->SetPos( timeStep ); } } void SliceNavigationController ::SetGeometrySlice(const itk::EventObject & geometrySliceEvent) { const SliceNavigationController::GeometrySliceEvent* sliceEvent = dynamic_cast( &geometrySliceEvent); assert(sliceEvent!=NULL); this->GetSlice()->SetPos(sliceEvent->GetPos()); } void SliceNavigationController::SelectSliceByPoint( const Point3D &point ) { //@todo add time to PositionEvent and use here!! SlicedGeometry3D* slicedWorldGeometry = dynamic_cast< SlicedGeometry3D * >( m_CreatedWorldGeometry->GetGeometryForTimeStep( this->GetTime()->GetPos() ).GetPointer() ); if ( slicedWorldGeometry ) { int bestSlice = -1; double bestDistance = itk::NumericTraits::max(); int s, slices; slices = slicedWorldGeometry->GetSlices(); if ( slicedWorldGeometry->GetEvenlySpaced() ) { - mitk::Geometry2D *plane = slicedWorldGeometry->GetGeometry2D( 0 ); + mitk::PlaneGeometry *plane = slicedWorldGeometry->GetGeometry2D( 0 ); const Vector3D &direction = slicedWorldGeometry->GetDirectionVector(); Point3D projectedPoint; plane->Project( point, projectedPoint ); // Check whether the point is somewhere within the slice stack volume; // otherwise, the defualt slice (0) will be selected if ( direction[0] * (point[0] - projectedPoint[0]) + direction[1] * (point[1] - projectedPoint[1]) + direction[2] * (point[2] - projectedPoint[2]) >= 0 ) { bestSlice = (int)(plane->Distance( point ) / slicedWorldGeometry->GetSpacing()[2] + 0.5); } } else { Point3D projectedPoint; for ( s = 0; s < slices; ++s ) { slicedWorldGeometry->GetGeometry2D( s )->Project( point, projectedPoint ); Vector3D distance = projectedPoint - point; ScalarType currentDistance = distance.GetSquaredNorm(); if ( currentDistance < bestDistance ) { bestDistance = currentDistance; bestSlice = s; } } } if ( bestSlice >= 0 ) { this->GetSlice()->SetPos( bestSlice ); } else { this->GetSlice()->SetPos( 0 ); } this->SendCreatedWorldGeometryUpdate(); } } void SliceNavigationController::ReorientSlices( const Point3D &point, const Vector3D &normal ) { PlaneOperation op( OpORIENT, point, normal ); m_CreatedWorldGeometry->ExecuteOperation( &op ); this->SendCreatedWorldGeometryUpdate(); } void SliceNavigationController::ReorientSlices(const mitk::Point3D &point, const mitk::Vector3D &axisVec0, const mitk::Vector3D &axisVec1 ) { PlaneOperation op( OpORIENT, point, axisVec0, axisVec1 ); m_CreatedWorldGeometry->ExecuteOperation( &op ); this->SendCreatedWorldGeometryUpdate(); } mitk::TimeGeometry * SliceNavigationController::GetCreatedWorldGeometry() { return m_CreatedWorldGeometry; } const mitk::BaseGeometry * SliceNavigationController::GetCurrentGeometry3D() { if ( m_CreatedWorldGeometry.IsNotNull() ) { return m_CreatedWorldGeometry->GetGeometryForTimeStep( this->GetTime()->GetPos() ); } else { return NULL; } } const mitk::PlaneGeometry * SliceNavigationController::GetCurrentPlaneGeometry() { const mitk::SlicedGeometry3D *slicedGeometry = dynamic_cast< const mitk::SlicedGeometry3D * > ( this->GetCurrentGeometry3D() ); if ( slicedGeometry ) { const mitk::PlaneGeometry *planeGeometry = dynamic_cast< mitk::PlaneGeometry * > ( slicedGeometry->GetGeometry2D(this->GetSlice()->GetPos()) ); return planeGeometry; } else { return NULL; } } void SliceNavigationController::SetRenderer( BaseRenderer *renderer ) { m_Renderer = renderer; } BaseRenderer * SliceNavigationController::GetRenderer() const { return m_Renderer; } void SliceNavigationController::AdjustSliceStepperRange() { const mitk::SlicedGeometry3D *slicedGeometry = dynamic_cast< const mitk::SlicedGeometry3D * > ( this->GetCurrentGeometry3D() ); const Vector3D &direction = slicedGeometry->GetDirectionVector(); int c = 0; int i, k = 0; for ( i = 0; i < 3; ++i ) { if ( fabs(direction[i]) < 0.000000001 ) { ++c; } else { k = i; } } if ( c == 2 ) { ScalarType min = slicedGeometry->GetOrigin()[k]; ScalarType max = min + slicedGeometry->GetExtentInMM( k ); m_Slice->SetRange( min, max ); } else { m_Slice->InvalidateRange(); } } void SliceNavigationController::ExecuteOperation( Operation *operation ) { // switch on type // - select best slice for a given point // - rotate created world geometry according to Operation->SomeInfo() if ( !operation ) { return; } switch ( operation->GetOperationType() ) { case OpMOVE: // should be a point operation { if ( !m_SliceLocked ) //do not move the cross position { // select a slice PointOperation *po = dynamic_cast< PointOperation * >( operation ); if ( po && po->GetIndex() == -1 ) { this->SelectSliceByPoint( po->GetPoint() ); } else if ( po && po->GetIndex() != -1 ) // undo case because index != -1, index holds the old position of this slice { this->GetSlice()->SetPos( po->GetIndex() ); } } break; } case OpRESTOREPLANEPOSITION: { m_CreatedWorldGeometry->ExecuteOperation( operation ); this->SendCreatedWorldGeometryUpdate(); break; } case OpAPPLYTRANSFORMMATRIX: { m_CreatedWorldGeometry->ExecuteOperation( operation ); this->SendCreatedWorldGeometryUpdate(); break; } default: { // do nothing break; } } } mitk::DataNode::Pointer SliceNavigationController::GetTopLayerNode(mitk::DataStorage::SetOfObjects::ConstPointer nodes,mitk::Point3D worldposition) { mitk::DataNode::Pointer node; int maxlayer = -32768; bool isHelper (false); if(nodes.IsNotNull()) { for (unsigned int x = 0; x < nodes->size(); x++) { nodes->at(x)->GetBoolProperty("helper object", isHelper); if(nodes->at(x)->GetData()->GetGeometry()->IsInside(worldposition) && isHelper == false) { int layer = 0; if(!(nodes->at(x)->GetIntProperty("layer", layer))) continue; if(layer > maxlayer) { if(static_cast(nodes->at(x))->IsVisible(m_Renderer)) { node = nodes->at(x); maxlayer = layer; } } } } } return node; } // Relict from the old times, when automous decisions were accepted // behavior. Remains in here, because some RenderWindows do exist outside // of StdMultiWidgets. bool SliceNavigationController ::ExecuteAction( Action* action, StateEvent const* stateEvent ) { bool ok = false; const PositionEvent* posEvent = dynamic_cast< const PositionEvent * >( stateEvent->GetEvent() ); if ( posEvent != NULL ) { if ( m_CreatedWorldGeometry.IsNull() ) { return true; } switch (action->GetActionId()) { case AcMOVE: { BaseRenderer *baseRenderer = posEvent->GetSender(); if ( !baseRenderer ) { baseRenderer = const_cast( GlobalInteraction::GetInstance()->GetFocus() ); } if ( baseRenderer ) if ( baseRenderer->GetMapperID() == 1 ) { PointOperation doOp(OpMOVE, posEvent->GetWorldPosition()); this->ExecuteOperation( &doOp ); // If click was performed in this render window than we have to update the status bar information about position and pixel value. if(baseRenderer == m_Renderer) { { std::string statusText; TNodePredicateDataType::Pointer isImageData = TNodePredicateDataType::New(); mitk::DataStorage::SetOfObjects::ConstPointer nodes = baseRenderer->GetDataStorage()->GetSubset(isImageData).GetPointer(); mitk::Point3D worldposition = posEvent->GetWorldPosition(); //int maxlayer = -32768; mitk::Image::Pointer image3D; mitk::DataNode::Pointer node; mitk::DataNode::Pointer topSourceNode; bool isBinary (false); node = this->GetTopLayerNode(nodes,worldposition); if(node.IsNotNull()) { node->GetBoolProperty("binary", isBinary); if(isBinary) { mitk::DataStorage::SetOfObjects::ConstPointer sourcenodes = baseRenderer->GetDataStorage()->GetSources(node, NULL, true); if(!sourcenodes->empty()) { topSourceNode = this->GetTopLayerNode(sourcenodes,worldposition); } if(topSourceNode.IsNotNull()) { image3D = dynamic_cast(topSourceNode->GetData()); } else { image3D = dynamic_cast(node->GetData()); } } else { image3D = dynamic_cast(node->GetData()); } } std::stringstream stream; stream.imbue(std::locale::classic()); // get the position and gray value from the image and build up status bar text if(image3D.IsNotNull()) { Index3D p; image3D->GetGeometry()->WorldToIndex(worldposition, p); stream.precision(2); stream<<"Position: <" << std::fixed < mm"; stream<<"; Index: <"< "; mitk::ScalarType pixelValue = image3D->GetPixelValueByIndex(p, baseRenderer->GetTimeStep()); if (fabs(pixelValue)>1000000 || fabs(pixelValue) < 0.01) { stream<<"; Time: " << baseRenderer->GetTime() << " ms; Pixelvalue: " << std::scientific<< pixelValue <<" "; } else { stream<<"; Time: " << baseRenderer->GetTime() << " ms; Pixelvalue: "<< pixelValue <<" "; } } else { stream << "No image information at this position!"; } statusText = stream.str(); mitk::StatusBar::GetInstance()->DisplayGreyValueText(statusText.c_str()); } } ok = true; break; } } default: ok = true; break; } return ok; } const DisplayPositionEvent *displPosEvent = dynamic_cast< const DisplayPositionEvent * >( stateEvent->GetEvent() ); if ( displPosEvent != NULL ) { return true; } return false; } } // namespace diff --git a/Core/Code/Controllers/mitkSliceNavigationController.h b/Core/Code/Controllers/mitkSliceNavigationController.h index a1b3169413..d41cb8831c 100644 --- a/Core/Code/Controllers/mitkSliceNavigationController.h +++ b/Core/Code/Controllers/mitkSliceNavigationController.h @@ -1,605 +1,605 @@ /*=================================================================== 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 SLICENAVIGATIONCONTROLLER_H_HEADER_INCLUDED_C1C55A2F #define SLICENAVIGATIONCONTROLLER_H_HEADER_INCLUDED_C1C55A2F #include #include "mitkBaseController.h" #include "mitkRenderingManager.h" #include "mitkTimeGeometry.h" #include "mitkMessage.h" #pragma GCC visibility push(default) #include #pragma GCC visibility pop #include #include #include "mitkRestorePlanePositionOperation.h" #include "mitkDataStorage.h" //DEPRECATED #include namespace mitk { #define mitkTimeSlicedGeometryEventMacro( classname , super ) \ class MITK_CORE_EXPORT DEPRECATED(classname) : public super { \ public: \ typedef classname Self; \ typedef super Superclass; \ classname(TimeGeometry* aTimeGeometry, unsigned int aPos) \ : Superclass(aTimeGeometry, aPos) {} \ virtual ~classname() {} \ virtual const char * GetEventName() const { return #classname; } \ virtual bool CheckEvent(const ::itk::EventObject* e) const \ { return dynamic_cast(e); } \ virtual ::itk::EventObject* MakeObject() const \ { return new Self(GetTimeGeometry(), GetPos()); } \ private: \ void operator=(const Self&); \ } #define mitkTimeGeometryEventMacro( classname , super ) \ class MITK_CORE_EXPORT classname : public super { \ public: \ typedef classname Self; \ typedef super Superclass; \ classname(TimeGeometry* aTimeGeometry, unsigned int aPos) \ : Superclass(aTimeGeometry, aPos) {} \ virtual ~classname() {} \ virtual const char * GetEventName() const { return #classname; } \ virtual bool CheckEvent(const ::itk::EventObject* e) const \ { return dynamic_cast(e); } \ virtual ::itk::EventObject* MakeObject() const \ { return new Self(GetTimeGeometry(), GetPos()); } \ private: \ void operator=(const Self&); \ } class PlaneGeometry; class BaseGeometry; class BaseRenderer; /** * \brief Controls the selection of the slice the associated BaseRenderer * will display * * A SliceNavigationController takes a Geometry3D or a TimeGeometry as input world geometry * (TODO what are the exact requirements?) and generates a TimeGeometry * as output. The TimeGeometry holds a number of SlicedGeometry3Ds and * these in turn hold a series of Geometry2Ds. One of these Geometry2Ds is * selected as world geometry for the BaseRenderers associated to 2D views. * * The SliceNavigationController holds has Steppers (one for the slice, a * second for the time step), which control the selection of a single - * Geometry2D from the TimeGeometry. SliceNavigationController generates + * PlaneGeometry from the TimeGeometry. SliceNavigationController generates * ITK events to tell observers, like a BaseRenderer, when the selected slice * or timestep changes. * * SliceNavigationControllers are registered as listeners to GlobalInteraction * by the QmitkStdMultiWidget. In ExecuteAction, the controllers react to * PositionEvents by setting the steppers to the slice which is nearest to the * point of the PositionEvent. * * Example: * \code * // Initialization * sliceCtrl = mitk::SliceNavigationController::New(); * * // Tell the navigator the geometry to be sliced (with geometry a * // Geometry3D::ConstPointer) * sliceCtrl->SetInputWorldGeometry(geometry.GetPointer()); * * // Tell the navigator in which direction it shall slice the data * sliceCtrl->SetViewDirection(mitk::SliceNavigationController::Axial); * * // Connect one or more BaseRenderer to this navigator, i.e.: events sent * // by the navigator when stepping through the slices (e.g. by * // sliceCtrl->GetSlice()->Next()) will be received by the BaseRenderer * // (in this example only slice-changes, see also ConnectGeometryTimeEvent * // and ConnectGeometryEvents.) * sliceCtrl->ConnectGeometrySliceEvent(renderer.GetPointer()); * * //create a world geometry and send the information to the connected renderer(s) * sliceCtrl->Update(); * \endcode * * * You can connect visible navigators to a SliceNavigationController, e.g., a * QmitkSliderNavigator (for Qt): * * \code * // Create the visible navigator (a slider with a spin-box) * QmitkSliderNavigator* navigator = * new QmitkSliderNavigator(parent, "slidernavigator"); * * // Connect the navigator to the slice-stepper of the * // SliceNavigationController. For initialization (position, mininal and * // maximal values) the values of the SliceNavigationController are used. * // Thus, accessing methods of a navigator is normally not necessary, since * // everything can be set via the (Qt-independent) SliceNavigationController. * // The QmitkStepperAdapter converts the Qt-signals to Qt-independent * // itk-events. * new QmitkStepperAdapter(navigator, sliceCtrl->GetSlice(), "navigatoradaptor"); * \endcode * * If you do not want that all renderwindows are updated when a new slice is * selected, you can use a specific RenderingManager, which updates only those * renderwindows that should be updated. This is sometimes useful when a 3D view * does not need to be updated when the slices in some 2D views are changed. * QmitkSliderNavigator (for Qt): * * \code * // create a specific RenderingManager * mitk::RenderingManager::Pointer myManager = mitk::RenderingManager::New(); * * // tell the RenderingManager to update only renderwindow1 and renderwindow2 * myManager->AddRenderWindow(renderwindow1); * myManager->AddRenderWindow(renderwindow2); * * // tell the SliceNavigationController of renderwindow1 and renderwindow2 * // to use the specific RenderingManager instead of the global one * renderwindow1->GetSliceNavigationController()->SetRenderingManager(myManager); * renderwindow2->GetSliceNavigationController()->SetRenderingManager(myManager); * \endcode * * \todo implement for non-evenly-timed geometry! * \ingroup NavigationControl */ class MITK_CORE_EXPORT SliceNavigationController : public BaseController { public: mitkClassMacro(SliceNavigationController,BaseController); itkNewMacro(Self); mitkNewMacro1Param(Self, const char *); /** * \brief Possible view directions, \a Original will uses - * the Geometry2D instances in a SlicedGeometry3D provided + * the PlaneGeometry instances in a SlicedGeometry3D provided * as input world geometry (by SetInputWorldGeometry). */ enum ViewDirection { #ifdef _MSC_VER Transversal, // deprecated #endif Axial = 0, Sagittal = 1, Frontal = 2, Original }; #ifdef __GNUC__ __attribute__ ((deprecated)) static const ViewDirection Transversal = ViewDirection(Axial); #endif /** * \brief Set the input world geometry3D out of which the * geometries for slicing will be created. * * Any previous previous set input geometry (3D or Time) will * be ignored in future. */ void SetInputWorldGeometry3D(const mitk::BaseGeometry* geometry); itkGetConstObjectMacro(InputWorldGeometry3D, mitk::BaseGeometry); /** * \brief Set the input world geometry3D out of which the * geometries for slicing will be created. * * Any previous previous set input geometry (3D or Time) will * be ignored in future. * \deprecatedSince{2013_09} Please use TimeGeometry instead of TimeSlicedGeometry. For more information see http://www.mitk.org/Development/Refactoring%20of%20the%20Geometry%20Classes%20-%20Part%201 */ DEPRECATED(void SetInputWorldGeometry(const mitk::TimeSlicedGeometry* geometry)); /** * \deprecatedSince{2013_09} Please use TimeGeometry instead of TimeSlicedGeometry. For more information see http://www.mitk.org/Development/Refactoring%20of%20the%20Geometry%20Classes%20-%20Part%201 */ DEPRECATED(TimeSlicedGeometry* GetInputWorldGeometry()); void SetInputWorldTimeGeometry(const mitk::TimeGeometry* geometry); itkGetConstObjectMacro(InputWorldTimeGeometry, mitk::TimeGeometry); /** * \brief Access the created geometry */ itkGetConstObjectMacro(CreatedWorldGeometry, mitk::TimeGeometry); /** * \brief Set the desired view directions * * \sa ViewDirection * \sa Update(ViewDirection viewDirection, bool top = true, * bool frontside = true, bool rotated = false) */ itkSetEnumMacro(ViewDirection, ViewDirection); itkGetEnumMacro(ViewDirection, ViewDirection); /** * \brief Set the default view direction * * This is used to re-initialize the view direction of the SNC to the * default value with SetViewDirectionToDefault() * * \sa ViewDirection * \sa Update(ViewDirection viewDirection, bool top = true, * bool frontside = true, bool rotated = false) */ itkSetEnumMacro(DefaultViewDirection, ViewDirection); itkGetEnumMacro(DefaultViewDirection, ViewDirection); const char* GetViewDirectionAsString(); virtual void SetViewDirectionToDefault(); /** * \brief Do the actual creation and send it to the connected * observers (renderers) * */ virtual void Update(); /** * \brief Extended version of Update, additionally allowing to * specify the direction/orientation of the created geometry. * */ virtual void Update(ViewDirection viewDirection, bool top = true, bool frontside = true, bool rotated = false); /** * \brief Send the created geometry to the connected * observers (renderers) * * Called by Update(). */ virtual void SendCreatedWorldGeometry(); /** * \brief Tell observers to re-read the currently selected 2D geometry * * Called by mitk::SlicesRotator during rotation. */ virtual void SendCreatedWorldGeometryUpdate(); /** * \brief Send the currently selected slice to the connected * observers (renderers) * * Called by Update(). */ virtual void SendSlice(); /** * \brief Send the currently selected time to the connected * observers (renderers) * * Called by Update(). */ virtual void SendTime(); /** * \brief Set the RenderingManager to be used * * If \a NULL, the default RenderingManager will be used. */ itkSetObjectMacro(RenderingManager, RenderingManager); mitk::RenderingManager* GetRenderingManager() const; #pragma GCC visibility push(default) itkEventMacro( UpdateEvent, itk::AnyEvent ); #pragma GCC visibility pop class MITK_CORE_EXPORT TimeGeometryEvent : public itk::AnyEvent { public: typedef TimeGeometryEvent Self; typedef itk::AnyEvent Superclass; TimeGeometryEvent( TimeGeometry* aTimeGeometry, unsigned int aPos) : m_TimeGeometry(aTimeGeometry), m_Pos(aPos) {} virtual ~TimeGeometryEvent() {} virtual const char * GetEventName() const { return "TimeGeometryEvent"; } virtual bool CheckEvent(const ::itk::EventObject* e) const { return dynamic_cast(e); } virtual ::itk::EventObject* MakeObject() const { return new Self(m_TimeGeometry, m_Pos); } TimeGeometry* GetTimeGeometry() const { return m_TimeGeometry; } unsigned int GetPos() const { return m_Pos; } private: TimeGeometry::Pointer m_TimeGeometry; unsigned int m_Pos; // TimeGeometryEvent(const Self&); void operator=(const Self&); //just hide }; /** * \deprecatedSince{2013_09} Please use TimeGeometryEvent instead: For additional information see http://www.mitk.org/Development/Refactoring%20of%20the%20Geometry%20Classes%20-%20Part%201 */ DEPRECATED(typedef TimeGeometryEvent TimeSlicedGeometryEvent); mitkTimeGeometryEventMacro( GeometrySendEvent,TimeGeometryEvent ); mitkTimeGeometryEventMacro( GeometryUpdateEvent, TimeGeometryEvent ); mitkTimeGeometryEventMacro( GeometryTimeEvent, TimeGeometryEvent ); mitkTimeGeometryEventMacro( GeometrySliceEvent, TimeGeometryEvent ); template void ConnectGeometrySendEvent(T* receiver) { typedef typename itk::ReceptorMemberCommand::Pointer ReceptorMemberCommandPointer; ReceptorMemberCommandPointer eventReceptorCommand = itk::ReceptorMemberCommand::New(); eventReceptorCommand->SetCallbackFunction(receiver, &T::SetGeometry); unsigned long tag = AddObserver(GeometrySendEvent(NULL,0), eventReceptorCommand); m_ReceiverToObserverTagsMap[static_cast(receiver)].push_back(tag); } template void ConnectGeometryUpdateEvent(T* receiver) { typedef typename itk::ReceptorMemberCommand::Pointer ReceptorMemberCommandPointer; ReceptorMemberCommandPointer eventReceptorCommand = itk::ReceptorMemberCommand::New(); eventReceptorCommand->SetCallbackFunction(receiver, &T::UpdateGeometry); unsigned long tag = AddObserver(GeometryUpdateEvent(NULL,0), eventReceptorCommand); m_ReceiverToObserverTagsMap[static_cast(receiver)].push_back(tag); } template void ConnectGeometrySliceEvent(T* receiver, bool connectSendEvent=true) { typedef typename itk::ReceptorMemberCommand::Pointer ReceptorMemberCommandPointer; ReceptorMemberCommandPointer eventReceptorCommand = itk::ReceptorMemberCommand::New(); eventReceptorCommand->SetCallbackFunction(receiver, &T::SetGeometrySlice); unsigned long tag = AddObserver(GeometrySliceEvent(NULL,0), eventReceptorCommand); m_ReceiverToObserverTagsMap[static_cast(receiver)].push_back(tag); if(connectSendEvent) ConnectGeometrySendEvent(receiver); } template void ConnectGeometryTimeEvent(T* receiver, bool connectSendEvent=true) { typedef typename itk::ReceptorMemberCommand::Pointer ReceptorMemberCommandPointer; ReceptorMemberCommandPointer eventReceptorCommand = itk::ReceptorMemberCommand::New(); eventReceptorCommand->SetCallbackFunction(receiver, &T::SetGeometryTime); unsigned long tag = AddObserver(GeometryTimeEvent(NULL,0), eventReceptorCommand); m_ReceiverToObserverTagsMap[static_cast(receiver)].push_back(tag); if(connectSendEvent) ConnectGeometrySendEvent(receiver); } template void ConnectGeometryEvents(T* receiver) { //connect sendEvent only once ConnectGeometrySliceEvent(receiver, false); ConnectGeometryTimeEvent(receiver); } // use a templated method to get the right offset when casting to void* template void Disconnect(T* receiver) { ObserverTagsMapType::iterator i = m_ReceiverToObserverTagsMap.find(static_cast(receiver)); if (i == m_ReceiverToObserverTagsMap.end()) return; const std::list& tags = i->second; for (std::list::const_iterator tagIter = tags.begin(); tagIter != tags.end(); ++tagIter) { RemoveObserver(*tagIter); } m_ReceiverToObserverTagsMap.erase(i); } Message<> crosshairPositionEvent; /** * \brief To connect multiple SliceNavigationController, we can * act as an observer ourselves: implemented interface * \warning not implemented */ virtual void SetGeometry(const itk::EventObject & geometrySliceEvent); /** * \brief To connect multiple SliceNavigationController, we can * act as an observer ourselves: implemented interface */ virtual void SetGeometrySlice(const itk::EventObject & geometrySliceEvent); /** * \brief To connect multiple SliceNavigationController, we can * act as an observer ourselves: implemented interface */ virtual void SetGeometryTime(const itk::EventObject & geometryTimeEvent); /** \brief Positions the SNC according to the specified point */ void SelectSliceByPoint( const mitk::Point3D &point ); /** \brief Returns the TimeGeometry created by the SNC. */ mitk::TimeGeometry *GetCreatedWorldGeometry(); /** \brief Returns the Geometry3D of the currently selected time step. */ const mitk::BaseGeometry *GetCurrentGeometry3D(); /** \brief Returns the currently selected Plane in the current * Geometry3D (if existent). */ const mitk::PlaneGeometry *GetCurrentPlaneGeometry(); /** \brief Sets the BaseRenderer associated with this SNC (if any). While * the BaseRenderer is not directly used by SNC, this is a convenience * method to enable BaseRenderer access via the SNC. */ void SetRenderer( BaseRenderer *renderer ); /** \brief Gets the BaseRenderer associated with this SNC (if any). While * the BaseRenderer is not directly used by SNC, this is a convenience * method to enable BaseRenderer access via the SNC. Returns NULL if no * BaseRenderer has been specified*/ BaseRenderer *GetRenderer() const; /** \brief Re-orients the slice stack. All slices will be oriented to the given normal vector. The given point (world coordinates) defines the selected slice. Careful: The resulting axis vectors are not clearly defined this way. If you want to define them clearly, use ReorientSlices (const mitk::Point3D &point, const mitk::Vector3D &axisVec0, const mitk::Vector3D &axisVec1). */ void ReorientSlices( const mitk::Point3D &point, const mitk::Vector3D &normal ); /** \brief Re-orients the slice stack so that all planes are oriented according to the * given axis vectors. The given Point eventually defines selected slice. */ void ReorientSlices( const mitk::Point3D &point, const mitk::Vector3D &axisVec0, const mitk::Vector3D &axisVec1 ); virtual bool ExecuteAction( Action* action, mitk::StateEvent const* stateEvent); void ExecuteOperation(Operation* operation); /** * \brief Feature option to lock planes during mouse interaction. * This option flag disables the mouse event which causes the center * cross to move near by. */ itkSetMacro(SliceLocked, bool); itkGetMacro(SliceLocked, bool); itkBooleanMacro(SliceLocked); /** * \brief Feature option to lock slice rotation. * * This option flag disables separately the rotation of a slice which is * implemented in mitkSliceRotator. */ itkSetMacro(SliceRotationLocked, bool); itkGetMacro(SliceRotationLocked, bool); itkBooleanMacro(SliceRotationLocked); /** * \brief Adjusts the numerical range of the slice stepper according to * the current geometry orientation of this SNC's SlicedGeometry. */ void AdjustSliceStepperRange(); protected: SliceNavigationController(const char * type = NULL); virtual ~SliceNavigationController(); mitk::DataNode::Pointer GetTopLayerNode(mitk::DataStorage::SetOfObjects::ConstPointer nodes,mitk::Point3D worldposition); /* template static void buildstring( mitkIpPicDescriptor *pic, itk::Point p, std::string &s, T = 0) { std::string value; std::stringstream stream; stream.imbue(std::locale::classic()); stream<=0 && p[1] >=0 && p[2]>=0) && (unsigned int)p[0] < pic->n[0] && (unsigned int)p[1] < pic->n[1] && (unsigned int)p[2] < pic->n[2] ) { if(pic->bpe!=24) { stream<<(((T*) pic->data)[ p[0] + p[1]*pic->n[0] + p[2]*pic->n[0]*pic->n[1] ]); } else { stream<<(((T*) pic->data)[p[0]*3 + 0 + p[1]*pic->n[0]*3 + p[2]*pic->n[0]*pic->n[1]*3 ]); stream<<(((T*) pic->data)[p[0]*3 + 1 + p[1]*pic->n[0]*3 + p[2]*pic->n[0]*pic->n[1]*3 ]); stream<<(((T*) pic->data)[p[0]*3 + 2 + p[1]*pic->n[0]*3 + p[2]*pic->n[0]*pic->n[1]*3 ]); } s = stream.str(); } else { s+= "point out of data"; } }; */ mitk::BaseGeometry::ConstPointer m_InputWorldGeometry3D; mitk::TimeGeometry::ConstPointer m_InputWorldTimeGeometry; mitk::TimeGeometry::Pointer m_CreatedWorldGeometry; ViewDirection m_ViewDirection; ViewDirection m_DefaultViewDirection; mitk::RenderingManager::Pointer m_RenderingManager; mitk::BaseRenderer *m_Renderer; itkSetMacro(Top, bool); itkGetMacro(Top, bool); itkBooleanMacro(Top); itkSetMacro(FrontSide, bool); itkGetMacro(FrontSide, bool); itkBooleanMacro(FrontSide); itkSetMacro(Rotated, bool); itkGetMacro(Rotated, bool); itkBooleanMacro(Rotated); bool m_Top; bool m_FrontSide; bool m_Rotated; bool m_BlockUpdate; bool m_SliceLocked; bool m_SliceRotationLocked; unsigned int m_OldPos; typedef std::map > ObserverTagsMapType; ObserverTagsMapType m_ReceiverToObserverTagsMap; }; } // namespace mitk #endif /* SLICENAVIGATIONCONTROLLER_H_HEADER_INCLUDED_C1C55A2F */ diff --git a/Core/Code/Controllers/mitkSlicesRotator.cpp b/Core/Code/Controllers/mitkSlicesRotator.cpp index 92d3480778..b773dedc92 100644 --- a/Core/Code/Controllers/mitkSlicesRotator.cpp +++ b/Core/Code/Controllers/mitkSlicesRotator.cpp @@ -1,512 +1,512 @@ /*=================================================================== 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 #include #include #include #include #include #include #include #include #include #include -#include +#include #include #include #include #include #include #include "rotate_cursor.xpm" namespace mitk { SlicesRotator::Pointer SlicesRotator::New() { return SlicesRotator::New("slices-rotator"); } SlicesRotator::SlicesRotator(const char* machine) : SlicesCoordinator(machine) { // make sure that AcSWITCHON and AcSWITCHOFF are defined int constants somewhere (e.g. mitkInteractionConst.h) CONNECT_ACTION( AcMOVE, DoSelectSlice ); CONNECT_ACTION( AcCHECKPOINT, DoDecideBetweenRotationAndSliceSelection ); CONNECT_ACTION( AcROTATESTART, DoStartRotation ); CONNECT_ACTION( AcROTATE, DoRotationStep ); CONNECT_ACTION( AcROTATEEND, DoEndRotation ); } SlicesRotator::~SlicesRotator() { } void SlicesRotator::OnSliceControllerAdded(SliceNavigationController* snc) { if (!snc) return; snc->ConnectGeometrySendEvent(this); // connects creation of new world geometry to Self::SetGeometry } void SlicesRotator::OnSliceControllerRemoved(SliceNavigationController* snc) { if (!snc) return; // nothing to do, base class does the bookkeeping } /// Is called whenever a SliceNavigationController invokes an event. Will update the list /// of SliceNavigationControllers that can handle rotation void SlicesRotator::SetGeometry(const itk::EventObject& /*EventObject*/) { // there is no way to determine the sender? // ==> update whole list of SNCs UpdateRotatableSNCs(); } void SlicesRotator::RotateToPoint( SliceNavigationController *rotationPlaneSNC, SliceNavigationController *rotatedPlaneSNC, const Point3D &point, bool linked ) { MITK_WARN << "Deprecated function! Use SliceNavigationController::ReorientSlices() instead"; SliceNavigationController *thirdSNC = NULL; SNCVector::iterator iter; for ( iter = m_RotatableSNCs.begin(); iter != m_RotatableSNCs.end(); ++iter ) { if ( ((*iter) != rotationPlaneSNC) && ((*iter) != rotatedPlaneSNC) ) { thirdSNC = *iter; break; } } if ( thirdSNC == NULL ) { return; } const PlaneGeometry *rotationPlane = rotationPlaneSNC->GetCurrentPlaneGeometry(); const PlaneGeometry *rotatedPlane = rotatedPlaneSNC->GetCurrentPlaneGeometry(); const PlaneGeometry *thirdPlane = thirdSNC->GetCurrentPlaneGeometry(); if ( (rotationPlane == NULL) || (rotatedPlane == NULL) || (thirdPlane == NULL) ) { return; } if ( rotatedPlane->DistanceFromPlane( point ) < 0.001 ) { // Skip irrelevant rotations return; } Point3D projectedPoint; Line3D intersection; Point3D rotationCenter; if ( !rotationPlane->Project( point, projectedPoint ) || !rotationPlane->IntersectionLine( rotatedPlane, intersection ) || !thirdPlane->IntersectionPoint( intersection, rotationCenter ) ) { return; } // All pre-requirements are met; execute the rotation Point3D referencePoint = intersection.Project( projectedPoint ); Vector3D toProjected = referencePoint - rotationCenter; Vector3D toCursor = projectedPoint - rotationCenter; // cross product: | A x B | = |A| * |B| * sin(angle) Vector3D axisOfRotation; vnl_vector_fixed< ScalarType, 3 > vnlDirection = vnl_cross_3d( toCursor.GetVnlVector(), toProjected.GetVnlVector() ); axisOfRotation.SetVnlVector( vnlDirection ); // scalar product: A * B = |A| * |B| * cos(angle) // tan = sin / cos ScalarType angle = - atan2( (double)(axisOfRotation.GetNorm()), (double)(toCursor * toProjected) ); angle *= 180.0 / vnl_math::pi; // create RotationOperation and apply to all SNCs that should be rotated RotationOperation op(OpROTATE, rotationCenter, axisOfRotation, angle); if ( !linked ) { BaseRenderer *renderer = rotatedPlaneSNC->GetRenderer(); if ( renderer == NULL ) { return; } DisplayGeometry *displayGeometry = renderer->GetDisplayGeometry(); Point2D point2DWorld, point2DDisplayPre, point2DDisplayPost; displayGeometry->Map( rotationCenter, point2DWorld ); displayGeometry->WorldToDisplay( point2DWorld, point2DDisplayPre ); TimeGeometry *timeGeometry= rotatedPlaneSNC->GetCreatedWorldGeometry(); if ( !timeGeometry ) { return; } timeGeometry->ExecuteOperation( &op ); displayGeometry->Map( rotationCenter, point2DWorld ); displayGeometry->WorldToDisplay( point2DWorld, point2DDisplayPost ); Vector2D vector2DDisplayDiff = point2DDisplayPost - point2DDisplayPre; //Vector2D origin = displayGeometry->GetOriginInMM(); displayGeometry->MoveBy( vector2DDisplayDiff ); rotatedPlaneSNC->SendCreatedWorldGeometryUpdate(); } else { SNCVector::iterator iter; for ( iter = m_RotatableSNCs.begin(); iter != m_RotatableSNCs.end(); ++iter ) { BaseRenderer *renderer = (*iter)->GetRenderer(); if ( renderer == NULL ) { continue; } DisplayGeometry *displayGeometry = renderer->GetDisplayGeometry(); Point2D point2DWorld, point2DDisplayPre, point2DDisplayPost; displayGeometry->Map( rotationCenter, point2DWorld ); displayGeometry->WorldToDisplay( point2DWorld, point2DDisplayPre ); TimeGeometry* timeGeometry = (*iter)->GetCreatedWorldGeometry(); if ( !timeGeometry ) { continue; } timeGeometry->ExecuteOperation( &op ); displayGeometry->Map( rotationCenter, point2DWorld ); displayGeometry->WorldToDisplay( point2DWorld, point2DDisplayPost ); Vector2D vector2DDisplayDiff = point2DDisplayPost - point2DDisplayPre; //Vector2D origin = displayGeometry->GetOriginInMM(); displayGeometry->MoveBy( vector2DDisplayDiff ); (*iter)->SendCreatedWorldGeometryUpdate(); } } } // end RotateToPoint /// Updates the list of SliceNavigationControllers that can handle rotation void SlicesRotator::UpdateRotatableSNCs() { m_RotatableSNCs.clear(); for (SNCVector::iterator iter = m_SliceNavigationControllers.begin(); iter != m_SliceNavigationControllers.end(); ++iter) { const TimeGeometry* timeGeometry = (*iter)->GetCreatedWorldGeometry(); if (!timeGeometry) continue; const SlicedGeometry3D* slicedGeometry = dynamic_cast( timeGeometry->GetGeometryForTimeStep(0).GetPointer() ); if (!slicedGeometry) continue; if (slicedGeometry->IsValidSlice(0)) { // there were some lines of additional checks here in previous versions, // all of which would always evaluate to true, so the check was irrelevant. // Since the original intent was not documented, I removed all checks, // i.e. m_RotatableSNCs ends up being a list of all the registered // SliceNavigationControllers which have a SlicedGeometry3D with at least one slice, // which covers most standard cases. m_RotatableSNCs.push_back( *iter ); } } } bool SlicesRotator::DoSelectSlice(Action* a, const StateEvent* e) { // just reach through for (SNCVector::iterator iter = m_RotatableSNCs.begin(); iter != m_RotatableSNCs.end(); ++iter) { if ( !(*iter)->GetSliceLocked() ) { (*iter)->ExecuteAction(a,e); } } return true; } bool SlicesRotator::DoDecideBetweenRotationAndSliceSelection(Action*, const StateEvent* e) { // Decide between moving and rotation slices. // For basic decision logic see class documentation. /* Detail logic: 1. Find the SliceNavigationController that has sent the event: this one defines our rendering plane and will NOT be rotated. Must not even be counted or checked.. 2. Inspect every other SliceNavigationController - calculate the line intersection of this SliceNavigationController's plane with our rendering plane - if there is no interesection, ignore and continue - IF there is an intersection - check the mouse cursor's distance from that line. 0. if the line is NOT near the cursor, remember the plane as "one of the other planes" (which can be rotated in "locked" mode) 1. on first line near the cursor, just remember this intersection line as THE other plane that we want to rotate 2. on every consecutive line near the cursor, check if the line is geometrically identical to the line that we want to rotate - if yes, we just push this line to the "other" lines and rotate it along - if no, then we have a situation where the mouse is near two other lines (e.g. crossing point) and don't want to rotate */ const DisplayPositionEvent* posEvent = dynamic_cast(e->GetEvent()); if (!posEvent) return false; BaseRenderer* clickedRenderer = e->GetEvent()->GetSender(); const PlaneGeometry* ourViewportGeometry = dynamic_cast( clickedRenderer->GetCurrentWorldGeometry2D() ); if (!ourViewportGeometry) return false; DisplayGeometry* clickedDisplayGeometry = clickedRenderer->GetDisplayGeometry(); if (!clickedDisplayGeometry) return false; MITK_DEBUG << "============================================="; MITK_DEBUG << "Renderer under cursor is " << clickedRenderer->GetName(); Point3D cursorPosition = posEvent->GetWorldPosition(); const PlaneGeometry* geometryToBeRotated = NULL; // this one is under the mouse cursor const PlaneGeometry* anyOtherGeometry = NULL; // this is also visible (for calculation of intersection ONLY) Line3D intersectionLineWithGeometryToBeRotated; bool hitMultipleLines(false); m_SNCsToBeRotated.clear(); const double threshholdDistancePixels = 12.0; for (SNCVector::iterator iter = m_RotatableSNCs.begin(); iter != m_RotatableSNCs.end(); ++iter) { // If the mouse cursor is in 3D Renderwindow, do not check for intersecting planes. if (clickedRenderer->GetMapperID() == BaseRenderer::Standard3D) break; const PlaneGeometry* otherRenderersRenderPlane = (*iter)->GetCurrentPlaneGeometry(); if (otherRenderersRenderPlane == NULL) continue; // ignore, we don't see a plane MITK_DEBUG << " Checking plane of renderer " << (*iter)->GetRenderer()->GetName(); // check if there is an intersection Line3D intersectionLine; // between rendered/clicked geometry and the one being analyzed if (!ourViewportGeometry->IntersectionLine( otherRenderersRenderPlane, intersectionLine )) { continue; // we ignore this plane, it's parallel to our plane } // check distance from intersection line double distanceFromIntersectionLine = intersectionLine.Distance( cursorPosition ); ScalarType distancePixels = distanceFromIntersectionLine / clickedDisplayGeometry->GetScaleFactorMMPerDisplayUnit(); MITK_DEBUG << " Distance of plane from cursor " << distanceFromIntersectionLine << " mm, which is around " << distancePixels << " px" ; // far away line, only remember for linked rotation if necessary if (distanceFromIntersectionLine > threshholdDistancePixels) { MITK_DEBUG << " Plane is too far away --> remember as otherRenderersRenderPlane"; anyOtherGeometry = otherRenderersRenderPlane; // we just take the last one, so overwrite each iteration (we just need some crossing point) // TODO what about multiple crossings? NOW we have undefined behavior / random crossing point is used if (m_LinkPlanes) { m_SNCsToBeRotated.push_back(*iter); } } else // close to cursor { MITK_DEBUG << " Plane is close enough to cursor..."; if ( geometryToBeRotated == NULL ) // first one close to the cursor { MITK_DEBUG << " It is the first close enough geometry, remember as geometryToBeRotated"; geometryToBeRotated = otherRenderersRenderPlane; intersectionLineWithGeometryToBeRotated = intersectionLine; m_SNCsToBeRotated.push_back(*iter); } else { MITK_DEBUG << " Second or later close enough geometry"; // compare to the line defined by geometryToBeRotated: if identical, just rotate this otherRenderersRenderPlane together with the primary one // if different, DON'T rotate if ( intersectionLine.IsParallel( intersectionLineWithGeometryToBeRotated ) && intersectionLine.Distance( intersectionLineWithGeometryToBeRotated.GetPoint1() ) < mitk::eps ) { MITK_DEBUG << " This line is the same as intersectionLineWithGeometryToBeRotated which we already know"; m_SNCsToBeRotated.push_back(*iter); } else { MITK_DEBUG << " This line is NOT the same as intersectionLineWithGeometryToBeRotated which we already know"; hitMultipleLines = true; } } } } bool moveSlices(true); if ( geometryToBeRotated && anyOtherGeometry && ourViewportGeometry && !hitMultipleLines ) { // assure all three are valid, so calculation of center of rotation can be done moveSlices = false; } MITK_DEBUG << "geometryToBeRotated: " << (void*)geometryToBeRotated; MITK_DEBUG << "anyOtherGeometry: " << (void*)anyOtherGeometry; MITK_DEBUG << "ourViewportGeometry: " << (void*)ourViewportGeometry; MITK_DEBUG << "hitMultipleLines? " << hitMultipleLines; MITK_DEBUG << "moveSlices? " << moveSlices; std::auto_ptr decidedEvent; // question in state machine is: "rotate?" if (moveSlices) // i.e. NOT rotate { // move all planes to posEvent->GetWorldPosition() decidedEvent.reset( new StateEvent(EIDNO, e->GetEvent()) ); MITK_DEBUG << "Rotation not possible, not enough information (other planes crossing rendering plane) "; } else { // we DO have enough information for rotation m_LastCursorPosition = intersectionLineWithGeometryToBeRotated.Project(cursorPosition); // remember where the last cursor position ON THE LINE has been observed if (anyOtherGeometry->IntersectionPoint(intersectionLineWithGeometryToBeRotated, m_CenterOfRotation)) // find center of rotation by intersection with any of the OTHER lines { decidedEvent.reset( new StateEvent(EIDYES, e->GetEvent()) ); MITK_DEBUG << "Rotation possible"; } else { MITK_DEBUG << "Rotation not possible, cannot determine the center of rotation!?"; decidedEvent.reset( new StateEvent(EIDNO, e->GetEvent()) ); } } this->HandleEvent( decidedEvent.get() ); return true; } bool SlicesRotator::DoStartRotation(Action*, const StateEvent*) { this->SetMouseCursor( rotate_cursor_xpm, 0, 0 ); this->InvokeEvent( SliceRotationEvent() ); // notify listeners return true; } bool SlicesRotator::DoEndRotation(Action*, const StateEvent*) { this->ResetMouseCursor(); this->InvokeEvent( SliceRotationEvent() ); // notify listeners return true; } bool SlicesRotator::DoRotationStep(Action*, const StateEvent* e) { const DisplayPositionEvent* posEvent = dynamic_cast(e->GetEvent()); if (!posEvent) return false; Point3D cursor = posEvent->GetWorldPosition(); Vector3D toProjected = m_LastCursorPosition - m_CenterOfRotation; Vector3D toCursor = cursor - m_CenterOfRotation; // cross product: | A x B | = |A| * |B| * sin(angle) Vector3D axisOfRotation; vnl_vector_fixed< ScalarType, 3 > vnlDirection = vnl_cross_3d( toCursor.GetVnlVector(), toProjected.GetVnlVector() ); axisOfRotation.SetVnlVector(vnlDirection); // scalar product: A * B = |A| * |B| * cos(angle) // tan = sin / cos ScalarType angle = - atan2( (double)(axisOfRotation.GetNorm()), (double)(toCursor * toProjected) ); angle *= 180.0 / vnl_math::pi; m_LastCursorPosition = cursor; // create RotationOperation and apply to all SNCs that should be rotated RotationOperation rotationOperation(OpROTATE, m_CenterOfRotation, axisOfRotation, angle); // iterate the OTHER slice navigation controllers: these are filled in DoDecideBetweenRotationAndSliceSelection for (SNCVector::iterator iter = m_SNCsToBeRotated.begin(); iter != m_SNCsToBeRotated.end(); ++iter) { // - remember the center of rotation on the 2D display BEFORE rotation // - execute rotation // - calculate new center of rotation on 2D display // - move display IF the center of rotation has moved slightly before and after rotation // DM 2012-10: this must probably be due to rounding errors only, right? // We don't have documentation on if/why this code is needed BaseRenderer *renderer = (*iter)->GetRenderer(); if ( !renderer ) continue; DisplayGeometry *displayGeometry = renderer->GetDisplayGeometry(); Point2D rotationCenter2DWorld, point2DDisplayPreRotation, point2DDisplayPostRotation; displayGeometry->Map( m_CenterOfRotation, rotationCenter2DWorld ); displayGeometry->WorldToDisplay( rotationCenter2DWorld, point2DDisplayPreRotation ); TimeGeometry* timeGeometry = (*iter)->GetCreatedWorldGeometry(); if (!timeGeometry) continue; timeGeometry->ExecuteOperation(&rotationOperation); displayGeometry->Map( m_CenterOfRotation, rotationCenter2DWorld ); displayGeometry->WorldToDisplay( rotationCenter2DWorld, point2DDisplayPostRotation ); Vector2D vector2DDisplayDiff = point2DDisplayPostRotation - point2DDisplayPreRotation; displayGeometry->MoveBy( vector2DDisplayDiff ); (*iter)->SendCreatedWorldGeometryUpdate(); } RenderingManager::GetInstance()->RequestUpdateAll(); this->InvokeEvent( SliceRotationEvent() ); // notify listeners return true; } } // namespace diff --git a/Core/Code/Controllers/mitkSlicesSwiveller.cpp b/Core/Code/Controllers/mitkSlicesSwiveller.cpp index 939046e055..7dacefebc6 100644 --- a/Core/Code/Controllers/mitkSlicesSwiveller.cpp +++ b/Core/Code/Controllers/mitkSlicesSwiveller.cpp @@ -1,395 +1,395 @@ /*=================================================================== 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 "mitkSlicesSwiveller.h" #include "mitkSliceNavigationController.h" #include "mitkStateEvent.h" #include "mitkAction.h" #include "mitkInteractionConst.h" #include "mitkDisplayPositionEvent.h" #include "mitkRotationOperation.h" #include "mitkBaseRenderer.h" #include "mitkRenderingManager.h" #include "mitkLine.h" #include "mitkGeometry3D.h" -#include "mitkGeometry2D.h" +#include "mitkPlaneGeometry.h" #include "mitkPlaneGeometry.h" #include "mitkDisplayGeometry.h" #include "mitkSlicedGeometry3D.h" #include namespace mitk { SlicesSwiveller::Pointer SlicesSwiveller::New() { return SlicesSwiveller::New("slices-rotator"); } SlicesSwiveller::SlicesSwiveller(const char* machine) : SlicesCoordinator(machine), m_PreviousRotationAngle( 0.0 ) { } SlicesSwiveller::~SlicesSwiveller() { } // check if the slices of this SliceNavigationController can be rotated (???) Possible void SlicesSwiveller::OnSliceControllerAdded(SliceNavigationController* snc) { if (!snc) return; // connects creation of new world geometry to Self::SetGeometry snc->ConnectGeometrySendEvent(this); } void SlicesSwiveller::OnSliceControllerRemoved(SliceNavigationController* snc) { if (!snc) return; // nothing to do } /// Is called whenever a SliceNavigationController invokes an event. Will // update the list of SliceNavigationControllers that can handle rotation void SlicesSwiveller::SetGeometry(const itk::EventObject& /*EventObject*/) { // there is no way to determine the sender? // ==> update whole list of SNCs UpdateRelevantSNCs(); } /// Updates the list of SliceNavigationControllers that can handle rotation void SlicesSwiveller::UpdateRelevantSNCs() { m_RelevantSNCs.clear(); SNCVector::iterator iter; for ( iter = m_SliceNavigationControllers.begin(); iter != m_SliceNavigationControllers.end(); ++iter) { const TimeGeometry* timeGeometry = (*iter)->GetCreatedWorldGeometry(); if (!timeGeometry) continue; const SlicedGeometry3D* slicedGeometry = dynamic_cast( timeGeometry->GetGeometryForTimeStep(0).GetPointer() ); if (!slicedGeometry) continue; - Geometry2D *firstSlice( NULL ); - //Geometry2D *secondSlice( NULL ); + PlaneGeometry *firstSlice( NULL ); + //PlaneGeometry *secondSlice( NULL ); if (slicedGeometry->IsValidSlice(0)) { firstSlice = slicedGeometry->GetGeometry2D(0); } // if (slicedGeometry->IsValidSlice(1)) // { // secondSlice = slicedGeometry->GetGeometry2D(1); // } // If the direction vector of these two slices is the same, then accept // this slice stack as rotatable Vector3D right1 = firstSlice->GetAxisVector(0); Vector3D up1 = firstSlice->GetAxisVector(1); vnl_vector_fixed< ScalarType, 3 > vnlDirection1 = vnl_cross_3d(right1.GetVnlVector(), up1.GetVnlVector()); Vector3D direction1; direction1.SetVnlVector(vnlDirection1); Vector3D right2 = firstSlice->GetAxisVector(0); Vector3D up2 = firstSlice->GetAxisVector(1); vnl_vector_fixed< ScalarType, 3 > vnlDirection2 = vnl_cross_3d(right2.GetVnlVector(), up2.GetVnlVector()); Vector3D direction2; direction2.SetVnlVector(vnlDirection2); bool equal = true; const ScalarType eps = 0.0001; for (int i = 0; i < 3; ++i) { if ( fabs(direction1[i] - direction2[i]) > eps ) { equal = false; } } if (equal) // equal direction vectors { m_RelevantSNCs.push_back( *iter ); } } } bool SlicesSwiveller ::ExecuteAction(Action* action, StateEvent const* stateEvent) { const ScalarType ThresholdDistancePixels = 6.0; bool ok = false; switch ( action->GetActionId() ) { case AcMOVE: { // just reach through SNCVector::iterator iter; for ( iter = m_RelevantSNCs.begin(); iter != m_RelevantSNCs.end(); ++iter ) { if ( !(*iter)->GetSliceRotationLocked() ) { (*iter)->ExecuteAction(action, stateEvent); } } ok = true; break; } case AcROTATE: { const DisplayPositionEvent *posEvent = dynamic_cast(stateEvent->GetEvent()); if (!posEvent) break; // Determine relative mouse movement projected onto world space Point2D cursor = posEvent->GetDisplayPosition(); Vector2D relativeCursor = cursor - m_ReferenceCursor; Vector3D relativeCursorAxis = m_RotationPlaneXVector * relativeCursor[0] + m_RotationPlaneYVector * relativeCursor[1]; // Determine rotation axis (perpendicular to rotation plane and cursor // movement) Vector3D rotationAxis = itk::CrossProduct( m_RotationPlaneNormal, relativeCursorAxis ); ScalarType rotationAngle = relativeCursor.GetNorm() / 2.0; // Restore the initial plane pose by undoing the previous rotation // operation RotationOperation op( OpROTATE, m_CenterOfRotation, m_PreviousRotationAxis, -m_PreviousRotationAngle ); SNCVector::iterator iter; for ( iter = m_SNCsToBeRotated.begin(); iter != m_SNCsToBeRotated.end(); ++iter ) { if ( !(*iter)->GetSliceRotationLocked() ) { TimeGeometry* timeGeometry = (*iter)->GetCreatedWorldGeometry(); if (!timeGeometry) continue; timeGeometry->ExecuteOperation(&op); (*iter)->SendCreatedWorldGeometryUpdate(); } } // Apply new rotation operation to all relevant SNCs RotationOperation op2( OpROTATE, m_CenterOfRotation, rotationAxis, rotationAngle ); for ( iter = m_SNCsToBeRotated.begin(); iter != m_SNCsToBeRotated.end(); ++iter) { if ( !(*iter)->GetSliceRotationLocked() ) { //// Map rotation center onto display geometry (will be used as //// pre-rotation vector for compensating a visual shift of the //// rotation center) //BaseRenderer *renderer = (*iter)->GetRenderer(); //DisplayGeometry *displayGeometry = renderer->GetDisplayGeometry(); //Point2D point2DWorld, point2DDisplayPre, point2DDisplayPost; //displayGeometry->Map( m_CenterOfRotation, point2DWorld ); //displayGeometry->WorldToDisplay( point2DWorld, point2DDisplayPre ); // Retrieve the TimeGeometry of this SliceNavigationController TimeGeometry* timeGeometry = (*iter)->GetCreatedWorldGeometry(); if (!timeGeometry) continue; // Execute the new rotation timeGeometry->ExecuteOperation(&op2); //// After rotation: map rotation center onto new display geometry... //displayGeometry->Map( m_CenterOfRotation, point2DWorld ); //displayGeometry->WorldToDisplay( point2DWorld, point2DDisplayPost ); //Vector2D vector2DDisplayDiff = point2DDisplayPost - point2DDisplayPre; //// And use the difference between pre- and post-rotation vectors to //// compensate for display geometry shift: //Vector2D origin = displayGeometry->GetOriginInMM(); //displayGeometry->MoveBy( vector2DDisplayDiff ); // Notify listeners (*iter)->SendCreatedWorldGeometryUpdate(); } } m_PreviousRotationAxis = rotationAxis; m_PreviousRotationAngle = rotationAngle; RenderingManager::GetInstance()->RequestUpdateAll(); this->InvokeEvent( SliceRotationEvent() ); // notify listeners ok = true; break; } case AcCHECKPOINT: { // Decide between moving and rotation: if we're close to the crossing // point of the planes, moving mode is entered, otherwise // rotation/swivel mode const DisplayPositionEvent *posEvent = dynamic_cast(stateEvent->GetEvent()); BaseRenderer *renderer = stateEvent->GetEvent()->GetSender(); if ( !posEvent || !renderer ) { break; } const Point3D &cursor = posEvent->GetWorldPosition(); m_SNCsToBeRotated.clear(); const PlaneGeometry *clickedGeometry( NULL ); const PlaneGeometry *otherGeometry1( NULL ); const PlaneGeometry *otherGeometry2( NULL ); SNCVector::iterator iter; for ( iter = m_RelevantSNCs.begin(); iter != m_RelevantSNCs.end(); ++iter ) { //unsigned int slice = (*iter)->GetSlice()->GetPos(); //unsigned int time = (*iter)->GetTime()->GetPos(); const PlaneGeometry *planeGeometry = (*iter)->GetCurrentPlaneGeometry(); if ( !planeGeometry ) continue; if ( *iter == renderer->GetSliceNavigationController() ) { clickedGeometry = planeGeometry; m_SNCsToBeRotated.push_back(*iter); } else { if ( otherGeometry1 == NULL ) { otherGeometry1 = planeGeometry; } else { otherGeometry2 = planeGeometry; } if ( m_LinkPlanes ) { // If planes are linked, apply rotation to all planes m_SNCsToBeRotated.push_back(*iter); } } } std::auto_ptr newStateEvent; mitk::Line3D line; mitk::Point3D point; if ( (clickedGeometry != NULL) && (otherGeometry1 != NULL) && (otherGeometry2 != NULL) && clickedGeometry->IntersectionLine( otherGeometry1, line ) && otherGeometry2->IntersectionPoint( line, point )) { m_CenterOfRotation = point; if ( m_CenterOfRotation.EuclideanDistanceTo( cursor ) < ThresholdDistancePixels ) { newStateEvent.reset(new StateEvent(EIDNO, stateEvent->GetEvent())); } else { m_ReferenceCursor = posEvent->GetDisplayPosition(); // Get main axes of rotation plane and store it for rotation step m_RotationPlaneNormal = clickedGeometry->GetNormal(); ScalarType xVector[] = { 1.0, 0.0, 0.0 }; ScalarType yVector[] = { 0.0, 1.0, 0.0 }; clickedGeometry->BaseGeometry::IndexToWorld( Vector3D( xVector), m_RotationPlaneXVector ); clickedGeometry->BaseGeometry::IndexToWorld( Vector3D( yVector), m_RotationPlaneYVector ); m_RotationPlaneNormal.Normalize(); m_RotationPlaneXVector.Normalize(); m_RotationPlaneYVector.Normalize(); m_PreviousRotationAxis.Fill( 0.0 ); m_PreviousRotationAxis[2] = 1.0; m_PreviousRotationAngle = 0.0; newStateEvent.reset(new StateEvent(EIDYES, stateEvent->GetEvent())); } } else { newStateEvent.reset(new StateEvent(EIDNO, stateEvent->GetEvent())); } this->HandleEvent( newStateEvent.get() ); ok = true; break; } case AcROTATESTART: { this->InvokeEvent( SliceRotationEvent() ); // notify listeners break; } case AcROTATEEND: { this->InvokeEvent( SliceRotationEvent() ); // notify listeners break; } default: { break; } } return ok; } } // namespace diff --git a/Core/Code/DataManagement/mitkAbstractTransformGeometry.h b/Core/Code/DataManagement/mitkAbstractTransformGeometry.h index 06731fefcf..bb75c3521d 100644 --- a/Core/Code/DataManagement/mitkAbstractTransformGeometry.h +++ b/Core/Code/DataManagement/mitkAbstractTransformGeometry.h @@ -1,219 +1,219 @@ /*=================================================================== 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 MITKVTKABSTRACTTRANSFORMPLANEGEOMETRY_H_HEADER_INCLUDED_C1C68A2C #define MITKVTKABSTRACTTRANSFORMPLANEGEOMETRY_H_HEADER_INCLUDED_C1C68A2C #include -#include "mitkGeometry2D.h" +#include "mitkPlaneGeometry.h" #include "mitkPlaneGeometry.h" #include "itkVtkAbstractTransform.h" class vtkAbstractTransform; namespace mitk { //##Documentation //## @brief Describes a geometry defined by an vtkAbstractTransform and a plane //## //## vtkAbstractTransform is the most general transform in vtk (superclass for //## all vtk geometric transformations). It defines an arbitrary 3D transformation, //## i.e., a transformation of 3D space into 3D space. In contrast, - //## AbstractTransformGeometry (since it is a subclass of Geometry2D) describes a + //## AbstractTransformGeometry (since it is a subclass of PlaneGeometry) describes a //## 2D manifold in 3D space. The 2D manifold is defined as the manifold that results //## from transforming a rectangle (given in m_Plane as a PlaneGeometry) by the //## vtkAbstractTransform (given in m_VtkAbstractTransform). //## The PlaneGeometry m_Plane is used to define the parameter space. 2D coordinates are //## first mapped by the PlaneGeometry and the resulting 3D coordinates are put into //## the vtkAbstractTransform. //## @note This class is the superclass of concrete geometries. Since there is no //## write access to the vtkAbstractTransform and m_Plane, this class is somehow //## abstract. For full write access from extern, use ExternAbstractTransformGeometry. //## @note The bounds of the PlaneGeometry are used as the parametric bounds. //## @sa ExternAbstractTransformGeometry //## @ingroup Geometry - class MITK_CORE_EXPORT AbstractTransformGeometry : public Geometry2D + class MITK_CORE_EXPORT AbstractTransformGeometry : public PlaneGeometry { public: - mitkClassMacro(AbstractTransformGeometry, Geometry2D); + mitkClassMacro(AbstractTransformGeometry, PlaneGeometry); itkNewMacro(Self); //##Documentation //## @brief Get the vtkAbstractTransform (stored in m_VtkAbstractTransform) virtual vtkAbstractTransform* GetVtkAbstractTransform() const; virtual unsigned long GetMTime() const; //##Documentation //## @brief Get the rectangular area that is used for transformation by //## m_VtkAbstractTransform and therewith defines the 2D manifold described by //## AbstractTransformGeometry itkGetConstObjectMacro(Plane, PlaneGeometry); /** * \brief projects the given point onto the curved plane */ virtual bool Project(const mitk::Point3D &pt3d_mm, mitk::Point3D &projectedPt3d_mm) const; /** * \brief projects a given vector starting from given point onto the curved plane * \warning no satisfiyng implementation existing yet */ virtual bool Project(const mitk::Point3D & atPt3d_mm, const mitk::Vector3D &vec3d_mm, mitk::Vector3D &projectedVec3d_mm) const; /** * \brief projects a given vector starting from standard point onto the curved plane * \warning no satisfying implementation existing yet */ virtual bool Project(const mitk::Vector3D &vec3d_mm, mitk::Vector3D &projectedVec3d_mm) const; virtual bool Map(const mitk::Point3D &pt3d_mm, mitk::Point2D &pt2d_mm) const; virtual void Map(const mitk::Point2D &pt2d_mm, mitk::Point3D &pt3d_mm) const; virtual bool Map(const mitk::Point3D & atPt3d_mm, const mitk::Vector3D &vec3d_mm, mitk::Vector2D &vec2d_mm) const; virtual void Map(const mitk::Point2D & atPt2d_mm, const mitk::Vector2D &vec2d_mm, mitk::Vector3D &vec3d_mm) const; virtual void IndexToWorld(const mitk::Point2D &pt_units, mitk::Point2D &pt_mm) const; virtual void WorldToIndex(const mitk::Point2D &pt_mm, mitk::Point2D &pt_units) const; //##Documentation //## @brief Convert (continuous or discrete) index coordinates of a \em vector //## \a vec_units to world coordinates (in mm) //## @deprecated First parameter (Point2D) is not used. If possible, please use void IndexToWorld(const mitk::Vector2D& vec_units, mitk::Vector2D& vec_mm) const. //## For further information about coordinates types, please see the Geometry documentation virtual void IndexToWorld(const mitk::Point2D &atPt2d_units, const mitk::Vector2D &vec_units, mitk::Vector2D &vec_mm) const; //##Documentation //## @brief Convert (continuous or discrete) index coordinates of a \em vector //## \a vec_units to world coordinates (in mm) //## For further information about coordinates types, please see the Geometry documentation virtual void IndexToWorld(const mitk::Vector2D &vec_units, mitk::Vector2D &vec_mm) const; //##Documentation //## @brief Convert world coordinates (in mm) of a \em vector //## \a vec_mm to (continuous!) index coordinates. //## @deprecated First parameter (Point2D) is not used. If possible, please use void WorldToIndex(const mitk::Vector2D& vec_mm, mitk::Vector2D& vec_units) const. //## For further information about coordinates types, please see the Geometry documentation virtual void WorldToIndex(const mitk::Point2D &atPt2d_mm, const mitk::Vector2D &vec_mm, mitk::Vector2D &vec_units) const; //##Documentation //## @brief Convert world coordinates (in mm) of a \em vector //## \a vec_mm to (continuous!) index coordinates. //## For further information about coordinates types, please see the Geometry documentation virtual void WorldToIndex(const mitk::Vector2D &vec_mm, mitk::Vector2D &vec_units) const; virtual bool IsAbove(const Point3D& pt3d_mm) const; virtual mitk::ScalarType GetParametricExtentInMM(int direction) const; virtual const Transform3D* GetParametricTransform() const; //##Documentation //## @brief Change the parametric bounds to @a oversampling times //## the bounds of m_Plane. //## //## The change is done once (immediately). Later changes of the bounds //## of m_Plane will not influence the parametric bounds. (Consequently, //## there is no method to get the oversampling.) virtual void SetOversampling(mitk::ScalarType oversampling); //##Documentation //## @brief Calculates the standard part of a Geometry3D //## (IndexToWorldTransform and bounding box) around the //## curved geometry. Has to be implemented in subclasses. //## //## \sa SetFrameGeometry virtual void CalculateFrameGeometry(); //##Documentation //## @brief Set the frame geometry which is used as the standard //## part of an Geometry3D (IndexToWorldTransform and bounding box) //## //## Maybe used as a hint within which the interpolation shall occur //## by concrete sub-classes. //## \sa CalculateFrameGeometry virtual void SetFrameGeometry(const mitk::BaseGeometry* frameGeometry); virtual itk::LightObject::Pointer InternalClone() const; //##Documentation //## @brief Get the parametric bounding-box //## //## See AbstractTransformGeometry for an example usage of this. itkGetConstObjectMacro(ParametricBoundingBox, BoundingBox); //##Documentation //## @brief Get the parametric bounds //## //## See AbstractTransformGeometry for an example usage of this. const BoundingBox::BoundsArrayType& GetParametricBounds() const; //##Documentation //## @brief Get the parametric extent //## //## See AbstractTransformGeometry for an example usage of this. mitk::ScalarType GetParametricExtent(int direction) const; protected: AbstractTransformGeometry(); AbstractTransformGeometry(const AbstractTransformGeometry& other); virtual ~AbstractTransformGeometry(); //##Documentation //## @brief Set the vtkAbstractTransform (stored in m_VtkAbstractTransform) //## //## Protected in this class, made public in ExternAbstractTransformGeometry. virtual void SetVtkAbstractTransform(vtkAbstractTransform* aVtkAbstractTransform); //##Documentation //## @brief Set the rectangular area that is used for transformation by //## m_VtkAbstractTransform and therewith defines the 2D manifold described by //## ExternAbstractTransformGeometry //## //## Protected in this class, made public in ExternAbstractTransformGeometry. //## @note The bounds of the PlaneGeometry are used as the parametric bounds. //## @note The PlaneGeometry is cloned, @em not linked/referenced. virtual void SetPlane(const mitk::PlaneGeometry* aPlane); //##Documentation //## @brief The rectangular area that is used for transformation by //## m_VtkAbstractTransform and therewith defines the 2D manifold described by //## AbstractTransformGeometry. mitk::PlaneGeometry::Pointer m_Plane; itk::VtkAbstractTransform::Pointer m_ItkVtkAbstractTransform; mitk::BaseGeometry::Pointer m_FrameGeometry; virtual void InternPostInitialize(); //##Documentation //## @brief Set the parametric bounds //## //## Protected in this class, made public in some sub-classes, e.g., //## ExternAbstractTransformGeometry. virtual void SetParametricBounds(const BoundingBox::BoundsArrayType& bounds); mutable mitk::BoundingBox::Pointer m_ParametricBoundingBox; }; } // namespace mitk #endif /* MITKVTKABSTRACTTRANSFORMPLANEGEOMETRY_H_HEADER_INCLUDED_C1C68A2C */ diff --git a/Core/Code/DataManagement/mitkDisplayGeometry.cpp b/Core/Code/DataManagement/mitkDisplayGeometry.cpp index d1b670ffef..141fb19c1c 100644 --- a/Core/Code/DataManagement/mitkDisplayGeometry.cpp +++ b/Core/Code/DataManagement/mitkDisplayGeometry.cpp @@ -1,613 +1,613 @@ /*=================================================================== 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 "mitkDisplayGeometry.h" itk::LightObject::Pointer mitk::DisplayGeometry::InternalClone() const { // itkExceptionMacro(<<"calling mitk::DisplayGeometry::Clone does not make much sense."); DisplayGeometry* returnValue = const_cast(this); return returnValue; } bool mitk::DisplayGeometry::IsValid() const { return m_WorldGeometry.IsNotNull() && m_WorldGeometry->IsValid(); } unsigned long mitk::DisplayGeometry::GetMTime() const { - if((m_WorldGeometry.IsNotNull()) && (Geometry2D::GetMTime() < m_WorldGeometry->GetMTime())) + if((m_WorldGeometry.IsNotNull()) && (PlaneGeometry::GetMTime() < m_WorldGeometry->GetMTime())) { Modified(); } - return Geometry2D::GetMTime(); + return PlaneGeometry::GetMTime(); } const mitk::TimeBounds& mitk::DisplayGeometry::GetTimeBounds() const { if(m_WorldGeometry.IsNull()) { return this->GetTimeBounds(); } return m_WorldGeometry->GetTimeBounds(); } // size definition methods -void mitk::DisplayGeometry::SetWorldGeometry(const Geometry2D* aWorldGeometry) +void mitk::DisplayGeometry::SetWorldGeometry(const PlaneGeometry* aWorldGeometry) { m_WorldGeometry = aWorldGeometry; Modified(); } bool mitk::DisplayGeometry::SetOriginInMM(const Vector2D& origin_mm) { m_OriginInMM = origin_mm; WorldToDisplay(m_OriginInMM, m_OriginInDisplayUnits); Modified(); return !this->RefitVisibleRect(); } mitk::Vector2D mitk::DisplayGeometry::GetOriginInMM() const { return m_OriginInMM; } mitk::Vector2D mitk::DisplayGeometry::GetOriginInDisplayUnits() const { return m_OriginInDisplayUnits; } void mitk::DisplayGeometry::SetSizeInDisplayUnits(unsigned int width, unsigned int height, bool keepDisplayedRegion) { Vector2D oldSizeInMM( m_SizeInMM ); Point2D oldCenterInMM; if(keepDisplayedRegion) { Point2D centerInDisplayUnits; centerInDisplayUnits[0] = m_SizeInDisplayUnits[0]*0.5; centerInDisplayUnits[1] = m_SizeInDisplayUnits[1]*0.5; DisplayToWorld(centerInDisplayUnits, oldCenterInMM); } m_SizeInDisplayUnits[0]=width; m_SizeInDisplayUnits[1]=height; if(m_SizeInDisplayUnits[0] <= 0) m_SizeInDisplayUnits[0] = 1; if(m_SizeInDisplayUnits[1] <= 0) m_SizeInDisplayUnits[1] = 1; DisplayToWorld(m_SizeInDisplayUnits, m_SizeInMM); if(keepDisplayedRegion) { Point2D positionOfOldCenterInCurrentDisplayUnits; WorldToDisplay(oldCenterInMM, positionOfOldCenterInCurrentDisplayUnits); Point2D currentNewCenterInDisplayUnits; currentNewCenterInDisplayUnits[0] = m_SizeInDisplayUnits[0]*0.5; currentNewCenterInDisplayUnits[1] = m_SizeInDisplayUnits[1]*0.5; Vector2D shift; shift=positionOfOldCenterInCurrentDisplayUnits.GetVectorFromOrigin()-currentNewCenterInDisplayUnits; MoveBy(shift); Zoom(m_SizeInMM.GetNorm()/oldSizeInMM.GetNorm(), currentNewCenterInDisplayUnits); } Modified(); } mitk::Vector2D mitk::DisplayGeometry::GetSizeInDisplayUnits() const { return m_SizeInDisplayUnits; } mitk::Vector2D mitk::DisplayGeometry::GetSizeInMM() const { return m_SizeInMM; } unsigned int mitk::DisplayGeometry::GetDisplayWidth() const { assert(m_SizeInDisplayUnits[0] >= 0); return (unsigned int)m_SizeInDisplayUnits[0]; } unsigned int mitk::DisplayGeometry::GetDisplayHeight() const { assert(m_SizeInDisplayUnits[1] >= 0); return (unsigned int)m_SizeInDisplayUnits[1]; } // zooming, panning, restriction of both void mitk::DisplayGeometry::SetConstrainZoomingAndPanning(bool constrain) { m_ConstrainZoomingAndPanning = constrain; if (m_ConstrainZoomingAndPanning) { this->RefitVisibleRect(); } } bool mitk::DisplayGeometry::GetConstrainZommingAndPanning() const { return m_ConstrainZoomingAndPanning; } bool mitk::DisplayGeometry::SetScaleFactor(ScalarType mmPerDisplayUnit) { if(mmPerDisplayUnit<0.0001) { mmPerDisplayUnit=0.0001; } m_ScaleFactorMMPerDisplayUnit = mmPerDisplayUnit; assert(m_ScaleFactorMMPerDisplayUnit < ScalarTypeNumericTraits::infinity()); DisplayToWorld(m_SizeInDisplayUnits, m_SizeInMM); return !this->RefitVisibleRect(); } mitk::ScalarType mitk::DisplayGeometry::GetScaleFactorMMPerDisplayUnit() const { return m_ScaleFactorMMPerDisplayUnit; } // Zooms with a factor (1.0=identity) around the specified center in display units bool mitk::DisplayGeometry::Zoom(ScalarType factor, const Point2D& centerInDisplayUnits) { assert(factor > 0); if ( SetScaleFactor(m_ScaleFactorMMPerDisplayUnit/factor) ) { return SetOriginInMM(m_OriginInMM-centerInDisplayUnits.GetVectorFromOrigin()*(1-factor)*m_ScaleFactorMMPerDisplayUnit); } else { return false; } } // Zooms with a factor (1.0=identity) around the specified center, but tries (if its within view contraints) to match the center in display units with the center in world coordinates. bool mitk::DisplayGeometry::ZoomWithFixedWorldCoordinates(ScalarType factor, const Point2D& focusDisplayUnits, const Point2D& focusUnitsInMM ) { assert(factor > 0); SetScaleFactor(m_ScaleFactorMMPerDisplayUnit/factor); SetOriginInMM(focusUnitsInMM.GetVectorFromOrigin()-focusDisplayUnits.GetVectorFromOrigin()*m_ScaleFactorMMPerDisplayUnit); return true; } bool mitk::DisplayGeometry::MoveBy(const Vector2D& shiftInDisplayUnits) { SetOriginInMM(m_OriginInMM+shiftInDisplayUnits*m_ScaleFactorMMPerDisplayUnit); Modified(); return !this->RefitVisibleRect(); } void mitk::DisplayGeometry::Fit() { if((m_WorldGeometry.IsNull()) || (m_WorldGeometry->IsValid() == false)) return; /// \FIXME: try to remove all the casts int width=(int)m_SizeInDisplayUnits[0]; int height=(int)m_SizeInDisplayUnits[1]; ScalarType w = width; ScalarType h = height; const ScalarType& widthInMM = m_WorldGeometry->GetExtentInMM(0); const ScalarType& heightInMM = m_WorldGeometry->GetExtentInMM(1); ScalarType aspRatio=((ScalarType)widthInMM)/heightInMM; ScalarType x = (ScalarType)w/widthInMM; ScalarType y = (ScalarType)h/heightInMM; if (x > y) { w = (int) (aspRatio*h); } else { h = (int) (w/aspRatio); } if(w>0) { SetScaleFactor(widthInMM/w); } Vector2D origin_display; origin_display[0]=-(width-w)/2.0; origin_display[1]=-(height-h)/2.0; SetOriginInMM(origin_display*m_ScaleFactorMMPerDisplayUnit); this->RefitVisibleRect(); Modified(); } // conversion methods void mitk::DisplayGeometry::DisplayToWorld(const Point2D &pt_display, Point2D &pt_mm) const { pt_mm[0]=m_ScaleFactorMMPerDisplayUnit*pt_display[0]+m_OriginInMM[0]; pt_mm[1]=m_ScaleFactorMMPerDisplayUnit*pt_display[1]+m_OriginInMM[1]; } void mitk::DisplayGeometry::WorldToDisplay(const Point2D &pt_mm, Point2D &pt_display) const { pt_display[0]=(pt_mm[0]-m_OriginInMM[0])*(1.0/m_ScaleFactorMMPerDisplayUnit); pt_display[1]=(pt_mm[1]-m_OriginInMM[1])*(1.0/m_ScaleFactorMMPerDisplayUnit); } void mitk::DisplayGeometry::DisplayToWorld(const Vector2D &vec_display, Vector2D &vec_mm) const { vec_mm=vec_display*m_ScaleFactorMMPerDisplayUnit; } void mitk::DisplayGeometry::WorldToDisplay(const Vector2D &vec_mm, Vector2D &vec_display) const { vec_display=vec_mm*(1.0/m_ScaleFactorMMPerDisplayUnit); } void mitk::DisplayGeometry::ULDisplayToMM(const Point2D &pt_ULdisplay, Point2D &pt_mm) const { ULDisplayToDisplay(pt_ULdisplay, pt_mm); DisplayToWorld(pt_mm, pt_mm); } void mitk::DisplayGeometry::MMToULDisplay(const Point2D &pt_mm, Point2D &pt_ULdisplay) const { WorldToDisplay(pt_mm, pt_ULdisplay); DisplayToULDisplay(pt_ULdisplay, pt_ULdisplay); } void mitk::DisplayGeometry::ULDisplayToMM(const Vector2D &vec_ULdisplay, Vector2D &vec_mm) const { ULDisplayToDisplay(vec_ULdisplay, vec_mm); DisplayToWorld(vec_mm, vec_mm); } void mitk::DisplayGeometry::MMToULDisplay(const Vector2D &vec_mm, Vector2D &vec_ULdisplay) const { WorldToDisplay(vec_mm, vec_ULdisplay); DisplayToULDisplay(vec_ULdisplay, vec_ULdisplay); } void mitk::DisplayGeometry::ULDisplayToDisplay(const Point2D &pt_ULdisplay, Point2D &pt_display) const { pt_display[0]=pt_ULdisplay[0]; pt_display[1]=GetDisplayHeight()-pt_ULdisplay[1]; } void mitk::DisplayGeometry::DisplayToULDisplay(const Point2D &pt_display, Point2D &pt_ULdisplay) const { ULDisplayToDisplay(pt_display, pt_ULdisplay); } void mitk::DisplayGeometry::ULDisplayToDisplay(const Vector2D &vec_ULdisplay, Vector2D &vec_display) const { vec_display[0]= vec_ULdisplay[0]; vec_display[1]=-vec_ULdisplay[1]; } void mitk::DisplayGeometry::DisplayToULDisplay(const Vector2D &vec_display, Vector2D &vec_ULdisplay) const { ULDisplayToDisplay(vec_display, vec_ULdisplay); } bool mitk::DisplayGeometry::Project(const Point3D &pt3d_mm, Point3D &projectedPt3d_mm) const { if(m_WorldGeometry.IsNotNull()) { return m_WorldGeometry->Project(pt3d_mm, projectedPt3d_mm); } else { return false; } } bool mitk::DisplayGeometry::Project(const Point3D & atPt3d_mm, const Vector3D &vec3d_mm, Vector3D &projectedVec3d_mm) const { if(m_WorldGeometry.IsNotNull()) { return m_WorldGeometry->Project(atPt3d_mm, vec3d_mm, projectedVec3d_mm); } else { return false; } } bool mitk::DisplayGeometry::Project(const Vector3D &vec3d_mm, Vector3D &projectedVec3d_mm) const { if(m_WorldGeometry.IsNotNull()) { return m_WorldGeometry->Project(vec3d_mm, projectedVec3d_mm); } else { return false; } } bool mitk::DisplayGeometry::Map(const Point3D &pt3d_mm, Point2D &pt2d_mm) const { if(m_WorldGeometry.IsNotNull()) { return m_WorldGeometry->Map(pt3d_mm, pt2d_mm); } else { return false; } } void mitk::DisplayGeometry::Map(const Point2D &pt2d_mm, Point3D &pt3d_mm) const { if(m_WorldGeometry.IsNull()) return; m_WorldGeometry->Map(pt2d_mm, pt3d_mm); } bool mitk::DisplayGeometry::Map(const Point3D & atPt3d_mm, const Vector3D &vec3d_mm, Vector2D &vec2d_mm) const { if(m_WorldGeometry.IsNotNull()) { return m_WorldGeometry->Map(atPt3d_mm, vec3d_mm, vec2d_mm); } else { return false; } } void mitk::DisplayGeometry::Map(const Point2D & atPt2d_mm, const Vector2D &vec2d_mm, Vector3D &vec3d_mm) const { if(m_WorldGeometry.IsNull()) return; m_WorldGeometry->Map(atPt2d_mm, vec2d_mm, vec3d_mm); } // protected methods mitk::DisplayGeometry::DisplayGeometry() :m_ScaleFactorMMPerDisplayUnit(1.0) ,m_WorldGeometry(NULL) ,m_ConstrainZoomingAndPanning(true) ,m_MaxWorldViewPercentage(1.0) ,m_MinWorldViewPercentage(0.1) { m_OriginInMM.Fill(0.0); m_OriginInDisplayUnits.Fill(0.0); m_SizeInMM.Fill(1.0); m_SizeInDisplayUnits.Fill(10.0); } mitk::DisplayGeometry::~DisplayGeometry() { } bool mitk::DisplayGeometry::RefitVisibleRect() { // do nothing if not asked to if (!m_ConstrainZoomingAndPanning) return false; // don't allow recursion (need to be fixed, singleton) static bool inRecalculate = false; if (inRecalculate) return false; inRecalculate = true; // rename some basic measures of the current viewport and world geometry (MM = milimeters Px = Pixels = display units) float displayXMM = m_OriginInMM[0]; float displayYMM = m_OriginInMM[1]; float displayWidthPx = m_SizeInDisplayUnits[0]; float displayHeightPx = m_SizeInDisplayUnits[1]; float displayWidthMM = m_SizeInDisplayUnits[0] * m_ScaleFactorMMPerDisplayUnit; float displayHeightMM = m_SizeInDisplayUnits[1] * m_ScaleFactorMMPerDisplayUnit; float worldWidthMM = m_WorldGeometry->GetExtentInMM(0); float worldHeightMM = m_WorldGeometry->GetExtentInMM(1); // reserve variables for the correction logic to save a corrected origin and zoom factor Vector2D newOrigin = m_OriginInMM; bool correctPanning = false; float newScaleFactor = m_ScaleFactorMMPerDisplayUnit; bool correctZooming = false; // start of the correction logic // zoom to big means: // at a given percentage of the world's width/height should be visible. Otherwise // the whole screen could show only one pixel // // zoom to small means: // zooming out should be limited at the point where the smaller of the world's sides is completely visible bool zoomXtooSmall = displayWidthPx * m_ScaleFactorMMPerDisplayUnit > m_MaxWorldViewPercentage * worldWidthMM; bool zoomXtooBig = displayWidthPx * m_ScaleFactorMMPerDisplayUnit < m_MinWorldViewPercentage * worldWidthMM; bool zoomYtooSmall = displayHeightPx * m_ScaleFactorMMPerDisplayUnit > m_MaxWorldViewPercentage * worldHeightMM; bool zoomYtooBig = displayHeightPx * m_ScaleFactorMMPerDisplayUnit < m_MinWorldViewPercentage * worldHeightMM; // constrain zooming in both direction if ( zoomXtooBig && zoomYtooBig) { double fx = worldWidthMM * m_MinWorldViewPercentage / displayWidthPx; double fy = worldHeightMM * m_MinWorldViewPercentage / displayHeightPx; newScaleFactor = fx < fy ? fx : fy; correctZooming = true; } // constrain zooming in x direction else if ( zoomXtooBig ) { newScaleFactor = worldWidthMM * m_MinWorldViewPercentage / displayWidthPx; correctZooming = true; } // constrain zooming in y direction else if ( zoomYtooBig ) { newScaleFactor = worldHeightMM * m_MinWorldViewPercentage / displayHeightPx; correctZooming = true; } // constrain zooming out // we stop zooming out at these situations: // // *** display // --- image // // ********************** // * * x side maxed out // * * // *--------------------* // *| |* // *| |* // *--------------------* // * * // * * // * * // ********************** // // ********************** // * |------| * y side maxed out // * | | * // * | | * // * | | * // * | | * // * | | * // * | | * // * | | * // * |------| * // ********************** // // In both situations we center the not-maxed out direction // if ( zoomXtooSmall && zoomYtooSmall ) { // determine and set the bigger scale factor float fx = worldWidthMM * m_MaxWorldViewPercentage / displayWidthPx; float fy = worldHeightMM * m_MaxWorldViewPercentage / displayHeightPx; newScaleFactor = fx > fy ? fx : fy; correctZooming = true; } // actually execute correction if (correctZooming) { SetScaleFactor(newScaleFactor); } displayWidthMM = m_SizeInDisplayUnits[0] * m_ScaleFactorMMPerDisplayUnit; displayHeightMM = m_SizeInDisplayUnits[1] * m_ScaleFactorMMPerDisplayUnit; // constrain panning if(worldWidthMM center x newOrigin[0] = (worldWidthMM - displayWidthMM) / 2.0; correctPanning = true; } else { // make sure left display border inside our world if (displayXMM < 0) { newOrigin[0] = 0; correctPanning = true; } // make sure right display border inside our world else if (displayXMM + displayWidthMM > worldWidthMM) { newOrigin[0] = worldWidthMM - displayWidthMM; correctPanning = true; } } if (worldHeightMM center y newOrigin[1] = (worldHeightMM - displayHeightMM) / 2.0; correctPanning = true; } else { // make sure top display border inside our world if (displayYMM + displayHeightMM > worldHeightMM) { newOrigin[1] = worldHeightMM - displayHeightMM; correctPanning = true; } // make sure bottom display border inside our world else if (displayYMM < 0) { newOrigin[1] = 0; correctPanning = true; } } if (correctPanning) { SetOriginInMM( newOrigin ); } inRecalculate = false; if ( correctPanning || correctZooming ) { Modified(); } // return true if any correction has been made return correctPanning || correctZooming; } void mitk::DisplayGeometry::PrintSelf(std::ostream& os, itk::Indent indent) const { if(m_WorldGeometry.IsNull()) { os << indent << " WorldGeometry: " << "NULL" << std::endl; } else { m_WorldGeometry->Print(os, indent); os << indent << " OriginInMM: " << m_OriginInMM << std::endl; os << indent << " OriginInDisplayUnits: " << m_OriginInDisplayUnits << std::endl; os << indent << " SizeInMM: " << m_SizeInMM << std::endl; os << indent << " SizeInDisplayUnits: " << m_SizeInDisplayUnits << std::endl; os << indent << " ScaleFactorMMPerDisplayUni: " << m_ScaleFactorMMPerDisplayUnit << std::endl; } Superclass::PrintSelf(os,indent); } diff --git a/Core/Code/DataManagement/mitkDisplayGeometry.h b/Core/Code/DataManagement/mitkDisplayGeometry.h index fdae96e680..e1263ae100 100644 --- a/Core/Code/DataManagement/mitkDisplayGeometry.h +++ b/Core/Code/DataManagement/mitkDisplayGeometry.h @@ -1,227 +1,229 @@ /*=================================================================== 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 mitkDisplayGeometry_h #define mitkDisplayGeometry_h -#include "mitkGeometry2D.h" +#include "mitkPlaneGeometry.h" namespace mitk { /** \brief Describes the geometry on the display/screen for 2D display. The main purpose of this class is to convert between display coordinates (in display-units) and world coordinates (in mm). DisplayGeometry depends on the size of the display area (widget width and - height, m_SizeInDisplayUnits) and on a Geometry2D (m_WoldGeometry). It + height, m_SizeInDisplayUnits) and on a PlaneGeometry (m_WoldGeometry). It represents a recangular view on this world-geometry. E.g., you can tell the DisplayGeometry to fit the world-geometry in the display area by calling Fit(). Provides methods for zooming and panning. Zooming and panning can be restricted within reasonable bounds by setting the ConstrainZoomingAndPanning flag. In these cases you can re-define what bounds you accept as "reasonable" by calling \warning \em Units refers to the units of the underlying world-geometry. Take care, whether these are really the units you want to convert to. E.g., when you want to convert a point \a pt_display (which is 2D) given in display coordinates into a point in units of a BaseData-object @a datum (the requested point is 3D!), use \code displaygeometry->DisplayToWorld(pt_display, pt2d_mm); displaygeometry->Map(pt2d_mm, pt3d_mm); datum->GetGeometry()->WorldToIndex(pt3d_mm, pt3d_datum_units); \endcode Even, if you want to convert the 2D point \a pt_display into a 2D point in units on a certain 2D geometry \a certaingeometry, it is safer to use \code displaygeometry->DisplayToWorld(pt_display, pt_mm); certaingeometry->WorldToIndex(pt_mm, pt_certain_geometry_units); \endcode unless you can be sure that the underlying geometry of \a displaygeometry is really the \a certaingeometry. \ingroup Geometry */ - class MITK_CORE_EXPORT DisplayGeometry : public Geometry2D + class MITK_CORE_EXPORT DisplayGeometry : public PlaneGeometry { public: - mitkClassMacro(DisplayGeometry,Geometry2D); + mitkClassMacro(DisplayGeometry,PlaneGeometry); /// Method for creation through the object factory. itkNewMacro(Self); /// \brief duplicates the geometry, NOT useful for this sub-class virtual itk::LightObject::Pointer InternalClone() const; /// \return this objects modified time. virtual unsigned long GetMTime() const; virtual const TimeBounds& GetTimeBounds() const; // size definition methods - virtual void SetWorldGeometry(const Geometry2D* aWorldGeometry); - itkGetConstObjectMacro(WorldGeometry, Geometry2D); + virtual void SetWorldGeometry(const PlaneGeometry* aWorldGeometry); + itkGetConstObjectMacro(WorldGeometry, PlaneGeometry); /// \return if new origin was within accepted limits virtual bool SetOriginInMM(const Vector2D& origin_mm); virtual Vector2D GetOriginInMM() const; virtual Vector2D GetOriginInDisplayUnits() const; /** \brief Set the size of the display in display units. This method must be called every time the display is resized (normally, the GUI-toolkit informs about resizing). \param keepDisplayedRegion: if \a true (the default), the displayed contents is zoomed/shrinked so that the displayed region is (approximately) the same as before: The point at the center will be kept at the center and the length of the diagonal of the displayed region \em in \em units will also be kept. When the aspect ration changes, the displayed region includes the old displayed region, but cannot be exaclty the same. */ virtual void SetSizeInDisplayUnits(unsigned int width, unsigned int height, bool keepDisplayedRegion=true); virtual Vector2D GetSizeInDisplayUnits() const; virtual Vector2D GetSizeInMM() const; unsigned int GetDisplayWidth() const; unsigned int GetDisplayHeight() const; // zooming, panning, restriction of both virtual void SetConstrainZoomingAndPanning(bool constrain); virtual bool GetConstrainZommingAndPanning() const; /// what percentage of the world should be visible at maximum zoom out (default 1.0, i.e. 100% of width or height) itkGetMacro(MaxWorldViewPercentage, float); itkSetMacro(MaxWorldViewPercentage, float); /// what percentage of the world should be visible at maximum zoom in (default 0.1, i.e. 10% of width or height) itkGetMacro(MinWorldViewPercentage, float); itkSetMacro(MinWorldViewPercentage, float); virtual bool SetScaleFactor(ScalarType mmPerDisplayUnit); ScalarType GetScaleFactorMMPerDisplayUnit() const; /** * \brief Zooms with a factor (1.0=identity) to/from the specified center in display units * \return true if zoom request was within accepted limits */ virtual bool Zoom(ScalarType factor, const Point2D& centerInDisplayUnits); /** * \brief Zooms with a factor (1.0=identity) to/from the specified center, trying to preserve the center of zoom in world coordiantes * * Same zoom as mentioned above but tries (if it's within view contraints) to match the center in display units with the center in world coordinates. * * \return true if zoom request was within accepted limits */ virtual bool ZoomWithFixedWorldCoordinates(ScalarType factor, const Point2D& focusDisplayUnits, const Point2D& focusUnitsInMM ); // \return true if move request was within accepted limits virtual bool MoveBy(const Vector2D& shiftInDisplayUnits); // \brief align display with world, make world completely visible virtual void Fit(); // conversion methods virtual void DisplayToWorld(const Point2D &pt_display, Point2D &pt_mm) const; virtual void WorldToDisplay(const Point2D &pt_mm, Point2D &pt_display) const; virtual void DisplayToWorld(const Vector2D &vec_display, Vector2D &vec_mm) const; virtual void WorldToDisplay(const Vector2D &vec_mm, Vector2D &vec_display) const; virtual void ULDisplayToMM(const Point2D &pt_ULdisplay, Point2D &pt_mm) const; virtual void MMToULDisplay(const Point2D &pt_mm, Point2D &pt_ULdisplay) const; virtual void ULDisplayToMM(const Vector2D &vec_ULdisplay, Vector2D &vec_mm) const; virtual void MMToULDisplay(const Vector2D &vec_mm, Vector2D &vec_ULdisplay) const; virtual void ULDisplayToDisplay(const Point2D &pt_ULdisplay, Point2D &pt_display) const; virtual void DisplayToULDisplay(const Point2D &pt_display, Point2D &pt_ULdisplay) const; virtual void ULDisplayToDisplay(const Vector2D &vec_ULdisplay, Vector2D &vec_display) const; virtual void DisplayToULDisplay(const Vector2D &vec_display, Vector2D &vec_ULdisplay) const; /** * \brief projects the given point onto current 2D world geometry plane */ virtual bool Project(const Point3D &pt3d_mm, Point3D &projectedPt3d_mm) const; /** * \brief projects the given vector onto current 2D world geometry plane. * \warning DEPRECATED, please use Project(const Vector3D &vec3d_mm, Vector3D &projectedVec3d_mm) instead */ virtual bool Project(const Point3D & atPt3d_mm, const Vector3D &vec3d_mm, Vector3D &projectedVec3d_mm) const; /** * \brief projects the given vector onto current 2D world geometry plane */ virtual bool Project(const Vector3D &vec3d_mm, Vector3D &projectedVec3d_mm) const; virtual bool Map(const Point3D &pt3d_mm, Point2D &pt2d_mm) const; virtual void Map(const Point2D &pt2d_mm, Point3D &pt3d_mm) const; virtual bool Map(const Point3D & atPt3d_mm, const Vector3D &vec3d_mm, Vector2D &vec2d_mm) const; virtual void Map(const Point2D & atPt2d_mm, const Vector2D &vec2d_mm, Vector3D &vec3d_mm) const; virtual bool IsValid() const; + virtual bool IsAbove( const Point3D &pt3d_mm , bool considerBoundingBox=false) const { return Superclass::IsAbove(pt3d_mm, true);}; + protected: DisplayGeometry(); virtual ~DisplayGeometry(); /** \brief Called after zooming/panning to restrict these operations to sensible measures. \return true if a correction in either zooming or panning was made Enforces a couple of constraints on the relation of the current viewport and the current world geometry. The basic logic in this lengthy method is:
  1. Make display region big enough (in case of too large zoom factors)
  2. Make display region small enough (so that the image cannot be scaled into a single screen pixel
  3. Correct panning for each border (left, right, bottom, top)
The little more complicated implementation is illustrated in the code itself. */ virtual bool RefitVisibleRect(); virtual void PrintSelf(std::ostream& os, itk::Indent indent) const; Vector2D m_OriginInMM; Vector2D m_OriginInDisplayUnits; ScalarType m_ScaleFactorMMPerDisplayUnit; Vector2D m_SizeInMM; Vector2D m_SizeInDisplayUnits; - Geometry2D::ConstPointer m_WorldGeometry; + PlaneGeometry::ConstPointer m_WorldGeometry; bool m_ConstrainZoomingAndPanning; float m_MaxWorldViewPercentage; float m_MinWorldViewPercentage; }; } // namespace #endif // include guard diff --git a/Core/Code/DataManagement/mitkGeometry2D.cpp b/Core/Code/DataManagement/mitkGeometry2D.cpp index 3f2f851335..b51839eafe 100644 --- a/Core/Code/DataManagement/mitkGeometry2D.cpp +++ b/Core/Code/DataManagement/mitkGeometry2D.cpp @@ -1,264 +1,15 @@ /*=================================================================== 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 "mitkGeometry2D.h" -#include - -mitk::Geometry2D::Geometry2D() - : m_ScaleFactorMMPerUnitX( 1.0 ), - m_ScaleFactorMMPerUnitY( 1.0 ), - m_ReferenceGeometry( NULL ) -{ -} - -mitk::Geometry2D::Geometry2D(const Geometry2D& other) - : BaseGeometry(other), m_ScaleFactorMMPerUnitX( other.m_ScaleFactorMMPerUnitX), - m_ScaleFactorMMPerUnitY( other.m_ScaleFactorMMPerUnitY), - m_ReferenceGeometry( other.m_ReferenceGeometry ) -{ -} - -mitk::Geometry2D::~Geometry2D() -{ -} - -void - mitk::Geometry2D::InternPostSetIndexToWorldTransform( - mitk::AffineTransform3D* transform) -{ - m_ScaleFactorMMPerUnitX=GetExtentInMM(0)/GetExtent(0); - m_ScaleFactorMMPerUnitY=GetExtentInMM(1)/GetExtent(1); - - assert(m_ScaleFactorMMPerUnitXIsBoundingBoxNull()==false); - - Point3D pt3d_units; - BackTransform(pt3d_mm, pt3d_units); - pt2d_mm[0]=pt3d_units[0]*m_ScaleFactorMMPerUnitX; - pt2d_mm[1]=pt3d_units[1]*m_ScaleFactorMMPerUnitY; - pt3d_units[2]=0; - return const_cast(this->GetBoundingBox())->IsInside(pt3d_units); -} - -void - mitk::Geometry2D::Map(const mitk::Point2D &pt2d_mm, mitk::Point3D &pt3d_mm) const -{ - Point3D pt3d_units; - pt3d_units[0]=pt2d_mm[0]/m_ScaleFactorMMPerUnitX; - pt3d_units[1]=pt2d_mm[1]/m_ScaleFactorMMPerUnitY; - pt3d_units[2]=0; - pt3d_mm = GetIndexToWorldTransform()->TransformPoint(pt3d_units); -} - -void - mitk::Geometry2D::IndexToWorld( - const mitk::Point2D &/*pt_units*/, mitk::Point2D &/*pt_mm*/) const -{ - itkExceptionMacro(<< "No general transform possible (only affine) ==> no general" \ - " IndexToWorld(const mitk::Point2D &pt_mm, mitk::Point2D &pt_units)" \ - " possible. Has to be implemented in sub-class."); -} - -void - mitk::Geometry2D::WorldToIndex( - const mitk::Point2D &/*pt_mm*/, mitk::Point2D &/*pt_units*/) const -{ - itkExceptionMacro(<< "No general back transform possible (only affine) ==> no general" \ - " WorldToIndex(const mitk::Point2D &pt_mm, mitk::Point2D &pt_units)" \ - " possible. Has to be implemented in sub-class."); -} - -void - mitk::Geometry2D::IndexToWorld(const mitk::Point2D &/*atPt2d_units*/, - const mitk::Vector2D &/*vec_units*/, mitk::Vector2D &/*vec_mm*/) const -{ - itkExceptionMacro(<< "No general transform possible (only affine) ==> no general" \ - " IndexToWorld(const mitk::Vector2D &vec_mm, mitk::Vector2D &vec_units)" \ - " possible. Has to be implemented in sub-class."); -} - -void - mitk::Geometry2D::WorldToIndex(const mitk::Point2D &/*atPt2d_mm*/, - const mitk::Vector2D &/*vec_mm*/, mitk::Vector2D &/*vec_units*/) const -{ - itkExceptionMacro(<< "No general back transform possible (only affine) ==> no general" \ - " WorldToIndex(const mitk::Vector2D &vec_mm, mitk::Vector2D &vec_units)" \ - " possible. Has to be implemented in sub-class."); -} - -void - mitk::Geometry2D::SetSizeInUnits(mitk::ScalarType width, mitk::ScalarType height) -{ - ScalarType bounds[6]={0, width, 0, height, 0, 1}; - ScalarType extent, newextentInMM; - if(GetExtent(0)>0) - { - extent = GetExtent(0); - if(width>extent) - newextentInMM = GetExtentInMM(0)/width*extent; - else - newextentInMM = GetExtentInMM(0)*extent/width; - SetExtentInMM(0, newextentInMM); - } - if(GetExtent(1)>0) - { - extent = GetExtent(1); - if(width>extent) - newextentInMM = GetExtentInMM(1)/height*extent; - else - newextentInMM = GetExtentInMM(1)*extent/height; - SetExtentInMM(1, newextentInMM); - } - SetBounds(bounds); -} - -bool - mitk::Geometry2D::Project( - const mitk::Point3D &pt3d_mm, mitk::Point3D &projectedPt3d_mm) const -{ - assert(this->IsBoundingBoxNull()==false); - - Point3D pt3d_units; - BackTransform(pt3d_mm, pt3d_units); - pt3d_units[2] = 0; - projectedPt3d_mm = GetIndexToWorldTransform()->TransformPoint(pt3d_units); - return const_cast(this->GetBoundingBox())->IsInside(pt3d_units); -} - -bool - mitk::Geometry2D::Project(const mitk::Vector3D &vec3d_mm, mitk::Vector3D &projectedVec3d_mm) const -{ - assert(this->IsBoundingBoxNull()==false); - - Vector3D vec3d_units; - BackTransform(vec3d_mm, vec3d_units); - vec3d_units[2] = 0; - projectedVec3d_mm = GetIndexToWorldTransform()->TransformVector(vec3d_units); - return true; -} - -bool - mitk::Geometry2D::Project(const mitk::Point3D & atPt3d_mm, - const mitk::Vector3D &vec3d_mm, mitk::Vector3D &projectedVec3d_mm) const -{ - MITK_WARN << "Deprecated function! Call Project(vec3D,vec3D) instead."; - assert(this->IsBoundingBoxNull()==false); - - Vector3D vec3d_units; - BackTransform(atPt3d_mm, vec3d_mm, vec3d_units); - vec3d_units[2] = 0; - projectedVec3d_mm = GetIndexToWorldTransform()->TransformVector(vec3d_units); - - Point3D pt3d_units; - BackTransform(atPt3d_mm, pt3d_units); - return const_cast(this->GetBoundingBox())->IsInside(pt3d_units); -} - -bool - mitk::Geometry2D::Map(const mitk::Point3D & atPt3d_mm, - const mitk::Vector3D &vec3d_mm, mitk::Vector2D &vec2d_mm) const -{ - Point2D pt2d_mm_start, pt2d_mm_end; - Point3D pt3d_mm_end; - bool inside=Map(atPt3d_mm, pt2d_mm_start); - pt3d_mm_end = atPt3d_mm+vec3d_mm; - inside&=Map(pt3d_mm_end, pt2d_mm_end); - vec2d_mm=pt2d_mm_end-pt2d_mm_start; - return inside; -} - -void - mitk::Geometry2D::Map(const mitk::Point2D &/*atPt2d_mm*/, - const mitk::Vector2D &/*vec2d_mm*/, mitk::Vector3D &/*vec3d_mm*/) const -{ - //@todo implement parallel to the other Map method! - assert(false); -} - -mitk::ScalarType - mitk::Geometry2D::SignedDistance(const mitk::Point3D& pt3d_mm) const -{ - Point3D projectedPoint; - Project(pt3d_mm, projectedPoint); - Vector3D direction = pt3d_mm-projectedPoint; - ScalarType distance = direction.GetNorm(); - - if(IsAbove(pt3d_mm) == false) - distance*=-1.0; - - return distance; -} - -bool - mitk::Geometry2D::IsAbove(const mitk::Point3D& pt3d_mm) const -{ - Point3D pt3d_units; - BaseGeometry::WorldToIndex(pt3d_mm, pt3d_units); - return (pt3d_units[2] > this->GetBoundingBox()->GetBounds()[4]); -} - -itk::LightObject::Pointer - mitk::Geometry2D::InternalClone() const -{ - Self::Pointer newGeometry = new Geometry2D(*this); - newGeometry->UnRegister(); - return newGeometry.GetPointer(); -} - -void - mitk::Geometry2D::PrintSelf(std::ostream& os, itk::Indent indent) const -{ - Superclass::PrintSelf(os,indent); - os << indent << " ScaleFactorMMPerUnitX: " - << m_ScaleFactorMMPerUnitX << std::endl; - os << indent << " ScaleFactorMMPerUnitY: " - << m_ScaleFactorMMPerUnitY << std::endl; -} - -void - mitk::Geometry2D::SetReferenceGeometry( mitk::BaseGeometry *geometry ) -{ - m_ReferenceGeometry = geometry; -} - -mitk::BaseGeometry * - mitk::Geometry2D::GetReferenceGeometry() const -{ - return m_ReferenceGeometry; -} - -bool - mitk::Geometry2D::HasReferenceGeometry() const -{ - return ( m_ReferenceGeometry != NULL ); -} diff --git a/Core/Code/DataManagement/mitkGeometry2D.h b/Core/Code/DataManagement/mitkGeometry2D.h index 15ff5d7efa..b51839eafe 100644 --- a/Core/Code/DataManagement/mitkGeometry2D.h +++ b/Core/Code/DataManagement/mitkGeometry2D.h @@ -1,269 +1,15 @@ /*=================================================================== 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 GEOMETRY2D_H_HEADER_INCLUDED_C1F4D8E0 -#define GEOMETRY2D_H_HEADER_INCLUDED_C1F4D8E0 - -#include -#include "mitkBaseGeometry.h" - -namespace mitk { - /** - * \brief Describes the geometry of a two-dimensional object - * - * Describes a two-dimensional manifold, i.e., to put it simply, - * an object that can be described using a 2D coordinate-system. - * - * Geometry2D can map points between 3D world coordinates - * (in mm) and the described 2D coordinate-system (in mm) by first projecting - * the 3D point onto the 2D manifold and then calculating the 2D-coordinates - * (in mm). These 2D-mm-coordinates can be further converted into - * 2D-unit-coordinates (e.g., pixels), giving a parameter representation of - * the object with parameter values inside a rectangle - * (e.g., [0,0]..[width, height]), which is the bounding box (bounding range - * in z-direction always [0]..[1]). - * - * A Geometry2D describes the 2D representation within a 3D object and is - * therefore itself a Geometry3D (derived from Geometry3D). For example, - * a single CT-image (slice) is 2D in the sense that you can access the - * pixels using 2D-coordinates, but is also 3D, as the pixels are really - * voxels, thus have an extension (thickness) in the 3rd dimension. - * - * Most often, instances of Geometry2D will be used to descibe a plane, - * which is represented by the sub-class PlaneGeometry, but curved - * surfaces are also possible. - * - * Optionally, a reference Geometry3D can be specified, which usually would - * be the geometry associated with the underlying dataset. This is currently - * used for calculating the intersection of inclined / rotated planes - * (represented as Geometry2D) with the bounding box of the associated - * Geometry3D. - * - * \warning The Geometry2Ds are not necessarily up-to-date and not even - * initialized. As described in the previous paragraph, one of the - * Generate-/Copy-/UpdateOutputInformation methods have to initialize it. - * mitk::BaseData::GetGeometry2D() makes sure, that the Geometry2D is - * up-to-date before returning it (by setting the update extent appropriately - * and calling UpdateOutputInformation). - * - * Rule: everything is in mm (or ms for temporal information) if not - * stated otherwise. - * \ingroup Geometry - */ - class MITK_CORE_EXPORT Geometry2D : public mitk::BaseGeometry - { - public: - mitkClassMacro(Geometry2D, mitk::BaseGeometry); - itkNewMacro(Self); - - /** - * \brief Project a 3D point given in mm (\a pt3d_mm) onto the 2D - * geometry. The result is a 2D point in mm (\a pt2d_mm). - * - * The result is a 2D point in mm (\a pt2d_mm) relative to the upper-left - * corner of the geometry. To convert this point into units (e.g., pixels - * in case of an image), use WorldToIndex. - * \return true projection was possible - * \sa Project(const mitk::Point3D &pt3d_mm, mitk::Point3D - * &projectedPt3d_mm) - */ - virtual bool Map(const mitk::Point3D &pt3d_mm, mitk::Point2D &pt2d_mm) const; - - /** - * \brief Converts a 2D point given in mm (\a pt2d_mm) relative to the - * upper-left corner of the geometry into the corresponding - * world-coordinate (a 3D point in mm, \a pt3d_mm). - * - * To convert a 2D point given in units (e.g., pixels in case of an - * image) into a 2D point given in mm (as required by this method), use - * IndexToWorld. - */ - virtual void Map(const mitk::Point2D &pt2d_mm, mitk::Point3D &pt3d_mm) const; - - /** - * \brief Convert a 2D point given in units (e.g., pixels in case of an - * image) into a 2D point given in mm - */ - virtual void IndexToWorld( - const mitk::Point2D &pt_units, mitk::Point2D &pt_mm) const; - - /** - * \brief Convert a 2D point given in mm into a 2D point given in mm - * (e.g., pixels in case of an image) - */ - virtual void WorldToIndex( - const mitk::Point2D &pt_mm, mitk::Point2D &pt_units) const; - - /** - * \brief Convert a 2D vector given in units (e.g., pixels in case of an - * image) into a 2D vector given in mm - * \warning strange: in contrast to vtkTransform the class itk::Transform - * does not have the parameter, \em where the vector that is to be - * transformed is located. This method here should also need this - * information for general transforms. - */ - virtual void IndexToWorld( - const mitk::Point2D &atPt2d_units, const mitk::Vector2D &vec_units, - mitk::Vector2D &vec_mm) const; - - /** - * \brief Convert a 2D vector given in mm into a 2D point vector in mm - * (e.g., pixels in case of an image) - * \warning strange: in contrast to vtkTransform the class itk::Transform - * does not have the parameter, \em where the vector that is to be - * transformed is located. This method here should also need this - * information for general transforms. - */ - virtual void WorldToIndex( - const mitk::Point2D &atPt2d_mm, const mitk::Vector2D &vec_mm, - mitk::Vector2D &vec_units) const; - - /** - * \brief Set the width and height of this 2D-geometry in units by calling - * SetBounds. This does \a not change the extent in mm! - * - * For an image, this is the number of pixels in x-/y-direction. - * \note In contrast to calling SetBounds directly, this does \a not change - * the extent in mm! - */ - virtual void SetSizeInUnits(mitk::ScalarType width, mitk::ScalarType height); - - /** - * \brief Project a 3D point given in mm (\a pt3d_mm) onto the 2D - * geometry. The result is a 3D point in mm (\a projectedPt3d_mm). - * - * \return true projection was possible - */ - virtual bool Project(const mitk::Point3D &pt3d_mm, - mitk::Point3D &projectedPt3d_mm) const; - - /** - * \brief Project a 3D vector given in mm (\a vec3d_mm) onto the 2D - * geometry. The result is a 2D vector in mm (\a vec2d_mm). - * - * The result is a 2D vector in mm (\a vec2d_mm) relative to the - * upper-left - * corner of the geometry. To convert this point into units (e.g., pixels - * in case of an image), use WorldToIndex. - * \return true projection was possible - * \sa Project(const mitk::Vector3D &vec3d_mm, mitk::Vector3D - * &projectedVec3d_mm) - */ - virtual bool Map(const mitk::Point3D & atPt3d_mm, - const mitk::Vector3D &vec3d_mm, mitk::Vector2D &vec2d_mm) const; - - /** - * \brief Converts a 2D vector given in mm (\a vec2d_mm) relative to the - * upper-left corner of the geometry into the corresponding - * world-coordinate (a 3D vector in mm, \a vec3d_mm). - * - * To convert a 2D vector given in units (e.g., pixels in case of an - * image) into a 2D vector given in mm (as required by this method), use - * IndexToWorld. - */ - virtual void Map(const mitk::Point2D & atPt2d_mm, - const mitk::Vector2D &vec2d_mm, mitk::Vector3D &vec3d_mm) const; - - /** - * \brief Project a 3D vector given in mm (\a vec3d_mm) onto the 2D - * geometry. The result is a 3D vector in mm (\a projectedVec3d_mm). - * - * DEPRECATED. Use Project(vector,vector) instead - * - * \return true projection was possible - */ - virtual bool Project(const mitk::Point3D & atPt3d_mm, - const mitk::Vector3D &vec3d_mm, mitk::Vector3D &projectedVec3d_mm) const; - - /** - * \brief Project a 3D vector given in mm (\a vec3d_mm) onto the 2D - * geometry. The result is a 3D vector in mm (\a projectedVec3d_mm). - * - * \return true projection was possible - */ - virtual bool Project( const mitk::Vector3D &vec3d_mm, mitk::Vector3D &projectedVec3d_mm) const; - - /** - * \brief Distance of the point from the geometry - * (bounding-box \em not considered) - * - */ - inline ScalarType Distance(const Point3D& pt3d_mm) const - { - return fabs(SignedDistance(pt3d_mm)); - } - - /** - * \brief Signed distance of the point from the geometry - * (bounding-box \em not considered) - * - */ - virtual ScalarType SignedDistance(const Point3D& pt3d_mm) const; - - /** - * \brief Test if the point is above the geometry - * (bounding-box \em not considered) - * - */ - virtual bool IsAbove(const Point3D& pt3d_mm) const; - - virtual itk::LightObject::Pointer InternalClone() const; - - /** - * \brief Set the geometrical frame of reference in which this Geometry2D - * is placed. - * - * This would usually be the Geometry3D of the underlying dataset, but - * setting it is optional. - */ - void SetReferenceGeometry( mitk::BaseGeometry *geometry ); - - /** - * \brief Get the geometrical frame of reference for this Geometry2D. - */ - BaseGeometry *GetReferenceGeometry() const; - bool HasReferenceGeometry() const; - - protected: - Geometry2D(); - - Geometry2D(const Geometry2D& other); - - virtual ~Geometry2D(); - - virtual void PrintSelf(std::ostream& os, itk::Indent indent) const; - - virtual void InternPostSetExtentInMM(int direction, ScalarType extentInMM); - - virtual void InternPostSetIndexToWorldTransform(mitk::AffineTransform3D* transform); - - /** - * \brief factor to convert x-coordinates from mm to units and vice versa - * - */ - mutable mitk::ScalarType m_ScaleFactorMMPerUnitX; - - /** - * \brief factor to convert y-coordinates from mm to units and vice versa - * - */ - mutable mitk::ScalarType m_ScaleFactorMMPerUnitY; - - mitk::BaseGeometry *m_ReferenceGeometry; - }; -} // namespace mitk - -#endif /* GEOMETRY2D_H_HEADER_INCLUDED_C1F4D8E0 */ diff --git a/Core/Code/DataManagement/mitkGeometry2DData.cpp b/Core/Code/DataManagement/mitkGeometry2DData.cpp index 8261754513..6e7b6e9e20 100644 --- a/Core/Code/DataManagement/mitkGeometry2DData.cpp +++ b/Core/Code/DataManagement/mitkGeometry2DData.cpp @@ -1,87 +1,87 @@ /*=================================================================== 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 "mitkGeometry2DData.h" #include "mitkBaseProcess.h" #include mitk::Geometry2DData::Geometry2DData() { } mitk::Geometry2DData::~Geometry2DData() { } void mitk::Geometry2DData::SetGeometry(mitk::BaseGeometry *geometry) { if(geometry==NULL) SetGeometry2D(NULL); else { - Geometry2D* geometry2d = dynamic_cast(geometry); + PlaneGeometry* geometry2d = dynamic_cast(geometry); if(geometry2d==NULL) - itkExceptionMacro(<<"Trying to set a geometry which is not a Geometry2D into Geometry2DData."); + itkExceptionMacro(<<"Trying to set a geometry which is not a PlaneGeometry into Geometry2DData."); SetGeometry2D(geometry2d); } } -void mitk::Geometry2DData::SetGeometry2D(mitk::Geometry2D *geometry2d) +void mitk::Geometry2DData::SetGeometry2D(mitk::PlaneGeometry *geometry2d) { if(geometry2d != NULL) { ProportionalTimeGeometry::Pointer timeGeometry = ProportionalTimeGeometry::New(); timeGeometry->Initialize(geometry2d, 1); SetTimeGeometry(timeGeometry); Modified(); } else Superclass::SetGeometry(geometry2d); } void mitk::Geometry2DData::UpdateOutputInformation() { Superclass::UpdateOutputInformation(); } void mitk::Geometry2DData::SetRequestedRegionToLargestPossibleRegion() { } bool mitk::Geometry2DData::RequestedRegionIsOutsideOfTheBufferedRegion() { if(GetGeometry2D()==NULL) return true; return false; } bool mitk::Geometry2DData::VerifyRequestedRegion() { if(GetGeometry2D()==NULL) return false; return true; } void mitk::Geometry2DData::SetRequestedRegion( const itk::DataObject *) { } void mitk::Geometry2DData::CopyInformation(const itk::DataObject *) { } diff --git a/Core/Code/DataManagement/mitkGeometry2DData.h b/Core/Code/DataManagement/mitkGeometry2DData.h index 9432f1aef7..ecf2efc1fa 100644 --- a/Core/Code/DataManagement/mitkGeometry2DData.h +++ b/Core/Code/DataManagement/mitkGeometry2DData.h @@ -1,77 +1,77 @@ /*=================================================================== 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 MITKGEOMETRY2DDATA_H_HEADER_INCLUDED_C19C01E2 #define MITKGEOMETRY2DDATA_H_HEADER_INCLUDED_C19C01E2 #include #include "mitkBaseData.h" #include "mitkGeometryData.h" -#include "mitkGeometry2D.h" +#include "mitkPlaneGeometry.h" namespace mitk { //##Documentation -//## @brief Data class containing Geometry2D objects +//## @brief Data class containing PlaneGeometry objects //## @ingroup Geometry //## class MITK_CORE_EXPORT Geometry2DData : public GeometryData { public: mitkClassMacro(Geometry2DData, GeometryData); itkNewMacro(Self); //##Documentation - //## @brief Set the reference to a Geometry2D that is stored + //## @brief Set the reference to a PlaneGeometry that is stored //## by the object //## - //## @warning Accepts only instances of Geometry2D or sub-classes. + //## @warning Accepts only instances of PlaneGeometry or sub-classes. virtual void SetGeometry(mitk::BaseGeometry *geometry); //##Documentation - //## @brief Set the reference to the Geometry2D that is stored + //## @brief Set the reference to the PlaneGeometry that is stored //## by the object - virtual void SetGeometry2D(mitk::Geometry2D* geometry2d); + virtual void SetGeometry2D(mitk::PlaneGeometry* geometry2d); //##Documentation - //## @brief Get the reference to the Geometry2D that is stored + //## @brief Get the reference to the PlaneGeometry that is stored //## by the object - virtual mitk::Geometry2D * GetGeometry2D() const + virtual mitk::PlaneGeometry * GetGeometry2D() const { - return static_cast(GetGeometry()); + return static_cast(GetGeometry()); }; virtual void UpdateOutputInformation(); virtual void SetRequestedRegionToLargestPossibleRegion(); virtual bool RequestedRegionIsOutsideOfTheBufferedRegion(); virtual bool VerifyRequestedRegion(); virtual void SetRequestedRegion( const itk::DataObject *data); virtual void CopyInformation(const itk::DataObject *data); protected: Geometry2DData(); virtual ~Geometry2DData(); }; } // namespace mitk #endif /* MITKGEOMETRY2DDATA_H_HEADER_INCLUDED_C19C01E2 */ diff --git a/Core/Code/DataManagement/mitkImage.cpp b/Core/Code/DataManagement/mitkImage.cpp index fb9a4564f1..5f34269f4e 100644 --- a/Core/Code/DataManagement/mitkImage.cpp +++ b/Core/Code/DataManagement/mitkImage.cpp @@ -1,1384 +1,1384 @@ /*=================================================================== 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. ===================================================================*/ //MITK #include "mitkImage.h" #include "mitkImageStatisticsHolder.h" #include "mitkPixelTypeMultiplex.h" #include #include "mitkCompareImageDataFilter.h" //VTK #include //Other #include #define FILL_C_ARRAY( _arr, _size, _value) for(unsigned int i=0u; i<_size; i++) \ { _arr[i] = _value; } mitk::Image::Image() : m_Dimension(0), m_Dimensions(NULL), m_ImageDescriptor(NULL), m_OffsetTable(NULL), m_CompleteData(NULL), m_ImageStatistics(NULL) { m_Dimensions = new unsigned int[MAX_IMAGE_DIMENSIONS]; FILL_C_ARRAY( m_Dimensions, MAX_IMAGE_DIMENSIONS, 0u); m_Initialized = false; } mitk::Image::Image(const Image &other) : SlicedData(other), m_Dimension(0), m_Dimensions(NULL), m_ImageDescriptor(NULL), m_OffsetTable(NULL), m_CompleteData(NULL), m_ImageStatistics(NULL) { m_Dimensions = new unsigned int[MAX_IMAGE_DIMENSIONS]; FILL_C_ARRAY( m_Dimensions, MAX_IMAGE_DIMENSIONS, 0u); this->Initialize( other.GetPixelType(), other.GetDimension(), other.GetDimensions()); //Since the above called "Initialize" method doesn't take the geometry into account we need to set it //here manually TimeGeometry::Pointer cloned = other.GetTimeGeometry()->Clone(); this->SetTimeGeometry(cloned.GetPointer()); if (this->GetDimension() > 3) { const unsigned int time_steps = this->GetDimension(3); for (unsigned int i = 0u; i < time_steps; ++i) { ImageDataItemPointer volume = const_cast(other).GetVolumeData(i); this->SetVolume(volume->GetData(), i); } } else { ImageDataItemPointer volume = const_cast(other).GetVolumeData(0); this->SetVolume(volume->GetData(), 0); } } mitk::Image::~Image() { Clear(); m_ReferenceCountLock.Lock(); m_ReferenceCount = 3; m_ReferenceCountLock.Unlock(); m_ReferenceCountLock.Lock(); m_ReferenceCount = 0; m_ReferenceCountLock.Unlock(); if(m_OffsetTable != NULL) delete [] m_OffsetTable; if(m_ImageStatistics != NULL) delete m_ImageStatistics; } const mitk::PixelType mitk::Image::GetPixelType(int n) const { return this->m_ImageDescriptor->GetChannelTypeById(n); } unsigned int mitk::Image::GetDimension() const { return m_Dimension; } unsigned int mitk::Image::GetDimension(int i) const { if((i>=0) && (i<(int)m_Dimension)) return m_Dimensions[i]; return 1; } void* mitk::Image::GetData() { if(m_Initialized==false) { if(GetSource().IsNull()) return NULL; if(GetSource()->Updating()==false) GetSource()->UpdateOutputInformation(); } m_CompleteData=GetChannelData(); // update channel's data // if data was not available at creation point, the m_Data of channel descriptor is NULL // if data present, it won't be overwritten m_ImageDescriptor->GetChannelDescriptor(0).SetData(m_CompleteData->GetData()); return m_CompleteData->GetData(); } template void AccessPixel( const mitk::PixelType ptype, void* data, const unsigned int offset, double& value ) { value = 0.0; if( data == NULL ) return; if(ptype.GetBpe() != 24) { value = (double) (((T*) data)[ offset ]); } else { const unsigned int rgboffset = 3 * offset; double returnvalue = (((T*) data)[rgboffset ]); returnvalue += (((T*) data)[rgboffset + 1]); returnvalue += (((T*) data)[rgboffset + 2]); value = returnvalue; } } double mitk::Image::GetPixelValueByIndex(const mitk::Index3D &position, unsigned int timestep) { double value = 0; if (this->GetTimeSteps() < timestep) { timestep = this->GetTimeSteps(); } value = 0.0; const unsigned int* imageDims = this->m_ImageDescriptor->GetDimensions(); const mitk::PixelType ptype = this->m_ImageDescriptor->GetChannelTypeById(0); // Comparison ?>=0 not needed since all position[i] and timestep are unsigned int // (position[0]>=0 && position[1] >=0 && position[2]>=0 && timestep>=0) // bug-11978 : we still need to catch index with negative values if ( position[0] < 0 || position[1] < 0 || position[2] < 0 ) { MITK_WARN << "Given position ("<< position << ") is out of image range, returning 0." ; } // check if the given position is inside the index range of the image, the 3rd dimension needs to be compared only if the dimension is not 0 else if ( (unsigned int)position[0] >= imageDims[0] || (unsigned int)position[1] >= imageDims[1] || ( imageDims[2] && (unsigned int)position[2] >= imageDims[2] )) { MITK_WARN << "Given position ("<< position << ") is out of image range, returning 0." ; } else { const unsigned int offset = position[0] + position[1]*imageDims[0] + position[2]*imageDims[0]*imageDims[1] + timestep*imageDims[0]*imageDims[1]*imageDims[2]; mitkPixelTypeMultiplex3( AccessPixel, ptype, this->GetData(), offset, value ); } return value; } double mitk::Image::GetPixelValueByWorldCoordinate(const mitk::Point3D& position, unsigned int timestep) { double value = 0.0; if (this->GetTimeSteps() < timestep) { timestep = this->GetTimeSteps(); } Index3D itkIndex; this->GetGeometry()->WorldToIndex(position, itkIndex); value = this->GetPixelValueByIndex( itkIndex, timestep); return value; } mitk::ImageVtkAccessor* mitk::Image::GetVtkImageData(int t, int n) { if(m_Initialized==false) { if(GetSource().IsNull()) return NULL; if(GetSource()->Updating()==false) GetSource()->UpdateOutputInformation(); } ImageDataItemPointer volume=GetVolumeData(t, n); if(volume.GetPointer()==NULL || volume->GetVtkImageData(this) == NULL) return NULL; SlicedGeometry3D* geom3d = GetSlicedGeometry(t); float *fspacing = const_cast(geom3d->GetFloatSpacing()); double dspacing[3] = {fspacing[0],fspacing[1],fspacing[2]}; volume->GetVtkImageData(this)->SetSpacing( dspacing ); return volume->GetVtkImageData(this); } mitk::Image::ImageDataItemPointer mitk::Image::GetSliceData(int s, int t, int n, void *data, ImportMemoryManagementType importMemoryManagement) { if(IsValidSlice(s,t,n)==false) return NULL; const size_t ptypeSize = this->m_ImageDescriptor->GetChannelTypeById(n).GetSize(); // slice directly available? int pos=GetSliceIndex(s,t,n); if(m_Slices[pos].GetPointer()!=NULL) return m_Slices[pos]; // is slice available as part of a volume that is available? ImageDataItemPointer sl, ch, vol; vol=m_Volumes[GetVolumeIndex(t,n)]; if((vol.GetPointer()!=NULL) && (vol->IsComplete())) { sl=new ImageDataItem(*vol, m_ImageDescriptor, 2, data, importMemoryManagement == ManageMemory, ((size_t) s)*m_OffsetTable[2]*(ptypeSize)); sl->SetComplete(true); return m_Slices[pos]=sl; } // is slice available as part of a channel that is available? ch=m_Channels[n]; if((ch.GetPointer()!=NULL) && (ch->IsComplete())) { sl=new ImageDataItem(*ch, m_ImageDescriptor, 2, data, importMemoryManagement == ManageMemory, (((size_t) s)*m_OffsetTable[2]+((size_t) t)*m_OffsetTable[3])*(ptypeSize)); sl->SetComplete(true); return m_Slices[pos]=sl; } // slice is unavailable. Can we calculate it? if((GetSource().IsNotNull()) && (GetSource()->Updating()==false)) { // ... wir mussen rechnen!!! .... m_RequestedRegion.SetIndex(0, 0); m_RequestedRegion.SetIndex(1, 0); m_RequestedRegion.SetIndex(2, s); m_RequestedRegion.SetIndex(3, t); m_RequestedRegion.SetIndex(4, n); m_RequestedRegion.SetSize(0, m_Dimensions[0]); m_RequestedRegion.SetSize(1, m_Dimensions[1]); m_RequestedRegion.SetSize(2, 1); m_RequestedRegion.SetSize(3, 1); m_RequestedRegion.SetSize(4, 1); m_RequestedRegionInitialized=true; GetSource()->Update(); if(IsSliceSet(s,t,n)) //yes: now we can call ourselves without the risk of a endless loop (see "if" above) return GetSliceData(s,t,n,data,importMemoryManagement); else return NULL; } else { ImageDataItemPointer item = AllocateSliceData(s,t,n,data,importMemoryManagement); item->SetComplete(true); return item; } } mitk::Image::ImageDataItemPointer mitk::Image::GetVolumeData(int t, int n, void *data, ImportMemoryManagementType importMemoryManagement) { if(IsValidVolume(t,n)==false) return NULL; ImageDataItemPointer ch, vol; // volume directly available? int pos=GetVolumeIndex(t,n); vol=m_Volumes[pos]; if((vol.GetPointer()!=NULL) && (vol->IsComplete())) return vol; const size_t ptypeSize = this->m_ImageDescriptor->GetChannelTypeById(n).GetSize(); // is volume available as part of a channel that is available? ch=m_Channels[n]; if((ch.GetPointer()!=NULL) && (ch->IsComplete())) { vol=new ImageDataItem(*ch, m_ImageDescriptor, 3, data, importMemoryManagement == ManageMemory, (((size_t) t)*m_OffsetTable[3])*(ptypeSize)); vol->SetComplete(true); return m_Volumes[pos]=vol; } // let's see if all slices of the volume are set, so that we can (could) combine them to a volume bool complete=true; unsigned int s; for(s=0;sSetComplete(true); } else { mitk::PixelType chPixelType = this->m_ImageDescriptor->GetChannelTypeById(n); vol=m_Volumes[pos]; // ok, let's combine the slices! if(vol.GetPointer()==NULL) vol=new ImageDataItem( chPixelType, 3, m_Dimensions, NULL, true); vol->SetComplete(true); size_t size=m_OffsetTable[2]*(ptypeSize); for(s=0;sGetParent()!=vol) { // copy data of slices in volume size_t offset = ((size_t) s)*size; std::memcpy(static_cast(vol->GetData())+offset, sl->GetData(), size); // FIXME mitkIpPicDescriptor * pic = sl->GetPicDescriptor(); // replace old slice with reference to volume sl=new ImageDataItem(*vol, m_ImageDescriptor, 2, data, importMemoryManagement == ManageMemory, ((size_t) s)*size); sl->SetComplete(true); //mitkIpFuncCopyTags(sl->GetPicDescriptor(), pic); m_Slices[posSl]=sl; } } //if(vol->GetPicDescriptor()->info->tags_head==NULL) // mitkIpFuncCopyTags(vol->GetPicDescriptor(), m_Slices[GetSliceIndex(0,t,n)]->GetPicDescriptor()); } return m_Volumes[pos]=vol; } // volume is unavailable. Can we calculate it? if((GetSource().IsNotNull()) && (GetSource()->Updating()==false)) { // ... wir muessen rechnen!!! .... m_RequestedRegion.SetIndex(0, 0); m_RequestedRegion.SetIndex(1, 0); m_RequestedRegion.SetIndex(2, 0); m_RequestedRegion.SetIndex(3, t); m_RequestedRegion.SetIndex(4, n); m_RequestedRegion.SetSize(0, m_Dimensions[0]); m_RequestedRegion.SetSize(1, m_Dimensions[1]); m_RequestedRegion.SetSize(2, m_Dimensions[2]); m_RequestedRegion.SetSize(3, 1); m_RequestedRegion.SetSize(4, 1); m_RequestedRegionInitialized=true; GetSource()->Update(); if(IsVolumeSet(t,n)) //yes: now we can call ourselves without the risk of a endless loop (see "if" above) return GetVolumeData(t,n,data,importMemoryManagement); else return NULL; } else { ImageDataItemPointer item = AllocateVolumeData(t,n,data,importMemoryManagement); item->SetComplete(true); return item; } } mitk::Image::ImageDataItemPointer mitk::Image::GetChannelData(int n, void *data, ImportMemoryManagementType importMemoryManagement) { if(IsValidChannel(n)==false) return NULL; ImageDataItemPointer ch, vol; ch=m_Channels[n]; if((ch.GetPointer()!=NULL) && (ch->IsComplete())) return ch; // let's see if all volumes are set, so that we can (could) combine them to a channel if(IsChannelSet(n)) { // if there is only one time frame we do not need to combine anything if(m_Dimensions[3]<=1) { vol=GetVolumeData(0,n,data,importMemoryManagement); ch=new ImageDataItem(*vol, m_ImageDescriptor, m_ImageDescriptor->GetNumberOfDimensions(), data, importMemoryManagement == ManageMemory); ch->SetComplete(true); } else { const size_t ptypeSize = this->m_ImageDescriptor->GetChannelTypeById(n).GetSize(); ch=m_Channels[n]; // ok, let's combine the volumes! if(ch.GetPointer()==NULL) ch=new ImageDataItem(this->m_ImageDescriptor, NULL, true); ch->SetComplete(true); size_t size=m_OffsetTable[m_Dimension-1]*(ptypeSize); unsigned int t; ImageDataItemPointerArray::iterator slicesIt = m_Slices.begin()+n*m_Dimensions[2]*m_Dimensions[3]; for(t=0;tGetParent()!=ch) { // copy data of volume in channel size_t offset = ((size_t) t)*m_OffsetTable[3]*(ptypeSize); std::memcpy(static_cast(ch->GetData())+offset, vol->GetData(), size); // REVEIW FIX mitkIpPicDescriptor * pic = vol->GetPicDescriptor(); // replace old volume with reference to channel vol=new ImageDataItem(*ch, m_ImageDescriptor, 3, data, importMemoryManagement == ManageMemory, offset); vol->SetComplete(true); //mitkIpFuncCopyTags(vol->GetPicDescriptor(), pic); m_Volumes[posVol]=vol; // get rid of slices - they may point to old volume ImageDataItemPointer dnull=NULL; for(unsigned int i = 0; i < m_Dimensions[2]; ++i, ++slicesIt) { assert(slicesIt != m_Slices.end()); *slicesIt = dnull; } } } // REVIEW FIX // if(ch->GetPicDescriptor()->info->tags_head==NULL) // mitkIpFuncCopyTags(ch->GetPicDescriptor(), m_Volumes[GetVolumeIndex(0,n)]->GetPicDescriptor()); } return m_Channels[n]=ch; } // channel is unavailable. Can we calculate it? if((GetSource().IsNotNull()) && (GetSource()->Updating()==false)) { // ... wir muessen rechnen!!! .... m_RequestedRegion.SetIndex(0, 0); m_RequestedRegion.SetIndex(1, 0); m_RequestedRegion.SetIndex(2, 0); m_RequestedRegion.SetIndex(3, 0); m_RequestedRegion.SetIndex(4, n); m_RequestedRegion.SetSize(0, m_Dimensions[0]); m_RequestedRegion.SetSize(1, m_Dimensions[1]); m_RequestedRegion.SetSize(2, m_Dimensions[2]); m_RequestedRegion.SetSize(3, m_Dimensions[3]); m_RequestedRegion.SetSize(4, 1); m_RequestedRegionInitialized=true; GetSource()->Update(); // did it work? if(IsChannelSet(n)) //yes: now we can call ourselves without the risk of a endless loop (see "if" above) return GetChannelData(n,data,importMemoryManagement); else return NULL; } else { ImageDataItemPointer item = AllocateChannelData(n,data,importMemoryManagement); item->SetComplete(true); return item; } } bool mitk::Image::IsSliceSet(int s, int t, int n) const { if(IsValidSlice(s,t,n)==false) return false; if(m_Slices[GetSliceIndex(s,t,n)].GetPointer()!=NULL) return true; ImageDataItemPointer ch, vol; vol=m_Volumes[GetVolumeIndex(t,n)]; if((vol.GetPointer()!=NULL) && (vol->IsComplete())) return true; ch=m_Channels[n]; if((ch.GetPointer()!=NULL) && (ch->IsComplete())) return true; return false; } bool mitk::Image::IsVolumeSet(int t, int n) const { if(IsValidVolume(t,n)==false) return false; ImageDataItemPointer ch, vol; // volume directly available? vol=m_Volumes[GetVolumeIndex(t,n)]; if((vol.GetPointer()!=NULL) && (vol->IsComplete())) return true; // is volume available as part of a channel that is available? ch=m_Channels[n]; if((ch.GetPointer()!=NULL) && (ch->IsComplete())) return true; // let's see if all slices of the volume are set, so that we can (could) combine them to a volume unsigned int s; for(s=0;sIsComplete())) return true; // let's see if all volumes are set, so that we can (could) combine them to a channel unsigned int t; for(t=0;t(data), s, t, n, CopyMemory); } bool mitk::Image::SetVolume(const void *data, int t, int n) { // const_cast is no risk for ImportMemoryManagementType == CopyMemory return SetImportVolume(const_cast(data), t, n, CopyMemory); } bool mitk::Image::SetChannel(const void *data, int n) { // const_cast is no risk for ImportMemoryManagementType == CopyMemory return SetImportChannel(const_cast(data), n, CopyMemory); } bool mitk::Image::SetImportSlice(void *data, int s, int t, int n, ImportMemoryManagementType importMemoryManagement) { if(IsValidSlice(s,t,n)==false) return false; ImageDataItemPointer sl; const size_t ptypeSize = this->m_ImageDescriptor->GetChannelTypeById(n).GetSize(); if(IsSliceSet(s,t,n)) { sl=GetSliceData(s,t,n,data,importMemoryManagement); if(sl->GetManageMemory()==false) { sl=AllocateSliceData(s,t,n,data,importMemoryManagement); if(sl.GetPointer()==NULL) return false; } if ( sl->GetData() != data ) std::memcpy(sl->GetData(), data, m_OffsetTable[2]*(ptypeSize)); sl->Modified(); //we have changed the data: call Modified()! Modified(); } else { sl=AllocateSliceData(s,t,n,data,importMemoryManagement); if(sl.GetPointer()==NULL) return false; if ( sl->GetData() != data ) std::memcpy(sl->GetData(), data, m_OffsetTable[2]*(ptypeSize)); //we just added a missing slice, which is not regarded as modification. //Therefore, we do not call Modified()! } return true; } bool mitk::Image::SetImportVolume(void *data, int t, int n, ImportMemoryManagementType importMemoryManagement) { if(IsValidVolume(t,n)==false) return false; const size_t ptypeSize = this->m_ImageDescriptor->GetChannelTypeById(n).GetSize(); ImageDataItemPointer vol; if(IsVolumeSet(t,n)) { vol=GetVolumeData(t,n,data,importMemoryManagement); if(vol->GetManageMemory()==false) { vol=AllocateVolumeData(t,n,data,importMemoryManagement); if(vol.GetPointer()==NULL) return false; } if ( vol->GetData() != data ) std::memcpy(vol->GetData(), data, m_OffsetTable[3]*(ptypeSize)); vol->Modified(); vol->SetComplete(true); //we have changed the data: call Modified()! Modified(); } else { vol=AllocateVolumeData(t,n,data,importMemoryManagement); if(vol.GetPointer()==NULL) return false; if ( vol->GetData() != data ) { std::memcpy(vol->GetData(), data, m_OffsetTable[3]*(ptypeSize)); } vol->SetComplete(true); this->m_ImageDescriptor->GetChannelDescriptor(n).SetData( vol->GetData() ); //we just added a missing Volume, which is not regarded as modification. //Therefore, we do not call Modified()! } return true; } bool mitk::Image::SetImportChannel(void *data, int n, ImportMemoryManagementType importMemoryManagement) { if(IsValidChannel(n)==false) return false; // channel descriptor const size_t ptypeSize = this->m_ImageDescriptor->GetChannelTypeById(n).GetSize(); ImageDataItemPointer ch; if(IsChannelSet(n)) { ch=GetChannelData(n,data,importMemoryManagement); if(ch->GetManageMemory()==false) { ch=AllocateChannelData(n,data,importMemoryManagement); if(ch.GetPointer()==NULL) return false; } if ( ch->GetData() != data ) std::memcpy(ch->GetData(), data, m_OffsetTable[4]*(ptypeSize)); ch->Modified(); ch->SetComplete(true); //we have changed the data: call Modified()! Modified(); } else { ch=AllocateChannelData(n,data,importMemoryManagement); if(ch.GetPointer()==NULL) return false; if ( ch->GetData() != data ) std::memcpy(ch->GetData(), data, m_OffsetTable[4]*(ptypeSize)); ch->SetComplete(true); this->m_ImageDescriptor->GetChannelDescriptor(n).SetData( ch->GetData() ); //we just added a missing Channel, which is not regarded as modification. //Therefore, we do not call Modified()! } return true; } void mitk::Image::Initialize() { ImageDataItemPointerArray::iterator it, end; for( it=m_Slices.begin(), end=m_Slices.end(); it!=end; ++it ) { (*it)=NULL; } for( it=m_Volumes.begin(), end=m_Volumes.end(); it!=end; ++it ) { (*it)=NULL; } for( it=m_Channels.begin(), end=m_Channels.end(); it!=end; ++it ) { (*it)=NULL; } m_CompleteData = NULL; if( m_ImageStatistics == NULL) { m_ImageStatistics = new mitk::ImageStatisticsHolder( this ); } SetRequestedRegionToLargestPossibleRegion(); } void mitk::Image::Initialize(const mitk::ImageDescriptor::Pointer inDesc) { // store the descriptor this->m_ImageDescriptor = inDesc; // initialize image this->Initialize( inDesc->GetChannelDescriptor(0).GetPixelType(), inDesc->GetNumberOfDimensions(), inDesc->GetDimensions(), 1 ); } void mitk::Image::Initialize(const mitk::PixelType& type, unsigned int dimension, const unsigned int *dimensions, unsigned int channels) { Clear(); m_Dimension=dimension; if(!dimensions) itkExceptionMacro(<< "invalid zero dimension image"); unsigned int i; for(i=0;im_ImageDescriptor = mitk::ImageDescriptor::New(); this->m_ImageDescriptor->Initialize( this->m_Dimensions, this->m_Dimension ); for(i=0;i<4;++i) { m_LargestPossibleRegion.SetIndex(i, 0); m_LargestPossibleRegion.SetSize (i, m_Dimensions[i]); } m_LargestPossibleRegion.SetIndex(i, 0); m_LargestPossibleRegion.SetSize(i, channels); if(m_LargestPossibleRegion.GetNumberOfPixels()==0) { delete [] m_Dimensions; m_Dimensions = NULL; return; } for( unsigned int i=0u; im_ImageDescriptor->AddNewChannel( type ); } PlaneGeometry::Pointer planegeometry = PlaneGeometry::New(); planegeometry->InitializeStandardPlane(m_Dimensions[0], m_Dimensions[1]); SlicedGeometry3D::Pointer slicedGeometry = SlicedGeometry3D::New(); slicedGeometry->InitializeEvenlySpaced(planegeometry, m_Dimensions[2]); if(dimension>=4) { TimeBounds timebounds; timebounds[0] = 0.0; timebounds[1] = 1.0; slicedGeometry->SetTimeBounds(timebounds); } ProportionalTimeGeometry::Pointer timeGeometry = ProportionalTimeGeometry::New(); timeGeometry->Initialize(slicedGeometry, m_Dimensions[3]); for (TimeStepType step = 0; step < timeGeometry->CountTimeSteps(); ++step) { timeGeometry->GetGeometryForTimeStep(step)->ImageGeometryOn(); } SetTimeGeometry(timeGeometry); ImageDataItemPointer dnull=NULL; m_Channels.assign(GetNumberOfChannels(), dnull); m_Volumes.assign(GetNumberOfChannels()*m_Dimensions[3], dnull); m_Slices.assign(GetNumberOfChannels()*m_Dimensions[3]*m_Dimensions[2], dnull); ComputeOffsetTable(); Initialize(); m_Initialized = true; } void mitk::Image::Initialize(const mitk::PixelType& type, const mitk::BaseGeometry& geometry, unsigned int channels, int tDim ) { mitk::ProportionalTimeGeometry::Pointer timeGeometry = ProportionalTimeGeometry::New(); BaseGeometry::Pointer geometry3D = geometry.Clone(); timeGeometry->Initialize(geometry3D.GetPointer(), tDim); this->Initialize(type, *timeGeometry, channels, tDim); } void mitk::Image::Initialize(const mitk::PixelType& type, const mitk::TimeGeometry& geometry, unsigned int channels, int tDim ) { unsigned int dimensions[5]; dimensions[0] = (unsigned int)(geometry.GetGeometryForTimeStep(0)->GetExtent(0)+0.5); dimensions[1] = (unsigned int)(geometry.GetGeometryForTimeStep(0)->GetExtent(1)+0.5); dimensions[2] = (unsigned int)(geometry.GetGeometryForTimeStep(0)->GetExtent(2)+0.5); dimensions[3] = (tDim > 0) ? tDim : geometry.CountTimeSteps(); dimensions[4] = 0; unsigned int dimension = 2; if ( dimensions[2] > 1 ) dimension = 3; if ( dimensions[3] > 1 ) dimension = 4; Initialize( type, dimension, dimensions, channels ); if (geometry.CountTimeSteps() > 1) { TimeGeometry::Pointer cloned = geometry.Clone(); SetTimeGeometry(cloned.GetPointer()); } else Superclass::SetGeometry(geometry.GetGeometryForTimeStep(0)); /* //Old //TODO_GOETZ Really necessary? mitk::BoundingBox::BoundsArrayType bounds = geometry.GetBoundingBoxInWorld()->GetBounds(); if( (bounds[0] != 0.0) || (bounds[2] != 0.0) || (bounds[4] != 0.0) ) { SlicedGeometry3D* slicedGeometry = GetSlicedGeometry(0); mitk::Point3D origin; origin.Fill(0.0); slicedGeometry->IndexToWorld(origin, origin); bounds[1]-=bounds[0]; bounds[3]-=bounds[2]; bounds[5]-=bounds[4]; bounds[0] = 0.0; bounds[2] = 0.0; bounds[4] = 0.0; this->m_ImageDescriptor->Initialize( this->m_Dimensions, this->m_Dimension ); slicedGeometry->SetBounds(bounds); slicedGeometry->GetIndexToWorldTransform()->SetOffset(origin.GetVnlVector().data_block()); ProportionalTimeGeometry::Pointer timeGeometry = ProportionalTimeGeometry::New(); timeGeometry->Initialize(slicedGeometry, m_Dimensions[3]); SetTimeGeometry(timeGeometry); }*/ } -void mitk::Image::Initialize(const mitk::PixelType& type, int sDim, const mitk::Geometry2D& geometry2d, bool flipped, unsigned int channels, int tDim ) +void mitk::Image::Initialize(const mitk::PixelType& type, int sDim, const mitk::PlaneGeometry& geometry2d, bool flipped, unsigned int channels, int tDim ) { SlicedGeometry3D::Pointer slicedGeometry = SlicedGeometry3D::New(); - slicedGeometry->InitializeEvenlySpaced(static_cast(geometry2d.Clone().GetPointer()), sDim, flipped); + slicedGeometry->InitializeEvenlySpaced(static_cast(geometry2d.Clone().GetPointer()), sDim, flipped); Initialize(type, *slicedGeometry, channels, tDim); } void mitk::Image::Initialize(const mitk::Image* image) { Initialize(image->GetPixelType(), *image->GetTimeGeometry()); } void mitk::Image::Initialize(vtkImageData* vtkimagedata, int channels, int tDim, int sDim, int pDim) { if(vtkimagedata==NULL) return; m_Dimension=vtkimagedata->GetDataDimension(); unsigned int i, *tmpDimensions=new unsigned int[m_Dimension>4?m_Dimension:4]; for(i=0;iGetDimensions()[i]; if(m_Dimension<4) { unsigned int *p; for(i=0,p=tmpDimensions+m_Dimension;i<4-m_Dimension;++i, ++p) *p=1; } if(pDim>=0) { tmpDimensions[1]=pDim; if(m_Dimension < 2) m_Dimension = 2; } if(sDim>=0) { tmpDimensions[2]=sDim; if(m_Dimension < 3) m_Dimension = 3; } if(tDim>=0) { tmpDimensions[3]=tDim; if(m_Dimension < 4) m_Dimension = 4; } switch ( vtkimagedata->GetScalarType() ) { case VTK_BIT: case VTK_CHAR: //pixelType.Initialize(typeid(char), vtkimagedata->GetNumberOfScalarComponents()); Initialize(mitk::MakeScalarPixelType(), m_Dimension, tmpDimensions, channels); break; case VTK_UNSIGNED_CHAR: //pixelType.Initialize(typeid(unsigned char), vtkimagedata->GetNumberOfScalarComponents()); Initialize(mitk::MakeScalarPixelType(), m_Dimension, tmpDimensions, channels); break; case VTK_SHORT: //pixelType.Initialize(typeid(short), vtkimagedata->GetNumberOfScalarComponents()); Initialize(mitk::MakeScalarPixelType(), m_Dimension, tmpDimensions, channels); break; case VTK_UNSIGNED_SHORT: //pixelType.Initialize(typeid(unsigned short), vtkimagedata->GetNumberOfScalarComponents()); Initialize(mitk::MakeScalarPixelType(), m_Dimension, tmpDimensions, channels); break; case VTK_INT: //pixelType.Initialize(typeid(int), vtkimagedata->GetNumberOfScalarComponents()); Initialize(mitk::MakeScalarPixelType(), m_Dimension, tmpDimensions, channels); break; case VTK_UNSIGNED_INT: //pixelType.Initialize(typeid(unsigned int), vtkimagedata->GetNumberOfScalarComponents()); Initialize(mitk::MakeScalarPixelType(), m_Dimension, tmpDimensions, channels); break; case VTK_LONG: //pixelType.Initialize(typeid(long), vtkimagedata->GetNumberOfScalarComponents()); Initialize(mitk::MakeScalarPixelType(), m_Dimension, tmpDimensions, channels); break; case VTK_UNSIGNED_LONG: //pixelType.Initialize(typeid(unsigned long), vtkimagedata->GetNumberOfScalarComponents()); Initialize(mitk::MakeScalarPixelType(), m_Dimension, tmpDimensions, channels); break; case VTK_FLOAT: //pixelType.Initialize(typeid(float), vtkimagedata->GetNumberOfScalarComponents()); Initialize(mitk::MakeScalarPixelType(), m_Dimension, tmpDimensions, channels); break; case VTK_DOUBLE: //pixelType.Initialize(typeid(double), vtkimagedata->GetNumberOfScalarComponents()); Initialize(mitk::MakeScalarPixelType(), m_Dimension, tmpDimensions, channels); break; default: break; } /* Initialize(pixelType, m_Dimension, tmpDimensions, channels); */ const double *spacinglist = vtkimagedata->GetSpacing(); Vector3D spacing; FillVector3D(spacing, spacinglist[0], 1.0, 1.0); if(m_Dimension>=2) spacing[1]=spacinglist[1]; if(m_Dimension>=3) spacing[2]=spacinglist[2]; // access origin of vtkImage Point3D origin; double vtkorigin[3]; vtkimagedata->GetOrigin(vtkorigin); FillVector3D(origin, vtkorigin[0], 0.0, 0.0); if(m_Dimension>=2) origin[1]=vtkorigin[1]; if(m_Dimension>=3) origin[2]=vtkorigin[2]; SlicedGeometry3D* slicedGeometry = GetSlicedGeometry(0); // re-initialize PlaneGeometry with origin and direction PlaneGeometry* planeGeometry = static_cast(slicedGeometry->GetGeometry2D(0)); planeGeometry->SetOrigin(origin); // re-initialize SlicedGeometry3D slicedGeometry->SetOrigin(origin); slicedGeometry->SetSpacing(spacing); ProportionalTimeGeometry::Pointer timeGeometry = ProportionalTimeGeometry::New(); timeGeometry->Initialize(slicedGeometry, m_Dimensions[3]); SetTimeGeometry(timeGeometry); delete [] tmpDimensions; } bool mitk::Image::IsValidSlice(int s, int t, int n) const { if(m_Initialized) return ((s>=0) && (s<(int)m_Dimensions[2]) && (t>=0) && (t< (int) m_Dimensions[3]) && (n>=0) && (n< (int)GetNumberOfChannels())); else return false; } bool mitk::Image::IsValidVolume(int t, int n) const { if(m_Initialized) return IsValidSlice(0, t, n); else return false; } bool mitk::Image::IsValidChannel(int n) const { if(m_Initialized) return IsValidSlice(0, 0, n); else return false; } void mitk::Image::ComputeOffsetTable() { if(m_OffsetTable!=NULL) delete [] m_OffsetTable; m_OffsetTable=new size_t[m_Dimension>4 ? m_Dimension+1 : 4+1]; unsigned int i; size_t num=1; m_OffsetTable[0] = 1; for (i=0; i < m_Dimension; ++i) { num *= m_Dimensions[i]; m_OffsetTable[i+1] = num; } for (;i < 4; ++i) m_OffsetTable[i+1] = num; } bool mitk::Image::IsValidTimeStep(int t) const { return ( ( m_Dimension >= 4 && t <= (int)m_Dimensions[3] && t > 0 ) || (t == 0) ); } void mitk::Image::Expand(unsigned int timeSteps) { if(timeSteps < 1) itkExceptionMacro(<< "Invalid timestep in Image!"); Superclass::Expand(timeSteps); } int mitk::Image::GetSliceIndex(int s, int t, int n) const { if(IsValidSlice(s,t,n)==false) return false; return ((size_t)s)+((size_t) t)*m_Dimensions[2]+((size_t) n)*m_Dimensions[3]*m_Dimensions[2]; //?? } int mitk::Image::GetVolumeIndex(int t, int n) const { if(IsValidVolume(t,n)==false) return false; return ((size_t)t)+((size_t) n)*m_Dimensions[3]; //?? } mitk::Image::ImageDataItemPointer mitk::Image::AllocateSliceData(int s, int t, int n, void *data, ImportMemoryManagementType importMemoryManagement) { int pos; pos=GetSliceIndex(s,t,n); const size_t ptypeSize = this->m_ImageDescriptor->GetChannelTypeById(n).GetSize(); // is slice available as part of a volume that is available? ImageDataItemPointer sl, ch, vol; vol=m_Volumes[GetVolumeIndex(t,n)]; if(vol.GetPointer()!=NULL) { sl=new ImageDataItem(*vol, m_ImageDescriptor, 2, data, importMemoryManagement == ManageMemory, ((size_t) s)*m_OffsetTable[2]*(ptypeSize)); sl->SetComplete(true); return m_Slices[pos]=sl; } // is slice available as part of a channel that is available? ch=m_Channels[n]; if(ch.GetPointer()!=NULL) { sl=new ImageDataItem(*ch, m_ImageDescriptor, 2, data, importMemoryManagement == ManageMemory, (((size_t) s)*m_OffsetTable[2]+((size_t) t)*m_OffsetTable[3])*(ptypeSize)); sl->SetComplete(true); return m_Slices[pos]=sl; } // allocate new volume (instead of a single slice to keep data together!) m_Volumes[GetVolumeIndex(t,n)]=vol=AllocateVolumeData(t,n,NULL,importMemoryManagement); sl=new ImageDataItem(*vol, m_ImageDescriptor, 2, data, importMemoryManagement == ManageMemory, ((size_t) s)*m_OffsetTable[2]*(ptypeSize)); sl->SetComplete(true); return m_Slices[pos]=sl; ////ALTERNATIVE: //// allocate new slice //sl=new ImageDataItem(*m_PixelType, 2, m_Dimensions); //m_Slices[pos]=sl; //return vol; } mitk::Image::ImageDataItemPointer mitk::Image::AllocateVolumeData(int t, int n, void *data, ImportMemoryManagementType importMemoryManagement) { int pos; pos=GetVolumeIndex(t,n); const size_t ptypeSize = this->m_ImageDescriptor->GetChannelTypeById(n).GetSize(); // is volume available as part of a channel that is available? ImageDataItemPointer ch, vol; ch=m_Channels[n]; if(ch.GetPointer()!=NULL) { vol=new ImageDataItem(*ch, m_ImageDescriptor, 3, data,importMemoryManagement == ManageMemory, (((size_t) t)*m_OffsetTable[3])*(ptypeSize)); return m_Volumes[pos]=vol; } mitk::PixelType chPixelType = this->m_ImageDescriptor->GetChannelTypeById(n); // allocate new volume if(importMemoryManagement == CopyMemory) { vol=new ImageDataItem( chPixelType, 3, m_Dimensions, NULL, true); if(data != NULL) std::memcpy(vol->GetData(), data, m_OffsetTable[3]*(ptypeSize)); } else { vol=new ImageDataItem( chPixelType, 3, m_Dimensions, data, importMemoryManagement == ManageMemory); } m_Volumes[pos]=vol; return vol; } mitk::Image::ImageDataItemPointer mitk::Image::AllocateChannelData(int n, void *data, ImportMemoryManagementType importMemoryManagement) { ImageDataItemPointer ch; // allocate new channel if(importMemoryManagement == CopyMemory) { const size_t ptypeSize = this->m_ImageDescriptor->GetChannelTypeById(n).GetSize(); ch=new ImageDataItem(this->m_ImageDescriptor, NULL, true); if(data != NULL) std::memcpy(ch->GetData(), data, m_OffsetTable[4]*(ptypeSize)); } else { ch=new ImageDataItem(this->m_ImageDescriptor, data, importMemoryManagement == ManageMemory); } m_Channels[n]=ch; return ch; } unsigned int* mitk::Image::GetDimensions() const { return m_Dimensions; } void mitk::Image::Clear() { Superclass::Clear(); delete [] m_Dimensions; m_Dimensions = NULL; } void mitk::Image::SetGeometry(BaseGeometry* aGeometry3D) { // Please be aware of the 0.5 offset/pixel-center issue! See Geometry documentation for further information if(aGeometry3D->GetImageGeometry()==false) { MITK_INFO << "WARNING: Applied a non-image geometry onto an image. Please be SURE that this geometry is pixel-center-based! If it is not, you need to call Geometry3D->ChangeImageGeometryConsideringOriginOffset(true) before calling image->setGeometry(..)\n"; } Superclass::SetGeometry(aGeometry3D); for (TimeStepType step = 0; step < GetTimeGeometry()->CountTimeSteps(); ++step) GetTimeGeometry()->GetGeometryForTimeStep(step)->ImageGeometryOn(); } void mitk::Image::PrintSelf(std::ostream& os, itk::Indent indent) const { unsigned char i; if(m_Initialized) { os << indent << " Dimension: " << m_Dimension << std::endl; os << indent << " Dimensions: "; for(i=0; i < m_Dimension; ++i) os << GetDimension(i) << " "; os << std::endl; for(unsigned int ch=0; ch < this->m_ImageDescriptor->GetNumberOfChannels(); ch++) { mitk::PixelType chPixelType = this->m_ImageDescriptor->GetChannelTypeById(ch); os << indent << " Channel: " << this->m_ImageDescriptor->GetChannelName(ch) << std::endl; os << indent << " PixelType: " << chPixelType.GetPixelTypeAsString() << std::endl; os << indent << " BytesPerElement: " << chPixelType.GetSize() << std::endl; os << indent << " ComponentType: " << chPixelType.GetComponentTypeAsString() << std::endl; os << indent << " NumberOfComponents: " << chPixelType.GetNumberOfComponents() << std::endl; os << indent << " BitsPerComponent: " << chPixelType.GetBitsPerComponent() << std::endl; } } else { os << indent << " Image not initialized: m_Initialized: false" << std::endl; } Superclass::PrintSelf(os,indent); } bool mitk::Image::IsRotated() const { const mitk::BaseGeometry* geo = this->GetGeometry(); bool ret = false; if(geo) { const vnl_matrix_fixed & mx = geo->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix(); mitk::ScalarType ref = 0; for(short k = 0; k < 3; ++k) ref += mx[k][k]; ref/=1000; // Arbitrary value; if a non-diagonal (nd) element is bigger then this, matrix is considered nd. for(short i = 0; i < 3; ++i) { for(short j = 0; j < 3; ++j) { if(i != j) { if(std::abs(mx[i][j]) > ref) // matrix is nd ret = true; } } } } return ret; } mitk::ScalarType mitk::Image::GetScalarValueMin(int t) const { return m_ImageStatistics->GetScalarValueMin(t); } //## \brief Get the maximum for scalar images mitk::ScalarType mitk::Image::GetScalarValueMax(int t) const { return m_ImageStatistics->GetScalarValueMax(t); } //## \brief Get the second smallest value for scalar images mitk::ScalarType mitk::Image::GetScalarValue2ndMin(int t) const { return m_ImageStatistics->GetScalarValue2ndMin(t); } mitk::ScalarType mitk::Image::GetScalarValueMinNoRecompute( unsigned int t ) const { return m_ImageStatistics->GetScalarValueMinNoRecompute(t); } mitk::ScalarType mitk::Image::GetScalarValue2ndMinNoRecompute( unsigned int t ) const { return m_ImageStatistics->GetScalarValue2ndMinNoRecompute(t); } mitk::ScalarType mitk::Image::GetScalarValue2ndMax(int t) const { return m_ImageStatistics->GetScalarValue2ndMax(t); } mitk::ScalarType mitk::Image::GetScalarValueMaxNoRecompute( unsigned int t) const { return m_ImageStatistics->GetScalarValueMaxNoRecompute(t); } mitk::ScalarType mitk::Image::GetScalarValue2ndMaxNoRecompute( unsigned int t ) const { return m_ImageStatistics->GetScalarValue2ndMaxNoRecompute(t); } mitk::ScalarType mitk::Image::GetCountOfMinValuedVoxels(int t ) const { return m_ImageStatistics->GetCountOfMinValuedVoxels(t); } mitk::ScalarType mitk::Image::GetCountOfMaxValuedVoxels(int t) const { return m_ImageStatistics->GetCountOfMaxValuedVoxels(t); } unsigned int mitk::Image::GetCountOfMaxValuedVoxelsNoRecompute( unsigned int t ) const { return m_ImageStatistics->GetCountOfMaxValuedVoxelsNoRecompute(t); } unsigned int mitk::Image::GetCountOfMinValuedVoxelsNoRecompute( unsigned int t ) const { return m_ImageStatistics->GetCountOfMinValuedVoxelsNoRecompute(t); } bool mitk::Equal(const mitk::Image* rightHandSide, const mitk::Image* leftHandSide, ScalarType eps, bool verbose) { bool returnValue = true; if( rightHandSide == NULL ) { if(verbose) MITK_INFO << "[( Image )] rightHandSide is NULL."; return false; } if( leftHandSide == NULL ) { if(verbose) MITK_INFO << "[( Image )] leftHandSide is NULL."; return false; } // Dimensionality if( rightHandSide->GetDimension() != leftHandSide->GetDimension() ) { if(verbose) { MITK_INFO << "[( Image )] Dimensionality differs."; MITK_INFO << "rightHandSide is " << rightHandSide->GetDimension() << "leftHandSide is " << leftHandSide->GetDimension(); } returnValue = false; } // Pair-wise dimension (size) comparison unsigned int minDimensionality = std::min(rightHandSide->GetDimension(),leftHandSide->GetDimension()); for( unsigned int i=0; i< minDimensionality; ++i) { if( rightHandSide->GetDimension(i) != leftHandSide->GetDimension(i) ) { returnValue = false; if(verbose) { MITK_INFO << "[( Image )] dimension differs."; MITK_INFO << "rightHandSide->GetDimension("<GetDimension(i) << "leftHandSide->GetDimension("<GetDimension(i); } } } // Pixeltype mitk::PixelType pixelTypeRightHandSide = rightHandSide->GetPixelType(); mitk::PixelType pixelTypeLeftHandSide = leftHandSide->GetPixelType(); if( !( pixelTypeRightHandSide == pixelTypeLeftHandSide ) ) { if(verbose) { MITK_INFO << "[( Image )] PixelType differs."; MITK_INFO << "rightHandSide is " << pixelTypeRightHandSide.GetTypeAsString() << "leftHandSide is " << pixelTypeLeftHandSide.GetTypeAsString(); } returnValue = false; } // Geometries if( !mitk::Equal( leftHandSide->GetGeometry(), rightHandSide->GetGeometry(), eps, verbose) ) { if(verbose) { MITK_INFO << "[( Image )] Geometries differ."; } returnValue = false; } // Pixel values - default mode [ 0 threshold in difference ] // compare only if all previous checks were successfull, otherwise the ITK filter will throw an exception if( returnValue ) { mitk::CompareImageDataFilter::Pointer compareFilter = mitk::CompareImageDataFilter::New(); compareFilter->SetInput(0, rightHandSide); compareFilter->SetInput(1, leftHandSide); compareFilter->SetTolerance(eps); compareFilter->Update(); if(( !compareFilter->GetResult() ) ) { returnValue = false; if(verbose) { MITK_INFO << "[(Image)] Pixel values differ: "; compareFilter->GetCompareResults().PrintSelf(); } } } return returnValue; } diff --git a/Core/Code/DataManagement/mitkImage.h b/Core/Code/DataManagement/mitkImage.h index cfc32edcdc..233624452d 100644 --- a/Core/Code/DataManagement/mitkImage.h +++ b/Core/Code/DataManagement/mitkImage.h @@ -1,712 +1,712 @@ /*=================================================================== 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 MITKIMAGE_H_HEADER_INCLUDED_C1C2FCD2 #define MITKIMAGE_H_HEADER_INCLUDED_C1C2FCD2 #include #include "mitkSlicedData.h" #include "mitkBaseData.h" #include "mitkLevelWindow.h" #include "mitkPlaneGeometry.h" #include #include "mitkImageDataItem.h" #include "mitkImageDescriptor.h" #include "mitkImageAccessorBase.h" #include "mitkImageVtkAccessor.h" //DEPRECATED #include #ifndef __itkHistogram_h #include #endif class vtkImageData; namespace mitk { class SubImageSelector; class ImageTimeSelector; class ImageStatisticsHolder; //##Documentation //## @brief Image class for storing images //## //## Can be asked for header information, the data vector, //## the mitkIpPicDescriptor struct or vtkImageData objects. If not the complete //## data is required, the appropriate SubImageSelector class should be used //## for access. //## Image organizes sets of slices (s x 2D), volumes (t x 3D) and channels (n //## x ND). Channels are for different kind of data, e.g., morphology in //## channel 0, velocities in channel 1. All channels must have the same Geometry! In //## particular, the dimensions of all channels are the same, only the pixel-type //## may differ between channels. //## //## For importing ITK images use of mitk::ITKImageImport is recommended, see //## \ref Adaptor. //## //## For ITK v3.8 and older: Converting coordinates from the ITK physical //## coordinate system (which does not support rotated images) to the MITK world //## coordinate system should be performed via the Geometry3D of the Image, see //## Geometry3D::WorldToItkPhysicalPoint. //## @ingroup Data class MITK_CORE_EXPORT Image : public SlicedData { friend class SubImageSelector; friend class ImageAccessorBase; friend class ImageVtkAccessor; friend class ImageReadAccessor; friend class ImageWriteAccessor; public: mitkClassMacro(Image, SlicedData); itkNewMacro(Self); mitkCloneMacro(Image); /** Smart Pointer type to a ImageDataItem. */ typedef itk::SmartPointer ImageDataItemPointer; typedef itk::Statistics::Histogram HistogramType; typedef mitk::ImageStatisticsHolder* StatisticsHolderPointer; //## @param ImportMemoryManagementType This parameter is evaluated when setting new data to an image. //## The different options are: //## CopyMemory: Data to be set is copied and assigned to a new memory block. Data memory block will be freed on deletion of mitk::Image. //## MamageMemory: Data to be set will be referenced, and Data memory block will be freed on deletion of mitk::Image. //## Reference Memory: Data to be set will be referenced, but Data memory block will not be freed on deletion of mitk::Image. //## DontManageMemory = ReferenceMemory. enum ImportMemoryManagementType { CopyMemory, ManageMemory, ReferenceMemory, DontManageMemory = ReferenceMemory }; //##Documentation //## @brief Vector container of SmartPointers to ImageDataItems; //## Class is only for internal usage to allow convenient access to all slices over iterators; //## See documentation of ImageDataItem for details. typedef std::vector ImageDataItemPointerArray; public: //##Documentation //## @brief Returns the PixelType of channel @a n. const mitk::PixelType GetPixelType(int n = 0) const; //##Documentation //## @brief Get dimension of the image //## unsigned int GetDimension() const; //##Documentation //## @brief Get the size of dimension @a i (e.g., i=0 results in the number of pixels in x-direction). //## //## @sa GetDimensions() unsigned int GetDimension(int i) const; /** @brief Get the data vector of the complete image, i.e., of all channels linked together. If you only want to access a slice, volume at a specific time or single channel use one of the SubImageSelector classes. \deprecatedSince{2012_09} Please use image accessors instead: See Doxygen/Related-Pages/Concepts/Image. This method can be replaced by ImageWriteAccessor::GetData() or ImageReadAccessor::GetData() */ DEPRECATED(virtual void* GetData()); public: /** @brief Get the pixel value at one specific index position. The pixel type is always being converted to double. \deprecatedSince{2012_09} Please use image accessors instead: See Doxygen/Related-Pages/Concepts/Image. This method can be replaced by a method from ImagePixelWriteAccessor or ImagePixelReadAccessor */ DEPRECATED(double GetPixelValueByIndex(const mitk::Index3D& position, unsigned int timestep = 0)); /** @brief Get the pixel value at one specific world position. The pixel type is always being converted to double. \deprecatedSince{2012_09} Please use image accessors instead: See Doxygen/Related-Pages/Concepts/Image. This method can be replaced by a method from ImagePixelWriteAccessor or ImagePixelReadAccessor */ DEPRECATED(double GetPixelValueByWorldCoordinate(const mitk::Point3D& position, unsigned int timestep = 0)); //##Documentation //## @brief Get a volume at a specific time @a t of channel @a n as a vtkImageData. virtual ImageVtkAccessor* GetVtkImageData(int t = 0, int n = 0); //##Documentation //## @brief Get the complete image, i.e., all channels linked together, as a @a mitkIpPicDescriptor. //## //## If you only want to access a slice, volume at a specific time or single channel //## use one of the SubImageSelector classes. //virtual mitkIpPicDescriptor* GetPic(); //##Documentation //## @brief Check whether slice @a s at time @a t in channel @a n is set virtual bool IsSliceSet(int s = 0, int t = 0, int n = 0) const; //##Documentation //## @brief Check whether volume at time @a t in channel @a n is set virtual bool IsVolumeSet(int t = 0, int n = 0) const; //##Documentation //## @brief Check whether the channel @a n is set virtual bool IsChannelSet(int n = 0) const; //##Documentation //## @brief Set @a data as slice @a s at time @a t in channel @a n. It is in //## the responsibility of the caller to ensure that the data vector @a data //## is really a slice (at least is not smaller than a slice), since there is //## no chance to check this. //## //## The data is copied to an array managed by the image. If the image shall //## reference the data, use SetImportSlice with ImportMemoryManagementType //## set to ReferenceMemory. For importing ITK images use of mitk:: //## ITKImageImport is recommended. //## @sa SetPicSlice, SetImportSlice, SetImportVolume virtual bool SetSlice(const void *data, int s = 0, int t = 0, int n = 0); //##Documentation //## @brief Set @a data as volume at time @a t in channel @a n. It is in //## the responsibility of the caller to ensure that the data vector @a data //## is really a volume (at least is not smaller than a volume), since there is //## no chance to check this. //## //## The data is copied to an array managed by the image. If the image shall //## reference the data, use SetImportVolume with ImportMemoryManagementType //## set to ReferenceMemory. For importing ITK images use of mitk:: //## ITKImageImport is recommended. //## @sa SetPicVolume, SetImportVolume virtual bool SetVolume(const void *data, int t = 0, int n = 0); //##Documentation //## @brief Set @a data in channel @a n. It is in //## the responsibility of the caller to ensure that the data vector @a data //## is really a channel (at least is not smaller than a channel), since there is //## no chance to check this. //## //## The data is copied to an array managed by the image. If the image shall //## reference the data, use SetImportChannel with ImportMemoryManagementType //## set to ReferenceMemory. For importing ITK images use of mitk:: //## ITKImageImport is recommended. //## @sa SetPicChannel, SetImportChannel virtual bool SetChannel(const void *data, int n = 0); //##Documentation //## @brief Set @a data as slice @a s at time @a t in channel @a n. It is in //## the responsibility of the caller to ensure that the data vector @a data //## is really a slice (at least is not smaller than a slice), since there is //## no chance to check this. //## //## The data is managed according to the parameter \a importMemoryManagement. //## @sa SetPicSlice virtual bool SetImportSlice(void *data, int s = 0, int t = 0, int n = 0, ImportMemoryManagementType importMemoryManagement = CopyMemory ); //##Documentation //## @brief Set @a data as volume at time @a t in channel @a n. It is in //## the responsibility of the caller to ensure that the data vector @a data //## is really a volume (at least is not smaller than a volume), since there is //## no chance to check this. //## //## The data is managed according to the parameter \a importMemoryManagement. //## @sa SetPicVolume virtual bool SetImportVolume(void *data, int t = 0, int n = 0, ImportMemoryManagementType importMemoryManagement = CopyMemory ); //##Documentation //## @brief Set @a data in channel @a n. It is in //## the responsibility of the caller to ensure that the data vector @a data //## is really a channel (at least is not smaller than a channel), since there is //## no chance to check this. //## //## The data is managed according to the parameter \a importMemoryManagement. //## @sa SetPicChannel virtual bool SetImportChannel(void *data, int n = 0, ImportMemoryManagementType importMemoryManagement = CopyMemory ); //##Documentation //## initialize new (or re-initialize) image information //## @warning Initialize() by pic assumes a plane, evenly spaced geometry starting at (0,0,0). virtual void Initialize(const mitk::PixelType& type, unsigned int dimension, const unsigned int *dimensions, unsigned int channels = 1); //##Documentation //## initialize new (or re-initialize) image information by a Geometry3D //## //## @param tDim defines the number of time steps for which the Image should be initialized virtual void Initialize(const mitk::PixelType& type, const mitk::BaseGeometry& geometry, unsigned int channels = 1, int tDim=1); /** * initialize new (or re-initialize) image information by a Geometry3D * * @param tDim defines the number of time steps for which the Image should be initialized * \deprecatedSince{2013_09} Please use TimeGeometry instead of TimeSlicedGeometry. For more information see http://www.mitk.org/Development/Refactoring%20of%20the%20Geometry%20Classes%20-%20Part%201 */ DEPRECATED(virtual void Initialize(const mitk::PixelType& /*type*/, const mitk::TimeSlicedGeometry* /*geometry*/, unsigned int /*channels = 1*/, int /*tDim=1*/)){} /** * \brief Initialize new (or re-initialize) image information by a TimeGeometry * * \param tDim override time dimension if the value is bigger than 0 (Default -1) */ virtual void Initialize(const mitk::PixelType& type, const mitk::TimeGeometry& geometry, unsigned int channels = 1, int tDim=-1 ); //##Documentation - //## initialize new (or re-initialize) image information by a Geometry2D and number of slices + //## initialize new (or re-initialize) image information by a PlaneGeometry and number of slices //## //## Initializes the bounding box according to the width/height of the - //## Geometry2D and @a sDim via SlicedGeometry3D::InitializeEvenlySpaced. - //## The spacing is calculated from the Geometry2D. + //## PlaneGeometry and @a sDim via SlicedGeometry3D::InitializeEvenlySpaced. + //## The spacing is calculated from the PlaneGeometry. //## \sa SlicedGeometry3D::InitializeEvenlySpaced - virtual void Initialize(const mitk::PixelType& type, int sDim, const mitk::Geometry2D& geometry2d, bool flipped = false, unsigned int channels = 1, int tDim=1); + virtual void Initialize(const mitk::PixelType& type, int sDim, const mitk::PlaneGeometry& geometry2d, bool flipped = false, unsigned int channels = 1, int tDim=1); //##Documentation //## initialize new (or re-initialize) image information by another //## mitk-image. //## Only the header is used, not the data vector! //## virtual void Initialize(const mitk::Image* image); virtual void Initialize(const mitk::ImageDescriptor::Pointer inDesc); //##Documentation //## initialize new (or re-initialize) image information by @a pic. - //## Dimensions and @a Geometry3D /@a Geometry2D are set according + //## Dimensions and @a Geometry3D /@a PlaneGeometry are set according //## to the tags in @a pic. //## Only the header is used, not the data vector! Use SetPicVolume(pic) //## to set the data vector. //## //## @param tDim override time dimension (@a n[3]) in @a pic (if >0) //## @param sDim override z-space dimension (@a n[2]) in @a pic (if >0) //## @warning Initialize() by pic assumes a plane, evenly spaced geometry starting at (0,0,0). //virtual void Initialize(const mitkIpPicDescriptor* pic, int channels = 1, int tDim = -1, int sDim = -1); //##Documentation //## initialize new (or re-initialize) image information by @a vtkimagedata, //## a vtk-image. //## Only the header is used, not the data vector! Use //## SetVolume(vtkimage->GetScalarPointer()) to set the data vector. //## //## @param tDim override time dimension in @a vtkimagedata (if >0 and <) //## @param sDim override z-space dimension in @a vtkimagedata (if >0 and <) //## @param pDim override y-space dimension in @a vtkimagedata (if >0 and <) virtual void Initialize(vtkImageData* vtkimagedata, int channels = 1, int tDim = -1, int sDim = -1, int pDim = -1); //##Documentation //## initialize new (or re-initialize) image information by @a itkimage, //## a templated itk-image. //## Only the header is used, not the data vector! Use //## SetVolume(itkimage->GetBufferPointer()) to set the data vector. //## //## @param tDim override time dimension in @a itkimage (if >0 and <) //## @param sDim override z-space dimension in @a itkimage (if >0 and <) template void InitializeByItk(const itkImageType* itkimage, int channels = 1, int tDim = -1, int sDim=-1) { if(itkimage==NULL) return; MITK_DEBUG << "Initializing MITK image from ITK image."; // build array with dimensions in each direction with at least 4 entries m_Dimension=itkimage->GetImageDimension(); unsigned int i, *tmpDimensions=new unsigned int[m_Dimension>4?m_Dimension:4]; for(i=0;iGetLargestPossibleRegion().GetSize().GetSize()[i]; if(m_Dimension<4) { unsigned int *p; for(i=0,p=tmpDimensions+m_Dimension;i<4-m_Dimension;++i, ++p) *p=1; } // overwrite number of slices if sDim is set if((m_Dimension>2) && (sDim>=0)) tmpDimensions[2]=sDim; // overwrite number of time points if tDim is set if((m_Dimension>3) && (tDim>=0)) tmpDimensions[3]=tDim; // rough initialization of Image // mitk::PixelType importType = ImportItkPixelType( itkimage::PixelType ); Initialize(MakePixelType(), m_Dimension, tmpDimensions, channels); const typename itkImageType::SpacingType & itkspacing = itkimage->GetSpacing(); MITK_DEBUG << "ITK spacing " << itkspacing; // access spacing of itk::Image Vector3D spacing; FillVector3D(spacing, itkspacing[0], 1.0, 1.0); if(m_Dimension >= 2) spacing[1]=itkspacing[1]; if(m_Dimension >= 3) spacing[2]=itkspacing[2]; // access origin of itk::Image Point3D origin; const typename itkImageType::PointType & itkorigin = itkimage->GetOrigin(); MITK_DEBUG << "ITK origin " << itkorigin; FillVector3D(origin, itkorigin[0], 0.0, 0.0); if(m_Dimension>=2) origin[1]=itkorigin[1]; if(m_Dimension>=3) origin[2]=itkorigin[2]; // access direction of itk::Imagm_PixelType = new mitk::PixelType(type);e and include spacing const typename itkImageType::DirectionType & itkdirection = itkimage->GetDirection(); MITK_DEBUG << "ITK direction " << itkdirection; mitk::Matrix3D matrix; matrix.SetIdentity(); unsigned int j, itkDimMax3 = (m_Dimension >= 3? 3 : m_Dimension); // check if spacing has no zero entry and itkdirection has no zero columns bool itkdirectionOk = true; mitk::ScalarType columnSum; for( j=0; j < itkDimMax3; ++j ) { columnSum = 0.0; for ( i=0; i < itkDimMax3; ++i) { columnSum += fabs(itkdirection[i][j]); } if(columnSum < mitk::eps) { itkdirectionOk = false; } if ( (spacing[j] < - mitk::eps) // (normally sized) negative value && (j==2) && (m_Dimensions[2] == 1) ) { // Negative spacings can occur when reading single DICOM slices with ITK via GDCMIO // In these cases spacing is not determind by ITK correctly (because it distinguishes correctly // between slice thickness and inter slice distance -- slice distance is meaningless for // single slices). // I experienced that ITK produced something meaningful nonetheless because is is // evaluating the tag "(0018,0088) Spacing between slices" as a fallback. This tag is not // reliable (http://www.itk.org/pipermail/insight-users/2005-September/014711.html) // but gives at least a hint. // In real world cases I experienced that this tag contained the correct inter slice distance // with a negative sign, so we just invert such negative spacings. MITK_WARN << "Illegal value of itk::Image::GetSpacing()[" << j <<"]=" << spacing[j] << ". Using inverted value " << -spacing[j]; spacing[j] = -spacing[j]; } else if (spacing[j] < mitk::eps) // value near zero { MITK_ERROR << "Illegal value of itk::Image::GetSpacing()[" << j <<"]=" << spacing[j] << ". Using 1.0 instead."; spacing[j] = 1.0; } } if(itkdirectionOk == false) { MITK_ERROR << "Illegal matrix returned by itk::Image::GetDirection():" << itkdirection << " Using identity instead."; for ( i=0; i < itkDimMax3; ++i) for( j=0; j < itkDimMax3; ++j ) if ( i == j ) matrix[i][j] = spacing[j]; else matrix[i][j] = 0.0; } else { for ( i=0; i < itkDimMax3; ++i) for( j=0; j < itkDimMax3; ++j ) matrix[i][j] = itkdirection[i][j]*spacing[j]; } // re-initialize PlaneGeometry with origin and direction PlaneGeometry* planeGeometry = static_cast(GetSlicedGeometry(0)->GetGeometry2D(0)); planeGeometry->SetOrigin(origin); planeGeometry->GetIndexToWorldTransform()->SetMatrix(matrix); // re-initialize SlicedGeometry3D SlicedGeometry3D* slicedGeometry = GetSlicedGeometry(0); slicedGeometry->InitializeEvenlySpaced(planeGeometry, m_Dimensions[2]); slicedGeometry->SetSpacing(spacing); // re-initialize TimeGeometry ProportionalTimeGeometry::Pointer timeGeometry = ProportionalTimeGeometry::New(); timeGeometry->Initialize(slicedGeometry, m_Dimensions[3]); SetTimeGeometry(timeGeometry); // clean-up delete [] tmpDimensions; this->Initialize(); }; //##Documentation //## @brief Check whether slice @a s at time @a t in channel @a n is valid, i.e., //## is (or can be) inside of the image virtual bool IsValidSlice(int s = 0, int t = 0, int n = 0) const; //##Documentation //## @brief Check whether volume at time @a t in channel @a n is valid, i.e., //## is (or can be) inside of the image virtual bool IsValidVolume(int t = 0, int n = 0) const; //##Documentation //## @brief Check whether the channel @a n is valid, i.e., //## is (or can be) inside of the image virtual bool IsValidChannel(int n = 0) const; //##Documentation //## @brief Returns true if an image is rotated, i.e. its geometry's //## transformation matrix has nonzero elements besides the diagonal. //## Non-diagonal elements are checked if larger then 1/1000 of the matrix' trace. bool IsRotated() const; //##Documentation //## @brief Get the sizes of all dimensions as an integer-array. //## //## @sa GetDimension(int i); unsigned int* GetDimensions() const; ImageDescriptor::Pointer GetImageDescriptor() const { return m_ImageDescriptor; } ChannelDescriptor GetChannelDescriptor( int id = 0 ) const { return m_ImageDescriptor->GetChannelDescriptor(id); } /** \brief Sets a geometry to an image. */ virtual void SetGeometry(BaseGeometry* aGeometry3D); /** * @warning for internal use only */ virtual ImageDataItemPointer GetSliceData(int s = 0, int t = 0, int n = 0, void *data = NULL, ImportMemoryManagementType importMemoryManagement = CopyMemory); /** * @warning for internal use only */ virtual ImageDataItemPointer GetVolumeData(int t = 0, int n = 0, void *data = NULL, ImportMemoryManagementType importMemoryManagement = CopyMemory); /** * @warning for internal use only */ virtual ImageDataItemPointer GetChannelData(int n = 0, void *data = NULL, ImportMemoryManagementType importMemoryManagement = CopyMemory); /** \brief (DEPRECATED) Get the minimum for scalar images */ DEPRECATED (ScalarType GetScalarValueMin(int t=0) const); /** \brief (DEPRECATED) Get the maximum for scalar images \warning This method is deprecated and will not be available in the future. Use the \a GetStatistics instead */ DEPRECATED (ScalarType GetScalarValueMax(int t=0) const); /** \brief (DEPRECATED) Get the second smallest value for scalar images \warning This method is deprecated and will not be available in the future. Use the \a GetStatistics instead */ DEPRECATED (ScalarType GetScalarValue2ndMin(int t=0) const); /** \brief (DEPRECATED) Get the smallest value for scalar images, but do not recompute it first \warning This method is deprecated and will not be available in the future. Use the \a GetStatistics instead */ DEPRECATED (ScalarType GetScalarValueMinNoRecompute( unsigned int t = 0 ) const); /** \brief (DEPRECATED) Get the second smallest value for scalar images, but do not recompute it first \warning This method is deprecated and will not be available in the future. Use the \a GetStatistics instead */ DEPRECATED (ScalarType GetScalarValue2ndMinNoRecompute( unsigned int t = 0 ) const); /** \brief (DEPRECATED) Get the second largest value for scalar images \warning This method is deprecated and will not be available in the future. Use the \a GetStatistics instead */ DEPRECATED (ScalarType GetScalarValue2ndMax(int t=0) const); /** \brief (DEPRECATED) Get the largest value for scalar images, but do not recompute it first \warning This method is deprecated and will not be available in the future. Use the \a GetStatistics instead */ DEPRECATED (ScalarType GetScalarValueMaxNoRecompute( unsigned int t = 0 ) const ); /** \brief (DEPRECATED) Get the second largest value for scalar images, but do not recompute it first \warning This method is deprecated and will not be available in the future. Use the \a GetStatistics instead */ DEPRECATED (ScalarType GetScalarValue2ndMaxNoRecompute( unsigned int t = 0 ) const); /** \brief (DEPRECATED) Get the count of voxels with the smallest scalar value in the dataset \warning This method is deprecated and will not be available in the future. Use the \a GetStatistics instead */ DEPRECATED (ScalarType GetCountOfMinValuedVoxels(int t = 0) const); /** \brief (DEPRECATED) Get the count of voxels with the largest scalar value in the dataset \warning This method is deprecated and will not be available in the future. Use the \a GetStatistics instead */ DEPRECATED (ScalarType GetCountOfMaxValuedVoxels(int t = 0) const); /** \brief (DEPRECATED) Get the count of voxels with the largest scalar value in the dataset \warning This method is deprecated and will not be available in the future. Use the \a GetStatistics instead */ DEPRECATED (unsigned int GetCountOfMaxValuedVoxelsNoRecompute( unsigned int t = 0 ) const); /** \brief (DEPRECATED) Get the count of voxels with the smallest scalar value in the dataset \warning This method is deprecated and will not be available in the future. Use the \a GetStatistics instead */ DEPRECATED (unsigned int GetCountOfMinValuedVoxelsNoRecompute( unsigned int t = 0 ) const); /** \brief Returns a pointer to the ImageStatisticsHolder object that holds all statistics information for the image. All Get-methods for statistics properties formerly accessible directly from an Image object are now moved to the new \a ImageStatisticsHolder object. */ StatisticsHolderPointer GetStatistics() const { return m_ImageStatistics; } protected: int GetSliceIndex(int s = 0, int t = 0, int n = 0) const; int GetVolumeIndex(int t = 0, int n = 0) const; void ComputeOffsetTable(); virtual bool IsValidTimeStep(int t) const; virtual void Expand( unsigned int timeSteps ); virtual ImageDataItemPointer AllocateSliceData(int s = 0, int t = 0, int n = 0, void *data = NULL, ImportMemoryManagementType importMemoryManagement = CopyMemory); virtual ImageDataItemPointer AllocateVolumeData(int t = 0, int n = 0, void *data = NULL, ImportMemoryManagementType importMemoryManagement = CopyMemory); virtual ImageDataItemPointer AllocateChannelData(int n = 0, void *data = NULL, ImportMemoryManagementType importMemoryManagement = CopyMemory); Image(); Image(const Image &other); virtual ~Image(); virtual void Clear(); //## @warning Has to be called by every Initialize method! virtual void Initialize(); virtual void PrintSelf(std::ostream& os, itk::Indent indent) const; mutable ImageDataItemPointerArray m_Channels; mutable ImageDataItemPointerArray m_Volumes; mutable ImageDataItemPointerArray m_Slices; unsigned int m_Dimension; unsigned int* m_Dimensions; ImageDescriptor::Pointer m_ImageDescriptor; size_t *m_OffsetTable; ImageDataItemPointer m_CompleteData; // Image statistics Holder replaces the former implementation directly inside this class friend class ImageStatisticsHolder; StatisticsHolderPointer m_ImageStatistics; private: /** Stores all existing ImageReadAccessors */ std::vector m_Readers; /** Stores all existing ImageWriteAccessors */ std::vector m_Writers; /** Stores all existing ImageVtkAccessors */ std::vector m_VtkReaders; /** A mutex, which needs to be locked to manage m_Readers and m_Writers */ itk::SimpleFastMutexLock m_ReadWriteLock; /** A mutex, which needs to be locked to manage m_VtkReaders */ itk::SimpleFastMutexLock m_VtkReadersLock; }; /** * @brief Equal A function comparing two images for beeing equal in meta- and imagedata * * @ingroup MITKTestingAPI * * Following aspects are tested for equality: * - dimension of the images * - size of the images * - pixel type * - pixel values : pixel values are expected to be identical at each position ( for other options see mitk::CompareImageFilter ) * * @param rightHandSide An image to be compared * @param leftHandSide An image to be compared * @param eps Tolarence for comparison. You can use mitk::eps in most cases. * @param verbose Flag indicating if the user wants detailed console output or not. * @return true, if all subsequent comparisons are true, false otherwise */ MITK_CORE_EXPORT bool Equal( const mitk::Image* leftHandSide, const mitk::Image* rightHandSide, ScalarType eps, bool verbose ); //} //##Documentation //## @brief Cast an itk::Image (with a specific type) to an mitk::Image. //## //## CastToMitkImage does not cast pixel types etc., just image data //## Needs "mitkImage.h" header included. //## If you get a compile error, try image.GetPointer(); //## @ingroup Adaptor //## \sa mitkITKImageImport template void CastToMitkImage(const itk::SmartPointer& itkimage, itk::SmartPointer& mitkoutputimage) { if(mitkoutputimage.IsNull()) { mitkoutputimage = mitk::Image::New(); } mitkoutputimage->InitializeByItk(itkimage.GetPointer()); mitkoutputimage->SetChannel(itkimage->GetBufferPointer()); } //##Documentation //## @brief Cast an itk::Image (with a specific type) to an mitk::Image. //## //## CastToMitkImage does not cast pixel types etc., just image data //## Needs "mitkImage.h" header included. //## If you get a compile error, try image.GetPointer(); //## @ingroup Adaptor //## \sa mitkITKImageImport template void CastToMitkImage(const ItkOutputImageType* itkimage, itk::SmartPointer& mitkoutputimage) { if(mitkoutputimage.IsNull()) { mitkoutputimage = mitk::Image::New(); } mitkoutputimage->InitializeByItk(itkimage); mitkoutputimage->SetChannel(itkimage->GetBufferPointer()); } } // namespace mitk #endif /* MITKIMAGE_H_HEADER_INCLUDED_C1C2FCD2 */ diff --git a/Core/Code/DataManagement/mitkPlaneGeometry.cpp b/Core/Code/DataManagement/mitkPlaneGeometry.cpp index c2385d74d8..bbd63dd3d2 100644 --- a/Core/Code/DataManagement/mitkPlaneGeometry.cpp +++ b/Core/Code/DataManagement/mitkPlaneGeometry.cpp @@ -1,727 +1,916 @@ /*=================================================================== 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 "mitkPlaneGeometry.h" #include "mitkPlaneOperation.h" #include "mitkInteractionConst.h" #include "mitkLine.h" #include #include namespace mitk { PlaneGeometry::PlaneGeometry() + : m_ScaleFactorMMPerUnitX( 1.0 ), + m_ScaleFactorMMPerUnitY( 1.0 ), + m_ReferenceGeometry( NULL ) { Initialize(); } PlaneGeometry::~PlaneGeometry() { } + PlaneGeometry::PlaneGeometry(const PlaneGeometry& other) + : Superclass(other), m_ScaleFactorMMPerUnitX( other.m_ScaleFactorMMPerUnitX), + m_ScaleFactorMMPerUnitY( other.m_ScaleFactorMMPerUnitY), + m_ReferenceGeometry( other.m_ReferenceGeometry ) + { + } + void PlaneGeometry::EnsurePerpendicularNormal(mitk::AffineTransform3D *transform) { //ensure row(2) of transform to be perpendicular to plane, keep length. VnlVector normal = vnl_cross_3d( transform->GetMatrix().GetVnlMatrix().get_column(0), transform->GetMatrix().GetVnlMatrix().get_column(1) ); normal.normalize(); ScalarType len = transform->GetMatrix() .GetVnlMatrix().get_column(2).two_norm(); if (len==0) len = 1; normal*=len; Matrix3D matrix = transform->GetMatrix(); matrix.GetVnlMatrix().set_column(2, normal); transform->SetMatrix(matrix); } void PlaneGeometry::InternPreSetIndexToWorldTransform(mitk::AffineTransform3D *transform) { EnsurePerpendicularNormal(transform); } void PlaneGeometry::InternPreSetBounds(const BoundingBox::BoundsArrayType &bounds) { //currently the unit rectangle must be starting at the origin [0,0] assert(bounds[0]==0); assert(bounds[2]==0); //the unit rectangle must be two-dimensional assert(bounds[1]>0); assert(bounds[3]>0); } void PlaneGeometry::IndexToWorld( const Point2D &pt_units, Point2D &pt_mm ) const { pt_mm[0]=m_ScaleFactorMMPerUnitX*pt_units[0]; pt_mm[1]=m_ScaleFactorMMPerUnitY*pt_units[1]; } void PlaneGeometry::WorldToIndex( const Point2D &pt_mm, Point2D &pt_units ) const { pt_units[0]=pt_mm[0]*(1.0/m_ScaleFactorMMPerUnitX); pt_units[1]=pt_mm[1]*(1.0/m_ScaleFactorMMPerUnitY); } void PlaneGeometry::IndexToWorld( const Point2D & /*atPt2d_units*/, const Vector2D &vec_units, Vector2D &vec_mm) const { MITK_WARN<<"Warning! Call of the deprecated function PlaneGeometry::IndexToWorld(point, vec, vec). Use PlaneGeometry::IndexToWorld(vec, vec) instead!"; this->IndexToWorld(vec_units, vec_mm); } void PlaneGeometry::IndexToWorld(const Vector2D &vec_units, Vector2D &vec_mm) const { vec_mm[0] = m_ScaleFactorMMPerUnitX * vec_units[0]; vec_mm[1] = m_ScaleFactorMMPerUnitY * vec_units[1]; } void PlaneGeometry::WorldToIndex( const Point2D & /*atPt2d_mm*/, const Vector2D &vec_mm, Vector2D &vec_units) const { MITK_WARN<<"Warning! Call of the deprecated function PlaneGeometry::WorldToIndex(point, vec, vec). Use PlaneGeometry::WorldToIndex(vec, vec) instead!"; this->WorldToIndex(vec_mm, vec_units); } void PlaneGeometry::WorldToIndex( const Vector2D &vec_mm, Vector2D &vec_units) const { vec_units[0] = vec_mm[0] * ( 1.0 / m_ScaleFactorMMPerUnitX ); vec_units[1] = vec_mm[1] * ( 1.0 / m_ScaleFactorMMPerUnitY ); } void PlaneGeometry::InitializeStandardPlane( mitk::ScalarType width, ScalarType height, const Vector3D & spacing, PlaneGeometry::PlaneOrientation planeorientation, ScalarType zPosition, bool frontside, bool rotated ) { AffineTransform3D::Pointer transform; transform = AffineTransform3D::New(); AffineTransform3D::MatrixType matrix; AffineTransform3D::MatrixType::InternalMatrixType &vnlmatrix = matrix.GetVnlMatrix(); vnlmatrix.set_identity(); vnlmatrix(0,0) = spacing[0]; vnlmatrix(1,1) = spacing[1]; vnlmatrix(2,2) = spacing[2]; transform->SetIdentity(); transform->SetMatrix(matrix); InitializeStandardPlane(width, height, transform.GetPointer(), planeorientation, zPosition, frontside, rotated); } void PlaneGeometry::InitializeStandardPlane( mitk::ScalarType width, ScalarType height, const AffineTransform3D* transform, PlaneGeometry::PlaneOrientation planeorientation, ScalarType zPosition, bool frontside, bool rotated ) { Superclass::Initialize(); //construct standard view Point3D origin; VnlVector rightDV(3), bottomDV(3); origin.Fill(0); int normalDirection; switch(planeorientation) { case Axial: if(frontside) { if(rotated==false) { FillVector3D(origin, 0, 0, zPosition); FillVector3D(rightDV, 1, 0, 0); FillVector3D(bottomDV, 0, 1, 0); } else { FillVector3D(origin, width, height, zPosition); FillVector3D(rightDV, -1, 0, 0); FillVector3D(bottomDV, 0, -1, 0); } } else { if(rotated==false) { FillVector3D(origin, width, 0, zPosition); FillVector3D(rightDV, -1, 0, 0); FillVector3D(bottomDV, 0, 1, 0); } else { FillVector3D(origin, 0, height, zPosition); FillVector3D(rightDV, 1, 0, 0); FillVector3D(bottomDV, 0, -1, 0); } } normalDirection = 2; break; case Frontal: if(frontside) { if(rotated==false) { FillVector3D(origin, 0, zPosition, 0); FillVector3D(rightDV, 1, 0, 0); FillVector3D(bottomDV, 0, 0, 1); } else { FillVector3D(origin, width, zPosition, height); FillVector3D(rightDV, -1, 0, 0); FillVector3D(bottomDV, 0, 0, -1); } } else { if(rotated==false) { FillVector3D(origin, width, zPosition, 0); FillVector3D(rightDV, -1, 0, 0); FillVector3D(bottomDV, 0, 0, 1); } else { FillVector3D(origin, 0, zPosition, height); FillVector3D(rightDV, 1, 0, 0); FillVector3D(bottomDV, 0, 0, -1); } } normalDirection = 1; break; case Sagittal: if(frontside) { if(rotated==false) { FillVector3D(origin, zPosition, 0, 0); FillVector3D(rightDV, 0, 1, 0); FillVector3D(bottomDV, 0, 0, 1); } else { FillVector3D(origin, zPosition, width, height); FillVector3D(rightDV, 0, -1, 0); FillVector3D(bottomDV, 0, 0, -1); } } else { if(rotated==false) { FillVector3D(origin, zPosition, width, 0); FillVector3D(rightDV, 0, -1, 0); FillVector3D(bottomDV, 0, 0, 1); } else { FillVector3D(origin, zPosition, 0, height); FillVector3D(rightDV, 0, 1, 0); FillVector3D(bottomDV, 0, 0, -1); } } normalDirection = 0; break; default: itkExceptionMacro("unknown PlaneOrientation"); } if ( transform != NULL ) { origin = transform->TransformPoint( origin ); rightDV = transform->TransformVector( rightDV ); bottomDV = transform->TransformVector( bottomDV ); } ScalarType bounds[6]= { 0, width, 0, height, 0, 1 }; this->SetBounds( bounds ); if ( transform == NULL ) { this->SetMatrixByVectors( rightDV, bottomDV ); } else { this->SetMatrixByVectors( rightDV, bottomDV, transform->GetMatrix().GetVnlMatrix() .get_column(normalDirection).magnitude() ); } this->SetOrigin(origin); } void PlaneGeometry::InitializeStandardPlane( const BaseGeometry *geometry3D, PlaneOrientation planeorientation, ScalarType zPosition, bool frontside, bool rotated ) { this->SetReferenceGeometry( const_cast< BaseGeometry * >( geometry3D ) ); ScalarType width, height; const BoundingBox::BoundsArrayType& boundsarray = geometry3D->GetBoundingBox()->GetBounds(); Vector3D originVector; FillVector3D(originVector, boundsarray[0], boundsarray[2], boundsarray[4]); if(geometry3D->GetImageGeometry()) { FillVector3D( originVector, originVector[0] - 0.5, originVector[1] - 0.5, originVector[2] - 0.5 ); } switch(planeorientation) { case Axial: width = geometry3D->GetExtent(0); height = geometry3D->GetExtent(1); break; case Frontal: width = geometry3D->GetExtent(0); height = geometry3D->GetExtent(2); break; case Sagittal: width = geometry3D->GetExtent(1); height = geometry3D->GetExtent(2); break; default: itkExceptionMacro("unknown PlaneOrientation"); } InitializeStandardPlane( width, height, geometry3D->GetIndexToWorldTransform(), planeorientation, zPosition, frontside, rotated ); ScalarType bounds[6]= { 0, width, 0, height, 0, 1 }; this->SetBounds( bounds ); Point3D origin; originVector = geometry3D->GetIndexToWorldTransform() ->TransformVector( originVector ); origin = GetOrigin() + originVector; SetOrigin(origin); } void PlaneGeometry::InitializeStandardPlane( const BaseGeometry *geometry3D, bool top, PlaneOrientation planeorientation, bool frontside, bool rotated ) { ScalarType zPosition; switch(planeorientation) { case Axial: zPosition = (top ? 0.5 : geometry3D->GetExtent(2)-1+0.5); break; case Frontal: zPosition = (top ? 0.5 : geometry3D->GetExtent(1)-1+0.5); break; case Sagittal: zPosition = (top ? 0.5 : geometry3D->GetExtent(0)-1+0.5); break; default: itkExceptionMacro("unknown PlaneOrientation"); } InitializeStandardPlane( geometry3D, planeorientation, zPosition, frontside, rotated ); } void PlaneGeometry::InitializeStandardPlane( const Vector3D &rightVector, const Vector3D &downVector, const Vector3D *spacing ) { InitializeStandardPlane( rightVector.GetVnlVector(), downVector.GetVnlVector(), spacing ); } void PlaneGeometry::InitializeStandardPlane( const VnlVector& rightVector, const VnlVector &downVector, const Vector3D *spacing ) { ScalarType width = rightVector.magnitude(); ScalarType height = downVector.magnitude(); InitializeStandardPlane( width, height, rightVector, downVector, spacing ); } void PlaneGeometry::InitializeStandardPlane( mitk::ScalarType width, ScalarType height, const Vector3D &rightVector, const Vector3D &downVector, const Vector3D *spacing ) { InitializeStandardPlane( width, height, rightVector.GetVnlVector(), downVector.GetVnlVector(), spacing ); } void PlaneGeometry::InitializeStandardPlane( mitk::ScalarType width, ScalarType height, const VnlVector &rightVector, const VnlVector &downVector, const Vector3D *spacing ) { assert(width > 0); assert(height > 0); VnlVector rightDV = rightVector; rightDV.normalize(); VnlVector downDV = downVector; downDV.normalize(); VnlVector normal = vnl_cross_3d(rightVector, downVector); normal.normalize(); if(spacing!=NULL) { rightDV *= (*spacing)[0]; downDV *= (*spacing)[1]; normal *= (*spacing)[2]; } AffineTransform3D::Pointer transform = AffineTransform3D::New(); Matrix3D matrix; matrix.GetVnlMatrix().set_column(0, rightDV); matrix.GetVnlMatrix().set_column(1, downDV); matrix.GetVnlMatrix().set_column(2, normal); transform->SetMatrix(matrix); transform->SetOffset(this->GetIndexToWorldTransform()->GetOffset()); ScalarType bounds[6] = { 0, width, 0, height, 0, 1 }; this->SetBounds( bounds ); this->SetIndexToWorldTransform( transform ); } void PlaneGeometry::InitializePlane( const Point3D &origin, const Vector3D &normal ) { VnlVector rightVectorVnl(3), downVectorVnl; if( Equal( normal[1], 0.0f ) == false ) { FillVector3D( rightVectorVnl, 1.0f, -normal[0]/normal[1], 0.0f ); rightVectorVnl.normalize(); } else { FillVector3D( rightVectorVnl, 0.0f, 1.0f, 0.0f ); } downVectorVnl = vnl_cross_3d( normal.GetVnlVector(), rightVectorVnl ); downVectorVnl.normalize(); InitializeStandardPlane( rightVectorVnl, downVectorVnl ); SetOrigin(origin); } void PlaneGeometry::SetMatrixByVectors( const VnlVector &rightVector, const VnlVector &downVector, ScalarType thickness ) { VnlVector normal = vnl_cross_3d(rightVector, downVector); normal.normalize(); normal *= thickness; AffineTransform3D::Pointer transform = AffineTransform3D::New(); Matrix3D matrix; matrix.GetVnlMatrix().set_column(0, rightVector); matrix.GetVnlMatrix().set_column(1, downVector); matrix.GetVnlMatrix().set_column(2, normal); transform->SetMatrix(matrix); transform->SetOffset(this->GetIndexToWorldTransform()->GetOffset()); SetIndexToWorldTransform(transform); } Vector3D PlaneGeometry::GetNormal() const { Vector3D frontToBack; frontToBack.SetVnlVector( this->GetIndexToWorldTransform() ->GetMatrix().GetVnlMatrix().get_column(2) ); return frontToBack; } VnlVector PlaneGeometry::GetNormalVnl() const { return this->GetIndexToWorldTransform() ->GetMatrix().GetVnlMatrix().get_column(2); } ScalarType PlaneGeometry::DistanceFromPlane( const Point3D &pt3d_mm ) const { return fabs(SignedDistance( pt3d_mm )); } ScalarType PlaneGeometry::SignedDistance( const Point3D &pt3d_mm ) const { return SignedDistanceFromPlane(pt3d_mm); } + //Function from Geometry2D + // mitk::ScalarType + // PlaneGeometry::SignedDistance(const mitk::Point3D& pt3d_mm) const + //{ + // Point3D projectedPoint; + // Project(pt3d_mm, projectedPoint); + // Vector3D direction = pt3d_mm-projectedPoint; + // ScalarType distance = direction.GetNorm(); + + // if(IsAbove(pt3d_mm) == false) + // distance*=-1.0; + + // return distance; + //} + bool - PlaneGeometry::IsAbove( const Point3D &pt3d_mm ) const + PlaneGeometry::IsAbove( const Point3D &pt3d_mm , bool considerBoundingBox) const { - return SignedDistanceFromPlane(pt3d_mm) > 0; + if(considerBoundingBox) + { + Point3D pt3d_units; + BaseGeometry::WorldToIndex(pt3d_mm, pt3d_units); + return (pt3d_units[2] > this->GetBoundingBox()->GetBounds()[4]); + } + else + return SignedDistanceFromPlane(pt3d_mm) > 0; } bool PlaneGeometry::IntersectionLine( const PlaneGeometry* plane, Line3D& crossline ) const { Vector3D normal = this->GetNormal(); normal.Normalize(); Vector3D planeNormal = plane->GetNormal(); planeNormal.Normalize(); Vector3D direction = itk::CrossProduct( normal, planeNormal ); if ( direction.GetSquaredNorm() < eps ) return false; crossline.SetDirection( direction ); double N1dN2 = normal * planeNormal; double determinant = 1.0 - N1dN2 * N1dN2; Vector3D origin = this->GetOrigin().GetVectorFromOrigin(); Vector3D planeOrigin = plane->GetOrigin().GetVectorFromOrigin(); double d1 = normal * origin; double d2 = planeNormal * planeOrigin; double c1 = ( d1 - d2 * N1dN2 ) / determinant; double c2 = ( d2 - d1 * N1dN2 ) / determinant; Vector3D p = normal * c1 + planeNormal * c2; crossline.GetPoint().GetVnlVector() = p.GetVnlVector(); return true; } unsigned int PlaneGeometry::IntersectWithPlane2D( const PlaneGeometry* plane, Point2D& lineFrom, Point2D &lineTo ) const { Line3D crossline; if ( this->IntersectionLine( plane, crossline ) == false ) return 0; Point2D point2; Vector2D direction2; this->Map( crossline.GetPoint(), point2 ); this->Map( crossline.GetPoint(), crossline.GetDirection(), direction2 ); return Line3D::RectangleLineIntersection( 0, 0, GetExtentInMM(0), GetExtentInMM(1), point2, direction2, lineFrom, lineTo ); } double PlaneGeometry::Angle( const PlaneGeometry *plane ) const { return angle(plane->GetMatrixColumn(2), GetMatrixColumn(2)); } double PlaneGeometry::Angle( const Line3D &line ) const { return vnl_math::pi_over_2 - angle( line.GetDirection().GetVnlVector(), GetMatrixColumn(2) ); } bool PlaneGeometry::IntersectionPoint( const Line3D &line, Point3D &intersectionPoint ) const { Vector3D planeNormal = this->GetNormal(); planeNormal.Normalize(); Vector3D lineDirection = line.GetDirection(); lineDirection.Normalize(); double t = planeNormal * lineDirection; if ( fabs( t ) < eps ) { return false; } Vector3D diff; diff = this->GetOrigin() - line.GetPoint(); t = ( planeNormal * diff ) / t; intersectionPoint = line.GetPoint() + lineDirection * t; return true; } bool PlaneGeometry::IntersectionPointParam( const Line3D &line, double &t ) const { Vector3D planeNormal = this->GetNormal(); Vector3D lineDirection = line.GetDirection(); t = planeNormal * lineDirection; if ( fabs( t ) < eps ) { return false; } Vector3D diff; diff = this->GetOrigin() - line.GetPoint(); t = ( planeNormal * diff ) / t; return true; } bool PlaneGeometry::IsParallel( const PlaneGeometry *plane ) const { return ( (Angle(plane) < 10.0 * mitk::sqrteps ) || ( Angle(plane) > ( vnl_math::pi - 10.0 * sqrteps ) ) ) ; } bool PlaneGeometry::IsOnPlane( const Point3D &point ) const { return Distance(point) < eps; } bool PlaneGeometry::IsOnPlane( const Line3D &line ) const { return ( (Distance( line.GetPoint() ) < eps) && (Distance( line.GetPoint2() ) < eps) ); } bool PlaneGeometry::IsOnPlane( const PlaneGeometry *plane ) const { return ( IsParallel( plane ) && (Distance( plane->GetOrigin() ) < eps) ); } Point3D PlaneGeometry::ProjectPointOntoPlane( const Point3D& pt ) const { ScalarType len = this->GetNormalVnl().two_norm(); return pt - this->GetNormal() * this->SignedDistanceFromPlane( pt ) / len; } itk::LightObject::Pointer PlaneGeometry::InternalClone() const { Self::Pointer newGeometry = new PlaneGeometry(*this); newGeometry->UnRegister(); return newGeometry.GetPointer(); } void PlaneGeometry::ExecuteOperation( Operation *operation ) { vtkTransform *transform = vtkTransform::New(); transform->SetMatrix( this->GetVtkMatrix()); switch ( operation->GetOperationType() ) { case OpORIENT: { mitk::PlaneOperation *planeOp = dynamic_cast< mitk::PlaneOperation * >( operation ); if ( planeOp == NULL ) { return; } Point3D center = planeOp->GetPoint(); Vector3D orientationVector = planeOp->GetNormal(); Vector3D defaultVector; FillVector3D( defaultVector, 0.0, 0.0, 1.0 ); Vector3D rotationAxis = itk::CrossProduct( orientationVector, defaultVector ); //double rotationAngle = acos( orientationVector[2] / orientationVector.GetNorm() ); double rotationAngle = atan2( (double) rotationAxis.GetNorm(), (double) (orientationVector * defaultVector) ); rotationAngle *= 180.0 / vnl_math::pi; transform->PostMultiply(); transform->Identity(); transform->Translate( center[0], center[1], center[2] ); transform->RotateWXYZ( rotationAngle, rotationAxis[0], rotationAxis[1], rotationAxis[2] ); transform->Translate( -center[0], -center[1], -center[2] ); break; } case OpRESTOREPLANEPOSITION: { RestorePlanePositionOperation *op = dynamic_cast< mitk::RestorePlanePositionOperation* >(operation); if(op == NULL) { return; } AffineTransform3D::Pointer transform2 = AffineTransform3D::New(); Matrix3D matrix; matrix.GetVnlMatrix().set_column(0, op->GetTransform()->GetMatrix().GetVnlMatrix().get_column(0)); matrix.GetVnlMatrix().set_column(1, op->GetTransform()->GetMatrix().GetVnlMatrix().get_column(1)); matrix.GetVnlMatrix().set_column(2, op->GetTransform()->GetMatrix().GetVnlMatrix().get_column(2)); transform2->SetMatrix(matrix); Vector3D offset = op->GetTransform()->GetOffset(); transform2->SetOffset(offset); this->SetIndexToWorldTransform(transform2); ScalarType bounds[6] = {0, op->GetWidth(), 0, op->GetHeight(), 0 ,1 }; this->SetBounds(bounds); TransferItkToVtkTransform(); this->Modified(); transform->Delete(); return; } default: Superclass::ExecuteOperation( operation ); transform->Delete(); return; } this->GetVtkMatrix()->DeepCopy(transform->GetMatrix()); this->TransferVtkToItkTransform(); this->Modified(); transform->Delete(); } void PlaneGeometry::PrintSelf( std::ostream& os, itk::Indent indent ) const { Superclass::PrintSelf(os,indent); + os << indent << " ScaleFactorMMPerUnitX: " + << m_ScaleFactorMMPerUnitX << std::endl; + os << indent << " ScaleFactorMMPerUnitY: " + << m_ScaleFactorMMPerUnitY << std::endl; os << indent << " Normal: " << GetNormal() << std::endl; } + + void PlaneGeometry::InternPostSetIndexToWorldTransform( + mitk::AffineTransform3D* transform) + { + m_ScaleFactorMMPerUnitX=GetExtentInMM(0)/GetExtent(0); + m_ScaleFactorMMPerUnitY=GetExtentInMM(1)/GetExtent(1); + + assert(m_ScaleFactorMMPerUnitXIsBoundingBoxNull()==false); + + Point3D pt3d_units; + BackTransform(pt3d_mm, pt3d_units); + pt2d_mm[0]=pt3d_units[0]*m_ScaleFactorMMPerUnitX; + pt2d_mm[1]=pt3d_units[1]*m_ScaleFactorMMPerUnitY; + pt3d_units[2]=0; + return const_cast(this->GetBoundingBox())->IsInside(pt3d_units); + } + + void + PlaneGeometry::Map(const mitk::Point2D &pt2d_mm, mitk::Point3D &pt3d_mm) const + { + Point3D pt3d_units; + pt3d_units[0]=pt2d_mm[0]/m_ScaleFactorMMPerUnitX; + pt3d_units[1]=pt2d_mm[1]/m_ScaleFactorMMPerUnitY; + pt3d_units[2]=0; + pt3d_mm = GetIndexToWorldTransform()->TransformPoint(pt3d_units); + } + + void + PlaneGeometry::SetSizeInUnits(mitk::ScalarType width, mitk::ScalarType height) + { + ScalarType bounds[6]={0, width, 0, height, 0, 1}; + ScalarType extent, newextentInMM; + if(GetExtent(0)>0) + { + extent = GetExtent(0); + if(width>extent) + newextentInMM = GetExtentInMM(0)/width*extent; + else + newextentInMM = GetExtentInMM(0)*extent/width; + SetExtentInMM(0, newextentInMM); + } + if(GetExtent(1)>0) + { + extent = GetExtent(1); + if(width>extent) + newextentInMM = GetExtentInMM(1)/height*extent; + else + newextentInMM = GetExtentInMM(1)*extent/height; + SetExtentInMM(1, newextentInMM); + } + SetBounds(bounds); + } + + bool + PlaneGeometry::Project( + const mitk::Point3D &pt3d_mm, mitk::Point3D &projectedPt3d_mm) const + { + assert(this->IsBoundingBoxNull()==false); + + Point3D pt3d_units; + BackTransform(pt3d_mm, pt3d_units); + pt3d_units[2] = 0; + projectedPt3d_mm = GetIndexToWorldTransform()->TransformPoint(pt3d_units); + return const_cast(this->GetBoundingBox())->IsInside(pt3d_units); + } + + bool + PlaneGeometry::Project(const mitk::Vector3D &vec3d_mm, mitk::Vector3D &projectedVec3d_mm) const + { + assert(this->IsBoundingBoxNull()==false); + + Vector3D vec3d_units; + BackTransform(vec3d_mm, vec3d_units); + vec3d_units[2] = 0; + projectedVec3d_mm = GetIndexToWorldTransform()->TransformVector(vec3d_units); + return true; + } + + bool + PlaneGeometry::Project(const mitk::Point3D & atPt3d_mm, + const mitk::Vector3D &vec3d_mm, mitk::Vector3D &projectedVec3d_mm) const + { + MITK_WARN << "Deprecated function! Call Project(vec3D,vec3D) instead."; + assert(this->IsBoundingBoxNull()==false); + + Vector3D vec3d_units; + BackTransform(atPt3d_mm, vec3d_mm, vec3d_units); + vec3d_units[2] = 0; + projectedVec3d_mm = GetIndexToWorldTransform()->TransformVector(vec3d_units); + + Point3D pt3d_units; + BackTransform(atPt3d_mm, pt3d_units); + return const_cast(this->GetBoundingBox())->IsInside(pt3d_units); + } + + bool + PlaneGeometry::Map(const mitk::Point3D & atPt3d_mm, + const mitk::Vector3D &vec3d_mm, mitk::Vector2D &vec2d_mm) const + { + Point2D pt2d_mm_start, pt2d_mm_end; + Point3D pt3d_mm_end; + bool inside=Map(atPt3d_mm, pt2d_mm_start); + pt3d_mm_end = atPt3d_mm+vec3d_mm; + inside&=Map(pt3d_mm_end, pt2d_mm_end); + vec2d_mm=pt2d_mm_end-pt2d_mm_start; + return inside; + } + + void + PlaneGeometry::Map(const mitk::Point2D &/*atPt2d_mm*/, + const mitk::Vector2D &/*vec2d_mm*/, mitk::Vector3D &/*vec3d_mm*/) const + { + //@todo implement parallel to the other Map method! + assert(false); + } + + + + void + PlaneGeometry::SetReferenceGeometry( mitk::BaseGeometry *geometry ) + { + m_ReferenceGeometry = geometry; + } + + mitk::BaseGeometry * + PlaneGeometry::GetReferenceGeometry() const + { + return m_ReferenceGeometry; + } + + bool + PlaneGeometry::HasReferenceGeometry() const + { + return ( m_ReferenceGeometry != NULL ); + } } // namespace diff --git a/Core/Code/DataManagement/mitkPlaneGeometry.h b/Core/Code/DataManagement/mitkPlaneGeometry.h index 8a30236d2a..5082c2a399 100644 --- a/Core/Code/DataManagement/mitkPlaneGeometry.h +++ b/Core/Code/DataManagement/mitkPlaneGeometry.h @@ -1,392 +1,575 @@ /*=================================================================== 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. ===================================================================*/ +//******************************* form Geometry2D ************** + + /** + * \brief Describes the geometry of a two-dimensional object + * + * Describes a two-dimensional manifold, i.e., to put it simply, + * an object that can be described using a 2D coordinate-system. + * + * Geometry2D can map points between 3D world coordinates + * (in mm) and the described 2D coordinate-system (in mm) by first projecting + * the 3D point onto the 2D manifold and then calculating the 2D-coordinates + * (in mm). These 2D-mm-coordinates can be further converted into + * 2D-unit-coordinates (e.g., pixels), giving a parameter representation of + * the object with parameter values inside a rectangle + * (e.g., [0,0]..[width, height]), which is the bounding box (bounding range + * in z-direction always [0]..[1]). + * + * A Geometry2D describes the 2D representation within a 3D object and is + * therefore itself a Geometry3D (derived from Geometry3D). For example, + * a single CT-image (slice) is 2D in the sense that you can access the + * pixels using 2D-coordinates, but is also 3D, as the pixels are really + * voxels, thus have an extension (thickness) in the 3rd dimension. + * + * Most often, instances of Geometry2D will be used to descibe a plane, + * which is represented by the sub-class PlaneGeometry, but curved + * surfaces are also possible. + * + * Optionally, a reference Geometry3D can be specified, which usually would + * be the geometry associated with the underlying dataset. This is currently + * used for calculating the intersection of inclined / rotated planes + * (represented as Geometry2D) with the bounding box of the associated + * Geometry3D. + * + * \warning The Geometry2Ds are not necessarily up-to-date and not even + * initialized. As described in the previous paragraph, one of the + * Generate-/Copy-/UpdateOutputInformation methods have to initialize it. + * mitk::BaseData::GetGeometry2D() makes sure, that the Geometry2D is + * up-to-date before returning it (by setting the update extent appropriately + * and calling UpdateOutputInformation). + * + * Rule: everything is in mm (or ms for temporal information) if not + * stated otherwise. + * \ingroup Geometry + */ + #ifndef PLANEGEOMETRY_H_HEADER_INCLUDED_C1C68A2C #define PLANEGEOMETRY_H_HEADER_INCLUDED_C1C68A2C #include -#include "mitkGeometry2D.h" +#include "mitkBaseGeometry.h" #include "mitkRestorePlanePositionOperation.h" #include namespace mitk { template < class TCoordRep, unsigned int NPointDimension > class Line; typedef Line Line3D; /** * \brief Describes a two-dimensional, rectangular plane * * \ingroup Geometry */ - class MITK_CORE_EXPORT PlaneGeometry : public Geometry2D + class MITK_CORE_EXPORT PlaneGeometry : public BaseGeometry { public: - mitkClassMacro(PlaneGeometry,Geometry2D); + mitkClassMacro(PlaneGeometry,BaseGeometry); /** Method for creation through the object factory. */ itkNewMacro(Self); enum PlaneOrientation { #ifdef _MSC_VER Transversal, // deprecated #endif Axial = 0, Sagittal, Frontal }; #ifdef __GNUC__ __attribute__ ((deprecated)) static const PlaneOrientation Transversal = PlaneOrientation(Axial); #endif virtual void IndexToWorld(const Point2D &pt_units, Point2D &pt_mm) const; virtual void WorldToIndex(const Point2D &pt_mm, Point2D &pt_units) const; //##Documentation //## @brief Convert (continuous or discrete) index coordinates of a \em vector //## \a vec_units to world coordinates (in mm) //## @deprecated First parameter (Point2D) is not used. If possible, please use void IndexToWorld(const mitk::Vector2D& vec_units, mitk::Vector2D& vec_mm) const. //## For further information about coordinates types, please see the Geometry documentation virtual void IndexToWorld(const mitk::Point2D &atPt2d_untis, const mitk::Vector2D &vec_units, mitk::Vector2D &vec_mm) const; //##Documentation //## @brief Convert (continuous or discrete) index coordinates of a \em vector //## \a vec_units to world coordinates (in mm) //## For further information about coordinates types, please see the Geometry documentation virtual void IndexToWorld(const mitk::Vector2D &vec_units, mitk::Vector2D &vec_mm) const; //##Documentation //## @brief Convert world coordinates (in mm) of a \em vector //## \a vec_mm to (continuous!) index coordinates. //## @deprecated First parameter (Point2D) is not used. If possible, please use void WorldToIndex(const mitk::Vector2D& vec_mm, mitk::Vector2D& vec_units) const. //## For further information about coordinates types, please see the Geometry documentation virtual void WorldToIndex(const mitk::Point2D &atPt2d_mm, const mitk::Vector2D &vec_mm, mitk::Vector2D &vec_units) const; //##Documentation //## @brief Convert world coordinates (in mm) of a \em vector //## \a vec_mm to (continuous!) index coordinates. //## For further information about coordinates types, please see the Geometry documentation virtual void WorldToIndex(const mitk::Vector2D &vec_mm, mitk::Vector2D &vec_units) const; /** * \brief Initialize a plane with orientation \a planeorientation * (default: axial) with respect to \a geometry3D (default: identity). * Spacing also taken from \a geometry3D. * * \warning A former version of this method created a geometry with unit * spacing. For unit spacing use * * \code * // for in-plane unit spacing: * thisgeometry->SetSizeInUnits(thisgeometry->GetExtentInMM(0), * thisgeometry->GetExtentInMM(1)); * // additionally, for unit spacing in normal direction (former version * // did not do this): * thisgeometry->SetExtentInMM(2, 1.0); * \endcode */ virtual void InitializeStandardPlane( const BaseGeometry* geometry3D, PlaneOrientation planeorientation = Axial, ScalarType zPosition = 0, bool frontside=true, bool rotated=false ); /** * \brief Initialize a plane with orientation \a planeorientation * (default: axial) with respect to \a geometry3D (default: identity). * Spacing also taken from \a geometry3D. * * \param top if \a true, create plane at top, otherwise at bottom * (for PlaneOrientation Axial, for other plane locations respectively) */ virtual void InitializeStandardPlane( const BaseGeometry* geometry3D, bool top, PlaneOrientation planeorientation = Axial, bool frontside=true, bool rotated=false ); /** * \brief Initialize a plane with orientation \a planeorientation * (default: axial) with respect to \a transform (default: identity) * given width and height in units. * */ virtual void InitializeStandardPlane( ScalarType width, ScalarType height, const AffineTransform3D* transform = NULL, PlaneOrientation planeorientation = Axial, ScalarType zPosition = 0, bool frontside=true, bool rotated=false ); /** * \brief Initialize plane with orientation \a planeorientation * (default: axial) given width, height and spacing. * */ virtual void InitializeStandardPlane( ScalarType width, ScalarType height, const Vector3D & spacing, PlaneOrientation planeorientation = Axial, ScalarType zPosition = 0, bool frontside = true, bool rotated = false ); /** * \brief Initialize plane by width and height in pixels, right-/down-vector * (itk) to describe orientation in world-space (vectors will be normalized) * and spacing (default: 1.0 mm in all directions). * * The vectors are normalized and multiplied by the respective spacing before * they are set in the matrix. */ virtual void InitializeStandardPlane( ScalarType width, ScalarType height, const Vector3D& rightVector, const Vector3D& downVector, const Vector3D *spacing = NULL ); /** * \brief Initialize plane by width and height in pixels, * right-/down-vector (vnl) to describe orientation in world-space (vectors * will be normalized) and spacing (default: 1.0 mm in all directions). * * The vectors are normalized and multiplied by the respective spacing * before they are set in the matrix. */ virtual void InitializeStandardPlane( ScalarType width, ScalarType height, const VnlVector& rightVector, const VnlVector& downVector, const Vector3D * spacing = NULL ); /** * \brief Initialize plane by right-/down-vector (itk) and spacing * (default: 1.0 mm in all directions). * * The length of the right-/-down-vector is used as width/height in units, * respectively. Then, the vectors are normalized and multiplied by the * respective spacing before they are set in the matrix. */ virtual void InitializeStandardPlane( const Vector3D& rightVector, const Vector3D& downVector, const Vector3D * spacing = NULL ); /** * \brief Initialize plane by right-/down-vector (vnl) and spacing * (default: 1.0 mm in all directions). * * The length of the right-/-down-vector is used as width/height in units, * respectively. Then, the vectors are normalized and multiplied by the * respective spacing before they are set in the matrix. */ virtual void InitializeStandardPlane( const VnlVector& rightVector, const VnlVector& downVector, const Vector3D * spacing = NULL ); /** * \brief Initialize plane by origin and normal (size is 1.0 mm in * all directions, direction of right-/down-vector valid but * undefined). * */ virtual void InitializePlane( const Point3D& origin, const Vector3D& normal); /** * \brief Initialize plane by right-/down-vector. * * \warning The vectors are set into the matrix as they are, * \em without normalization! */ void SetMatrixByVectors( const VnlVector& rightVector, const VnlVector& downVector, ScalarType thickness=1.0 ); /** * \brief Change \a transform so that the third column of the * transform-martix is perpendicular to the first two columns * */ static void EnsurePerpendicularNormal( AffineTransform3D* transform ); /** * \brief Normal of the plane * */ Vector3D GetNormal() const; /** * \brief Normal of the plane as VnlVector * */ VnlVector GetNormalVnl() const; virtual ScalarType SignedDistance( const Point3D& pt3d_mm ) const; - virtual bool IsAbove( const Point3D& pt3d_mm ) const; + virtual bool IsAbove( const Point3D& pt3d_mm , bool considerBoundingBox=false) const; /** * \brief Distance of the point from the plane * (bounding-box \em not considered) * */ ScalarType DistanceFromPlane( const Point3D& pt3d_mm ) const ; /** * \brief Signed distance of the point from the plane * (bounding-box \em not considered) * * > 0 : point is in the direction of the direction vector. */ inline ScalarType SignedDistanceFromPlane( const Point3D& pt3d_mm ) const { ScalarType len = GetNormalVnl().two_norm(); if( len == 0 ) return 0; return (pt3d_mm-GetOrigin())*GetNormal() / len; } /** * \brief Distance of the plane from another plane * (bounding-box \em not considered) * * Result is 0 if planes are not parallel. */ ScalarType DistanceFromPlane(const PlaneGeometry* plane) const { return fabs(SignedDistanceFromPlane(plane)); } /** * \brief Signed distance of the plane from another plane * (bounding-box \em not considered) * * Result is 0 if planes are not parallel. */ inline ScalarType SignedDistanceFromPlane( const PlaneGeometry *plane ) const { if(IsParallel(plane)) { return SignedDistance(plane->GetOrigin()); } return 0; } /** * \brief Calculate the intersecting line of two planes * * \return \a true planes are intersecting * \return \a false planes do not intersect */ bool IntersectionLine( const PlaneGeometry *plane, Line3D &crossline ) const; /** * \brief Calculate two points where another plane intersects the border of this plane * * \return number of intersection points (0..2). First interection point (if existing) * is returned in \a lineFrom, second in \a lineTo. */ unsigned int IntersectWithPlane2D(const PlaneGeometry *plane, Point2D &lineFrom, Point2D &lineTo ) const ; /** * \brief Calculate the angle between two planes * * \return angle in radiants */ double Angle( const PlaneGeometry *plane ) const; /** * \brief Calculate the angle between the plane and a line * * \return angle in radiants */ double Angle( const Line3D &line ) const; /** * \brief Calculate intersection point between the plane and a line * * \param intersectionPoint intersection point * \return \a true if \em unique intersection exists, i.e., if line * is \em not on or parallel to the plane */ bool IntersectionPoint( const Line3D &line, Point3D &intersectionPoint ) const; /** * \brief Calculate line parameter of intersection point between the * plane and a line * * \param t parameter of line: intersection point is * line.GetPoint()+t*line.GetDirection() * \return \a true if \em unique intersection exists, i.e., if line * is \em not on or parallel to the plane */ bool IntersectionPointParam( const Line3D &line, double &t ) const; /** * \brief Returns whether the plane is parallel to another plane * * @return true iff the normal vectors both point to the same or exactly oposit direction */ bool IsParallel( const PlaneGeometry *plane ) const; /** * \brief Returns whether the point is on the plane * (bounding-box \em not considered) */ bool IsOnPlane( const Point3D &point ) const; /** * \brief Returns whether the line is on the plane * (bounding-box \em not considered) */ bool IsOnPlane( const Line3D &line ) const; /** * \brief Returns whether the plane is on the plane * (bounding-box \em not considered) * * @return true iff the normal vector of the planes point to the same or the exactly oposit direction and * the distance of the planes is < eps * */ bool IsOnPlane( const PlaneGeometry *plane ) const; /** * \brief Returns the lot from the point to the plane */ Point3D ProjectPointOntoPlane( const Point3D &pt ) const; virtual itk::LightObject::Pointer InternalClone() const; /** Implements operation to re-orient the plane */ virtual void ExecuteOperation( Operation *operation ); + //******************************** functions from Geometry2D ********************** + + /** + * \brief Project a 3D point given in mm (\a pt3d_mm) onto the 2D + * geometry. The result is a 2D point in mm (\a pt2d_mm). + * + * The result is a 2D point in mm (\a pt2d_mm) relative to the upper-left + * corner of the geometry. To convert this point into units (e.g., pixels + * in case of an image), use WorldToIndex. + * \return true projection was possible + * \sa Project(const mitk::Point3D &pt3d_mm, mitk::Point3D + * &projectedPt3d_mm) + */ + virtual bool Map(const mitk::Point3D &pt3d_mm, mitk::Point2D &pt2d_mm) const; + + /** + * \brief Converts a 2D point given in mm (\a pt2d_mm) relative to the + * upper-left corner of the geometry into the corresponding + * world-coordinate (a 3D point in mm, \a pt3d_mm). + * + * To convert a 2D point given in units (e.g., pixels in case of an + * image) into a 2D point given in mm (as required by this method), use + * IndexToWorld. + */ + virtual void Map(const mitk::Point2D &pt2d_mm, mitk::Point3D &pt3d_mm) const; + + /** + * \brief Set the width and height of this 2D-geometry in units by calling + * SetBounds. This does \a not change the extent in mm! + * + * For an image, this is the number of pixels in x-/y-direction. + * \note In contrast to calling SetBounds directly, this does \a not change + * the extent in mm! + */ + virtual void SetSizeInUnits(mitk::ScalarType width, mitk::ScalarType height); + + /** + * \brief Project a 3D point given in mm (\a pt3d_mm) onto the 2D + * geometry. The result is a 3D point in mm (\a projectedPt3d_mm). + * + * \return true projection was possible + */ + virtual bool Project(const mitk::Point3D &pt3d_mm, + mitk::Point3D &projectedPt3d_mm) const; + + /** + * \brief Project a 3D vector given in mm (\a vec3d_mm) onto the 2D + * geometry. The result is a 2D vector in mm (\a vec2d_mm). + * + * The result is a 2D vector in mm (\a vec2d_mm) relative to the + * upper-left + * corner of the geometry. To convert this point into units (e.g., pixels + * in case of an image), use WorldToIndex. + * \return true projection was possible + * \sa Project(const mitk::Vector3D &vec3d_mm, mitk::Vector3D + * &projectedVec3d_mm) + */ + virtual bool Map(const mitk::Point3D & atPt3d_mm, + const mitk::Vector3D &vec3d_mm, mitk::Vector2D &vec2d_mm) const; + + /** + * \brief Converts a 2D vector given in mm (\a vec2d_mm) relative to the + * upper-left corner of the geometry into the corresponding + * world-coordinate (a 3D vector in mm, \a vec3d_mm). + * + * To convert a 2D vector given in units (e.g., pixels in case of an + * image) into a 2D vector given in mm (as required by this method), use + * IndexToWorld. + */ + virtual void Map(const mitk::Point2D & atPt2d_mm, + const mitk::Vector2D &vec2d_mm, mitk::Vector3D &vec3d_mm) const; + + /** + * \brief Project a 3D vector given in mm (\a vec3d_mm) onto the 2D + * geometry. The result is a 3D vector in mm (\a projectedVec3d_mm). + * + * DEPRECATED. Use Project(vector,vector) instead + * + * \return true projection was possible + */ + virtual bool Project(const mitk::Point3D & atPt3d_mm, + const mitk::Vector3D &vec3d_mm, mitk::Vector3D &projectedVec3d_mm) const; + + /** + * \brief Project a 3D vector given in mm (\a vec3d_mm) onto the 2D + * geometry. The result is a 3D vector in mm (\a projectedVec3d_mm). + * + * \return true projection was possible + */ + virtual bool Project( const mitk::Vector3D &vec3d_mm, mitk::Vector3D &projectedVec3d_mm) const; + + /** + * \brief Distance of the point from the geometry + * (bounding-box \em not considered) + * + */ + inline ScalarType Distance(const Point3D& pt3d_mm) const + { + return fabs(SignedDistance(pt3d_mm)); + } + + /** + * \brief Set the geometrical frame of reference in which this PlaneGeometry + * is placed. + * + * This would usually be the Geometry3D of the underlying dataset, but + * setting it is optional. + */ + void SetReferenceGeometry( mitk::BaseGeometry *geometry ); + + /** + * \brief Get the geometrical frame of reference for this PlaneGeometry. + */ + BaseGeometry *GetReferenceGeometry() const; + bool HasReferenceGeometry() const; + + protected: PlaneGeometry(); + PlaneGeometry(const PlaneGeometry& other); + virtual ~PlaneGeometry(); virtual void PrintSelf( std::ostream &os, itk::Indent indent ) const; virtual void InternPreSetBounds( const BoundingBox::BoundsArrayType &bounds ); virtual void InternPreSetIndexToWorldTransform( AffineTransform3D *transform); + //******************** from Geometry2D ********************* + virtual void InternPostSetExtentInMM(int direction, ScalarType extentInMM); + + virtual void InternPostSetIndexToWorldTransform(mitk::AffineTransform3D* transform); + + /** + * \brief factor to convert x-coordinates from mm to units and vice versa + * + */ + mutable mitk::ScalarType m_ScaleFactorMMPerUnitX; + + /** + * \brief factor to convert y-coordinates from mm to units and vice versa + * + */ + mutable mitk::ScalarType m_ScaleFactorMMPerUnitY; + + mitk::BaseGeometry *m_ReferenceGeometry; + private: /** * \brief Compares plane with another plane: \a true if IsOnPlane * (bounding-box \em not considered) */ virtual bool operator==( const PlaneGeometry * ) const { return false; }; /** * \brief Compares plane with another plane: \a false if IsOnPlane * (bounding-box \em not considered) */ virtual bool operator!=( const PlaneGeometry * ) const { return false; }; }; } // namespace mitk #endif /* PLANEGEOMETRY_H_HEADER_INCLUDED_C1C68A2C */ diff --git a/Core/Code/DataManagement/mitkSlicedData.cpp b/Core/Code/DataManagement/mitkSlicedData.cpp index 855d16432b..0de1afde52 100644 --- a/Core/Code/DataManagement/mitkSlicedData.cpp +++ b/Core/Code/DataManagement/mitkSlicedData.cpp @@ -1,359 +1,359 @@ /*=================================================================== 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 "mitkSlicedData.h" #include "mitkBaseProcess.h" #include mitk::SlicedData::SlicedData() : m_UseLargestPossibleRegion(false) { unsigned int i; for(i=0;i<4;++i) { m_LargestPossibleRegion.SetIndex(i, 0); m_LargestPossibleRegion.SetSize (i, 1); } } mitk::SlicedData::SlicedData( const SlicedData &other ): BaseData(other), m_LargestPossibleRegion(other.m_LargestPossibleRegion), m_RequestedRegion(other.m_RequestedRegion), m_BufferedRegion(other.m_BufferedRegion), m_UseLargestPossibleRegion(other.m_UseLargestPossibleRegion) { } mitk::SlicedData::~SlicedData() { } void mitk::SlicedData::UpdateOutputInformation() { Superclass::UpdateOutputInformation(); if (this->GetSource().IsNull()) // If we don't have a source, then let's make our Image // span our buffer { m_UseLargestPossibleRegion = true; } // Now we should know what our largest possible region is. If our // requested region was not set yet, (or has been set to something // invalid - with no data in it ) then set it to the largest possible // region. if ( ! m_RequestedRegionInitialized) { this->SetRequestedRegionToLargestPossibleRegion(); m_RequestedRegionInitialized = true; } m_LastRequestedRegionWasOutsideOfTheBufferedRegion = 0; } void mitk::SlicedData::PrepareForNewData() { if ( GetUpdateMTime() < GetPipelineMTime() || GetDataReleased() ) { ReleaseData(); } } void mitk::SlicedData::SetRequestedRegionToLargestPossibleRegion() { m_UseLargestPossibleRegion = true; if(GetGeometry()==NULL) return; unsigned int i; const RegionType::IndexType & index = GetLargestPossibleRegion().GetIndex(); const RegionType::SizeType & size = GetLargestPossibleRegion().GetSize(); for(i=0;i(requestedRegionSize[4]); if(requestedRegionSize[3] == largestPossibleRegionSize[3]) { for (; c< cEnd; ++c) if(IsChannelSet(c)==false) return true; return false; } // are whole volumes requested? int t, tEnd; t=requestedRegionIndex[3]; tEnd=t+static_cast(requestedRegionSize[3]); if(requestedRegionSize[2] == largestPossibleRegionSize[2]) { for (; c< cEnd; ++c) for (; t< tEnd; ++t) if(IsVolumeSet(t, c)==false) return true; return false; } // ok, only slices are requested. Check if they are available. int s, sEnd; s=requestedRegionIndex[2]; sEnd=s+static_cast(requestedRegionSize[2]); for (; c< cEnd; ++c) for (; t< tEnd; ++t) for (; s< sEnd; ++s) if(IsSliceSet(s, t, c)==false) return true; return false; } bool mitk::SlicedData::VerifyRequestedRegion() { if(GetTimeGeometry() == NULL) return false; unsigned int i; // Is the requested region within the LargestPossibleRegion? // Note that the test is indeed against the largest possible region // rather than the buffered region; see DataObject::VerifyRequestedRegion. const IndexType &requestedRegionIndex = m_RequestedRegion.GetIndex(); const IndexType &largestPossibleRegionIndex = GetLargestPossibleRegion().GetIndex(); const SizeType& requestedRegionSize = m_RequestedRegion.GetSize(); const SizeType& largestPossibleRegionSize = GetLargestPossibleRegion().GetSize(); for (i=0; i< RegionDimension; ++i) { if ( (requestedRegionIndex[i] < largestPossibleRegionIndex[i]) || ((requestedRegionIndex[i] + static_cast(requestedRegionSize[i])) > (largestPossibleRegionIndex[i]+static_cast(largestPossibleRegionSize[i])))) { return false; } } return true; } void mitk::SlicedData::SetRequestedRegion( const itk::DataObject *data) { m_UseLargestPossibleRegion=false; const mitk::SlicedData *slicedData = dynamic_cast(data); if (slicedData) { m_RequestedRegion = slicedData->GetRequestedRegion(); m_RequestedRegionInitialized = true; } else { // pointer could not be cast back down itkExceptionMacro( << "mitk::SlicedData::SetRequestedRegion(DataObject*) cannot cast " << typeid(data).name() << " to " << typeid(SlicedData*).name() ); } } void mitk::SlicedData::SetRequestedRegion(SlicedData::RegionType *region) { m_UseLargestPossibleRegion=false; if(region!=NULL) { m_RequestedRegion = *region; m_RequestedRegionInitialized = true; } else { // pointer could not be cast back down itkExceptionMacro( << "mitk::SlicedData::SetRequestedRegion(SlicedData::RegionType*) cannot cast " << typeid(region).name() << " to " << typeid(SlicedData*).name() ); } } void mitk::SlicedData::SetLargestPossibleRegion(SlicedData::RegionType *region) { if(region!=NULL) { m_LargestPossibleRegion = *region; m_UseLargestPossibleRegion=true; } else { // pointer could not be cast back down itkExceptionMacro( << "mitk::SlicedData::SetLargestPossibleRegion(SlicedData::RegionType*) cannot cast " << typeid(region).name() << " to " << typeid(SlicedData*).name() ); } } void mitk::SlicedData::CopyInformation(const itk::DataObject *data) { // Standard call to the superclass' method Superclass::CopyInformation(data); const mitk::SlicedData *slicedData; slicedData = dynamic_cast(data); if (slicedData) { m_LargestPossibleRegion = slicedData->GetLargestPossibleRegion(); } else { // pointer could not be cast back down itkExceptionMacro( << "mitk::SlicedData::CopyInformation(const DataObject *data) cannot cast " << typeid(data).name() << " to " << typeid(SlicedData*).name() ); } } -//const mitk::Geometry2D* mitk::SlicedData::GetGeometry2D(int s, int t) const +//const mitk::PlaneGeometry* mitk::SlicedData::GetGeometry2D(int s, int t) const //{ // const_cast(this)->SetRequestedRegionToLargestPossibleRegion(); // // const_cast(this)->UpdateOutputInformation(); // // return GetSlicedGeometry(t)->GetGeometry2D(s); //} // mitk::SlicedGeometry3D* mitk::SlicedData::GetSlicedGeometry(unsigned int t) const { if (GetTimeGeometry() == NULL) return NULL; return dynamic_cast(GetTimeGeometry()->GetGeometryForTimeStep(t).GetPointer()); } const mitk::SlicedGeometry3D* mitk::SlicedData::GetUpdatedSlicedGeometry(unsigned int t) { SetRequestedRegionToLargestPossibleRegion(); UpdateOutputInformation(); return GetSlicedGeometry(t); } void mitk::SlicedData::SetGeometry(BaseGeometry* aGeometry3D) { if(aGeometry3D!=NULL) { ProportionalTimeGeometry::Pointer timeGeometry = ProportionalTimeGeometry::New(); SlicedGeometry3D::Pointer slicedGeometry = dynamic_cast(aGeometry3D); if(slicedGeometry.IsNull()) { - Geometry2D* geometry2d = dynamic_cast(aGeometry3D); + PlaneGeometry* geometry2d = dynamic_cast(aGeometry3D); if(geometry2d!=NULL) { if((GetSlicedGeometry()->GetGeometry2D(0)==geometry2d) && (GetSlicedGeometry()->GetSlices()==1)) return; slicedGeometry = SlicedGeometry3D::New(); slicedGeometry->InitializeEvenlySpaced(geometry2d, 1); } else { slicedGeometry = SlicedGeometry3D::New(); PlaneGeometry::Pointer planeGeometry = PlaneGeometry::New(); planeGeometry->InitializeStandardPlane(aGeometry3D); slicedGeometry->InitializeEvenlySpaced(planeGeometry, (unsigned int)(aGeometry3D->GetExtent(2))); } } assert(slicedGeometry.IsNotNull()); timeGeometry->Initialize(slicedGeometry, 1); Superclass::SetTimeGeometry(timeGeometry); } else { if(GetGeometry()==NULL) return; Superclass::SetGeometry(NULL); } } void mitk::SlicedData::SetSpacing(const ScalarType aSpacing[3]) { this->SetSpacing((mitk::Vector3D)aSpacing); } void mitk::SlicedData::SetOrigin(const mitk::Point3D& origin) { TimeGeometry* timeGeometry = GetTimeGeometry(); assert(timeGeometry!=NULL); mitk::SlicedGeometry3D* slicedGeometry; unsigned int steps = timeGeometry->CountTimeSteps(); for(unsigned int timestep = 0; timestep < steps; ++timestep) { slicedGeometry = GetSlicedGeometry(timestep); if(slicedGeometry != NULL) { slicedGeometry->SetOrigin(origin); if(slicedGeometry->GetEvenlySpaced()) { - mitk::Geometry2D* geometry2D = slicedGeometry->GetGeometry2D(0); + mitk::PlaneGeometry* geometry2D = slicedGeometry->GetGeometry2D(0); geometry2D->SetOrigin(origin); slicedGeometry->InitializeEvenlySpaced(geometry2D, slicedGeometry->GetSlices()); } } //ProportionalTimeGeometry* timeGeometry = dynamic_cast(GetTimeGeometry()); //if(timeGeometry != NULL) //{ // timeGeometry->Initialize(slicedGeometry, steps); // break; //} } } void mitk::SlicedData::SetSpacing(mitk::Vector3D aSpacing) { TimeGeometry* timeGeometry = GetTimeGeometry(); assert(timeGeometry!=NULL); mitk::SlicedGeometry3D* slicedGeometry; unsigned int steps = timeGeometry->CountTimeSteps(); for(unsigned int timestep = 0; timestep < steps; ++timestep) { slicedGeometry = GetSlicedGeometry(timestep); if(slicedGeometry != NULL) { slicedGeometry->SetSpacing(aSpacing); } } timeGeometry->Update(); } diff --git a/Core/Code/DataManagement/mitkSlicedData.h b/Core/Code/DataManagement/mitkSlicedData.h index 46edad9876..4ea79a0ebb 100644 --- a/Core/Code/DataManagement/mitkSlicedData.h +++ b/Core/Code/DataManagement/mitkSlicedData.h @@ -1,227 +1,227 @@ /*=================================================================== 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 SLICEDDATA_H_HEADER_INCLUDED #define SLICEDDATA_H_HEADER_INCLUDED #include #include "mitkBaseData.h" #include "mitkSlicedGeometry3D.h" #include "itkIndex.h" #include "itkOffset.h" #include "itkSize.h" #include "itkImageRegion.h" namespace mitk { class SlicedGeometry3D; //##Documentation //## @brief Super class of data objects consisting of slices //## //## Super class of data objects consisting of slices, e.g., images or a stack -//## of contours. (GetGeometry will return a Geometry3D containing Geometry2D +//## of contours. (GetGeometry will return a Geometry3D containing PlaneGeometry //## objects). //## //## SlicedData-objects have geometries of type SlicedGeometry3D or sub-classes. //## @ingroup Data class MITK_CORE_EXPORT SlicedData : public BaseData { public: mitkClassMacro(SlicedData, BaseData); itkStaticConstMacro(RegionDimension, unsigned int, 5); /** Region typedef support. A region is used to specify a subset of a @a SlicedData. */ typedef itk::ImageRegion RegionType; /** Index typedef support. An index is used to access pixel values. */ typedef itk::Index IndexType; typedef IndexType::IndexValueType IndexValueType; /** Offset typedef support. An offset represent relative position * between indices. */ typedef itk::Offset OffsetType; typedef OffsetType::OffsetValueType OffsetValueType; /** Size typedef support. A size is used to define region bounds. */ typedef itk::Size SizeType; typedef SizeType::SizeValueType SizeValueType; //##Documentation //## Update the information for this DataObject so that it can be used as //## an output of a ProcessObject. This method is used in the pipeline //## mechanism to propagate information and initialize the meta data //## associated with a itk::DataObject. Any implementation of this method //## in a derived class of itk::DataObject is assumed to call its source's //## ProcessObject::UpdateOutputInformation() which determines modified //## times, LargestPossibleRegions, and any extra meta data like spacing, //## origin, etc. virtual void UpdateOutputInformation(); virtual void PrepareForNewData(); //##Documentation //## Set the RequestedRegion to the LargestPossibleRegion. This forces a //## filter to produce all of the output in one execution (i.e. not //## streaming) on the next call to Update(). virtual void SetRequestedRegionToLargestPossibleRegion(); //##Documentation //## Determine whether the RequestedRegion is outside of the //## BufferedRegion. This method returns true if the RequestedRegion is //## outside the BufferedRegion (true if at least one pixel is outside). //## This is used by the pipeline mechanism to determine whether a filter //## needs to re-execute in order to satisfy the current request. If the //## current RequestedRegion is already inside the BufferedRegion from the //## previous execution (and the current filter is up to date), then a //## given filter does not need to re-execute virtual bool RequestedRegionIsOutsideOfTheBufferedRegion(); //##Documentation //## @brief Verify that the RequestedRegion is within the //## LargestPossibleRegion. //## //## Verify that the RequestedRegion is within the LargestPossibleRegion. //## If the RequestedRegion is not within the LargestPossibleRegion, //## then the filter cannot possibly satisfy the request. This method //## returns true if the request can be satisfied (even if it will be //## necessary to process the entire LargestPossibleRegion) and //## returns false otherwise. This method is used by //## PropagateRequestedRegion(). PropagateRequestedRegion() throws a //## InvalidRequestedRegionError exception if the requested region is //## not within the LargestPossibleRegion. virtual bool VerifyRequestedRegion(); //##Documentation //## Set the requested region from this data object to match the requested //## region of the data object passed in as a parameter. This method is //## implemented in the concrete subclasses of DataObject. virtual void SetRequestedRegion( const itk::DataObject *data); //##Documentation //## Set the requested region from this data object to match the requested //## region of the data object passed in as a parameter. This method is //## implemented in the concrete subclasses of DataObject. virtual void SetRequestedRegion(SlicedData::RegionType *region); /*! Documentation \brief Sets the largest possible region. The largest possible region is the entire region occupied by the data object. Note that the largest possible region should always be bigger then the requested region of a certain operation.*/ void SetLargestPossibleRegion(SlicedData::RegionType *region); const RegionType& GetLargestPossibleRegion() const { return m_LargestPossibleRegion; } //##Documentation //## Get the region object that defines the size and starting index //## for the region of the image requested (i.e., the region of the //## image to be operated on by a filter). virtual const RegionType& GetRequestedRegion() const { return m_RequestedRegion; } virtual bool IsSliceSet(int s = 0, int t = 0, int n = 0) const = 0; virtual bool IsVolumeSet(int t = 0, int n = 0) const = 0; virtual bool IsChannelSet(int n = 0) const = 0; virtual void CopyInformation(const itk::DataObject *data); //##Documentation //## @brief Get the number of channels unsigned int GetNumberOfChannels() const { return m_LargestPossibleRegion.GetSize(4); } ////##Documentation - ////## @brief Return the Geometry2D of the slice (@a s, @a t). + ////## @brief Return the PlaneGeometry of the slice (@a s, @a t). ////## ////## The method does not simply call GetGeometry()->GetGeometry2D(). Before doing this, it - ////## makes sure that the Geometry2D is up-to-date before returning it (by + ////## makes sure that the PlaneGeometry is up-to-date before returning it (by ////## setting the update extent appropriately and calling ////## UpdateOutputInformation). ////## ////## @warning GetGeometry2D not yet completely implemented. ////## @todo Appropriate setting of the update extent is missing. - //virtual const mitk::Geometry2D* GetGeometry2D(int s, int t=0) const; + //virtual const mitk::PlaneGeometry* GetGeometry2D(int s, int t=0) const; //##Documentation //## @brief Convenience access method for the geometry, which is of type SlicedGeometry3D (or a sub-class of it). //## //## @em No update will be called. Normally used in GenerateOutputInformation of //## subclasses of BaseProcess. SlicedGeometry3D* GetSlicedGeometry(unsigned int t=0) const; //##Documentation //## @brief Convenience access method for the geometry, which is of type SlicedGeometry3D (or a sub-class of it). //## //## The method does not simply return the value of the m_Geometry3D member. //## Before doing this, it makes sure that the Geometry3D is up-to-date before //## returning it (by setting the update extent appropriately and calling //## UpdateOutputInformation). //## //## @warning GetGeometry not yet completely implemented. //## @todo Appropriate setting of the update extent is missing. const SlicedGeometry3D* GetUpdatedSlicedGeometry(unsigned int t=0); //##Documentation //## @brief Set the Geometry3D of the data, which will be referenced (not copied!). It //## has to be a sub-class of SlicedGeometry3D. //## //## @warning This method will normally be called internally by the sub-class of SlicedData //## during initialization. virtual void SetGeometry(BaseGeometry* aGeometry3D); //##Documentation //## @brief Convenience method for setting the origin of //## the SlicedGeometry3D instances of all time steps //## //## In case the SlicedGeometry3D is evenly spaced, //## the origin of the first slice is set to \a origin. //## \sa mitk::BaseData::SetOrigin virtual void SetOrigin(const Point3D& origin); //##Documentation //## @brief Convenience method for setting the spacing of //## the SlicedGeometry3D instances of all time steps virtual void SetSpacing(const ScalarType aSpacing[]); //##Documentation //## @brief Convenience method for setting the spacing of //## the SlicedGeometry3D instances of all time steps virtual void SetSpacing(mitk::Vector3D aSpacing); protected: SlicedData(); SlicedData(const SlicedData &other); virtual ~SlicedData(); RegionType m_LargestPossibleRegion; RegionType m_RequestedRegion; RegionType m_BufferedRegion; bool m_UseLargestPossibleRegion; }; } // namespace mitk #endif /* SLICEDDATA_H_HEADER_INCLUDED */ diff --git a/Core/Code/DataManagement/mitkSlicedGeometry3D.cpp b/Core/Code/DataManagement/mitkSlicedGeometry3D.cpp index e015554e1a..161589f698 100644 --- a/Core/Code/DataManagement/mitkSlicedGeometry3D.cpp +++ b/Core/Code/DataManagement/mitkSlicedGeometry3D.cpp @@ -1,1031 +1,1031 @@ /*=================================================================== 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 "mitkSlicedGeometry3D.h" #include "mitkPlaneGeometry.h" #include "mitkRotationOperation.h" #include "mitkPlaneOperation.h" #include "mitkRestorePlanePositionOperation.h" #include "mitkApplyTransformMatrixOperation.h" #include "mitkInteractionConst.h" #include "mitkSliceNavigationController.h" const mitk::ScalarType PI = 3.14159265359; mitk::SlicedGeometry3D::SlicedGeometry3D() : m_EvenlySpaced( true ), m_Slices( 0 ), m_ReferenceGeometry( NULL ), m_SliceNavigationController( NULL ) { m_DirectionVector.Fill(0); this->InitializeSlicedGeometry( m_Slices ); } mitk::SlicedGeometry3D::SlicedGeometry3D(const SlicedGeometry3D& other) : Superclass(other), m_EvenlySpaced( other.m_EvenlySpaced ), m_Slices( other.m_Slices ), m_ReferenceGeometry( other.m_ReferenceGeometry ), m_SliceNavigationController( other.m_SliceNavigationController ) { m_DirectionVector.Fill(0); SetSpacing( other.GetSpacing() ); SetDirectionVector( other.GetDirectionVector() ); if ( m_EvenlySpaced ) { - Geometry2D::Pointer geometry = other.m_Geometry2Ds[0]->Clone(); - Geometry2D* geometry2D = dynamic_cast(geometry.GetPointer()); + PlaneGeometry::Pointer geometry = other.m_Geometry2Ds[0]->Clone(); + PlaneGeometry* geometry2D = dynamic_cast(geometry.GetPointer()); assert(geometry2D!=NULL); SetGeometry2D(geometry2D, 0); } else { unsigned int s; for ( s = 0; s < other.m_Slices; ++s ) { if ( other.m_Geometry2Ds[s].IsNull() ) { assert(other.m_EvenlySpaced); m_Geometry2Ds[s] = NULL; } else { - Geometry2D* geometry2D = other.m_Geometry2Ds[s]->Clone(); + PlaneGeometry* geometry2D = other.m_Geometry2Ds[s]->Clone(); assert(geometry2D!=NULL); SetGeometry2D(geometry2D, s); } } } } mitk::SlicedGeometry3D::~SlicedGeometry3D() { } -mitk::Geometry2D * +mitk::PlaneGeometry * mitk::SlicedGeometry3D::GetGeometry2D( int s ) const { - mitk::Geometry2D::Pointer geometry2D = NULL; + mitk::PlaneGeometry::Pointer geometry2D = NULL; if ( this->IsValidSlice(s) ) { geometry2D = m_Geometry2Ds[s]; - // If (a) m_EvenlySpaced==true, (b) we don't have a Geometry2D stored + // If (a) m_EvenlySpaced==true, (b) we don't have a PlaneGeometry stored // for the requested slice, and (c) the first slice (s=0) // is a PlaneGeometry instance, then we calculate the geometry of the // requested as the plane of the first slice shifted by m_Spacing[2]*s // in the direction of m_DirectionVector. if ( (m_EvenlySpaced) && (geometry2D.IsNull()) ) { PlaneGeometry *firstSlice = dynamic_cast< PlaneGeometry * > ( m_Geometry2Ds[0].GetPointer() ); if ( firstSlice != NULL ) { if ( (m_DirectionVector[0] == 0.0) && (m_DirectionVector[1] == 0.0) && (m_DirectionVector[2] == 0.0) ) { m_DirectionVector = firstSlice->GetNormal(); m_DirectionVector.Normalize(); } Vector3D direction; direction = m_DirectionVector * this->GetSpacing()[2]; mitk::PlaneGeometry::Pointer requestedslice; requestedslice = static_cast< mitk::PlaneGeometry * >( firstSlice->Clone().GetPointer() ); requestedslice->SetOrigin( requestedslice->GetOrigin() + direction * s ); geometry2D = requestedslice; m_Geometry2Ds[s] = geometry2D; } } return geometry2D; } else { return NULL; } } const mitk::BoundingBox * mitk::SlicedGeometry3D::GetBoundingBox() const { assert(this->IsBoundingBoxNull()==false); return Superclass::GetBoundingBox(); } bool - mitk::SlicedGeometry3D::SetGeometry2D( mitk::Geometry2D *geometry2D, int s ) + mitk::SlicedGeometry3D::SetGeometry2D( mitk::PlaneGeometry *geometry2D, int s ) { if ( this->IsValidSlice(s) ) { m_Geometry2Ds[s] = geometry2D; m_Geometry2Ds[s]->SetReferenceGeometry( m_ReferenceGeometry ); return true; } return false; } void mitk::SlicedGeometry3D::InitializeSlicedGeometry( unsigned int slices ) { Superclass::Initialize(); m_Slices = slices; - Geometry2D::Pointer gnull = NULL; + PlaneGeometry::Pointer gnull = NULL; m_Geometry2Ds.assign( m_Slices, gnull ); Vector3D spacing; spacing.Fill( 1.0 ); this->SetSpacing( spacing ); m_DirectionVector.Fill( 0 ); } void mitk::SlicedGeometry3D::InitializeEvenlySpaced( - mitk::Geometry2D* geometry2D, unsigned int slices, bool flipped ) + mitk::PlaneGeometry* geometry2D, unsigned int slices, bool flipped ) { assert( geometry2D != NULL ); this->InitializeEvenlySpaced( geometry2D, geometry2D->GetExtentInMM(2)/geometry2D->GetExtent(2), slices, flipped ); } void mitk::SlicedGeometry3D::InitializeEvenlySpaced( - mitk::Geometry2D* geometry2D, mitk::ScalarType zSpacing, + mitk::PlaneGeometry* geometry2D, mitk::ScalarType zSpacing, unsigned int slices, bool flipped ) { assert( geometry2D != NULL ); assert( geometry2D->GetExtent(0) > 0 ); assert( geometry2D->GetExtent(1) > 0 ); geometry2D->Register(); Superclass::Initialize(); m_Slices = slices; BoundingBox::BoundsArrayType bounds = geometry2D->GetBounds(); bounds[4] = 0; bounds[5] = slices; // clear and reserve - Geometry2D::Pointer gnull = NULL; + PlaneGeometry::Pointer gnull = NULL; m_Geometry2Ds.assign( m_Slices, gnull ); Vector3D directionVector = geometry2D->GetAxisVector(2); directionVector.Normalize(); directionVector *= zSpacing; if ( flipped == false ) { // Normally we should use the following four lines to create a copy of // the transform contrained in geometry2D, because it may not be changed // by us. But we know that SetSpacing creates a new transform without // changing the old (coming from geometry2D), so we can use the fifth // line instead. We check this at (**). // // AffineTransform3D::Pointer transform = AffineTransform3D::New(); // transform->SetMatrix(geometry2D->GetIndexToWorldTransform()->GetMatrix()); // transform->SetOffset(geometry2D->GetIndexToWorldTransform()->GetOffset()); // SetIndexToWorldTransform(transform); this->SetIndexToWorldTransform( const_cast< AffineTransform3D * >( geometry2D->GetIndexToWorldTransform() )); } else { directionVector *= -1.0; this->SetIndexToWorldTransform( AffineTransform3D::New()); this->GetIndexToWorldTransform()->SetMatrix( geometry2D->GetIndexToWorldTransform()->GetMatrix() ); AffineTransform3D::OutputVectorType scaleVector; FillVector3D(scaleVector, 1.0, 1.0, -1.0); this->GetIndexToWorldTransform()->Scale(scaleVector, true); this->GetIndexToWorldTransform()->SetOffset( geometry2D->GetIndexToWorldTransform()->GetOffset() ); } mitk::Vector3D spacing; FillVector3D( spacing, geometry2D->GetExtentInMM(0) / bounds[1], geometry2D->GetExtentInMM(1) / bounds[3], zSpacing ); this->SetDirectionVector( directionVector ); this->SetBounds( bounds ); this->SetGeometry2D( geometry2D, 0 ); this->SetSpacing( spacing ,true); this->SetEvenlySpaced(); this->SetTimeBounds( geometry2D->GetTimeBounds() ); assert(this->GetIndexToWorldTransform() != geometry2D->GetIndexToWorldTransform()); // (**) see above. this->SetFrameOfReferenceID( geometry2D->GetFrameOfReferenceID() ); this->SetImageGeometry( geometry2D->GetImageGeometry() ); geometry2D->UnRegister(); } void mitk::SlicedGeometry3D::InitializePlanes( const mitk::BaseGeometry *geometry3D, mitk::PlaneGeometry::PlaneOrientation planeorientation, bool top, bool frontside, bool rotated ) { m_ReferenceGeometry = const_cast< BaseGeometry * >( geometry3D ); PlaneGeometry::Pointer planeGeometry = mitk::PlaneGeometry::New(); planeGeometry->InitializeStandardPlane( geometry3D, top, planeorientation, frontside, rotated ); ScalarType viewSpacing = 1; unsigned int slices = 1; switch ( planeorientation ) { case PlaneGeometry::Axial: viewSpacing = geometry3D->GetSpacing()[2]; slices = (unsigned int) geometry3D->GetExtent( 2 ); break; case PlaneGeometry::Frontal: viewSpacing = geometry3D->GetSpacing()[1]; slices = (unsigned int) geometry3D->GetExtent( 1 ); break; case PlaneGeometry::Sagittal: viewSpacing = geometry3D->GetSpacing()[0]; slices = (unsigned int) geometry3D->GetExtent( 0 ); break; default: itkExceptionMacro("unknown PlaneOrientation"); } mitk::Vector3D normal = this->AdjustNormal( planeGeometry->GetNormal() ); ScalarType directedExtent = std::abs( m_ReferenceGeometry->GetExtentInMM( 0 ) * normal[0] ) + std::abs( m_ReferenceGeometry->GetExtentInMM( 1 ) * normal[1] ) + std::abs( m_ReferenceGeometry->GetExtentInMM( 2 ) * normal[2] ); if ( directedExtent >= viewSpacing ) { slices = static_cast< int >(directedExtent / viewSpacing + 0.5); } else { slices = 1; } bool flipped = (top == false); if ( frontside == false ) { flipped = !flipped; } if ( planeorientation == PlaneGeometry::Frontal ) { flipped = !flipped; } this->InitializeEvenlySpaced( planeGeometry, viewSpacing, slices, flipped ); } void mitk::SlicedGeometry3D ::ReinitializePlanes( const Point3D ¢er, const Point3D &referencePoint ) { // Need a reference frame to align the rotated planes if ( !m_ReferenceGeometry ) { return; } // Get first plane of plane stack PlaneGeometry *firstPlane = dynamic_cast< PlaneGeometry * >( m_Geometry2Ds[0].GetPointer() ); // If plane stack is empty, exit if ( firstPlane == NULL ) { return; } // Calculate the "directed" spacing when taking the plane (defined by its axes // vectors and normal) as the reference coordinate frame. // // This is done by calculating the radius of the ellipsoid defined by the // original volume spacing axes, in the direction of the respective axis of the // reference frame. mitk::Vector3D axis0 = firstPlane->GetAxisVector(0); mitk::Vector3D axis1 = firstPlane->GetAxisVector(1); mitk::Vector3D normal = firstPlane->GetNormal(); normal.Normalize(); Vector3D spacing; spacing[0] = this->CalculateSpacing( axis0 ); spacing[1] = this->CalculateSpacing( axis1 ); spacing[2] = this->CalculateSpacing( normal ); Superclass::SetSpacing( spacing ); // Now we need to calculate the number of slices in the plane's normal // direction, so that the entire volume is covered. This is done by first // calculating the dot product between the volume diagonal (the maximum // distance inside the volume) and the normal, and dividing this value by // the directed spacing calculated above. ScalarType directedExtent = std::abs( m_ReferenceGeometry->GetExtentInMM( 0 ) * normal[0] ) + std::abs( m_ReferenceGeometry->GetExtentInMM( 1 ) * normal[1] ) + std::abs( m_ReferenceGeometry->GetExtentInMM( 2 ) * normal[2] ); if ( directedExtent >= spacing[2] ) { m_Slices = static_cast< unsigned int >(directedExtent / spacing[2] + 0.5); } else { m_Slices = 1; } // The origin of our "first plane" needs to be adapted to this new extent. // To achieve this, we first calculate the current distance to the volume's // center, and then shift the origin in the direction of the normal by the // difference between this distance and half of the new extent. double centerOfRotationDistance = firstPlane->SignedDistanceFromPlane( center ); if ( centerOfRotationDistance > 0 ) { firstPlane->SetOrigin( firstPlane->GetOrigin() + normal * (centerOfRotationDistance - directedExtent / 2.0) ); m_DirectionVector = normal; } else { firstPlane->SetOrigin( firstPlane->GetOrigin() + normal * (directedExtent / 2.0 + centerOfRotationDistance) ); m_DirectionVector = -normal; } // Now we adjust this distance according with respect to the given reference // point: we need to make sure that the point is touched by one slice of the // new slice stack. double referencePointDistance = firstPlane->SignedDistanceFromPlane( referencePoint ); int referencePointSlice = static_cast< int >( referencePointDistance / spacing[2]); double alignmentValue = referencePointDistance / spacing[2] - referencePointSlice; firstPlane->SetOrigin( firstPlane->GetOrigin() + normal * alignmentValue * spacing[2] ); // Finally, we can clear the previous geometry stack and initialize it with // our re-initialized "first plane". - m_Geometry2Ds.assign( m_Slices, Geometry2D::Pointer( NULL ) ); + m_Geometry2Ds.assign( m_Slices, PlaneGeometry::Pointer( NULL ) ); if ( m_Slices > 0 ) { m_Geometry2Ds[0] = firstPlane; } // Reinitialize SNC with new number of slices m_SliceNavigationController->GetSlice()->SetSteps( m_Slices ); this->Modified(); } double mitk::SlicedGeometry3D::CalculateSpacing( const mitk::Vector3D &d ) const { // Need the spacing of the underlying dataset / geometry if ( !m_ReferenceGeometry ) { return 1.0; } const mitk::Vector3D &spacing = m_ReferenceGeometry->GetSpacing(); return SlicedGeometry3D::CalculateSpacing( spacing, d ); } double mitk::SlicedGeometry3D::CalculateSpacing( const mitk::Vector3D spacing, const mitk::Vector3D &d ) { // The following can be derived from the ellipsoid equation // // 1 = x^2/a^2 + y^2/b^2 + z^2/c^2 // // where (a,b,c) = spacing of original volume (ellipsoid radii) // and (x,y,z) = scaled coordinates of vector d (according to ellipsoid) // double scaling = d[0]*d[0] / (spacing[0] * spacing[0]) + d[1]*d[1] / (spacing[1] * spacing[1]) + d[2]*d[2] / (spacing[2] * spacing[2]); scaling = sqrt( scaling ); return ( sqrt( d[0]*d[0] + d[1]*d[1] + d[2]*d[2] ) / scaling ); } mitk::Vector3D mitk::SlicedGeometry3D::AdjustNormal( const mitk::Vector3D &normal ) const { TransformType::Pointer inverse = TransformType::New(); m_ReferenceGeometry->GetIndexToWorldTransform()->GetInverse( inverse ); Vector3D transformedNormal = inverse->TransformVector( normal ); transformedNormal.Normalize(); return transformedNormal; } void mitk::SlicedGeometry3D::SetImageGeometry( const bool isAnImageGeometry ) { Superclass::SetImageGeometry( isAnImageGeometry ); mitk::BaseGeometry* geometry; unsigned int s; for ( s = 0; s < m_Slices; ++s ) { geometry = m_Geometry2Ds[s]; if ( geometry!=NULL ) { geometry->SetImageGeometry( isAnImageGeometry ); } } } void mitk::SlicedGeometry3D::ChangeImageGeometryConsideringOriginOffset( const bool isAnImageGeometry ) { mitk::BaseGeometry* geometry; unsigned int s; for ( s = 0; s < m_Slices; ++s ) { geometry = m_Geometry2Ds[s]; if ( geometry!=NULL ) { geometry->ChangeImageGeometryConsideringOriginOffset( isAnImageGeometry ); } } Superclass::ChangeImageGeometryConsideringOriginOffset( isAnImageGeometry ); } bool mitk::SlicedGeometry3D::IsValidSlice( int s ) const { return ((s >= 0) && (s < (int)m_Slices)); } void mitk::SlicedGeometry3D::SetReferenceGeometry( BaseGeometry *referenceGeometry ) { m_ReferenceGeometry = referenceGeometry; - std::vector::iterator it; + std::vector::iterator it; for ( it = m_Geometry2Ds.begin(); it != m_Geometry2Ds.end(); ++it ) { (*it)->SetReferenceGeometry( referenceGeometry ); } } void mitk::SlicedGeometry3D::InternPreSetSpacing( const mitk::Vector3D &aSpacing ) { bool hasEvenlySpacedPlaneGeometry = false; mitk::Point3D origin; mitk::Vector3D rightDV, bottomDV; BoundingBox::BoundsArrayType bounds; assert(aSpacing[0]>0 && aSpacing[1]>0 && aSpacing[2]>0); - // In case of evenly-spaced data: re-initialize instances of Geometry2D, + // In case of evenly-spaced data: re-initialize instances of PlaneGeometry, // since the spacing influences them if ((m_EvenlySpaced) && (m_Geometry2Ds.size() > 0)) { - mitk::Geometry2D::ConstPointer firstGeometry = + mitk::PlaneGeometry::ConstPointer firstGeometry = m_Geometry2Ds[0].GetPointer(); const PlaneGeometry *planeGeometry = dynamic_cast< const PlaneGeometry * >( firstGeometry.GetPointer() ); if (planeGeometry != NULL ) { this->WorldToIndex( planeGeometry->GetOrigin(), origin ); this->WorldToIndex( planeGeometry->GetAxisVector(0), rightDV ); this->WorldToIndex( planeGeometry->GetAxisVector(1), bottomDV ); bounds = planeGeometry->GetBounds(); hasEvenlySpacedPlaneGeometry = true; } } InternSetSpacing(aSpacing); - mitk::Geometry2D::Pointer firstGeometry; + mitk::PlaneGeometry::Pointer firstGeometry; - // In case of evenly-spaced data: re-initialize instances of Geometry2D, + // In case of evenly-spaced data: re-initialize instances of PlaneGeometry, // since the spacing influences them if ( hasEvenlySpacedPlaneGeometry ) { //create planeGeometry according to new spacing this->IndexToWorld( origin, origin ); this->IndexToWorld( rightDV, rightDV ); this->IndexToWorld( bottomDV, bottomDV ); mitk::PlaneGeometry::Pointer planeGeometry = mitk::PlaneGeometry::New(); planeGeometry->SetImageGeometry( this->GetImageGeometry() ); planeGeometry->SetReferenceGeometry( m_ReferenceGeometry ); //Store spacing, as Initialize... needs a pointer mitk::Vector3D lokalSpacing = this->GetSpacing(); planeGeometry->InitializeStandardPlane( rightDV.GetVnlVector(), bottomDV.GetVnlVector(), &lokalSpacing ); planeGeometry->SetOrigin(origin); planeGeometry->SetBounds(bounds); firstGeometry = planeGeometry; } else if ( (m_EvenlySpaced) && (m_Geometry2Ds.size() > 0) ) { firstGeometry = m_Geometry2Ds[0].GetPointer(); } //clear and reserve - Geometry2D::Pointer gnull=NULL; + PlaneGeometry::Pointer gnull=NULL; m_Geometry2Ds.assign(m_Slices, gnull); if ( m_Slices > 0 ) { m_Geometry2Ds[0] = firstGeometry; } this->Modified(); } void mitk::SlicedGeometry3D ::SetSliceNavigationController( SliceNavigationController *snc ) { m_SliceNavigationController = snc; } mitk::SliceNavigationController * mitk::SlicedGeometry3D::GetSliceNavigationController() { return m_SliceNavigationController; } void mitk::SlicedGeometry3D::SetEvenlySpaced(bool on) { if(m_EvenlySpaced!=on) { m_EvenlySpaced=on; this->Modified(); } } void mitk::SlicedGeometry3D ::SetDirectionVector( const mitk::Vector3D& directionVector ) { Vector3D newDir = directionVector; newDir.Normalize(); if ( newDir != m_DirectionVector ) { m_DirectionVector = newDir; this->Modified(); } } void mitk::SlicedGeometry3D::InternPostSetTimeBounds( const mitk::TimeBounds& timebounds ) { unsigned int s; for ( s = 0; s < m_Slices; ++s ) { if(m_Geometry2Ds[s].IsNotNull()) { m_Geometry2Ds[s]->SetTimeBounds( timebounds ); } } } itk::LightObject::Pointer mitk::SlicedGeometry3D::InternalClone() const { Self::Pointer newGeometry = new SlicedGeometry3D(*this); newGeometry->UnRegister(); return newGeometry.GetPointer(); } void mitk::SlicedGeometry3D::PrintSelf( std::ostream& os, itk::Indent indent ) const { Superclass::PrintSelf(os,indent); os << indent << " EvenlySpaced: " << m_EvenlySpaced << std::endl; if ( m_EvenlySpaced ) { os << indent << " DirectionVector: " << m_DirectionVector << std::endl; } os << indent << " Slices: " << m_Slices << std::endl; os << std::endl; os << indent << " GetGeometry2D(0): "; if ( this->GetGeometry2D(0) == NULL ) { os << "NULL" << std::endl; } else { this->GetGeometry2D(0)->Print(os, indent); } } void mitk::SlicedGeometry3D::ExecuteOperation(Operation* operation) { switch ( operation->GetOperationType() ) { case OpNOTHING: break; case OpROTATE: if ( m_EvenlySpaced ) { // Need a reference frame to align the rotation if ( m_ReferenceGeometry ) { // Clear all generated geometries and then rotate only the first slice. // The other slices will be re-generated on demand // Save first slice - Geometry2D::Pointer geometry2D = m_Geometry2Ds[0]; + PlaneGeometry::Pointer geometry2D = m_Geometry2Ds[0]; RotationOperation *rotOp = dynamic_cast< RotationOperation * >( operation ); // Generate a RotationOperation using the dataset center instead of // the supplied rotation center. This is necessary so that the rotated // zero-plane does not shift away. The supplied center is instead used // to adjust the slice stack afterwards. Point3D center = m_ReferenceGeometry->GetCenter(); RotationOperation centeredRotation( rotOp->GetOperationType(), center, rotOp->GetVectorOfRotation(), rotOp->GetAngleOfRotation() ); // Rotate first slice geometry2D->ExecuteOperation( ¢eredRotation ); // Clear the slice stack and adjust it according to the center of // the dataset and the supplied rotation center (see documentation of // ReinitializePlanes) this->ReinitializePlanes( center, rotOp->GetCenterOfRotation() ); geometry2D->SetSpacing(this->GetSpacing()); if ( m_SliceNavigationController ) { m_SliceNavigationController->SelectSliceByPoint( rotOp->GetCenterOfRotation() ); m_SliceNavigationController->AdjustSliceStepperRange(); } BaseGeometry::ExecuteOperation( ¢eredRotation ); } else { // we also have to consider the case, that there is no reference geometry available. if ( m_Geometry2Ds.size() > 0 ) { // Reach through to all slices in my container - for (std::vector::iterator iter = m_Geometry2Ds.begin(); + for (std::vector::iterator iter = m_Geometry2Ds.begin(); iter != m_Geometry2Ds.end(); ++iter) { // Test for empty slices, which can happen if evenly spaced geometry if ((*iter).IsNotNull()) { (*iter)->ExecuteOperation(operation); } } // rotate overall geometry RotationOperation *rotOp = dynamic_cast< RotationOperation * >( operation ); BaseGeometry::ExecuteOperation( rotOp); } } } else { // Reach through to all slices - for (std::vector::iterator iter = m_Geometry2Ds.begin(); + for (std::vector::iterator iter = m_Geometry2Ds.begin(); iter != m_Geometry2Ds.end(); ++iter) { (*iter)->ExecuteOperation(operation); } } break; case OpORIENT: if ( m_EvenlySpaced ) { // get operation data PlaneOperation *planeOp = dynamic_cast< PlaneOperation * >( operation ); // Get first slice - Geometry2D::Pointer geometry2D = m_Geometry2Ds[0]; + PlaneGeometry::Pointer geometry2D = m_Geometry2Ds[0]; PlaneGeometry *planeGeometry = dynamic_cast< PlaneGeometry * >( geometry2D.GetPointer() ); // Need a PlaneGeometry, a PlaneOperation and a reference frame to // carry out the re-orientation. If not all avaialble, stop here if ( !m_ReferenceGeometry || !planeGeometry || !planeOp ) { break; } // General Behavior: // Clear all generated geometries and then rotate only the first slice. // The other slices will be re-generated on demand // // 1st Step: Reorient Normal Vector of first plane // Point3D center = planeOp->GetPoint(); //m_ReferenceGeometry->GetCenter(); mitk::Vector3D currentNormal = planeGeometry->GetNormal(); mitk::Vector3D newNormal; if (planeOp->AreAxisDefined()) { // If planeOp was defined by one centerpoint and two axis vectors newNormal = CrossProduct(planeOp->GetAxisVec0(), planeOp->GetAxisVec1()); } else { // If planeOp was defined by one centerpoint and one normal vector newNormal = planeOp->GetNormal(); } // Get Rotation axis und angle currentNormal.Normalize(); newNormal.Normalize(); ScalarType rotationAngle = angle(currentNormal.GetVnlVector(),newNormal.GetVnlVector()); rotationAngle *= 180.0 / vnl_math::pi; // from rad to deg Vector3D rotationAxis = itk::CrossProduct( currentNormal, newNormal ); if (std::abs(rotationAngle-180) < mitk::eps ) { // current Normal and desired normal are not linear independent!!(e.g 1,0,0 and -1,0,0). // Rotation Axis should be ANY vector that is 90� to current Normal mitk::Vector3D helpNormal; helpNormal = currentNormal; helpNormal[0] += 1; helpNormal[1] -= 1; helpNormal[2] += 1; helpNormal.Normalize(); rotationAxis = itk::CrossProduct( helpNormal, currentNormal ); } RotationOperation centeredRotation( mitk::OpROTATE, center, rotationAxis, rotationAngle ); // Rotate first slice geometry2D->ExecuteOperation( ¢eredRotation ); // Reinitialize planes and select slice, if my rotations are all done. if (!planeOp->AreAxisDefined()) { // Clear the slice stack and adjust it according to the center of // rotation and plane position (see documentation of ReinitializePlanes) this->ReinitializePlanes( center, planeOp->GetPoint() ); if ( m_SliceNavigationController ) { m_SliceNavigationController->SelectSliceByPoint( planeOp->GetPoint() ); m_SliceNavigationController->AdjustSliceStepperRange(); } } // Also apply rotation on the slicedGeometry - Geometry3D (Bounding geometry) BaseGeometry::ExecuteOperation( ¢eredRotation ); // // 2nd step. If axis vectors were defined, rotate the plane around its normal to fit these // if (planeOp->AreAxisDefined()) { mitk::Vector3D vecAxixNew = planeOp->GetAxisVec0(); vecAxixNew.Normalize(); mitk::Vector3D VecAxisCurr = geometry2D->GetAxisVector(0); VecAxisCurr.Normalize(); ScalarType rotationAngle = angle(VecAxisCurr.GetVnlVector(),vecAxixNew.GetVnlVector()); rotationAngle = rotationAngle * 180 / PI; // Rad to Deg // we rotate around the normal of the plane, but we do not know, if we need to rotate clockwise // or anti-clockwise. So we rotate around the crossproduct of old and new Axisvector. // Since both axis vectors lie in the plane, the crossproduct is the planes normal or the negative planes normal rotationAxis = itk::CrossProduct( VecAxisCurr, vecAxixNew ); if (std::abs(rotationAngle-180) < mitk::eps ) { // current axisVec and desired axisVec are not linear independent!!(e.g 1,0,0 and -1,0,0). // Rotation Axis can be just plane Normal. (have to rotate by 180�) rotationAxis = newNormal; } // Perfom Rotation mitk::RotationOperation op(mitk::OpROTATE, center, rotationAxis, rotationAngle); geometry2D->ExecuteOperation( &op ); // Apply changes on first slice to whole slice stack this->ReinitializePlanes( center, planeOp->GetPoint() ); if ( m_SliceNavigationController ) { m_SliceNavigationController->SelectSliceByPoint( planeOp->GetPoint() ); m_SliceNavigationController->AdjustSliceStepperRange(); } // Also apply rotation on the slicedGeometry - Geometry3D (Bounding geometry) BaseGeometry::ExecuteOperation( &op ); } } else { // Reach through to all slices - for (std::vector::iterator iter = m_Geometry2Ds.begin(); + for (std::vector::iterator iter = m_Geometry2Ds.begin(); iter != m_Geometry2Ds.end(); ++iter) { (*iter)->ExecuteOperation(operation); } } break; case OpRESTOREPLANEPOSITION: if ( m_EvenlySpaced ) { // Save first slice - Geometry2D::Pointer geometry2D = m_Geometry2Ds[0]; + PlaneGeometry::Pointer geometry2D = m_Geometry2Ds[0]; PlaneGeometry* planeGeometry = dynamic_cast< PlaneGeometry * >( geometry2D.GetPointer() ); RestorePlanePositionOperation *restorePlaneOp = dynamic_cast< RestorePlanePositionOperation* >( operation ); // Need a PlaneGeometry, a PlaneOperation and a reference frame to // carry out the re-orientation if ( m_ReferenceGeometry && planeGeometry && restorePlaneOp ) { // Clear all generated geometries and then rotate only the first slice. // The other slices will be re-generated on demand // Rotate first slice geometry2D->ExecuteOperation( restorePlaneOp ); m_DirectionVector = restorePlaneOp->GetDirectionVector(); double centerOfRotationDistance = planeGeometry->SignedDistanceFromPlane( m_ReferenceGeometry->GetCenter() ); if ( centerOfRotationDistance > 0 ) { m_DirectionVector = m_DirectionVector; } else { m_DirectionVector = -m_DirectionVector; } Vector3D spacing = restorePlaneOp->GetSpacing(); Superclass::SetSpacing( spacing ); // /*Now we need to calculate the number of slices in the plane's normal // direction, so that the entire volume is covered. This is done by first // calculating the dot product between the volume diagonal (the maximum // distance inside the volume) and the normal, and dividing this value by // the directed spacing calculated above.*/ ScalarType directedExtent = std::abs( m_ReferenceGeometry->GetExtentInMM( 0 ) * m_DirectionVector[0] ) + std::abs( m_ReferenceGeometry->GetExtentInMM( 1 ) * m_DirectionVector[1] ) + std::abs( m_ReferenceGeometry->GetExtentInMM( 2 ) * m_DirectionVector[2] ); if ( directedExtent >= spacing[2] ) { m_Slices = static_cast< unsigned int >(directedExtent / spacing[2] + 0.5); } else { m_Slices = 1; } - m_Geometry2Ds.assign( m_Slices, Geometry2D::Pointer( NULL ) ); + m_Geometry2Ds.assign( m_Slices, PlaneGeometry::Pointer( NULL ) ); if ( m_Slices > 0 ) { m_Geometry2Ds[0] = geometry2D; } m_SliceNavigationController->GetSlice()->SetSteps( m_Slices ); this->Modified(); //End Reinitialization if ( m_SliceNavigationController ) { m_SliceNavigationController->GetSlice()->SetPos( restorePlaneOp->GetPos() ); m_SliceNavigationController->AdjustSliceStepperRange(); } BaseGeometry::ExecuteOperation(restorePlaneOp); } } else { // Reach through to all slices - for (std::vector::iterator iter = m_Geometry2Ds.begin(); + for (std::vector::iterator iter = m_Geometry2Ds.begin(); iter != m_Geometry2Ds.end(); ++iter) { (*iter)->ExecuteOperation(operation); } } break; case OpAPPLYTRANSFORMMATRIX: // Clear all generated geometries and then transform only the first slice. // The other slices will be re-generated on demand // Save first slice - Geometry2D::Pointer geometry2D = m_Geometry2Ds[0]; + PlaneGeometry::Pointer geometry2D = m_Geometry2Ds[0]; ApplyTransformMatrixOperation *applyMatrixOp = dynamic_cast< ApplyTransformMatrixOperation* >( operation ); // Apply transformation to first plane geometry2D->ExecuteOperation( applyMatrixOp ); // Generate a ApplyTransformMatrixOperation using the dataset center instead of // the supplied rotation center. The supplied center is instead used to adjust the // slice stack afterwards (see OpROTATE). Point3D center = m_ReferenceGeometry->GetCenter(); // Clear the slice stack and adjust it according to the center of // the dataset and the supplied rotation center (see documentation of // ReinitializePlanes) this->ReinitializePlanes( center, applyMatrixOp->GetReferencePoint() ); BaseGeometry::ExecuteOperation( applyMatrixOp ); break; } this->Modified(); } diff --git a/Core/Code/DataManagement/mitkSlicedGeometry3D.h b/Core/Code/DataManagement/mitkSlicedGeometry3D.h index 6aa00533b0..952cdc2a04 100644 --- a/Core/Code/DataManagement/mitkSlicedGeometry3D.h +++ b/Core/Code/DataManagement/mitkSlicedGeometry3D.h @@ -1,311 +1,311 @@ /*=================================================================== 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 MITKSLICEDGEOMETRY3D_H_HEADER_INCLUDED_C1EBD0AD #define MITKSLICEDGEOMETRY3D_H_HEADER_INCLUDED_C1EBD0AD #include "mitkBaseGeometry.h" #include "mitkPlaneGeometry.h" namespace mitk { class SliceNavigationController; class NavigationController; /** \brief Describes the geometry of a data object consisting of slices. * - * A Geometry2D can be requested for each slice. In the case of + * A PlaneGeometry can be requested for each slice. In the case of * \em evenly-spaced, \em plane geometries (m_EvenlySpaced==true), * only the 2D-geometry of the first slice has to be set (to an instance of * PlaneGeometry). The 2D geometries of the other slices are calculated * by shifting the first slice in the direction m_DirectionVector by * m_Spacing.z * sliceNumber. The m_Spacing member (which is only * relevant in the case m_EvenlySpaced==true) descibes the size of a voxel * (in mm), i.e., m_Spacing.x is the voxel width in the x-direction of the * plane. It is derived from the reference geometry of this SlicedGeometry3D, * which usually would be the global geometry describing how datasets are to * be resliced. * * By default, slices are oriented in the direction of one of the main axes * (x, y, z). However, by means of rotation, it is possible to realign the * slices in any possible direction. In case of an inclined plane, the spacing * is derived as a product of the (regular) geometry spacing and the direction * vector of the plane. * * SlicedGeometry3D and the associated Geometry2Ds have to be initialized in * the method GenerateOutputInformation() of BaseProcess (or CopyInformation / * UpdateOutputInformation of BaseData, if possible, e.g., by analyzing pic * tags in Image) subclasses. See also * * \sa itk::ProcessObject::GenerateOutputInformation(), * \sa itk::DataObject::CopyInformation() and * \a itk::DataObject::UpdateOutputInformation(). * * Rule: everything is in mm (or ms for temporal information) if not * stated otherwise. * * \warning The hull (i.e., transform, bounding-box and * time-bounds) is only guaranteed to be up-to-date after calling * UpdateInformation(). * * \ingroup Geometry */ class MITK_CORE_EXPORT SlicedGeometry3D : public mitk::BaseGeometry { public: mitkClassMacro(SlicedGeometry3D, BaseGeometry); /** Method for creation through the object factory. */ itkNewMacro(Self); /** - * \brief Returns the Geometry2D of the slice (\a s). + * \brief Returns the PlaneGeometry of the slice (\a s). * - * If (a) m_EvenlySpaced==true, (b) we don't have a Geometry2D stored + * If (a) m_EvenlySpaced==true, (b) we don't have a PlaneGeometry stored * for the requested slice, and (c) the first slice (s=0) * is a PlaneGeometry instance, then we calculate the geometry of the * requested as the plane of the first slice shifted by m_Spacing[3]*s * in the direction of m_DirectionVector. * * \warning The Geometry2Ds are not necessarily up-to-date and not even * initialized. * * The Geometry2Ds have to be initialized in the method * GenerateOutputInformation() of BaseProcess (or CopyInformation / * UpdateOutputInformation of BaseData, if possible, e.g., by analyzing * pic tags in Image) subclasses. See also * * \sa itk::ProcessObject::GenerateOutputInformation(), * \sa itk::DataObject::CopyInformation() and * \sa itk::DataObject::UpdateOutputInformation(). */ - virtual mitk::Geometry2D* GetGeometry2D( int s ) const; + virtual mitk::PlaneGeometry* GetGeometry2D( int s ) const; /** - * \brief Set Geometry2D of slice \a s. + * \brief Set PlaneGeometry of slice \a s. */ - virtual bool SetGeometry2D( mitk::Geometry2D *geometry2D, int s ); + virtual bool SetGeometry2D( mitk::PlaneGeometry *geometry2D, int s ); //##Documentation //## @brief When switching from an Image Geometry to a normal Geometry (and the other way around), you have to change the origin as well (See Geometry Documentation)! This function will change the "isImageGeometry" bool flag and changes the origin respectively. virtual void ChangeImageGeometryConsideringOriginOffset( const bool isAnImageGeometry ); virtual const mitk::BoundingBox* GetBoundingBox() const; /** * \brief Get the number of slices */ itkGetConstMacro( Slices, unsigned int ); /** * \brief Check whether a slice exists */ virtual bool IsValidSlice( int s = 0 ) const; virtual void SetReferenceGeometry( BaseGeometry *referenceGeometry ); /** * \brief Set the SliceNavigationController corresponding to this sliced * geometry. * * The SNC needs to be informed when the number of slices in the geometry * changes, which can occur whenthe slices are re-oriented by rotation. */ virtual void SetSliceNavigationController( mitk::SliceNavigationController *snc ); mitk::SliceNavigationController *GetSliceNavigationController(); /** * \brief Set/Get whether the SlicedGeometry3D is evenly-spaced * (m_EvenlySpaced) * - * If (a) m_EvenlySpaced==true, (b) we don't have a Geometry2D stored for + * If (a) m_EvenlySpaced==true, (b) we don't have a PlaneGeometry stored for * the requested slice, and (c) the first slice (s=0) is a PlaneGeometry * instance, then we calculate the geometry of the requested as the plane * of the first slice shifted by m_Spacing.z * s in the direction of * m_DirectionVector. * * \sa GetGeometry2D */ itkGetConstMacro(EvenlySpaced, bool); virtual void SetEvenlySpaced(bool on = true); /** * \brief Set/Get the vector between slices for the evenly-spaced case * (m_EvenlySpaced==true). * * If the direction-vector is (0,0,0) (the default) and the first * 2D geometry is a PlaneGeometry, then the direction-vector will be * calculated from the plane normal. * * \sa m_DirectionVector */ virtual void SetDirectionVector(const mitk::Vector3D& directionVector); itkGetConstMacro(DirectionVector, const mitk::Vector3D&); virtual itk::LightObject::Pointer InternalClone() const; static const std::string SLICES; const static std::string DIRECTION_VECTOR; const static std::string EVENLY_SPACED; /** * \brief Tell this instance how many Geometry2Ds it shall manage. Bounding * box and the Geometry2Ds must be set additionally by calling the respective * methods! * * \warning Bounding box and the 2D-geometries must be set additionally: use * SetBounds(), SetGeometry(). */ virtual void InitializeSlicedGeometry( unsigned int slices ); /** * \brief Completely initialize this instance as evenly-spaced with slices - * parallel to the provided Geometry2D that is used as the first slice and + * parallel to the provided PlaneGeometry that is used as the first slice and * for spacing calculation. * * Initializes the bounding box according to the width/height of the - * Geometry2D and \a slices. The spacing is calculated from the Geometry2D. + * PlaneGeometry and \a slices. The spacing is calculated from the PlaneGeometry. */ - virtual void InitializeEvenlySpaced( mitk::Geometry2D *geometry2D, + virtual void InitializeEvenlySpaced( mitk::PlaneGeometry *geometry2D, unsigned int slices, bool flipped=false ); /** * \brief Completely initialize this instance as evenly-spaced with slices - * parallel to the provided Geometry2D that is used as the first slice and + * parallel to the provided PlaneGeometry that is used as the first slice and * for spacing calculation (except z-spacing). * * Initializes the bounding box according to the width/height of the - * Geometry2D and \a slices. The x-/y-spacing is calculated from the - * Geometry2D. + * PlaneGeometry and \a slices. The x-/y-spacing is calculated from the + * PlaneGeometry. */ - virtual void InitializeEvenlySpaced( mitk::Geometry2D *geometry2D, + virtual void InitializeEvenlySpaced( mitk::PlaneGeometry *geometry2D, mitk::ScalarType zSpacing, unsigned int slices, bool flipped=false ); /** * \brief Completely initialize this instance as evenly-spaced plane slices * parallel to a side of the provided Geometry3D and using its spacing * information. * * Initializes the bounding box according to the width/height of the * Geometry3D and the number of slices according to * Geometry3D::GetExtent(2). * * \param planeorientation side parallel to which the slices will be oriented * \param top if \a true, create plane at top, otherwise at bottom * (for PlaneOrientation Axial, for other plane locations respectively) * \param frontside defines the side of the plane (the definition of * front/back is somewhat arbitrary) * * \param rotate rotates the plane by 180 degree around its normal (the * definition of rotated vs not rotated is somewhat arbitrary) */ virtual void InitializePlanes( const mitk::BaseGeometry *geometry3D, mitk::PlaneGeometry::PlaneOrientation planeorientation, bool top=true, bool frontside=true, bool rotated=false ); virtual void SetImageGeometry(const bool isAnImageGeometry); virtual void ExecuteOperation(Operation* operation); static double CalculateSpacing( const mitk::Vector3D spacing, const mitk::Vector3D &d ); protected: SlicedGeometry3D(); SlicedGeometry3D(const SlicedGeometry3D& other); virtual ~SlicedGeometry3D(); virtual void InternPostSetTimeBounds( const mitk::TimeBounds& timebounds ); /** * \brief Set the spacing (m_Spacing), in direction of the plane normal. * * INTERNAL METHOD. */ virtual void InternPreSetSpacing( const mitk::Vector3D &aSpacing ); /** * Reinitialize plane stack after rotation. More precisely, the first plane * of the stack needs to spatially aligned, in two respects: * * 1. Re-alignment with respect to the dataset center; this is necessary * since the distance from the first plane to the center could otherwise * continuously decrease or increase. * 2. Re-alignment with respect to a given reference point; the reference * point is a location which the user wants to be exactly touched by one * plane of the plane stack. The first plane is minimally shifted to * ensure this touching. Usually, the reference point would be the * point around which the geometry is rotated. */ virtual void ReinitializePlanes( const Point3D ¢er, const Point3D &referencePoint ); ScalarType GetLargestExtent( const BaseGeometry *geometry ); void PrintSelf(std::ostream& os, itk::Indent indent) const; /** Calculate "directed spacing", i.e. the spacing in directions * non-orthogonal to the coordinate axes. This is done via the * ellipsoid equation. */ double CalculateSpacing( const mitk::Vector3D &direction ) const; /** The extent of the slice stack, i.e. the number of slices, depends on the * plane normal. For rotated geometries, the geometry's transform needs to * be accounted in this calculation. */ mitk::Vector3D AdjustNormal( const mitk::Vector3D &normal ) const; /** * Container for the 2D-geometries contained within this SliceGeometry3D. */ - mutable std::vector m_Geometry2Ds; + mutable std::vector m_Geometry2Ds; /** - * If (a) m_EvenlySpaced==true, (b) we don't have a Geometry2D stored + * If (a) m_EvenlySpaced==true, (b) we don't have a PlaneGeometry stored * for the requested slice, and (c) the first slice (s=0) * is a PlaneGeometry instance, then we calculate the geometry of the * requested as the plane of the first slice shifted by m_Spacing.z*s * in the direction of m_DirectionVector. * * \sa GetGeometry2D */ bool m_EvenlySpaced; /** * Vector between slices for the evenly-spaced case (m_EvenlySpaced==true). * If the direction-vector is (0,0,0) (the default) and the first * 2D geometry is a PlaneGeometry, then the direction-vector will be * calculated from the plane normal. */ mutable mitk::Vector3D m_DirectionVector; /** Number of slices this SliceGeometry3D is descibing. */ unsigned int m_Slices; /** Underlying Geometry3D for this SlicedGeometry */ mitk::BaseGeometry *m_ReferenceGeometry; /** SNC correcsponding to this geometry; used to reflect changes in the * number of slices due to rotation. */ //mitk::NavigationController *m_NavigationController; mitk::SliceNavigationController *m_SliceNavigationController; }; } // namespace mitk #endif /* MITKSLICEDGEOMETRY3D_H_HEADER_INCLUDED_C1EBD0AD */ diff --git a/Core/Code/Interactions/mitkCoordinateSupplier.cpp b/Core/Code/Interactions/mitkCoordinateSupplier.cpp index 3055f0dae4..a18ea3757e 100755 --- a/Core/Code/Interactions/mitkCoordinateSupplier.cpp +++ b/Core/Code/Interactions/mitkCoordinateSupplier.cpp @@ -1,169 +1,169 @@ /*=================================================================== 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 "mitkDisplayCoordinateOperation.h" //has to be on top, otherwise compiler error! #include "mitkCoordinateSupplier.h" #include "mitkOperation.h" #include "mitkOperationActor.h" #include "mitkPointOperation.h" #include "mitkPositionEvent.h" #include "mitkStateEvent.h" #include "mitkUndoController.h" //and not here! #include #include "mitkInteractionConst.h" #include "mitkAction.h" mitk::CoordinateSupplier::CoordinateSupplier(const char * type, mitk::OperationActor* operationActor) : mitk::StateMachine(type), m_Destination(operationActor) { m_CurrentPoint.Fill(0); } mitk::CoordinateSupplier::~CoordinateSupplier() { } bool mitk::CoordinateSupplier::ExecuteAction(Action* action, mitk::StateEvent const* stateEvent) { bool ok = false; const PositionEvent* posEvent = dynamic_cast(stateEvent->GetEvent()); PointOperation* doOp=NULL; if(posEvent!=NULL) { ScalarType timeInMS = 0; if(stateEvent->GetEvent()->GetSender()!=NULL) { - const Geometry2D* worldGeometry = stateEvent->GetEvent()->GetSender()->GetCurrentWorldGeometry2D(); + const PlaneGeometry* worldGeometry = stateEvent->GetEvent()->GetSender()->GetCurrentWorldGeometry2D(); assert( worldGeometry != NULL ); timeInMS = worldGeometry->GetTimeBounds()[ 0 ]; } else { itkWarningMacro(<<"StateEvent::GetSender()==NULL - setting timeInMS to 0"); } switch (action->GetActionId()) { case AcNEWPOINT: { if (m_Destination == NULL) return false; m_OldPoint = posEvent->GetWorldPosition(); doOp = new mitk::PointOperation(OpADD, timeInMS, m_OldPoint, 0); //Undo if (m_UndoEnabled) { PointOperation* undoOp = new PointOperation(OpDELETE, m_OldPoint, 0); OperationEvent *operationEvent = new OperationEvent( m_Destination, doOp, undoOp ); m_UndoController->SetOperationEvent(operationEvent); } //execute the Operation m_Destination->ExecuteOperation(doOp); if (!m_UndoEnabled) delete doOp; ok = true; break; } case AcINITMOVEMENT: { if (m_Destination == NULL) return false; //move the point to the coordinate //not used, cause same to MovePoint... check xml-file mitk::Point3D movePoint = posEvent->GetWorldPosition(); mitk::PointOperation doPointOp(OpMOVE, timeInMS, movePoint, 0); //execute the Operation m_Destination->ExecuteOperation(&doPointOp); ok = true; break; } case AcMOVEPOINT: case AcMOVE: { mitk::Point3D movePoint = posEvent->GetWorldPosition(); m_CurrentPoint = movePoint; if (m_Destination == NULL) return false; mitk::PointOperation doPointOp(OpMOVE, timeInMS, movePoint, 0); //execute the Operation m_Destination->ExecuteOperation(&doPointOp); ok = true; break; } case AcFINISHMOVEMENT: { if (m_Destination == NULL) return false; /*finishes a Movement from the coordinate supplier: gets the lastpoint from the undolist and writes an undo-operation so that the movement of the coordinatesupplier is undoable.*/ mitk::Point3D movePoint = posEvent->GetWorldPosition(); mitk::Point3D oldMovePoint; oldMovePoint.Fill(0); doOp = new mitk::PointOperation(OpMOVE, timeInMS, movePoint, 0); PointOperation finishOp(OpTERMINATE, movePoint, 0); if (m_UndoEnabled ) { //get the last Position from the UndoList OperationEvent *lastOperationEvent = m_UndoController->GetLastOfType(m_Destination, OpMOVE); if (lastOperationEvent != NULL) { PointOperation* lastOp = dynamic_cast(lastOperationEvent->GetOperation()); if (lastOp != NULL) { oldMovePoint = lastOp->GetPoint(); } } PointOperation* undoOp = new PointOperation(OpMOVE, timeInMS, oldMovePoint, 0, "Move slices"); OperationEvent *operationEvent = new OperationEvent(m_Destination, doOp, undoOp, "Move slices"); m_UndoController->SetOperationEvent(operationEvent); } //execute the Operation m_Destination->ExecuteOperation(doOp); if (!m_UndoEnabled) delete doOp; m_Destination->ExecuteOperation(&finishOp); ok = true; break; } default: ok = false; break; } return ok; } const mitk::DisplayPositionEvent* displPosEvent = dynamic_cast(stateEvent->GetEvent()); if(displPosEvent!=NULL) { return true; } return false; } diff --git a/Core/Code/Rendering/mitkBaseRenderer.cpp b/Core/Code/Rendering/mitkBaseRenderer.cpp index 048c282001..0237d53deb 100644 --- a/Core/Code/Rendering/mitkBaseRenderer.cpp +++ b/Core/Code/Rendering/mitkBaseRenderer.cpp @@ -1,889 +1,889 @@ /*=================================================================== 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 "mitkBaseRenderer.h" #include "mitkMapper.h" #include "mitkResliceMethodProperty.h" #include "mitkKeyEvent.h" // Geometries #include "mitkPlaneGeometry.h" #include "mitkSlicedGeometry3D.h" // Controllers #include "mitkCameraController.h" #include "mitkSliceNavigationController.h" #include "mitkCameraRotationController.h" #include "mitkVtkInteractorCameraController.h" #ifdef MITK_USE_TD_MOUSE #include "mitkTDMouseVtkCameraController.h" #else #include "mitkCameraController.h" #endif #include "mitkVtkLayerController.h" // Events // TODO: INTERACTION_LEGACY #include "mitkEventMapper.h" #include "mitkGlobalInteraction.h" #include "mitkPositionEvent.h" #include "mitkDisplayPositionEvent.h" #include "mitkProperties.h" #include "mitkWeakPointerProperty.h" #include "mitkInteractionConst.h" #include "mitkOverlayManager.h" // VTK #include #include #include #include #include #include #include mitk::BaseRenderer::BaseRendererMapType mitk::BaseRenderer::baseRendererMap; mitk::BaseRenderer* mitk::BaseRenderer::GetInstance(vtkRenderWindow * renWin) { for (BaseRendererMapType::iterator mapit = baseRendererMap.begin(); mapit != baseRendererMap.end(); mapit++) { if ((*mapit).first == renWin) return (*mapit).second; } return NULL; } void mitk::BaseRenderer::AddInstance(vtkRenderWindow* renWin, BaseRenderer* baseRenderer) { if (renWin == NULL || baseRenderer == NULL) return; // ensure that no BaseRenderer is managed twice mitk::BaseRenderer::RemoveInstance(renWin); baseRendererMap.insert(BaseRendererMapType::value_type(renWin, baseRenderer)); } void mitk::BaseRenderer::RemoveInstance(vtkRenderWindow* renWin) { BaseRendererMapType::iterator mapit = baseRendererMap.find(renWin); if (mapit != baseRendererMap.end()) baseRendererMap.erase(mapit); } mitk::BaseRenderer* mitk::BaseRenderer::GetByName(const std::string& name) { for (BaseRendererMapType::iterator mapit = baseRendererMap.begin(); mapit != baseRendererMap.end(); mapit++) { if ((*mapit).second->m_Name == name) return (*mapit).second; } return NULL; } vtkRenderWindow* mitk::BaseRenderer::GetRenderWindowByName(const std::string& name) { for (BaseRendererMapType::iterator mapit = baseRendererMap.begin(); mapit != baseRendererMap.end(); mapit++) { if ((*mapit).second->m_Name == name) return (*mapit).first; } return NULL; } mitk::BaseRenderer::BaseRenderer(const char* name, vtkRenderWindow * renWin, mitk::RenderingManager* rm) : m_RenderWindow(NULL), m_VtkRenderer(NULL), m_MapperID(defaultMapper), m_DataStorage(NULL), m_RenderingManager(rm), m_LastUpdateTime(0), m_CameraController( NULL), m_SliceNavigationController(NULL), m_CameraRotationController(NULL), /*m_Size(),*/ m_Focused(false), m_WorldGeometry(NULL), m_WorldTimeGeometry(NULL), m_CurrentWorldGeometry(NULL), m_CurrentWorldGeometry2D(NULL), m_DisplayGeometry( NULL), m_Slice(0), m_TimeStep(), m_CurrentWorldGeometry2DUpdateTime(), m_DisplayGeometryUpdateTime(), m_TimeStepUpdateTime(), m_WorldGeometryData( NULL), m_DisplayGeometryData(NULL), m_CurrentWorldGeometry2DData(NULL), m_WorldGeometryNode(NULL), m_DisplayGeometryNode(NULL), m_CurrentWorldGeometry2DNode( NULL), m_DisplayGeometryTransformTime(0), m_CurrentWorldGeometry2DTransformTime(0), m_Name(name), /*m_Bounds(),*/m_EmptyWorldGeometry( true), m_DepthPeelingEnabled(true), m_MaxNumberOfPeels(100), m_NumberOfVisibleLODEnabledMappers(0) { m_Bounds[0] = 0; m_Bounds[1] = 0; m_Bounds[2] = 0; m_Bounds[3] = 0; m_Bounds[4] = 0; m_Bounds[5] = 0; if (name != NULL) { m_Name = name; } else { m_Name = "unnamed renderer"; itkWarningMacro(<< "Created unnamed renderer. Bad for serialization. Please choose a name."); } if (renWin != NULL) { m_RenderWindow = renWin; m_RenderWindow->Register(NULL); } else { itkWarningMacro(<< "Created mitkBaseRenderer without vtkRenderWindow present."); } m_Size[0] = 0; m_Size[1] = 0; //instances.insert( this ); //adding this BaseRenderer to the List of all BaseRenderer // TODO: INTERACTION_LEGACY m_RenderingManager->GetGlobalInteraction()->AddFocusElement(this); m_BindDispatcherInteractor = new mitk::BindDispatcherInteractor( GetName() ); WeakPointerProperty::Pointer rendererProp = WeakPointerProperty::New((itk::Object*) this); m_CurrentWorldGeometry2D = mitk::PlaneGeometry::New(); m_CurrentWorldGeometry2DData = mitk::Geometry2DData::New(); m_CurrentWorldGeometry2DData->SetGeometry2D(m_CurrentWorldGeometry2D); m_CurrentWorldGeometry2DNode = mitk::DataNode::New(); m_CurrentWorldGeometry2DNode->SetData(m_CurrentWorldGeometry2DData); m_CurrentWorldGeometry2DNode->GetPropertyList()->SetProperty("renderer", rendererProp); m_CurrentWorldGeometry2DNode->GetPropertyList()->SetProperty("layer", IntProperty::New(1000)); m_CurrentWorldGeometry2DNode->SetProperty("reslice.thickslices", mitk::ResliceMethodProperty::New()); m_CurrentWorldGeometry2DNode->SetProperty("reslice.thickslices.num", mitk::IntProperty::New(1)); m_CurrentWorldGeometry2DTransformTime = m_CurrentWorldGeometry2DNode->GetVtkTransform()->GetMTime(); m_DisplayGeometry = mitk::DisplayGeometry::New(); m_DisplayGeometry->SetWorldGeometry(m_CurrentWorldGeometry2D); m_DisplayGeometryData = mitk::Geometry2DData::New(); m_DisplayGeometryData->SetGeometry2D(m_DisplayGeometry); m_DisplayGeometryNode = mitk::DataNode::New(); m_DisplayGeometryNode->SetData(m_DisplayGeometryData); m_DisplayGeometryNode->GetPropertyList()->SetProperty("renderer", rendererProp); m_DisplayGeometryTransformTime = m_DisplayGeometryNode->GetVtkTransform()->GetMTime(); mitk::SliceNavigationController::Pointer sliceNavigationController = mitk::SliceNavigationController::New("navigation"); sliceNavigationController->SetRenderer(this); sliceNavigationController->ConnectGeometrySliceEvent(this); sliceNavigationController->ConnectGeometryUpdateEvent(this); sliceNavigationController->ConnectGeometryTimeEvent(this, false); m_SliceNavigationController = sliceNavigationController; m_CameraRotationController = mitk::CameraRotationController::New(); m_CameraRotationController->SetRenderWindow(m_RenderWindow); m_CameraRotationController->AcquireCamera(); //if TD Mouse Interaction is activated, then call TDMouseVtkCameraController instead of VtkInteractorCameraController #ifdef MITK_USE_TD_MOUSE m_CameraController = mitk::TDMouseVtkCameraController::New(); #else m_CameraController = mitk::CameraController::New(NULL); #endif m_VtkRenderer = vtkRenderer::New(); if (mitk::VtkLayerController::GetInstance(m_RenderWindow) == NULL) { mitk::VtkLayerController::AddInstance(m_RenderWindow, m_VtkRenderer); mitk::VtkLayerController::GetInstance(m_RenderWindow)->InsertSceneRenderer(m_VtkRenderer); } else mitk::VtkLayerController::GetInstance(m_RenderWindow)->InsertSceneRenderer(m_VtkRenderer); } mitk::BaseRenderer::~BaseRenderer() { if (m_OverlayManager.IsNotNull()) { m_OverlayManager->RemoveBaseRenderer(this); } if (m_VtkRenderer != NULL) { m_VtkRenderer->Delete(); m_VtkRenderer = NULL; } if (m_CameraController.IsNotNull()) m_CameraController->SetRenderer(NULL); m_RenderingManager->GetGlobalInteraction()->RemoveFocusElement(this); mitk::VtkLayerController::RemoveInstance(m_RenderWindow); RemoveAllLocalStorages(); m_DataStorage = NULL; if (m_BindDispatcherInteractor != NULL) { delete m_BindDispatcherInteractor; } if (m_RenderWindow != NULL) { m_RenderWindow->Delete(); m_RenderWindow = NULL; } } void mitk::BaseRenderer::RemoveAllLocalStorages() { this->InvokeEvent(mitk::BaseRenderer::RendererResetEvent()); std::list::iterator it; for (it = m_RegisteredLocalStorageHandlers.begin(); it != m_RegisteredLocalStorageHandlers.end(); it++) (*it)->ClearLocalStorage(this, false); m_RegisteredLocalStorageHandlers.clear(); } void mitk::BaseRenderer::RegisterLocalStorageHandler(mitk::BaseLocalStorageHandler *lsh) { m_RegisteredLocalStorageHandlers.push_back(lsh); } mitk::Dispatcher::Pointer mitk::BaseRenderer::GetDispatcher() const { return m_BindDispatcherInteractor->GetDispatcher(); } mitk::Point3D mitk::BaseRenderer::Map2DRendererPositionTo3DWorldPosition(Point2D* mousePosition) const { Point2D p_mm; Point3D position; if (m_MapperID == 1) { GetDisplayGeometry()->ULDisplayToDisplay(*mousePosition, *mousePosition); GetDisplayGeometry()->DisplayToWorld(*mousePosition, p_mm); GetDisplayGeometry()->Map(p_mm, position); } else if (m_MapperID == 2) { GetDisplayGeometry()->ULDisplayToDisplay(*mousePosition, *mousePosition); PickWorldPoint(*mousePosition, position); } return position; } void mitk::BaseRenderer::UnregisterLocalStorageHandler(mitk::BaseLocalStorageHandler *lsh) { m_RegisteredLocalStorageHandlers.remove(lsh); } void mitk::BaseRenderer::SetDataStorage(DataStorage* storage) { if (storage != NULL) { m_DataStorage = storage; m_BindDispatcherInteractor->SetDataStorage(m_DataStorage); this->Modified(); } } const mitk::BaseRenderer::MapperSlotId mitk::BaseRenderer::defaultMapper = 1; void mitk::BaseRenderer::Paint() { } void mitk::BaseRenderer::Initialize() { } void mitk::BaseRenderer::Resize(int w, int h) { m_Size[0] = w; m_Size[1] = h; if (m_CameraController) m_CameraController->Resize(w, h); //(formerly problematic on windows: vtkSizeBug) GetDisplayGeometry()->SetSizeInDisplayUnits(w, h); } void mitk::BaseRenderer::InitRenderer(vtkRenderWindow* renderwindow) { if (m_RenderWindow != NULL) { m_RenderWindow->Delete(); } m_RenderWindow = renderwindow; if (m_RenderWindow != NULL) { m_RenderWindow->Register(NULL); } RemoveAllLocalStorages(); if (m_CameraController.IsNotNull()) { m_CameraController->SetRenderer(this); } //BUG (#1551) added settings for depth peeling m_RenderWindow->SetAlphaBitPlanes(1); m_VtkRenderer->SetUseDepthPeeling(m_DepthPeelingEnabled); m_VtkRenderer->SetMaximumNumberOfPeels(m_MaxNumberOfPeels); m_VtkRenderer->SetOcclusionRatio(0.1); } void mitk::BaseRenderer::InitSize(int w, int h) { m_Size[0] = w; m_Size[1] = h; GetDisplayGeometry()->SetSizeInDisplayUnits(w, h, false); GetDisplayGeometry()->Fit(); } void mitk::BaseRenderer::SetSlice(unsigned int slice) { if (m_Slice != slice) { m_Slice = slice; if (m_WorldTimeGeometry.IsNotNull()) { SlicedGeometry3D* slicedWorldGeometry = dynamic_cast(m_WorldTimeGeometry->GetGeometryForTimeStep(m_TimeStep).GetPointer()); if (slicedWorldGeometry != NULL) { if (m_Slice >= slicedWorldGeometry->GetSlices()) m_Slice = slicedWorldGeometry->GetSlices() - 1; SetCurrentWorldGeometry2D(slicedWorldGeometry->GetGeometry2D(m_Slice)); SetCurrentWorldGeometry(slicedWorldGeometry); } } else Modified(); } } void mitk::BaseRenderer::SetOverlayManager(itk::SmartPointer overlayManager) { if(overlayManager.IsNull()) return; if(this->m_OverlayManager.IsNotNull()) { if(this->m_OverlayManager.GetPointer() == overlayManager.GetPointer()) { return; } else { this->m_OverlayManager->RemoveBaseRenderer(this); } } this->m_OverlayManager = overlayManager; this->m_OverlayManager->AddBaseRenderer(this); //TODO } itk::SmartPointer mitk::BaseRenderer::GetOverlayManager() { if(this->m_OverlayManager.IsNull()) { m_OverlayManager = mitk::OverlayManager::New(); m_OverlayManager->AddBaseRenderer(this); } return this->m_OverlayManager; } void mitk::BaseRenderer::SetTimeStep(unsigned int timeStep) { if (m_TimeStep != timeStep) { m_TimeStep = timeStep; m_TimeStepUpdateTime.Modified(); if (m_WorldTimeGeometry.IsNotNull()) { if (m_TimeStep >= m_WorldTimeGeometry->CountTimeSteps()) m_TimeStep = m_WorldTimeGeometry->CountTimeSteps() - 1; SlicedGeometry3D* slicedWorldGeometry = dynamic_cast(m_WorldTimeGeometry->GetGeometryForTimeStep(m_TimeStep).GetPointer()); if (slicedWorldGeometry != NULL) { SetCurrentWorldGeometry2D(slicedWorldGeometry->GetGeometry2D(m_Slice)); SetCurrentWorldGeometry(slicedWorldGeometry); } } else Modified(); } } int mitk::BaseRenderer::GetTimeStep(const mitk::BaseData* data) const { if ((data == NULL) || (data->IsInitialized() == false)) { return -1; } return data->GetTimeGeometry()->TimePointToTimeStep(GetTime()); } mitk::ScalarType mitk::BaseRenderer::GetTime() const { if (m_WorldTimeGeometry.IsNull()) { return 0; } else { ScalarType timeInMS = m_WorldTimeGeometry->TimeStepToTimePoint(GetTimeStep()); if (timeInMS == ScalarTypeNumericTraits::NonpositiveMin()) return 0; else return timeInMS; } } void mitk::BaseRenderer::SetWorldTimeGeometry(mitk::TimeGeometry* geometry) { assert(geometry != NULL); itkDebugMacro("setting WorldTimeGeometry to " << geometry); if (m_WorldTimeGeometry != geometry) { if (geometry->GetBoundingBoxInWorld()->GetDiagonalLength2() == 0) return; m_WorldTimeGeometry = geometry; itkDebugMacro("setting WorldTimeGeometry to " << m_WorldTimeGeometry); if (m_TimeStep >= m_WorldTimeGeometry->CountTimeSteps()) m_TimeStep = m_WorldTimeGeometry->CountTimeSteps() - 1; BaseGeometry* geometry3d; geometry3d = m_WorldTimeGeometry->GetGeometryForTimeStep(m_TimeStep); SetWorldGeometry3D(geometry3d); } } void mitk::BaseRenderer::SetWorldGeometry3D(mitk::BaseGeometry* geometry) { itkDebugMacro("setting WorldGeometry3D to " << geometry); if (m_WorldGeometry != geometry) { if (geometry->GetBoundingBox()->GetDiagonalLength2() == 0) return; m_WorldGeometry = geometry; SlicedGeometry3D* slicedWorldGeometry; slicedWorldGeometry = dynamic_cast(geometry); - Geometry2D::Pointer geometry2d; + PlaneGeometry::Pointer geometry2d; if (slicedWorldGeometry != NULL) { if (m_Slice >= slicedWorldGeometry->GetSlices() && (m_Slice != 0)) m_Slice = slicedWorldGeometry->GetSlices() - 1; geometry2d = slicedWorldGeometry->GetGeometry2D(m_Slice); if (geometry2d.IsNull()) { PlaneGeometry::Pointer plane = mitk::PlaneGeometry::New(); plane->InitializeStandardPlane(slicedWorldGeometry); geometry2d = plane; } SetCurrentWorldGeometry(slicedWorldGeometry); } else { - geometry2d = dynamic_cast(geometry); + geometry2d = dynamic_cast(geometry); if (geometry2d.IsNull()) { PlaneGeometry::Pointer plane = PlaneGeometry::New(); plane->InitializeStandardPlane(geometry); geometry2d = plane; } SetCurrentWorldGeometry(geometry); } SetCurrentWorldGeometry2D(geometry2d); // calls Modified() } if (m_CurrentWorldGeometry2D.IsNull()) itkWarningMacro("m_CurrentWorldGeometry2D is NULL"); } void mitk::BaseRenderer::SetDisplayGeometry(mitk::DisplayGeometry* geometry2d) { itkDebugMacro("setting DisplayGeometry to " << geometry2d); if (m_DisplayGeometry != geometry2d) { m_DisplayGeometry = geometry2d; m_DisplayGeometryData->SetGeometry2D(m_DisplayGeometry); m_DisplayGeometryUpdateTime.Modified(); Modified(); } } -void mitk::BaseRenderer::SetCurrentWorldGeometry2D(mitk::Geometry2D* geometry2d) +void mitk::BaseRenderer::SetCurrentWorldGeometry2D(mitk::PlaneGeometry* geometry2d) { if (m_CurrentWorldGeometry2D != geometry2d) { m_CurrentWorldGeometry2D = geometry2d; m_CurrentWorldGeometry2DData->SetGeometry2D(m_CurrentWorldGeometry2D); m_DisplayGeometry->SetWorldGeometry(m_CurrentWorldGeometry2D); m_CurrentWorldGeometry2DUpdateTime.Modified(); Modified(); } } void mitk::BaseRenderer::SendUpdateSlice() { m_DisplayGeometryUpdateTime.Modified(); m_CurrentWorldGeometry2DUpdateTime.Modified(); } void mitk::BaseRenderer::SetCurrentWorldGeometry(mitk::BaseGeometry* geometry) { m_CurrentWorldGeometry = geometry; if (geometry == NULL) { m_Bounds[0] = 0; m_Bounds[1] = 0; m_Bounds[2] = 0; m_Bounds[3] = 0; m_Bounds[4] = 0; m_Bounds[5] = 0; m_EmptyWorldGeometry = true; return; } BoundingBox::Pointer boundingBox = m_CurrentWorldGeometry->CalculateBoundingBoxRelativeToTransform(NULL); const BoundingBox::BoundsArrayType& worldBounds = boundingBox->GetBounds(); m_Bounds[0] = worldBounds[0]; m_Bounds[1] = worldBounds[1]; m_Bounds[2] = worldBounds[2]; m_Bounds[3] = worldBounds[3]; m_Bounds[4] = worldBounds[4]; m_Bounds[5] = worldBounds[5]; if (boundingBox->GetDiagonalLength2() <= mitk::eps) m_EmptyWorldGeometry = true; else m_EmptyWorldGeometry = false; } void mitk::BaseRenderer::UpdateOverlays() { if(m_OverlayManager.IsNotNull()) { m_OverlayManager->UpdateOverlays(this); } } void mitk::BaseRenderer::SetGeometry(const itk::EventObject & geometrySendEvent) { const SliceNavigationController::GeometrySendEvent* sendEvent = dynamic_cast(&geometrySendEvent); assert(sendEvent!=NULL); SetWorldTimeGeometry(sendEvent->GetTimeGeometry()); } void mitk::BaseRenderer::UpdateGeometry(const itk::EventObject & geometryUpdateEvent) { const SliceNavigationController::GeometryUpdateEvent* updateEvent = dynamic_cast(&geometryUpdateEvent); if (updateEvent == NULL) return; if (m_CurrentWorldGeometry.IsNotNull()) { SlicedGeometry3D* slicedWorldGeometry = dynamic_cast(m_CurrentWorldGeometry.GetPointer()); if (slicedWorldGeometry) { - Geometry2D* geometry2D = slicedWorldGeometry->GetGeometry2D(m_Slice); + PlaneGeometry* geometry2D = slicedWorldGeometry->GetGeometry2D(m_Slice); SetCurrentWorldGeometry2D(geometry2D); // calls Modified() } } } void mitk::BaseRenderer::SetGeometrySlice(const itk::EventObject & geometrySliceEvent) { const SliceNavigationController::GeometrySliceEvent* sliceEvent = dynamic_cast(&geometrySliceEvent); assert(sliceEvent!=NULL); SetSlice(sliceEvent->GetPos()); } void mitk::BaseRenderer::SetGeometryTime(const itk::EventObject & geometryTimeEvent) { const SliceNavigationController::GeometryTimeEvent * timeEvent = dynamic_cast(&geometryTimeEvent); assert(timeEvent!=NULL); SetTimeStep(timeEvent->GetPos()); } const double* mitk::BaseRenderer::GetBounds() const { return m_Bounds; } void mitk::BaseRenderer::MousePressEvent(mitk::MouseEvent *me) { //set the Focus on the renderer /*bool success =*/m_RenderingManager->GetGlobalInteraction()->SetFocus(this); /* if (! success) mitk::StatusBar::GetInstance()->DisplayText("Warning! from mitkBaseRenderer.cpp: Couldn't focus this BaseRenderer!"); */ //if (m_CameraController) //{ // if(me->GetButtonState()!=512) // provisorisch: Ctrl nicht durchlassen. Bald wird aus m_CameraController eine StateMachine // m_CameraController->MousePressEvent(me); //} if (m_MapperID == 1) { Point2D p(me->GetDisplayPosition()); Point2D p_mm; Point3D position; GetDisplayGeometry()->ULDisplayToDisplay(p, p); GetDisplayGeometry()->DisplayToWorld(p, p_mm); GetDisplayGeometry()->Map(p_mm, position); mitk::PositionEvent event(this, me->GetType(), me->GetButton(), me->GetButtonState(), mitk::Key_unknown, p, position); mitk::EventMapper::MapEvent(&event, m_RenderingManager->GetGlobalInteraction()); } else if (m_MapperID > 1) //==2 for 3D and ==5 for stencil { Point2D p(me->GetDisplayPosition()); GetDisplayGeometry()->ULDisplayToDisplay(p, p); me->SetDisplayPosition(p); mitk::EventMapper::MapEvent(me, m_RenderingManager->GetGlobalInteraction()); } } void mitk::BaseRenderer::MouseReleaseEvent(mitk::MouseEvent *me) { //if (m_CameraController) //{ // if(me->GetButtonState()!=512) // provisorisch: Ctrl nicht durchlassen. Bald wird aus m_CameraController eine StateMachine // m_CameraController->MouseReleaseEvent(me); //} if (m_MapperID == 1) { Point2D p(me->GetDisplayPosition()); Point2D p_mm; Point3D position; GetDisplayGeometry()->ULDisplayToDisplay(p, p); GetDisplayGeometry()->DisplayToWorld(p, p_mm); GetDisplayGeometry()->Map(p_mm, position); mitk::PositionEvent event(this, me->GetType(), me->GetButton(), me->GetButtonState(), mitk::Key_unknown, p, position); mitk::EventMapper::MapEvent(&event, m_RenderingManager->GetGlobalInteraction()); } else if (m_MapperID == 2) { Point2D p(me->GetDisplayPosition()); GetDisplayGeometry()->ULDisplayToDisplay(p, p); me->SetDisplayPosition(p); mitk::EventMapper::MapEvent(me, m_RenderingManager->GetGlobalInteraction()); } } void mitk::BaseRenderer::MouseMoveEvent(mitk::MouseEvent *me) { //if (m_CameraController) //{ // if((me->GetButtonState()<=512) || (me->GetButtonState()>=516))// provisorisch: Ctrl nicht durchlassen. Bald wird aus m_CameraController eine StateMachine // m_CameraController->MouseMoveEvent(me); //} if (m_MapperID == 1) { Point2D p(me->GetDisplayPosition()); Point2D p_mm; Point3D position; GetDisplayGeometry()->ULDisplayToDisplay(p, p); GetDisplayGeometry()->DisplayToWorld(p, p_mm); GetDisplayGeometry()->Map(p_mm, position); mitk::PositionEvent event(this, me->GetType(), me->GetButton(), me->GetButtonState(), mitk::Key_unknown, p, position); mitk::EventMapper::MapEvent(&event, m_RenderingManager->GetGlobalInteraction()); } else if (m_MapperID == 2) { Point2D p(me->GetDisplayPosition()); GetDisplayGeometry()->ULDisplayToDisplay(p, p); me->SetDisplayPosition(p); mitk::EventMapper::MapEvent(me, m_RenderingManager->GetGlobalInteraction()); } } void mitk::BaseRenderer::PickWorldPoint(const mitk::Point2D& displayPoint, mitk::Point3D& worldPoint) const { mitk::Point2D worldPoint2D; GetDisplayGeometry()->DisplayToWorld(displayPoint, worldPoint2D); GetDisplayGeometry()->Map(worldPoint2D, worldPoint); } void mitk::BaseRenderer::WheelEvent(mitk::WheelEvent * we) { if (m_MapperID == 1) { Point2D p(we->GetDisplayPosition()); Point2D p_mm; Point3D position; GetDisplayGeometry()->ULDisplayToDisplay(p, p); GetDisplayGeometry()->DisplayToWorld(p, p_mm); GetDisplayGeometry()->Map(p_mm, position); mitk::PositionEvent event(this, we->GetType(), we->GetButton(), we->GetButtonState(), mitk::Key_unknown, p, position); mitk::EventMapper::MapEvent(we, m_RenderingManager->GetGlobalInteraction()); mitk::EventMapper::MapEvent(&event, m_RenderingManager->GetGlobalInteraction()); } else if (m_MapperID == 2) { Point2D p(we->GetDisplayPosition()); GetDisplayGeometry()->ULDisplayToDisplay(p, p); we->SetDisplayPosition(p); mitk::EventMapper::MapEvent(we, m_RenderingManager->GetGlobalInteraction()); } } void mitk::BaseRenderer::KeyPressEvent(mitk::KeyEvent *ke) { if (m_MapperID == 1) { Point2D p(ke->GetDisplayPosition()); Point2D p_mm; Point3D position; GetDisplayGeometry()->ULDisplayToDisplay(p, p); GetDisplayGeometry()->DisplayToWorld(p, p_mm); GetDisplayGeometry()->Map(p_mm, position); mitk::KeyEvent event(this, ke->GetType(), ke->GetButton(), ke->GetButtonState(), ke->GetKey(), ke->GetText(), p); mitk::EventMapper::MapEvent(&event, m_RenderingManager->GetGlobalInteraction()); } else if (m_MapperID == 2) { Point2D p(ke->GetDisplayPosition()); GetDisplayGeometry()->ULDisplayToDisplay(p, p); ke->SetDisplayPosition(p); mitk::EventMapper::MapEvent(ke, m_RenderingManager->GetGlobalInteraction()); } } void mitk::BaseRenderer::DrawOverlayMouse(mitk::Point2D& itkNotUsed(p2d)) { MITK_INFO<<"BaseRenderer::DrawOverlayMouse()- should be inconcret implementation OpenGLRenderer."<RequestUpdate(this->m_RenderWindow); } void mitk::BaseRenderer::ForceImmediateUpdate() { m_RenderingManager->ForceImmediateUpdate(this->m_RenderWindow); } unsigned int mitk::BaseRenderer::GetNumberOfVisibleLODEnabledMappers() const { return m_NumberOfVisibleLODEnabledMappers; } mitk::RenderingManager* mitk::BaseRenderer::GetRenderingManager() const { return m_RenderingManager.GetPointer(); } /*! Sets the new Navigation controller */ void mitk::BaseRenderer::SetSliceNavigationController(mitk::SliceNavigationController *SlicenavigationController) { if (SlicenavigationController == NULL) return; //disconnect old from globalinteraction m_RenderingManager->GetGlobalInteraction()->RemoveListener(SlicenavigationController); //copy worldgeometry SlicenavigationController->SetInputWorldTimeGeometry(SlicenavigationController->GetCreatedWorldGeometry()); SlicenavigationController->Update(); //set new m_SliceNavigationController = SlicenavigationController; m_SliceNavigationController->SetRenderer(this); if (m_SliceNavigationController.IsNotNull()) { m_SliceNavigationController->ConnectGeometrySliceEvent(this); m_SliceNavigationController->ConnectGeometryUpdateEvent(this); m_SliceNavigationController->ConnectGeometryTimeEvent(this, false); } } /*! Sets the new camera controller and deletes the vtkRenderWindowInteractor in case of the VTKInteractorCameraController */ void mitk::BaseRenderer::SetCameraController(CameraController* cameraController) { mitk::VtkInteractorCameraController::Pointer vtkInteractorCameraController = dynamic_cast(cameraController); if (vtkInteractorCameraController.IsNotNull()) MITK_INFO<<"!!!WARNING!!!: RenderWindow interaction events are no longer handled via CameraController (See Bug #954)."<SetRenderer(NULL); m_CameraController = NULL; m_CameraController = cameraController; m_CameraController->SetRenderer(this); } void mitk::BaseRenderer::PrintSelf(std::ostream& os, itk::Indent indent) const { os << indent << " MapperID: " << m_MapperID << std::endl; os << indent << " Slice: " << m_Slice << std::endl; os << indent << " TimeStep: " << m_TimeStep << std::endl; os << indent << " WorldGeometry: "; if (m_WorldGeometry.IsNull()) os << "NULL" << std::endl; else m_WorldGeometry->Print(os, indent); os << indent << " CurrentWorldGeometry2D: "; if (m_CurrentWorldGeometry2D.IsNull()) os << "NULL" << std::endl; else m_CurrentWorldGeometry2D->Print(os, indent); os << indent << " CurrentWorldGeometry2DUpdateTime: " << m_CurrentWorldGeometry2DUpdateTime << std::endl; os << indent << " CurrentWorldGeometry2DTransformTime: " << m_CurrentWorldGeometry2DTransformTime << std::endl; os << indent << " DisplayGeometry: "; if (m_DisplayGeometry.IsNull()) os << "NULL" << std::endl; else m_DisplayGeometry->Print(os, indent); os << indent << " DisplayGeometryTransformTime: " << m_DisplayGeometryTransformTime << std::endl; Superclass::PrintSelf(os, indent); } void mitk::BaseRenderer::SetDepthPeelingEnabled(bool enabled) { m_DepthPeelingEnabled = enabled; m_VtkRenderer->SetUseDepthPeeling(enabled); } void mitk::BaseRenderer::SetMaxNumberOfPeels(int maxNumber) { m_MaxNumberOfPeels = maxNumber; m_VtkRenderer->SetMaximumNumberOfPeels(maxNumber); } diff --git a/Core/Code/Rendering/mitkBaseRenderer.h b/Core/Code/Rendering/mitkBaseRenderer.h index 8ce1939e89..dcdf3b881e 100644 --- a/Core/Code/Rendering/mitkBaseRenderer.h +++ b/Core/Code/Rendering/mitkBaseRenderer.h @@ -1,644 +1,644 @@ /*=================================================================== 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 BASERENDERER_H_HEADER_INCLUDED_C1CCA0F4 #define BASERENDERER_H_HEADER_INCLUDED_C1CCA0F4 #include "mitkDataStorage.h" -#include "mitkGeometry2D.h" +#include "mitkPlaneGeometry.h" #include "mitkTimeGeometry.h" #include "mitkDisplayGeometry.h" #include "mitkGeometry2DData.h" #include "mitkCameraController.h" #include "mitkDisplayPositionEvent.h" #include "mitkWheelEvent.h" //#include "mitkMapper.h" #include "mitkSliceNavigationController.h" #include "mitkCameraController.h" #include "mitkCameraRotationController.h" #include "mitkBindDispatcherInteractor.h" #include "mitkDispatcher.h" #include #include #include #include // DEPRECATED #include namespace mitk { class NavigationController; class SliceNavigationController; class CameraRotationController; class CameraController; class DataStorage; class Mapper; class BaseLocalStorageHandler; class OverlayManager; //##Documentation //## @brief Organizes the rendering process //## //## Organizes the rendering process. A Renderer contains a reference to a //## DataStorage and asks the mappers of the data objects to render //## the data into the renderwindow it is associated to. //## //## \#Render() checks if rendering is currently allowed by calling //## RenderWindow::PrepareRendering(). Initialization of a rendering context //## can also be performed in this method. //## //## The actual rendering code has been moved to \#Repaint() //## Both \#Repaint() and \#Update() are declared protected now. //## //## Note: Separation of the Repaint and Update processes (rendering vs //## creating a vtk prop tree) still needs to be worked on. The whole //## rendering process also should be reworked to use VTK based classes for //## both 2D and 3D rendering. //## @ingroup Renderer class MITK_CORE_EXPORT BaseRenderer: public itk::Object { public: typedef std::map BaseRendererMapType; static BaseRendererMapType baseRendererMap; static BaseRenderer* GetInstance(vtkRenderWindow * renWin); static void AddInstance(vtkRenderWindow* renWin, BaseRenderer* baseRenderer); static void RemoveInstance(vtkRenderWindow* renWin); static BaseRenderer* GetByName(const std::string& name); static vtkRenderWindow* GetRenderWindowByName(const std::string& name); #pragma GCC visibility push(default) itkEventMacro( RendererResetEvent, itk::AnyEvent ); #pragma GCC visibility pop /** Standard class typedefs. */ mitkClassMacro(BaseRenderer, itk::Object); BaseRenderer(const char* name = NULL, vtkRenderWindow * renWin = NULL, mitk::RenderingManager* rm = NULL); //##Documentation //## @brief MapperSlotId defines which kind of mapper (e.g., 2D or 3D) shoud be used. typedef int MapperSlotId; enum StandardMapperSlot { Standard2D = 1, Standard3D = 2 }; virtual void SetDataStorage(DataStorage* storage); ///< set the datastorage that will be used for rendering //##Documentation //## return the DataStorage that is used for rendering virtual DataStorage::Pointer GetDataStorage() const { return m_DataStorage.GetPointer(); } //##Documentation //## @brief Access the RenderWindow into which this renderer renders. vtkRenderWindow* GetRenderWindow() const { return m_RenderWindow; } vtkRenderer* GetVtkRenderer() const { return m_VtkRenderer; } //##Documentation //## @brief Returns the Dispatcher which handles Events for this BaseRenderer Dispatcher::Pointer GetDispatcher() const; //##Documentation //## @brief Default mapper id to use. static const MapperSlotId defaultMapper; //##Documentation //## @brief Do the rendering and flush the result. virtual void Paint(); //##Documentation //## @brief Initialize the RenderWindow. Should only be called from RenderWindow. virtual void Initialize(); //##Documentation //## @brief Called to inform the renderer that the RenderWindow has been resized. virtual void Resize(int w, int h); //##Documentation //## @brief Initialize the renderer with a RenderWindow (@a renderwindow). virtual void InitRenderer(vtkRenderWindow* renderwindow); //##Documentation //## @brief Set the initial size. Called by RenderWindow after it has become //## visible for the first time. virtual void InitSize(int w, int h); //##Documentation //## @brief Draws a point on the widget. //## Should be used during conferences to show the position of the remote mouse virtual void DrawOverlayMouse(Point2D& p2d); //##Documentation //## @brief Set/Get the WorldGeometry (m_WorldGeometry) for 3D and 2D rendering, that describing the //## (maximal) area to be rendered. //## //## Depending of the type of the passed Geometry3D more or less information can be extracted: - //## \li if it is a Geometry2D (which is a sub-class of Geometry3D), m_CurrentWorldGeometry2D is + //## \li if it is a PlaneGeometry (which is a sub-class of Geometry3D), m_CurrentWorldGeometry2D is //## also set to point to it. m_WorldTimeGeometry is set to NULL. //## \li if it is a TimeGeometry, m_WorldTimeGeometry is also set to point to it. //## If m_WorldTimeGeometry contains instances of SlicedGeometry3D, m_CurrentWorldGeometry2D is set to //## one of geometries stored in the SlicedGeometry3D according to the value of m_Slice; otherwise //## a PlaneGeometry describing the top of the bounding-box of the Geometry3D is set as the //## m_CurrentWorldGeometry2D. //## \li otherwise a PlaneGeometry describing the top of the bounding-box of the Geometry3D //## is set as the m_CurrentWorldGeometry2D. m_WorldTimeGeometry is set to NULL. //## @todo add calculation of PlaneGeometry describing the top of the bounding-box of the Geometry3D //## when the passed Geometry3D is not sliced. //## \sa m_WorldGeometry //## \sa m_WorldTimeGeometry //## \sa m_CurrentWorldGeometry2D virtual void SetWorldGeometry3D(BaseGeometry* geometry); virtual void SetWorldTimeGeometry(mitk::TimeGeometry* geometry); /** * \deprecatedSince{2013_09} Please use TimeGeometry instead of TimeSlicedGeometry. For more information see http://www.mitk.org/Development/Refactoring%20of%20the%20Geometry%20Classes%20-%20Part%201 */ DEPRECATED(void SetWorldGeometry3D(TimeSlicedGeometry* geometry)); itkGetConstObjectMacro(WorldGeometry, BaseGeometry) itkGetObjectMacro(WorldGeometry, BaseGeometry) itkGetConstObjectMacro(WorldTimeGeometry, TimeGeometry) itkGetObjectMacro(WorldTimeGeometry, TimeGeometry) //##Documentation //## @brief Get the current 3D-worldgeometry (m_CurrentWorldGeometry) used for 3D-rendering itkGetConstObjectMacro(CurrentWorldGeometry, BaseGeometry) //##Documentation //## @brief Get the current 2D-worldgeometry (m_CurrentWorldGeometry2D) used for 2D-rendering - itkGetConstObjectMacro(CurrentWorldGeometry2D, Geometry2D) + itkGetConstObjectMacro(CurrentWorldGeometry2D, PlaneGeometry) //##Documentation //## Calculates the bounds of the DataStorage (if it contains any valid data), //## creates a geometry from these bounds and sets it as world geometry of the renderer. //## //## Call this method to re-initialize the renderer to the current DataStorage //## (e.g. after loading an additional dataset), to ensure that the view is //## aligned correctly. //## \warn This is not implemented yet. virtual bool SetWorldGeometryToDataStorageBounds() { return false; } //##Documentation //## @brief Set/Get the DisplayGeometry (for 2D rendering) //## - //## The DisplayGeometry describes which part of the Geometry2D m_CurrentWorldGeometry2D + //## The DisplayGeometry describes which part of the PlaneGeometry m_CurrentWorldGeometry2D //## is displayed. virtual void SetDisplayGeometry(DisplayGeometry* geometry2d); itkGetConstObjectMacro(DisplayGeometry, DisplayGeometry) itkGetObjectMacro(DisplayGeometry, DisplayGeometry) //##Documentation //## @brief Set/Get m_Slice which defines together with m_TimeStep the 2D geometry //## stored in m_WorldTimeGeometry used as m_CurrentWorldGeometry2D //## //## \sa m_Slice virtual void SetSlice(unsigned int slice); //##Documentation //## @brief Sets an OverlayManager which is used to add various Overlays to this //## renderer. If an OverlayManager was already set it will be overwritten. void SetOverlayManager(itk::SmartPointer overlayManager); //##Documentation //## @brief Get the OverlayManager registered with this renderer //## if none was set, it will be created at this point. itk::SmartPointer GetOverlayManager(); itkGetConstMacro(Slice, unsigned int) //##Documentation //## @brief Set/Get m_TimeStep which defines together with m_Slice the 2D geometry //## stored in m_WorldTimeGeometry used as m_CurrentWorldGeometry2D //## //## \sa m_TimeStep virtual void SetTimeStep(unsigned int timeStep); itkGetConstMacro(TimeStep, unsigned int) //##Documentation //## @brief Get the time-step of a BaseData object which //## exists at the time of the currently displayed content //## //## Returns -1 or mitk::BaseData::m_TimeSteps if there //## is no data at the current time. //## \sa GetTimeStep, m_TimeStep int GetTimeStep(const BaseData* data) const; //##Documentation //## @brief Get the time in ms of the currently displayed content //## //## \sa GetTimeStep, m_TimeStep ScalarType GetTime() const; //##Documentation //## @brief SetWorldGeometry is called according to the geometrySliceEvent, //## which is supposed to be a SliceNavigationController::GeometrySendEvent virtual void SetGeometry(const itk::EventObject & geometrySliceEvent); //##Documentation //## @brief UpdateWorldGeometry is called to re-read the 2D geometry from the //## slice navigation controller virtual void UpdateGeometry(const itk::EventObject & geometrySliceEvent); //##Documentation //## @brief SetSlice is called according to the geometrySliceEvent, //## which is supposed to be a SliceNavigationController::GeometrySliceEvent virtual void SetGeometrySlice(const itk::EventObject & geometrySliceEvent); //##Documentation //## @brief SetTimeStep is called according to the geometrySliceEvent, //## which is supposed to be a SliceNavigationController::GeometryTimeEvent virtual void SetGeometryTime(const itk::EventObject & geometryTimeEvent); //##Documentation //## @brief Get a data object containing the DisplayGeometry (for 2D rendering) itkGetObjectMacro(DisplayGeometryData, Geometry2DData) //##Documentation //## @brief Get a data object containing the WorldGeometry (for 2D rendering) itkGetObjectMacro(WorldGeometryData, Geometry2DData) //##Documentation //## @brief Get a DataNode pointing to a data object containing the WorldGeometry (3D and 2D rendering) itkGetObjectMacro(WorldGeometryNode, DataNode) //##Documentation //## @brief Get a DataNode pointing to a data object containing the DisplayGeometry (for 2D rendering) itkGetObjectMacro(DisplayGeometryNode, DataNode) //##Documentation //## @brief Get a DataNode pointing to a data object containing the current 2D-worldgeometry m_CurrentWorldGeometry2D (for 2D rendering) itkGetObjectMacro(CurrentWorldGeometry2DNode, DataNode) //##Documentation //## @brief Sets timestamp of CurrentWorldGeometry2D and DisplayGeometry and forces so reslicing in that renderwindow void SendUpdateSlice(); //##Documentation //## @brief Get timestamp of last call of SetCurrentWorldGeometry2D unsigned long GetCurrentWorldGeometry2DUpdateTime() { return m_CurrentWorldGeometry2DUpdateTime; } //##Documentation //## @brief Get timestamp of last call of SetDisplayGeometry unsigned long GetDisplayGeometryUpdateTime() { return m_CurrentWorldGeometry2DUpdateTime; } //##Documentation //## @brief Get timestamp of last change of current TimeStep unsigned long GetTimeStepUpdateTime() { return m_TimeStepUpdateTime; } //##Documentation //## @brief Perform a picking: find the x,y,z world coordinate of a //## display x,y coordinate. //## @warning Has to be overwritten in subclasses for the 3D-case. //## //## Implemented here only for 2D-rendering by using //## m_DisplayGeometry virtual void PickWorldPoint(const Point2D& diplayPosition, Point3D& worldPosition) const; /** \brief Determines the object (mitk::DataNode) closest to the current * position by means of picking * * \warning Implementation currently empty for 2D rendering; intended to be * implemented for 3D renderers */ virtual DataNode* PickObject(const Point2D& /*displayPosition*/, Point3D& /*worldPosition*/) const { return NULL; } //##Documentation //## @brief Get the MapperSlotId to use. itkGetMacro(MapperID, MapperSlotId) itkGetConstMacro(MapperID, MapperSlotId) //##Documentation //## @brief Set the MapperSlotId to use. itkSetMacro(MapperID, MapperSlotId) //##Documentation //## @brief Has the renderer the focus? itkGetMacro(Focused, bool) //##Documentation //## @brief Tell the renderer that it is focused. The caller is responsible for focus management, //## not the renderer itself. itkSetMacro(Focused, bool) //##Documentation //## @brief Sets whether depth peeling is enabled or not void SetDepthPeelingEnabled(bool enabled); //##Documentation //## @brief Sets maximal number of peels void SetMaxNumberOfPeels(int maxNumber); itkGetMacro(Size, int*) void SetSliceNavigationController(SliceNavigationController* SlicenavigationController); void SetCameraController(CameraController* cameraController); itkGetObjectMacro(CameraController, CameraController) itkGetObjectMacro(SliceNavigationController, SliceNavigationController) itkGetObjectMacro(CameraRotationController, CameraRotationController) itkGetMacro(EmptyWorldGeometry, bool) //##Documentation //## @brief Mouse event dispatchers //## @note for internal use only. preliminary. virtual void MousePressEvent(MouseEvent*); //##Documentation //## @brief Mouse event dispatchers //## @note for internal use only. preliminary. virtual void MouseReleaseEvent(MouseEvent*); //##Documentation //## @brief Mouse event dispatchers //## @note for internal use only. preliminary. virtual void MouseMoveEvent(MouseEvent*); //##Documentation //## @brief Wheel event dispatcher //## @note for internal use only. preliminary. virtual void WheelEvent(mitk::WheelEvent* we); //##Documentation //## @brief Key event dispatcher //## @note for internal use only. preliminary. virtual void KeyPressEvent(KeyEvent*); //##Documentation //## @brief get the name of the Renderer //## @note const char * GetName() const { return m_Name.c_str(); } //##Documentation //## @brief get the x_size of the RendererWindow //## @note int GetSizeX() const { return m_Size[0]; } //##Documentation //## @brief get the y_size of the RendererWindow //## @note int GetSizeY() const { return m_Size[1]; } const double* GetBounds() const; void RequestUpdate(); void ForceImmediateUpdate(); /** Returns number of mappers which are visible and have level-of-detail * rendering enabled */ unsigned int GetNumberOfVisibleLODEnabledMappers() const; ///** //* \brief Setter for the RenderingManager that handles this instance of BaseRenderer //*/ //void SetRenderingManager( mitk::RenderingManager* ); /** * \brief Getter for the RenderingManager that handles this instance of BaseRenderer */ virtual mitk::RenderingManager* GetRenderingManager() const; /** * \brief Provides (1) world coordinates for a given mouse position and (2) * translates mousePosition to Display coordinates */ virtual Point3D Map2DRendererPositionTo3DWorldPosition(Point2D* mousePosition) const; protected: virtual ~BaseRenderer(); //##Documentation //## @brief Call update of all mappers. To be implemented in subclasses. virtual void Update() = 0; vtkRenderWindow* m_RenderWindow; vtkRenderer* m_VtkRenderer; //##Documentation //## @brief MapperSlotId to use. Defines which kind of mapper (e.g., 2D or 3D) shoud be used. MapperSlotId m_MapperID; //##Documentation //## @brief The DataStorage that is used for rendering. DataStorage::Pointer m_DataStorage; //##Documentation //## @brief The RenderingManager that manages this instance RenderingManager::Pointer m_RenderingManager; //##Documentation //## @brief Timestamp of last call of Update(). unsigned long m_LastUpdateTime; //##Documentation //## @brief CameraController for 3D rendering //## @note preliminary. CameraController::Pointer m_CameraController; SliceNavigationController::Pointer m_SliceNavigationController; CameraRotationController::Pointer m_CameraRotationController; //##Documentation //## @brief Size of the RenderWindow. int m_Size[2]; //##Documentation //## @brief Contains whether the renderer that it is focused. The caller of //## SetFocused is responsible for focus management, not the renderer itself. //## is doubled because of mitk::FocusManager in GlobalInteraction!!! (ingmar) bool m_Focused; //##Documentation //## @brief Sets m_CurrentWorldGeometry2D - virtual void SetCurrentWorldGeometry2D(Geometry2D* geometry2d); + virtual void SetCurrentWorldGeometry2D(PlaneGeometry* geometry2d); //##Documentation //## @brief Sets m_CurrentWorldGeometry virtual void SetCurrentWorldGeometry(BaseGeometry* geometry); //##Documentation //## @brief This method is called during the rendering process to update or render the Overlays //## which are stored in the OverlayManager void UpdateOverlays(); private: //##Documentation //## Pointer to the worldgeometry, describing the maximal area to be rendered //## (3D as well as 2D). //## It is const, since we are not allowed to change it (it may be taken //## directly from the geometry of an image-slice and thus it would be //## very strange when suddenly the image-slice changes its geometry). //## \sa SetWorldGeometry BaseGeometry::Pointer m_WorldGeometry; itk::SmartPointer m_OverlayManager; //##Documentation //## m_WorldTimeGeometry is set by SetWorldGeometry if the passed Geometry3D is a //## TimeGeometry (or a sub-class of it). If it contains instances of SlicedGeometry3D, //## m_Slice and m_TimeStep (set via SetSlice and SetTimeStep, respectively) define //## which 2D geometry stored in m_WorldTimeGeometry (if available) //## is used as m_CurrentWorldGeometry2D. //## \sa m_CurrentWorldGeometry2D TimeGeometry::Pointer m_WorldTimeGeometry; //##Documentation //## Pointer to the current 3D-worldgeometry. BaseGeometry::Pointer m_CurrentWorldGeometry; //##Documentation //## Pointer to the current 2D-worldgeometry. The 2D-worldgeometry //## describes the maximal area (2D manifold) to be rendered in case we //## are doing 2D-rendering. More precisely, a subpart of this according //## to m_DisplayGeometry is displayed. //## It is const, since we are not allowed to change it (it may be taken //## directly from the geometry of an image-slice and thus it would be //## very strange when suddenly the image-slice changes its geometry). - Geometry2D::Pointer m_CurrentWorldGeometry2D; + PlaneGeometry::Pointer m_CurrentWorldGeometry2D; //##Documentation //## Pointer to the displaygeometry. The displaygeometry describes the //## geometry of the \em visible area in the window controlled by the renderer //## in case we are doing 2D-rendering. //## It is const, since we are not allowed to change it. DisplayGeometry::Pointer m_DisplayGeometry; //##Documentation //## Defines together with m_Slice which 2D geometry stored in m_WorldTimeGeometry //## is used as m_CurrentWorldGeometry2D: m_WorldTimeGeometry->GetGeometry2D(m_Slice, m_TimeStep). //## \sa m_WorldTimeGeometry unsigned int m_Slice; //##Documentation //## Defines together with m_TimeStep which 2D geometry stored in m_WorldTimeGeometry //## is used as m_CurrentWorldGeometry2D: m_WorldTimeGeometry->GetGeometry2D(m_Slice, m_TimeStep). //## \sa m_WorldTimeGeometry unsigned int m_TimeStep; //##Documentation //## @brief timestamp of last call of SetWorldGeometry itk::TimeStamp m_CurrentWorldGeometry2DUpdateTime; //##Documentation //## @brief timestamp of last call of SetDisplayGeometry itk::TimeStamp m_DisplayGeometryUpdateTime; //##Documentation //## @brief timestamp of last change of the current time step itk::TimeStamp m_TimeStepUpdateTime; //##Documentation //## @brief Helper class which establishes connection between Interactors and Dispatcher via a common DataStorage. BindDispatcherInteractor* m_BindDispatcherInteractor; protected: virtual void PrintSelf(std::ostream& os, itk::Indent indent) const; //##Documentation //## Data object containing the m_WorldGeometry defined above. Geometry2DData::Pointer m_WorldGeometryData; //##Documentation //## Data object containing the m_DisplayGeometry defined above. Geometry2DData::Pointer m_DisplayGeometryData; //##Documentation //## Data object containing the m_CurrentWorldGeometry2D defined above. Geometry2DData::Pointer m_CurrentWorldGeometry2DData; //##Documentation //## DataNode objects containing the m_WorldGeometryData defined above. DataNode::Pointer m_WorldGeometryNode; //##Documentation //## DataNode objects containing the m_DisplayGeometryData defined above. DataNode::Pointer m_DisplayGeometryNode; //##Documentation //## DataNode objects containing the m_CurrentWorldGeometry2DData defined above. DataNode::Pointer m_CurrentWorldGeometry2DNode; //##Documentation //## @brief test only unsigned long m_DisplayGeometryTransformTime; //##Documentation //## @brief test only unsigned long m_CurrentWorldGeometry2DTransformTime; std::string m_Name; double m_Bounds[6]; bool m_EmptyWorldGeometry; bool m_DepthPeelingEnabled; int m_MaxNumberOfPeels; typedef std::set LODEnabledMappersType; /** Number of mappers which are visible and have level-of-detail * rendering enabled */ unsigned int m_NumberOfVisibleLODEnabledMappers; // Local Storage Handling for mappers protected: std::list m_RegisteredLocalStorageHandlers; public: void RemoveAllLocalStorages(); void RegisterLocalStorageHandler(mitk::BaseLocalStorageHandler *lsh); void UnregisterLocalStorageHandler(mitk::BaseLocalStorageHandler *lsh); }; } // namespace mitk #endif /* BASERENDERER_H_HEADER_INCLUDED_C1CCA0F4 */ diff --git a/Core/Code/Rendering/mitkGeometry2DDataMapper2D.cpp b/Core/Code/Rendering/mitkGeometry2DDataMapper2D.cpp index 5fa398cdb3..53b80dd4b0 100644 --- a/Core/Code/Rendering/mitkGeometry2DDataMapper2D.cpp +++ b/Core/Code/Rendering/mitkGeometry2DDataMapper2D.cpp @@ -1,671 +1,671 @@ /*=================================================================== 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 "mitkGL.h" #include "mitkGeometry2DDataMapper2D.h" #include "mitkBaseRenderer.h" #include "mitkPlaneGeometry.h" #include "mitkColorProperty.h" #include "mitkProperties.h" #include "mitkSmartPointerProperty.h" #include "mitkPlaneOrientationProperty.h" #include "mitkGeometry2DDataToSurfaceFilter.h" #include "mitkSurfaceGLMapper2D.h" #include "mitkLine.h" #include "mitkNodePredicateDataType.h" #include "mitkResliceMethodProperty.h" mitk::Geometry2DDataMapper2D::Geometry2DDataMapper2D() : m_SurfaceMapper( NULL ), m_DataStorage(NULL), m_ParentNode(NULL), m_OtherGeometry2Ds(), m_RenderOrientationArrows( false ), m_ArrowOrientationPositive( true ) { } mitk::Geometry2DDataMapper2D::~Geometry2DDataMapper2D() { } const mitk::Geometry2DData* mitk::Geometry2DDataMapper2D::GetInput(void) { return static_cast ( GetDataNode()->GetData() ); } void mitk::Geometry2DDataMapper2D::GenerateDataForRenderer(mitk::BaseRenderer* renderer) { BaseLocalStorage *ls = m_LSH.GetLocalStorage(renderer); if(!ls->IsGenerateDataRequired(renderer,this,GetDataNode())) return; ls->UpdateGenerateDataTime(); // collect all Geometry2DDatas accessible from the DataStorage m_OtherGeometry2Ds.clear(); if (m_DataStorage.IsNull()) return; mitk::NodePredicateDataType::Pointer p = mitk::NodePredicateDataType::New("Geometry2DData"); mitk::DataStorage::SetOfObjects::ConstPointer all = m_DataStorage->GetDerivations(m_ParentNode, p, false); for (mitk::DataStorage::SetOfObjects::ConstIterator it = all->Begin(); it != all->End(); ++it) { if(it->Value().IsNull()) continue; BaseData* data = it->Value()->GetData(); if (data == NULL) continue; Geometry2DData* geometry2dData = dynamic_cast(data); if(geometry2dData == NULL) continue; PlaneGeometry* planegeometry = dynamic_cast(geometry2dData->GetGeometry2D()); if (planegeometry != NULL) m_OtherGeometry2Ds.push_back(it->Value()); } } void mitk::Geometry2DDataMapper2D::Paint(BaseRenderer *renderer) { bool visible = true; GetDataNode()->GetVisibility(visible, renderer, "visible"); if(!visible) return; Geometry2DData::Pointer input = const_cast< Geometry2DData * >(this->GetInput()); // intersecting with ourself? if ( input.IsNull() || (this->GetInput()->GetGeometry2D() == renderer->GetCurrentWorldGeometry2D()) ) { return; // do nothing! } const PlaneGeometry *inputPlaneGeometry = dynamic_cast< const PlaneGeometry * >( input->GetGeometry2D() ); const PlaneGeometry *worldPlaneGeometry = dynamic_cast< const PlaneGeometry* >( renderer->GetCurrentWorldGeometry2D() ); if ( worldPlaneGeometry && inputPlaneGeometry && inputPlaneGeometry->GetReferenceGeometry() ) { DisplayGeometry *displayGeometry = renderer->GetDisplayGeometry(); assert( displayGeometry ); const BaseGeometry *referenceGeometry = inputPlaneGeometry->GetReferenceGeometry(); // calculate intersection of the plane data with the border of the // world geometry rectangle Point2D lineFrom, lineTo; const Geometry3D::TransformType *transform = dynamic_cast< const Geometry3D::TransformType * >( referenceGeometry->GetIndexToWorldTransform() ); Geometry3D::TransformType::Pointer inverseTransform = Geometry3D::TransformType::New(); transform->GetInverse( inverseTransform ); Line3D crossLine, otherCrossLine; // Calculate the intersection line of the input plane with the world plane if ( worldPlaneGeometry->IntersectionLine( inputPlaneGeometry, crossLine ) ) { BoundingBox::PointType boundingBoxMin, boundingBoxMax; boundingBoxMin = referenceGeometry->GetBoundingBox()->GetMinimum(); boundingBoxMax = referenceGeometry->GetBoundingBox()->GetMaximum(); if(referenceGeometry->GetImageGeometry()) { for(unsigned int i = 0; i < 3; ++i) { boundingBoxMin[i]-=0.5; boundingBoxMax[i]-=0.5; } } crossLine.Transform( *inverseTransform ); Point3D point1, point2; // Then, clip this line with the (transformed) bounding box of the // reference geometry. if ( crossLine.BoxLineIntersection( boundingBoxMin[0], boundingBoxMin[1], boundingBoxMin[2], boundingBoxMax[0], boundingBoxMax[1], boundingBoxMax[2], crossLine.GetPoint(), crossLine.GetDirection(), point1, point2 ) == 2 ) { // Transform the resulting line start and end points into display // coordinates. worldPlaneGeometry->Map( transform->TransformPoint( point1 ), lineFrom ); worldPlaneGeometry->Map( transform->TransformPoint( point2 ), lineTo ); Line< ScalarType, 2 > mainLine, otherLine; Line< ScalarType, 2 > primaryHelperLine, secondaryHelperLine; mainLine.SetPoints( lineFrom, lineTo ); primaryHelperLine.SetPoints( lineFrom, lineTo ); secondaryHelperLine.SetPoints( lineFrom, lineTo ); displayGeometry->WorldToDisplay( lineFrom, lineFrom ); displayGeometry->WorldToDisplay( lineTo, lineTo ); ScalarType lengthInDisplayUnits = (lineTo - lineFrom).GetNorm(); Vector2D mainLineDirectionOrthogonal; mainLineDirectionOrthogonal[0] = -mainLine.GetDirection()[1]; mainLineDirectionOrthogonal[1] = mainLine.GetDirection()[0]; // lineParams stores the individual segments of the line, which are // separated by a gap each (to mark the intersection with another // displayed line) std::vector< ScalarType > mainLineParams; std::vector< ScalarType > primaryHelperLineParams; std::vector< ScalarType > secondaryHelperLineParams; mainLineParams.reserve( m_OtherGeometry2Ds.size() + 2 ); mainLineParams.push_back( 0.0 ); mainLineParams.push_back( 1.0 ); primaryHelperLineParams.reserve( m_OtherGeometry2Ds.size() + 2 ); primaryHelperLineParams.push_back( 0.0 ); primaryHelperLineParams.push_back( 1.0 ); secondaryHelperLineParams.reserve( m_OtherGeometry2Ds.size() + 2 ); secondaryHelperLineParams.push_back( 0.0 ); secondaryHelperLineParams.push_back( 1.0 ); // Now iterate through all other lines displayed in this window and // calculate the positions of intersection with the line to be // rendered; these positions will be stored in lineParams to form a // gap afterwards. NodesVectorType::iterator otherPlanesIt = m_OtherGeometry2Ds.begin(); NodesVectorType::iterator otherPlanesEnd = m_OtherGeometry2Ds.end(); //int mainLineThickSlicesMode = 0; int mainLineThickSlicesNum = 1; DataNode* dataNodeOfInputPlaneGeometry = NULL; // Now we have to find the DataNode that contains the inputPlaneGeometry // in order to determine the state of the thick-slice rendering while ( otherPlanesIt != otherPlanesEnd ) { PlaneGeometry *otherPlane = static_cast< PlaneGeometry * >( static_cast< Geometry2DData * >( (*otherPlanesIt)->GetData() )->GetGeometry2D() ); // if we have found the correct node if ( (otherPlane == inputPlaneGeometry) && worldPlaneGeometry->IntersectionLine( otherPlane, otherCrossLine ) ) { dataNodeOfInputPlaneGeometry = (*otherPlanesIt); // if( dataNodeOfInputPlaneGeometry ) // { // mainLineThickSlicesMode = this->DetermineThickSliceMode(dataNodeOfInputPlaneGeometry, mainLineThickSlicesNum); // } break; } otherPlanesIt++; } // if we did not find a dataNode for the inputPlaneGeometry there is nothing we can do from here if ( dataNodeOfInputPlaneGeometry == NULL ) return; // Determine if we should draw the area covered by the thick slicing, default is false. // This will also show the area of slices that do not have thick slice mode enabled bool showAreaOfThickSlicing = false; dataNodeOfInputPlaneGeometry->GetBoolProperty( "reslice.thickslices.showarea", showAreaOfThickSlicing ); // get the normal of the inputPlaneGeometry Vector3D normal = inputPlaneGeometry->GetNormal(); // determine the pixelSpacing in that direction double thickSliceDistance = SlicedGeometry3D::CalculateSpacing( referenceGeometry->GetSpacing(), normal ); // As the inputPlaneGeometry cuts through the center of the slice in the middle // we have to add 0.5 pixel in order to compensate. thickSliceDistance *= mainLineThickSlicesNum+0.5; // not the nicest place to do it, but we have the width of the visible bloc in MM here // so we store it in this fancy property dataNodeOfInputPlaneGeometry->SetFloatProperty( "reslice.thickslices.sizeinmm", thickSliceDistance*2 ); if ( showAreaOfThickSlicing ) { // vectorToHelperLine defines how to reach the helperLine from the mainLine Vector2D vectorToHelperLine; vectorToHelperLine = mainLineDirectionOrthogonal; vectorToHelperLine.Normalize(); // got the right direction, so we multiply the width vectorToHelperLine *= thickSliceDistance; // and create the corresponding points primaryHelperLine.SetPoints( primaryHelperLine.GetPoint1() - vectorToHelperLine, primaryHelperLine.GetPoint2() - vectorToHelperLine ); secondaryHelperLine.SetPoints( secondaryHelperLine.GetPoint1() + vectorToHelperLine, secondaryHelperLine.GetPoint2() + vectorToHelperLine ); } //int otherLineThickSlicesMode = 0; int otherLineThickSlicesNum = 1; // by default, there is no gap for the helper lines ScalarType gapSize = 0.0; otherPlanesIt = m_OtherGeometry2Ds.begin(); while ( otherPlanesIt != otherPlanesEnd ) { PlaneGeometry *otherPlane = static_cast< PlaneGeometry * >( static_cast< Geometry2DData * >( (*otherPlanesIt)->GetData() )->GetGeometry2D() ); // Just as with the original line, calculate the intersection with // the world geometry... if ( (otherPlane != inputPlaneGeometry) && worldPlaneGeometry->IntersectionLine( otherPlane, otherCrossLine ) ) { //otherLineThickSlicesMode = this->DetermineThickSliceMode((*otherPlanesIt), otherLineThickSlicesNum); Vector3D normal = otherPlane->GetNormal(); double otherLineThickSliceDistance = SlicedGeometry3D::CalculateSpacing( referenceGeometry->GetSpacing(), normal ); otherLineThickSliceDistance *= (otherLineThickSlicesNum+0.5)*2; Point2D otherLineFrom, otherLineTo; // ... and clip the resulting line segment with the reference // geometry bounding box. otherCrossLine.Transform( *inverseTransform ); if ( otherCrossLine.BoxLineIntersection( boundingBoxMin[0], boundingBoxMin[1], boundingBoxMin[2], boundingBoxMax[0], boundingBoxMax[1], boundingBoxMax[2], otherCrossLine.GetPoint(), otherCrossLine.GetDirection(), point1, point2 ) == 2 ) { worldPlaneGeometry->Map( transform->TransformPoint( point1 ), otherLineFrom ); worldPlaneGeometry->Map( transform->TransformPoint( point2 ), otherLineTo ); otherLine.SetPoints( otherLineFrom, otherLineTo ); // then we have to determine the gap position of the main line // by finding the position at which the two lines cross this->DetermineParametricCrossPositions( mainLine, otherLine, mainLineParams ); // if the other line is also in thick slice mode, we have to determine the // gapsize considering the width of that other line and the spacing in its direction if ( showAreaOfThickSlicing ) { Vector2D otherLineDirection = otherLine.GetDirection(); otherLineDirection.Normalize(); mainLineDirectionOrthogonal.Normalize(); // determine the gapsize gapSize = fabs( otherLineThickSliceDistance / ( otherLineDirection*mainLineDirectionOrthogonal ) ); gapSize = gapSize / displayGeometry->GetScaleFactorMMPerDisplayUnit(); // determine the gap positions for the helper lines as well this->DetermineParametricCrossPositions( primaryHelperLine, otherLine, primaryHelperLineParams ); this->DetermineParametricCrossPositions( secondaryHelperLine, otherLine, secondaryHelperLineParams ); } } } ++otherPlanesIt; } // If we have to draw the helperlines, the mainline will be drawn as a dashed line // with a fixed gapsize of 10 pixels this->DrawLine(renderer, lengthInDisplayUnits, mainLine, mainLineParams, inputPlaneGeometry, showAreaOfThickSlicing, 10.0 ); // If drawn, the helperlines are drawn as a solid line. The gapsize depends on the // width of the crossed line. if ( showAreaOfThickSlicing ) { this->DrawLine(renderer, lengthInDisplayUnits, primaryHelperLine, primaryHelperLineParams, inputPlaneGeometry, false, gapSize ); this->DrawLine(renderer, lengthInDisplayUnits, secondaryHelperLine, secondaryHelperLineParams, inputPlaneGeometry, false, gapSize ); } } } } else { Geometry2DDataToSurfaceFilter::Pointer surfaceCreator; SmartPointerProperty::Pointer surfacecreatorprop; surfacecreatorprop = dynamic_cast< SmartPointerProperty * >( GetDataNode()->GetProperty( "surfacegeometry", renderer)); if( (surfacecreatorprop.IsNull()) || (surfacecreatorprop->GetSmartPointer().IsNull()) || ((surfaceCreator = dynamic_cast< Geometry2DDataToSurfaceFilter * >( surfacecreatorprop->GetSmartPointer().GetPointer())).IsNull()) ) { surfaceCreator = Geometry2DDataToSurfaceFilter::New(); surfacecreatorprop = SmartPointerProperty::New(surfaceCreator); surfaceCreator->PlaceByGeometryOn(); GetDataNode()->SetProperty( "surfacegeometry", surfacecreatorprop ); } surfaceCreator->SetInput( input ); - // Clip the Geometry2D with the reference geometry bounds (if available) + // Clip the PlaneGeometry with the reference geometry bounds (if available) if ( input->GetGeometry2D()->HasReferenceGeometry() ) { surfaceCreator->SetBoundingBox( input->GetGeometry2D()->GetReferenceGeometry()->GetBoundingBox() ); } int res; bool usegeometryparametricbounds = true; if ( GetDataNode()->GetIntProperty("xresolution", res, renderer)) { surfaceCreator->SetXResolution(res); usegeometryparametricbounds=false; } if (GetDataNode()->GetIntProperty("yresolution", res, renderer)) { surfaceCreator->SetYResolution(res); usegeometryparametricbounds=false; } surfaceCreator->SetUseGeometryParametricBounds(usegeometryparametricbounds); - // Calculate the surface of the Geometry2D + // Calculate the surface of the PlaneGeometry surfaceCreator->Update(); if (m_SurfaceMapper.IsNull()) { m_SurfaceMapper=SurfaceGLMapper2D::New(); } m_SurfaceMapper->SetSurface(surfaceCreator->GetOutput()); m_SurfaceMapper->SetDataNode(GetDataNode()); m_SurfaceMapper->Paint(renderer); } } void mitk::Geometry2DDataMapper2D::DrawOrientationArrow( mitk::Point2D &outerPoint, mitk::Point2D &innerPoint, const mitk::PlaneGeometry *planeGeometry, const mitk::PlaneGeometry *rendererPlaneGeometry, const mitk::DisplayGeometry *displayGeometry, bool positiveOrientation ) { // Draw arrows to indicate plane orientation // Vector along line Vector2D v1 = innerPoint - outerPoint; v1.Normalize(); v1 *= 7.0; // Orthogonal vector Vector2D v2; v2[0] = v1[1]; v2[1] = -v1[0]; // Calculate triangle tip for one side and project it back into world // coordinates to determine whether it is above or below the plane Point2D worldPoint2D; Point3D worldPoint; displayGeometry->DisplayToWorld( outerPoint + v1 + v2, worldPoint2D ); rendererPlaneGeometry->Map( worldPoint2D, worldPoint ); // Initialize remaining triangle coordinates accordingly // (above/below state is XOR'ed with orientation flag) Point2D p1 = outerPoint + v1 * 2.0; Point2D p2 = outerPoint + v1 + ((positiveOrientation ^ planeGeometry->IsAbove( worldPoint )) ? v2 : -v2); // Draw the arrow (triangle) glBegin( GL_TRIANGLES ); glVertex2f( outerPoint[0], outerPoint[1] ); glVertex2f( p1[0], p1[1] ); glVertex2f( p2[0], p2[1] ); glEnd(); } void mitk::Geometry2DDataMapper2D::ApplyAllProperties( BaseRenderer *renderer ) { Superclass::ApplyColorAndOpacityProperties(renderer); PlaneOrientationProperty* decorationProperty; this->GetDataNode()->GetProperty( decorationProperty, "decoration", renderer ); if ( decorationProperty != NULL ) { if ( decorationProperty->GetPlaneDecoration() == PlaneOrientationProperty::PLANE_DECORATION_POSITIVE_ORIENTATION ) { m_RenderOrientationArrows = true; m_ArrowOrientationPositive = true; } else if ( decorationProperty->GetPlaneDecoration() == PlaneOrientationProperty::PLANE_DECORATION_NEGATIVE_ORIENTATION ) { m_RenderOrientationArrows = true; m_ArrowOrientationPositive = false; } else { m_RenderOrientationArrows = false; } } } void mitk::Geometry2DDataMapper2D::SetDatastorageAndGeometryBaseNode( mitk::DataStorage::Pointer ds, mitk::DataNode::Pointer parent ) { if (ds.IsNotNull()) { m_DataStorage = ds; } if (parent.IsNotNull()) { m_ParentNode = parent; } } void mitk::Geometry2DDataMapper2D::DrawLine( BaseRenderer* renderer, ScalarType lengthInDisplayUnits, Line &line, std::vector &gapPositions, const PlaneGeometry* inputPlaneGeometry, bool drawDashed, ScalarType gapSizeInPixel ) { DisplayGeometry *displayGeometry = renderer->GetDisplayGeometry(); const PlaneGeometry *worldPlaneGeometry = dynamic_cast< const PlaneGeometry* >( renderer->GetCurrentWorldGeometry2D() ); // Apply color and opacity read from the PropertyList. this->ApplyAllProperties( renderer ); ScalarType gapSizeInParamUnits = 1.0 / lengthInDisplayUnits * gapSizeInPixel; std::sort( gapPositions.begin(), gapPositions.end() ); Point2D p1, p2; ScalarType p1Param, p2Param; p1Param = gapPositions[0]; p1 = line.GetPoint( p1Param ); displayGeometry->WorldToDisplay( p1, p1 ); //Workaround to show the crosshair always on top of a 2D render window //The image is usually located at depth = 0 or negative depth values, and thus, //the crosshair with depth = 1 is always on top. float depthPosition = 1.0f; if ( drawDashed ) { glEnable(GL_LINE_STIPPLE); glLineStipple(1, 0xF0F0); } glEnable(GL_DEPTH_TEST); // Iterate over all line segments and display each, with a gap // in between. unsigned int i, preLastLineParam = gapPositions.size() - 1; for ( i = 1; i < preLastLineParam; ++i ) { p2Param = gapPositions[i] - gapSizeInParamUnits * 0.5; p2 = line.GetPoint( p2Param ); if ( p2Param > p1Param ) { // Convert intersection points (until now mm) to display // coordinates (units). displayGeometry->WorldToDisplay( p2, p2 ); // draw glBegin (GL_LINES); glVertex3f(p1[0],p1[1], depthPosition); glVertex3f(p2[0],p2[1], depthPosition); glEnd (); if ( (i == 1) && (m_RenderOrientationArrows) ) { // Draw orientation arrow for first line segment this->DrawOrientationArrow( p1, p2, inputPlaneGeometry, worldPlaneGeometry, displayGeometry, m_ArrowOrientationPositive ); } } p1Param = p2Param + gapSizeInParamUnits; p1 = line.GetPoint( p1Param ); displayGeometry->WorldToDisplay( p1, p1 ); } // Draw last line segment p2Param = gapPositions[i]; p2 = line.GetPoint( p2Param ); displayGeometry->WorldToDisplay( p2, p2 ); glBegin( GL_LINES ); glVertex3f( p1[0], p1[1], depthPosition); glVertex3f( p2[0], p2[1], depthPosition); glEnd(); if ( drawDashed ) { glDisable(GL_LINE_STIPPLE); } // Draw orientation arrows if ( m_RenderOrientationArrows ) { this->DrawOrientationArrow( p2, p1, inputPlaneGeometry, worldPlaneGeometry, displayGeometry, m_ArrowOrientationPositive ); if ( preLastLineParam < 2 ) { // If we only have one line segment, draw other arrow, too this->DrawOrientationArrow( p1, p2, inputPlaneGeometry, worldPlaneGeometry, displayGeometry, m_ArrowOrientationPositive ); } } } int mitk::Geometry2DDataMapper2D::DetermineThickSliceMode( DataNode * dn, int &thickSlicesNum ) { int thickSlicesMode = 0; // determine the state and the extend of the thick-slice mode mitk::ResliceMethodProperty *resliceMethodEnumProperty=0; if( dn->GetProperty( resliceMethodEnumProperty, "reslice.thickslices" ) && resliceMethodEnumProperty ) thickSlicesMode = resliceMethodEnumProperty->GetValueAsId(); IntProperty *intProperty=0; if( dn->GetProperty( intProperty, "reslice.thickslices.num" ) && intProperty ) { thickSlicesNum = intProperty->GetValue(); if(thickSlicesNum < 1) thickSlicesNum=0; if(thickSlicesNum > 10) thickSlicesNum=10; } if ( thickSlicesMode == 0 ) thickSlicesNum = 0; return thickSlicesMode; } void mitk::Geometry2DDataMapper2D::DetermineParametricCrossPositions( Line< mitk::ScalarType, 2 > &mainLine, Line< mitk::ScalarType, 2 > &otherLine, std::vector< mitk::ScalarType > &crossPositions ) { Vector2D direction, dOrth; // By means of the dot product, calculate the gap position as // parametric value in the range [0, 1] direction = otherLine.GetDirection(); dOrth[0] = -direction[1]; dOrth[1] = direction[0]; ScalarType gapPosition = ( otherLine.GetPoint1() - mainLine.GetPoint1() ) * dOrth; ScalarType norm = mainLine.GetDirection() * dOrth; if ( fabs( norm ) > eps ) { gapPosition /= norm; if ( (gapPosition > 0.0) && (gapPosition < 1.0) ) { crossPositions.push_back(gapPosition); } } } diff --git a/Core/Code/Rendering/mitkGeometry2DDataMapper2D.h b/Core/Code/Rendering/mitkGeometry2DDataMapper2D.h index df8164324c..c00f9b6129 100644 --- a/Core/Code/Rendering/mitkGeometry2DDataMapper2D.h +++ b/Core/Code/Rendering/mitkGeometry2DDataMapper2D.h @@ -1,122 +1,122 @@ /*=================================================================== 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 MITKGEOMETRY2DDATAMAPPER2D_H_HEADER_INCLUDED_C19C0BFB #define MITKGEOMETRY2DDATAMAPPER2D_H_HEADER_INCLUDED_C19C0BFB #include #include "mitkGLMapper.h" #include "mitkSurfaceGLMapper2D.h" #include "mitkDataStorage.h" #include "mitkDataNode.h" #include "mitkWeakPointer.h" namespace mitk { class BaseRenderer; /** - * \brief OpenGL-based mapper to display a Geometry2D in a 2D window + * \brief OpenGL-based mapper to display a PlaneGeometry in a 2D window * * Currently implemented for mapping on PlaneGeometry. * The result is normally a line. An important usage of this class is to show * the orientation of the slices displayed in other 2D windows. * * * Properties that can be set and influence the Geometry2DDataMapper2D are: * * - \b "PlaneOrientationProperty": (PlaneOrientationProperty) * \todo implement for AbstractTransformGeometry. * \ingroup Mapper */ class MITK_CORE_EXPORT Geometry2DDataMapper2D : public GLMapper { public: mitkClassMacro(Geometry2DDataMapper2D, GLMapper); itkNewMacro(Self); /** * \brief Get the Geometry2DData to map */ const Geometry2DData *GetInput(); virtual void Paint( BaseRenderer *renderer ); virtual void SetDatastorageAndGeometryBaseNode(mitk::DataStorage::Pointer ds, mitk::DataNode::Pointer parent); /** Applies properties specific to this mapper */ virtual void ApplyAllProperties( BaseRenderer *renderer ); LocalStorageHandler m_LSH; protected: Geometry2DDataMapper2D(); virtual ~Geometry2DDataMapper2D(); virtual void GenerateDataForRenderer(mitk::BaseRenderer* renderer); /** * \brief Returns the thick slice mode for the given datanode. * * This method returns the value of the 'reslice.thickslices' property for * the given datanode. * '0': thick slice mode disabled * '1': thick slice mode enabled * * The variable 'thickSlicesNum' contains the value of the 'reslice.thickslices.num' * property that defines how many slices are shown at once. */ int DetermineThickSliceMode( DataNode * dn, int &thickSlicesNum ); /** * \brief Determines the cross position of two lines and stores them as parametric coordinates * * This method determines the parametric position at which a line 'otherLine' crosses another line * 'mainLine'. The result is stored in 'crossPositions'. */ void DetermineParametricCrossPositions( Line &mainLine, Line &otherLine, std::vector &crossPositions ); void DrawLine( BaseRenderer * renderer, ScalarType lengthInDisplayUnits, Line< ScalarType, 2 > &line, std::vector< ScalarType > &gapPositions, const PlaneGeometry * inputPlaneGeometry, bool drawDashed, ScalarType gapSizeInPixel ); void DrawOrientationArrow( Point2D &outerPoint, Point2D &innerPoint, const PlaneGeometry *planeGeometry, const PlaneGeometry *rendererPlaneGeometry, const DisplayGeometry *displayGeometry, bool positiveOrientation = true ); SurfaceGLMapper2D::Pointer m_SurfaceMapper; mitk::WeakPointer m_DataStorage; ///< DataStorage that will be searched for sub nodes DataNode::Pointer m_ParentNode; ///< parent node that will be used to search for sub nodes typedef std::vector NodesVectorType; NodesVectorType m_OtherGeometry2Ds; bool m_RenderOrientationArrows; bool m_ArrowOrientationPositive; }; } // namespace mitk #endif /* MITKGEOMETRY2DDATAMAPPER2D_H_HEADER_INCLUDED_C19C0BFB */ diff --git a/Core/Code/Rendering/mitkGeometry2DDataVtkMapper3D.cpp b/Core/Code/Rendering/mitkGeometry2DDataVtkMapper3D.cpp index deec504c3f..9bed9b4e9f 100644 --- a/Core/Code/Rendering/mitkGeometry2DDataVtkMapper3D.cpp +++ b/Core/Code/Rendering/mitkGeometry2DDataVtkMapper3D.cpp @@ -1,587 +1,587 @@ /*=================================================================== 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 "mitkGeometry2DDataVtkMapper3D.h" #include "mitkImageVtkMapper2D.h" #include "mitkSmartPointerProperty.h" #include "mitkSurface.h" #include "mitkVtkRepresentationProperty.h" #include "mitkWeakPointerProperty.h" #include "mitkNodePredicateDataType.h" #include "mitkNodePredicateOr.h" #include "vtkNeverTranslucentTexture.h" #include "vtkMitkLevelWindowFilter.h" #include #include #include #include #include #include #include #include #include #include #include #include namespace mitk { Geometry2DDataVtkMapper3D::Geometry2DDataVtkMapper3D() : m_NormalsActorAdded(false), m_DataStorage(NULL) { m_EdgeTuber = vtkTubeFilter::New(); m_EdgeMapper = vtkPolyDataMapper::New(); m_SurfaceCreator = Geometry2DDataToSurfaceFilter::New(); m_SurfaceCreatorBoundingBox = BoundingBox::New(); m_SurfaceCreatorPointsContainer = BoundingBox::PointsContainer::New(); m_Edges = vtkFeatureEdges::New(); m_Edges->BoundaryEdgesOn(); m_Edges->FeatureEdgesOff(); m_Edges->NonManifoldEdgesOff(); m_Edges->ManifoldEdgesOff(); m_EdgeTransformer = vtkTransformPolyDataFilter::New(); m_NormalsTransformer = vtkTransformPolyDataFilter::New(); m_EdgeActor = vtkActor::New(); m_BackgroundMapper = vtkPolyDataMapper::New(); m_BackgroundActor = vtkActor::New(); m_Prop3DAssembly = vtkAssembly::New(); m_ImageAssembly = vtkAssembly::New(); m_SurfaceCreatorBoundingBox->SetPoints( m_SurfaceCreatorPointsContainer ); m_Cleaner = vtkCleanPolyData::New(); m_Cleaner->PieceInvariantOn(); m_Cleaner->ConvertLinesToPointsOn(); m_Cleaner->ConvertPolysToLinesOn(); m_Cleaner->ConvertStripsToPolysOn(); m_Cleaner->PointMergingOn(); // Make sure that the FeatureEdge algorithm is initialized with a "valid" // (though empty) input vtkPolyData *emptyPolyData = vtkPolyData::New(); m_Cleaner->SetInputData( emptyPolyData ); emptyPolyData->Delete(); m_Edges->SetInputConnection(m_Cleaner->GetOutputPort()); m_EdgeTransformer->SetInputConnection( m_Edges->GetOutputPort() ); m_EdgeTuber->SetInputConnection( m_EdgeTransformer->GetOutputPort() ); m_EdgeTuber->SetVaryRadiusToVaryRadiusOff(); m_EdgeTuber->SetNumberOfSides( 12 ); m_EdgeTuber->CappingOn(); m_EdgeMapper->SetInputConnection( m_EdgeTuber->GetOutputPort() ); m_EdgeMapper->ScalarVisibilityOff(); m_BackgroundMapper->SetInputData(emptyPolyData); m_BackgroundMapper->Update(); m_EdgeActor->SetMapper( m_EdgeMapper ); m_BackgroundActor->GetProperty()->SetAmbient( 0.5 ); m_BackgroundActor->GetProperty()->SetColor( 0.0, 0.0, 0.0 ); m_BackgroundActor->GetProperty()->SetOpacity( 0.0 ); m_BackgroundActor->SetMapper( m_BackgroundMapper ); vtkProperty * backfaceProperty = m_BackgroundActor->MakeProperty(); backfaceProperty->SetColor( 0.0, 0.0, 0.0 ); m_BackgroundActor->SetBackfaceProperty( backfaceProperty ); backfaceProperty->Delete(); m_FrontHedgeHog = vtkHedgeHog::New(); m_BackHedgeHog = vtkHedgeHog::New(); m_FrontNormalsMapper = vtkPolyDataMapper::New(); m_FrontNormalsMapper->SetInputConnection( m_FrontHedgeHog->GetOutputPort()); m_BackNormalsMapper = vtkPolyDataMapper::New(); m_Prop3DAssembly->AddPart( m_EdgeActor ); m_Prop3DAssembly->AddPart( m_ImageAssembly ); m_FrontNormalsActor = vtkActor::New(); m_FrontNormalsActor->SetMapper(m_FrontNormalsMapper); m_BackNormalsActor = vtkActor::New(); m_BackNormalsActor->SetMapper(m_BackNormalsMapper); m_ImageMapperDeletedCommand = MemberCommandType::New(); m_ImageMapperDeletedCommand->SetCallbackFunction( this, &Geometry2DDataVtkMapper3D::ImageMapperDeletedCallback ); } Geometry2DDataVtkMapper3D::~Geometry2DDataVtkMapper3D() { m_ImageAssembly->Delete(); m_Prop3DAssembly->Delete(); m_EdgeTuber->Delete(); m_EdgeMapper->Delete(); m_EdgeTransformer->Delete(); m_Cleaner->Delete(); m_Edges->Delete(); m_NormalsTransformer->Delete(); m_EdgeActor->Delete(); m_BackgroundMapper->Delete(); m_BackgroundActor->Delete(); m_FrontNormalsMapper->Delete(); m_FrontNormalsActor->Delete(); m_FrontHedgeHog->Delete(); m_BackNormalsMapper->Delete(); m_BackNormalsActor->Delete(); m_BackHedgeHog->Delete(); // Delete entries in m_ImageActors list one by one m_ImageActors.clear(); m_DataStorage = NULL; } vtkProp* Geometry2DDataVtkMapper3D::GetVtkProp(mitk::BaseRenderer * /*renderer*/) { if ( (this->GetDataNode() != NULL ) && (m_ImageAssembly != NULL) ) { // Do not transform the entire Prop3D assembly, but only the image part // here. The colored frame is transformed elsewhere (via m_EdgeTransformer), // since only vertices should be transformed there, not the poly data // itself, to avoid distortion for anisotropic datasets. m_ImageAssembly->SetUserTransform( this->GetDataNode()->GetVtkTransform() ); } return m_Prop3DAssembly; } void Geometry2DDataVtkMapper3D::UpdateVtkTransform(mitk::BaseRenderer * /*renderer*/) { m_ImageAssembly->SetUserTransform( this->GetDataNode()->GetVtkTransform(this->GetTimestep()) ); } const Geometry2DData* Geometry2DDataVtkMapper3D::GetInput() { return static_cast ( GetDataNode()->GetData() ); } void Geometry2DDataVtkMapper3D::SetDataStorageForTexture(mitk::DataStorage* storage) { if(storage != NULL && m_DataStorage != storage ) { m_DataStorage = storage; this->Modified(); } } void Geometry2DDataVtkMapper3D::ImageMapperDeletedCallback( itk::Object *caller, const itk::EventObject& /*event*/ ) { ImageVtkMapper2D *imageMapper = dynamic_cast< ImageVtkMapper2D * >( caller ); if ( (imageMapper != NULL) ) { if ( m_ImageActors.count( imageMapper ) > 0) { m_ImageActors[imageMapper].m_Sender = NULL; // sender is already destroying itself m_ImageActors.erase( imageMapper ); } } } void Geometry2DDataVtkMapper3D::GenerateDataForRenderer(BaseRenderer* renderer) { SetVtkMapperImmediateModeRendering(m_EdgeMapper); SetVtkMapperImmediateModeRendering(m_BackgroundMapper); // Remove all actors from the assembly, and re-initialize it with the // edge actor m_ImageAssembly->GetParts()->RemoveAllItems(); bool visible = true; GetDataNode()->GetVisibility(visible, renderer, "visible"); if ( !visible ) { // visibility has explicitly to be set in the single actors // due to problems when using cell picking: // even if the assembly is invisible, the renderer contains // references to the assemblies parts. During picking the // visibility of each part is checked, and not only for the // whole assembly. m_ImageAssembly->VisibilityOff(); m_EdgeActor->VisibilityOff(); return; } // visibility has explicitly to be set in the single actors // due to problems when using cell picking: // even if the assembly is invisible, the renderer contains // references to the assemblies parts. During picking the // visibility of each part is checked, and not only for the // whole assembly. m_ImageAssembly->VisibilityOn(); bool drawEdges = true; this->GetDataNode()->GetBoolProperty("draw edges", drawEdges, renderer); m_EdgeActor->SetVisibility(drawEdges); Geometry2DData::Pointer input = const_cast< Geometry2DData * >(this->GetInput()); if (input.IsNotNull() && (input->GetGeometry2D() != NULL)) { SmartPointerProperty::Pointer surfacecreatorprop; surfacecreatorprop = dynamic_cast< SmartPointerProperty * >(GetDataNode()->GetProperty("surfacegeometry", renderer)); if ( (surfacecreatorprop.IsNull()) || (surfacecreatorprop->GetSmartPointer().IsNull()) || ((m_SurfaceCreator = dynamic_cast (surfacecreatorprop->GetSmartPointer().GetPointer())).IsNull() ) ) { m_SurfaceCreator->PlaceByGeometryOn(); surfacecreatorprop = SmartPointerProperty::New( m_SurfaceCreator ); GetDataNode()->SetProperty("surfacegeometry", surfacecreatorprop); } m_SurfaceCreator->SetInput(input); int res; if (GetDataNode()->GetIntProperty("xresolution", res, renderer)) { m_SurfaceCreator->SetXResolution(res); } if (GetDataNode()->GetIntProperty("yresolution", res, renderer)) { m_SurfaceCreator->SetYResolution(res); } double tubeRadius = 1.0; // Radius of tubular edge surrounding plane - // Clip the Geometry2D with the reference geometry bounds (if available) + // Clip the PlaneGeometry with the reference geometry bounds (if available) if ( input->GetGeometry2D()->HasReferenceGeometry() ) { BaseGeometry *referenceGeometry = input->GetGeometry2D()->GetReferenceGeometry(); BoundingBox::PointType boundingBoxMin, boundingBoxMax; boundingBoxMin = referenceGeometry->GetBoundingBox()->GetMinimum(); boundingBoxMax = referenceGeometry->GetBoundingBox()->GetMaximum(); if ( referenceGeometry->GetImageGeometry() ) { for ( unsigned int i = 0; i < 3; ++i ) { boundingBoxMin[i] -= 0.5; boundingBoxMax[i] -= 0.5; } } m_SurfaceCreatorPointsContainer->CreateElementAt( 0 ) = boundingBoxMin; m_SurfaceCreatorPointsContainer->CreateElementAt( 1 ) = boundingBoxMax; m_SurfaceCreatorBoundingBox->ComputeBoundingBox(); m_SurfaceCreator->SetBoundingBox( m_SurfaceCreatorBoundingBox ); tubeRadius = referenceGeometry->GetDiagonalLength() / 450.0; } // If no reference geometry is available, clip with the current global // bounds else if (m_DataStorage.IsNotNull()) { m_SurfaceCreator->SetBoundingBox(m_DataStorage->ComputeVisibleBoundingBox(NULL, "includeInBoundingBox")); tubeRadius = sqrt( m_SurfaceCreator->GetBoundingBox()->GetDiagonalLength2() ) / 450.0; } - // Calculate the surface of the Geometry2D + // Calculate the surface of the PlaneGeometry m_SurfaceCreator->Update(); Surface *surface = m_SurfaceCreator->GetOutput(); // Check if there's something to display, otherwise return if ( (surface->GetVtkPolyData() == 0 ) || (surface->GetVtkPolyData()->GetNumberOfCells() == 0) ) { m_ImageAssembly->VisibilityOff(); return; } // add a graphical representation of the surface normals if requested DataNode* node = this->GetDataNode(); bool displayNormals = false; bool colorTwoSides = false; bool invertNormals = false; node->GetBoolProperty("draw normals 3D", displayNormals, renderer); node->GetBoolProperty("color two sides", colorTwoSides, renderer); node->GetBoolProperty("invert normals", invertNormals, renderer); //if we want to draw the display normals or render two sides we have to get the colors if( displayNormals || colorTwoSides ) { //get colors float frontColor[3] = { 0.0, 0.0, 1.0 }; node->GetColor( frontColor, renderer, "front color" ); float backColor[3] = { 1.0, 0.0, 0.0 }; node->GetColor( backColor, renderer, "back color" ); if ( displayNormals ) { m_NormalsTransformer->SetInputData( surface->GetVtkPolyData() ); m_NormalsTransformer->SetTransform(node->GetVtkTransform(this->GetTimestep()) ); m_FrontHedgeHog->SetInputConnection(m_NormalsTransformer->GetOutputPort() ); m_FrontHedgeHog->SetVectorModeToUseNormal(); m_FrontHedgeHog->SetScaleFactor( invertNormals ? 1.0 : -1.0 ); m_FrontHedgeHog->Update(); m_FrontNormalsActor->GetProperty()->SetColor( frontColor[0], frontColor[1], frontColor[2] ); m_BackHedgeHog->SetInputConnection( m_NormalsTransformer->GetOutputPort() ); m_BackHedgeHog->SetVectorModeToUseNormal(); m_BackHedgeHog->SetScaleFactor( invertNormals ? -1.0 : 1.0 ); m_BackHedgeHog->Update(); m_BackNormalsActor->GetProperty()->SetColor( backColor[0], backColor[1], backColor[2] ); //if there is no actor added yet, add one if ( !m_NormalsActorAdded ) { m_Prop3DAssembly->AddPart( m_FrontNormalsActor ); m_Prop3DAssembly->AddPart( m_BackNormalsActor ); m_NormalsActorAdded = true; } } //if we don't want to display normals AND there is an actor added remove the actor else if ( m_NormalsActorAdded ) { m_Prop3DAssembly->RemovePart( m_FrontNormalsActor ); m_Prop3DAssembly->RemovePart( m_BackNormalsActor ); m_NormalsActorAdded = false; } if ( colorTwoSides ) { if ( !invertNormals ) { m_BackgroundActor->GetProperty()->SetColor( backColor[0], backColor[1], backColor[2] ); m_BackgroundActor->GetBackfaceProperty()->SetColor( frontColor[0], frontColor[1], frontColor[2] ); } else { m_BackgroundActor->GetProperty()->SetColor( frontColor[0], frontColor[1], frontColor[2] ); m_BackgroundActor->GetBackfaceProperty()->SetColor( backColor[0], backColor[1], backColor[2] ); } } } // Add black background for all images (which may be transparent) m_BackgroundMapper->SetInputData( surface->GetVtkPolyData() ); m_ImageAssembly->AddPart( m_BackgroundActor ); LayerSortedActorList layerSortedActors; // Traverse the data tree to find nodes resliced by ImageMapperGL2D //use a predicate to get all data nodes which are "images" or inherit from mitk::Image mitk::TNodePredicateDataType< mitk::Image >::Pointer predicateAllImages = mitk::TNodePredicateDataType< mitk::Image >::New(); mitk::DataStorage::SetOfObjects::ConstPointer all = m_DataStorage->GetSubset(predicateAllImages); //process all found images for (mitk::DataStorage::SetOfObjects::ConstIterator it = all->Begin(); it != all->End(); ++it) { DataNode *node = it->Value(); if (node != NULL) this->ProcessNode(node, renderer, surface, layerSortedActors); } // Add all image actors to the assembly, sorted according to // layer property LayerSortedActorList::iterator actorIt; for ( actorIt = layerSortedActors.begin(); actorIt != layerSortedActors.end(); ++actorIt ) { m_ImageAssembly->AddPart( actorIt->second ); } // Configurate the tube-shaped frame: size according to the surface // bounds, color as specified in the plane's properties vtkPolyData *surfacePolyData = surface->GetVtkPolyData(); m_Cleaner->SetInputData(surfacePolyData); m_EdgeTransformer->SetTransform(this->GetDataNode()->GetVtkTransform(this->GetTimestep()) ); // Adjust the radius according to extent m_EdgeTuber->SetRadius( tubeRadius ); // Get the plane's color and set the tube properties accordingly ColorProperty::Pointer colorProperty; colorProperty = dynamic_cast(this->GetDataNode()->GetProperty( "color" )); if ( colorProperty.IsNotNull() ) { const Color& color = colorProperty->GetColor(); m_EdgeActor->GetProperty()->SetColor(color.GetRed(), color.GetGreen(), color.GetBlue()); } else { m_EdgeActor->GetProperty()->SetColor( 1.0, 1.0, 1.0 ); } m_ImageAssembly->SetUserTransform(this->GetDataNode()->GetVtkTransform(this->GetTimestep()) ); } VtkRepresentationProperty* representationProperty; this->GetDataNode()->GetProperty(representationProperty, "material.representation", renderer); if ( representationProperty != NULL ) m_BackgroundActor->GetProperty()->SetRepresentation( representationProperty->GetVtkRepresentation() ); } void Geometry2DDataVtkMapper3D::ProcessNode( DataNode * node, BaseRenderer* renderer, Surface * surface, LayerSortedActorList &layerSortedActors ) { if ( node != NULL ) { //we need to get the information from the 2D mapper to render the texture on the 3D plane ImageVtkMapper2D *imageMapper = dynamic_cast< ImageVtkMapper2D * >( node->GetMapper(1) ); //GetMapper(1) provides the 2D mapper for the data node //if there is a 2D mapper, which is not the standard image mapper... if(!imageMapper && node->GetMapper(1)) { //... check if it is the composite mapper std::string cname(node->GetMapper(1)->GetNameOfClass()); if(!cname.compare("CompositeMapper")) //string.compare returns 0 if the two strings are equal. { //get the standard image mapper. //This is a special case in MITK and does only work for the CompositeMapper. imageMapper = dynamic_cast( node->GetMapper(3) ); } } if ( (node->IsVisible(renderer)) && imageMapper ) { WeakPointerProperty::Pointer rendererProp = dynamic_cast< WeakPointerProperty * >(GetDataNode()->GetPropertyList()->GetProperty("renderer")); if ( rendererProp.IsNotNull() ) { BaseRenderer::Pointer planeRenderer = dynamic_cast< BaseRenderer * >(rendererProp->GetWeakPointer().GetPointer()); // Retrieve and update image to be mapped const ImageVtkMapper2D::LocalStorage* localStorage = imageMapper->GetLocalStorage(planeRenderer); if ( planeRenderer.IsNotNull() ) { // perform update of imagemapper if needed (maybe the respective 2D renderwindow is not rendered/update before) imageMapper->Update(planeRenderer); // If it has not been initialized already in a previous pass, // generate an actor and a texture object to // render the image associated with the ImageVtkMapper2D. vtkActor *imageActor; vtkDataSetMapper *dataSetMapper = NULL; vtkTexture *texture; if ( m_ImageActors.count( imageMapper ) == 0 ) { dataSetMapper = vtkDataSetMapper::New(); //Enable rendering without copying the image. dataSetMapper->ImmediateModeRenderingOn(); texture = vtkNeverTranslucentTexture::New(); texture->RepeatOff(); imageActor = vtkActor::New(); imageActor->SetMapper( dataSetMapper ); imageActor->SetTexture( texture ); imageActor->GetProperty()->SetOpacity(0.999); // HACK! otherwise VTK wouldn't recognize this as translucent surface (if LUT values map to alpha < 255 // improvement: apply "opacity" property onle HERE and also in 2D image mapper. DO NOT change LUT to achieve translucent images (see method ChangeOpacity in image mapper 2D) // Make imageActor the sole owner of the mapper and texture // objects dataSetMapper->UnRegister( NULL ); texture->UnRegister( NULL ); // Store the actor so that it may be accessed in following // passes. m_ImageActors[imageMapper].Initialize(imageActor, imageMapper, m_ImageMapperDeletedCommand); } else { // Else, retrieve the actor and associated objects from the // previous pass. imageActor = m_ImageActors[imageMapper].m_Actor; dataSetMapper = (vtkDataSetMapper *)imageActor->GetMapper(); texture = imageActor->GetTexture(); } // Set poly data new each time its object changes (e.g. when // switching between planar and curved geometries) if ( (dataSetMapper != NULL) && (dataSetMapper->GetInput() != surface->GetVtkPolyData()) ) { dataSetMapper->SetInputData( surface->GetVtkPolyData() ); } dataSetMapper->Update(); //Check if the m_ReslicedImage is NULL. //This is the case when no image geometry is met by //the reslicer. In that case, the texture has to be //empty (black) and we don't have to do anything. //See fixed bug #13275 if(localStorage->m_ReslicedImage != NULL) { texture->SetInputConnection(localStorage->m_LevelWindowFilter->GetOutputPort()); // do not use a VTK lookup table (we do that ourselves in m_LevelWindowFilter) texture->MapColorScalarsThroughLookupTableOff(); //re-use properties from the 2D image mapper imageActor->SetProperty( localStorage->m_Actor->GetProperty() ); imageActor->GetProperty()->SetAmbient(0.5); // Set texture interpolation on/off bool textureInterpolation = node->IsOn( "texture interpolation", renderer ); texture->SetInterpolate( textureInterpolation ); // Store this actor to be added to the actor assembly, sort // by layer int layer = 1; node->GetIntProperty( "layer", layer ); layerSortedActors.insert(std::pair< int, vtkActor * >( layer, imageActor ) ); } } } } } } void Geometry2DDataVtkMapper3D::ActorInfo::Initialize(vtkActor* actor, itk::Object* sender, itk::Command* command) { m_Actor = actor; m_Sender = sender; // Get informed when ImageMapper object is deleted, so that // the data structures built here can be deleted as well m_ObserverID = sender->AddObserver( itk::DeleteEvent(), command ); } Geometry2DDataVtkMapper3D::ActorInfo::ActorInfo() : m_Actor(NULL), m_Sender(NULL), m_ObserverID(0) { } Geometry2DDataVtkMapper3D::ActorInfo::~ActorInfo() { if(m_Sender != NULL) { m_Sender->RemoveObserver(m_ObserverID); } if(m_Actor != NULL) { m_Actor->Delete(); } } } // namespace mitk diff --git a/Core/Code/Rendering/mitkGeometry2DDataVtkMapper3D.h b/Core/Code/Rendering/mitkGeometry2DDataVtkMapper3D.h index 38fdd96980..3a0c02b5f9 100644 --- a/Core/Code/Rendering/mitkGeometry2DDataVtkMapper3D.h +++ b/Core/Code/Rendering/mitkGeometry2DDataVtkMapper3D.h @@ -1,211 +1,211 @@ /*=================================================================== 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 MITKGEOMETRY2DDATAVTKMAPPER3D_H_HEADER_INCLUDED_C196C71F #define MITKGEOMETRY2DDATAVTKMAPPER3D_H_HEADER_INCLUDED_C196C71F #include #include "mitkVtkMapper.h" #include "mitkDataStorage.h" #include "mitkGeometry2DDataToSurfaceFilter.h" #include "mitkWeakPointer.h" #include #include class vtkActor; class vtkPolyDataMapper; class vtkAssembly; class vtkFeatureEdges; class vtkTubeFilter; class vtkTransformPolyDataFilter; class vtkHedgeHog; namespace mitk { class Geometry2DData; class BaseRenderer; class ImageVtkMapper2D; class DataStorage; /** - * \brief Vtk-based mapper to display a Geometry2D in a 3D window + * \brief Vtk-based mapper to display a PlaneGeometry in a 3D window * \ingroup Mapper * - * Uses a Geometry2DDataToSurfaceFilter object to create a vtkPolyData representation of a given Geometry2D instance. - * Geometry2D may either contain a common flat plane or a curved plane (ThinPlateSplineCurvedGeometry). + * Uses a Geometry2DDataToSurfaceFilter object to create a vtkPolyData representation of a given PlaneGeometry instance. + * PlaneGeometry may either contain a common flat plane or a curved plane (ThinPlateSplineCurvedGeometry). * * The vtkPolyData object is then decorated by a colored tube on the edges and by image textures if possible * (currently this requires that there is a 2D render window rendering the same geometry as this mapper). * * Properties that influence rendering are: * * - \b "draw edges": (Bool) Toggle display of the tubed frame * - \b "color": (ColorProperty) Color of the tubed frame. * - \b "xresolution": (FloatProperty) Resolution (=number of tiles) in x direction. Only relevant for ThinPlateSplineCurvedGeometry * - \b "yresolution": (FloatProperty) Resolution (=number of tiles) in y direction. Only relevant for ThinPlateSplineCurvedGeometry * - \b "draw normals 3D": (BoolProperty) If true, a vtkHedgeHog is used to display normals for the generated surface object. Useful to distinguish front and back of a plane. Hedgehogs are colored according to "front color" and "back color" * - \b "color two sides": (BoolProperty) If true, front and back side of the plane are colored differently ("front color" and "back color") * - \b "invert normals": (BoolProperty) Inverts front/back for display. * - \b "front color": (ColorProperty) Color for front side of the plane * - \b "back color": (ColorProperty) Color for back side of the plane * - \b "material.representation": (BoolProperty) Choose the representation to draw the mesh in (Surface, Wireframe, Point Cloud) * - \b "surfacegeometry": TODO: Add documentation * - \b "LookupTable": (LookupTableProperty) Set the lookuptable to render with. * * Note: The following properties are set for each image individually, and thus, also influence the rendering of this mapper: * * - \b "texture interpolation": (BoolProperty) Turn on/off the texture interpolation of each image * - \b "use color": (BoolProperty) Decide whether we want to use the color property or a lookuptable. * - \b "binary": (BoolProperty) Binary image handling: Color the value=1.0 with the color property and make the background (value=0.0) of the image translucent. * - \b "layer": (IntProperty) Controls what image is considered "on top" of another. In the case that two should inhabit the same space, higher layer occludes lower layer. * - \b "opacity": (FloatProperty) Set the opacity for each rendered image. * - \b "color": (FloatProperty) Set the color for each rendered image. * * The internal filter pipeline which combines a (sometimes deformed) 2D surface * with a nice frame and image textures is illustrated in the following sketch: * * \image html mitkGeometry2DDataVtkMapper3D.png "Internal filter pipeline" * */ class MITK_CORE_EXPORT Geometry2DDataVtkMapper3D : public VtkMapper { public: mitkClassMacro(Geometry2DDataVtkMapper3D, VtkMapper); itkNewMacro(Geometry2DDataVtkMapper3D); /** * Overloaded since the displayed color-frame of the image mustn't be * transformed after generation of poly data, but before (vertex coordinates * only) */ virtual vtkProp *GetVtkProp(mitk::BaseRenderer *renderer); virtual void UpdateVtkTransform(mitk::BaseRenderer *renderer); /** * \brief Get the Geometry2DData to map */ virtual const Geometry2DData *GetInput(); /** * \brief All images found when traversing the (sub-) tree starting at * \a iterator which are resliced by an ImageVtkMapper2D will be mapped. * This method is used to set the data storage to traverse. This offers * the possibility to use this mapper for other data storages (not only * the default data storage). */ virtual void SetDataStorageForTexture(mitk::DataStorage* storage); protected: typedef std::multimap< int, vtkActor * > LayerSortedActorList; Geometry2DDataVtkMapper3D(); virtual ~Geometry2DDataVtkMapper3D(); virtual void GenerateDataForRenderer(BaseRenderer* renderer); void ProcessNode( DataNode * node, BaseRenderer* renderer, Surface * surface, LayerSortedActorList &layerSortedActors ); void ImageMapperDeletedCallback( itk::Object *caller, const itk::EventObject &event ); /** \brief general PropAssembly to hold the entire scene */ vtkAssembly *m_Prop3DAssembly; /** \brief PropAssembly to hold the planes */ vtkAssembly *m_ImageAssembly; Geometry2DDataToSurfaceFilter::Pointer m_SurfaceCreator; BoundingBox::Pointer m_SurfaceCreatorBoundingBox; BoundingBox::PointsContainer::Pointer m_SurfaceCreatorPointsContainer; /** \brief Edge extractor for tube-shaped frame */ vtkFeatureEdges *m_Edges; /** \brief Filter to apply object transform to the extracted edges */ vtkTransformPolyDataFilter *m_EdgeTransformer; /** \brief Source to create the tube-shaped frame */ vtkTubeFilter *m_EdgeTuber; /** \brief Mapper for the tube-shaped frame */ vtkPolyDataMapper *m_EdgeMapper; /** \brief Actor for the tube-shaped frame */ vtkActor *m_EdgeActor; /** \brief Mapper for black plane background */ vtkPolyDataMapper *m_BackgroundMapper; /** \brief Actor for black plane background */ vtkActor *m_BackgroundActor; /** \brief Transforms the suface before applying the glyph filter */ vtkTransformPolyDataFilter* m_NormalsTransformer; /** \brief Mapper for normals representation (thin lines) */ vtkPolyDataMapper* m_FrontNormalsMapper; vtkPolyDataMapper* m_BackNormalsMapper; /** \brief Generates lines for surface normals */ vtkHedgeHog* m_FrontHedgeHog; vtkHedgeHog* m_BackHedgeHog; /** \brief Actor to hold the normals arrows */ vtkActor* m_FrontNormalsActor; vtkActor* m_BackNormalsActor; /** Cleans the polyline in order to avoid phantom boundaries */ vtkCleanPolyData *m_Cleaner; /** Internal flag, if actors for normals are already added to m_Prop3DAssembly*/ bool m_NormalsActorAdded; /** \brief The DataStorage defines which part of the data tree is traversed for renderering. */ mitk::WeakPointer m_DataStorage; class MITK_CORE_EXPORT ActorInfo { public: vtkActor * m_Actor; // we do not need a smart-pointer, because we delete our // connection, when the referenced mapper is destroyed itk::Object* m_Sender; unsigned long m_ObserverID; void Initialize(vtkActor* actor, itk::Object* sender, itk::Command* command); ActorInfo(); ~ActorInfo(); }; /** \brief List holding the vtkActor to map the image into 3D for each * ImageMapper */ typedef std::map< ImageVtkMapper2D *, ActorInfo > ActorList; ActorList m_ImageActors; // responsiblity to remove the observer upon its destruction typedef itk::MemberCommand< Geometry2DDataVtkMapper3D > MemberCommandType; MemberCommandType::Pointer m_ImageMapperDeletedCommand; }; } // namespace mitk #endif /* MITKGEOMETRY2DDATAVTKMAPPER3D_H_HEADER_INCLUDED_C196C71F */ diff --git a/Core/Code/Rendering/mitkImageVtkMapper2D.cpp b/Core/Code/Rendering/mitkImageVtkMapper2D.cpp index b66f25408f..61a84c3b10 100644 --- a/Core/Code/Rendering/mitkImageVtkMapper2D.cpp +++ b/Core/Code/Rendering/mitkImageVtkMapper2D.cpp @@ -1,1099 +1,1099 @@ /*=================================================================== 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. ===================================================================*/ //MITK #include #include #include #include #include #include #include #include #include #include #include //#include #include #include "mitkImageStatisticsHolder.h" #include "mitkPlaneClipping.h" //MITK Rendering #include "mitkImageVtkMapper2D.h" #include "vtkMitkThickSlicesFilter.h" #include "vtkMitkLevelWindowFilter.h" #include "vtkNeverTranslucentTexture.h" //VTK #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include //ITK #include #include mitk::ImageVtkMapper2D::ImageVtkMapper2D() { } mitk::ImageVtkMapper2D::~ImageVtkMapper2D() { //The 3D RW Mapper (Geometry2DDataVtkMapper3D) is listening to this event, //in order to delete the images from the 3D RW. this->InvokeEvent( itk::DeleteEvent() ); } //set the two points defining the textured plane according to the dimension and spacing void mitk::ImageVtkMapper2D::GeneratePlane(mitk::BaseRenderer* renderer, double planeBounds[6]) { LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); float depth = this->CalculateLayerDepth(renderer); //Set the origin to (xMin; yMin; depth) of the plane. This is necessary for obtaining the correct //plane size in crosshair rotation and swivel mode. localStorage->m_Plane->SetOrigin(planeBounds[0], planeBounds[2], depth); //These two points define the axes of the plane in combination with the origin. //Point 1 is the x-axis and point 2 the y-axis. //Each plane is transformed according to the view (axial, coronal and saggital) afterwards. localStorage->m_Plane->SetPoint1(planeBounds[1] , planeBounds[2], depth); //P1: (xMax, yMin, depth) localStorage->m_Plane->SetPoint2(planeBounds[0], planeBounds[3], depth); //P2: (xMin, yMax, depth) } float mitk::ImageVtkMapper2D::CalculateLayerDepth(mitk::BaseRenderer* renderer) { //get the clipping range to check how deep into z direction we can render images double maxRange = renderer->GetVtkRenderer()->GetActiveCamera()->GetClippingRange()[1]; //Due to a VTK bug, we cannot use the whole clipping range. /100 is empirically determined float depth = -maxRange*0.01; // divide by 100 int layer = 0; GetDataNode()->GetIntProperty( "layer", layer, renderer); //add the layer property for each image to render images with a higher layer on top of the others depth += layer*10; //*10: keep some room for each image (e.g. for QBalls in between) if(depth > 0.0f) { depth = 0.0f; MITK_WARN << "Layer value exceeds clipping range. Set to minimum instead."; } return depth; } const mitk::Image* mitk::ImageVtkMapper2D::GetInput( void ) { return static_cast< const mitk::Image * >( GetDataNode()->GetData() ); } vtkProp* mitk::ImageVtkMapper2D::GetVtkProp(mitk::BaseRenderer* renderer) { //return the actor corresponding to the renderer return m_LSH.GetLocalStorage(renderer)->m_Actors; } void mitk::ImageVtkMapper2D::GenerateDataForRenderer( mitk::BaseRenderer *renderer ) { LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); mitk::Image *input = const_cast< mitk::Image * >( this->GetInput() ); mitk::DataNode* datanode = this->GetDataNode(); if ( input == NULL || input->IsInitialized() == false ) { return; } //check if there is a valid worldGeometry - const Geometry2D *worldGeometry = renderer->GetCurrentWorldGeometry2D(); + const PlaneGeometry *worldGeometry = renderer->GetCurrentWorldGeometry2D(); if( ( worldGeometry == NULL ) || ( !worldGeometry->IsValid() ) || ( !worldGeometry->HasReferenceGeometry() )) { return; } input->Update(); // early out if there is no intersection of the current rendering geometry // and the geometry of the image that is to be rendered. if ( !RenderingGeometryIntersectsImage( worldGeometry, input->GetSlicedGeometry() ) ) { // set image to NULL, to clear the texture in 3D, because // the latest image is used there if the plane is out of the geometry // see bug-13275 localStorage->m_ReslicedImage = NULL; localStorage->m_Mapper->SetInputData( localStorage->m_EmptyPolyData ); return; } //set main input for ExtractSliceFilter localStorage->m_Reslicer->SetInput(input); localStorage->m_Reslicer->SetWorldGeometry(worldGeometry); localStorage->m_Reslicer->SetTimeStep( this->GetTimestep() ); //set the transformation of the image to adapt reslice axis localStorage->m_Reslicer->SetResliceTransformByGeometry( input->GetTimeGeometry()->GetGeometryForTimeStep( this->GetTimestep() ) ); //is the geometry of the slice based on the input image or the worldgeometry? bool inPlaneResampleExtentByGeometry = false; datanode->GetBoolProperty("in plane resample extent by geometry", inPlaneResampleExtentByGeometry, renderer); localStorage->m_Reslicer->SetInPlaneResampleExtentByGeometry(inPlaneResampleExtentByGeometry); // Initialize the interpolation mode for resampling; switch to nearest // neighbor if the input image is too small. if ( (input->GetDimension() >= 3) && (input->GetDimension(2) > 1) ) { VtkResliceInterpolationProperty *resliceInterpolationProperty; datanode->GetProperty( resliceInterpolationProperty, "reslice interpolation" ); int interpolationMode = VTK_RESLICE_NEAREST; if ( resliceInterpolationProperty != NULL ) { interpolationMode = resliceInterpolationProperty->GetInterpolation(); } switch ( interpolationMode ) { case VTK_RESLICE_NEAREST: localStorage->m_Reslicer->SetInterpolationMode(ExtractSliceFilter::RESLICE_NEAREST); break; case VTK_RESLICE_LINEAR: localStorage->m_Reslicer->SetInterpolationMode(ExtractSliceFilter::RESLICE_LINEAR); break; case VTK_RESLICE_CUBIC: localStorage->m_Reslicer->SetInterpolationMode(ExtractSliceFilter::RESLICE_CUBIC); break; } } else { localStorage->m_Reslicer->SetInterpolationMode(ExtractSliceFilter::RESLICE_NEAREST); } //set the vtk output property to true, makes sure that no unneeded mitk image convertion //is done. localStorage->m_Reslicer->SetVtkOutputRequest(true); //Thickslicing int thickSlicesMode = 0; int thickSlicesNum = 1; // Thick slices parameters if( input->GetPixelType().GetNumberOfComponents() == 1 ) // for now only single component are allowed { DataNode *dn=renderer->GetCurrentWorldGeometry2DNode(); if(dn) { ResliceMethodProperty *resliceMethodEnumProperty=0; if( dn->GetProperty( resliceMethodEnumProperty, "reslice.thickslices" ) && resliceMethodEnumProperty ) thickSlicesMode = resliceMethodEnumProperty->GetValueAsId(); IntProperty *intProperty=0; if( dn->GetProperty( intProperty, "reslice.thickslices.num" ) && intProperty ) { thickSlicesNum = intProperty->GetValue(); if(thickSlicesNum < 1) thickSlicesNum=1; if(thickSlicesNum > 10) thickSlicesNum=10; } } else { MITK_WARN << "no associated widget plane data tree node found"; } } const PlaneGeometry *planeGeometry = dynamic_cast< const PlaneGeometry * >( worldGeometry ); if(thickSlicesMode > 0) { double dataZSpacing = 1.0; Vector3D normInIndex, normal; if ( planeGeometry != NULL ){ normal = planeGeometry->GetNormal(); }else{ const mitk::AbstractTransformGeometry* abstractGeometry = dynamic_cast< const AbstractTransformGeometry * >(worldGeometry); if(abstractGeometry != NULL) normal = abstractGeometry->GetPlane()->GetNormal(); else return; //no fitting geometry set } normal.Normalize(); input->GetTimeGeometry()->GetGeometryForTimeStep( this->GetTimestep() )->WorldToIndex( normal, normInIndex ); dataZSpacing = 1.0 / normInIndex.GetNorm(); localStorage->m_Reslicer->SetOutputDimensionality( 3 ); localStorage->m_Reslicer->SetOutputSpacingZDirection(dataZSpacing); localStorage->m_Reslicer->SetOutputExtentZDirection( -thickSlicesNum, 0+thickSlicesNum ); // Do the reslicing. Modified() is called to make sure that the reslicer is // executed even though the input geometry information did not change; this // is necessary when the input /em data, but not the /em geometry changes. localStorage->m_TSFilter->SetThickSliceMode( thickSlicesMode-1 ); localStorage->m_TSFilter->SetInputData( localStorage->m_Reslicer->GetVtkOutput() ); //vtkFilter=>mitkFilter=>vtkFilter update mechanism will fail without calling manually localStorage->m_Reslicer->Modified(); localStorage->m_Reslicer->Update(); localStorage->m_TSFilter->Modified(); localStorage->m_TSFilter->Update(); localStorage->m_ReslicedImage = localStorage->m_TSFilter->GetOutput(); } else { //this is needed when thick mode was enable bevore. These variable have to be reset to default values localStorage->m_Reslicer->SetOutputDimensionality( 2 ); localStorage->m_Reslicer->SetOutputSpacingZDirection(1.0); localStorage->m_Reslicer->SetOutputExtentZDirection( 0, 0 ); localStorage->m_Reslicer->Modified(); //start the pipeline with updating the largest possible, needed if the geometry of the input has changed localStorage->m_Reslicer->UpdateLargestPossibleRegion(); localStorage->m_ReslicedImage = localStorage->m_Reslicer->GetVtkOutput(); } // Bounds information for reslicing (only reuqired if reference geometry // is present) //this used for generating a vtkPLaneSource with the right size double sliceBounds[6]; for ( int i = 0; i < 6; ++i ) { sliceBounds[i] = 0.0; } localStorage->m_Reslicer->GetClippedPlaneBounds(sliceBounds); //get the spacing of the slice localStorage->m_mmPerPixel = localStorage->m_Reslicer->GetOutputSpacing(); // calculate minimum bounding rect of IMAGE in texture { double textureClippingBounds[6]; for ( int i = 0; i < 6; ++i ) { textureClippingBounds[i] = 0.0; } // Calculate the actual bounds of the transformed plane clipped by the // dataset bounding box; this is required for drawing the texture at the // correct position during 3D mapping. mitk::PlaneClipping::CalculateClippedPlaneBounds( input->GetGeometry(), planeGeometry, textureClippingBounds ); textureClippingBounds[0] = static_cast< int >( textureClippingBounds[0] / localStorage->m_mmPerPixel[0] + 0.5 ); textureClippingBounds[1] = static_cast< int >( textureClippingBounds[1] / localStorage->m_mmPerPixel[0] + 0.5 ); textureClippingBounds[2] = static_cast< int >( textureClippingBounds[2] / localStorage->m_mmPerPixel[1] + 0.5 ); textureClippingBounds[3] = static_cast< int >( textureClippingBounds[3] / localStorage->m_mmPerPixel[1] + 0.5 ); //clipping bounds for cutting the image localStorage->m_LevelWindowFilter->SetClippingBounds(textureClippingBounds); } //get the number of scalar components to distinguish between different image types int numberOfComponents = localStorage->m_ReslicedImage->GetNumberOfScalarComponents(); //get the binary property bool binary = false; bool binaryOutline = false; datanode->GetBoolProperty( "binary", binary, renderer ); if(binary) //binary image { datanode->GetBoolProperty( "outline binary", binaryOutline, renderer ); if(binaryOutline) //contour rendering { if ( input->GetPixelType().GetBpe() <= 8 ) { //generate contours/outlines localStorage->m_OutlinePolyData = CreateOutlinePolyData(renderer); float binaryOutlineWidth(1.0); if ( datanode->GetFloatProperty( "outline width", binaryOutlineWidth, renderer ) ) { if ( localStorage->m_Actors->GetNumberOfPaths() > 1 ) { float binaryOutlineShadowWidth(1.5); datanode->GetFloatProperty( "outline shadow width", binaryOutlineShadowWidth, renderer ); dynamic_cast(localStorage->m_Actors->GetParts()->GetItemAsObject(0)) ->GetProperty()->SetLineWidth( binaryOutlineWidth * binaryOutlineShadowWidth ); } localStorage->m_Actor->GetProperty()->SetLineWidth( binaryOutlineWidth ); } } else { binaryOutline = false; this->ApplyLookuptable(renderer); MITK_WARN << "Type of all binary images should be (un)signed char. Outline does not work on other pixel types!"; } } else //standard binary image { if(numberOfComponents != 1) { MITK_ERROR << "Rendering Error: Binary Images with more then 1 component are not supported!"; } } } this->ApplyOpacity( renderer ); this->ApplyRenderingMode(renderer); // do not use a VTK lookup table (we do that ourselves in m_LevelWindowFilter) localStorage->m_Texture->MapColorScalarsThroughLookupTableOff(); int displayedComponent = 0; if (datanode->GetIntProperty("Image.Displayed Component", displayedComponent, renderer) && numberOfComponents > 1) { localStorage->m_VectorComponentExtractor->SetComponents(displayedComponent); localStorage->m_VectorComponentExtractor->SetInputData(localStorage->m_ReslicedImage); localStorage->m_LevelWindowFilter->SetInputConnection(localStorage->m_VectorComponentExtractor->GetOutputPort(0)); } else { //connect the input with the levelwindow filter localStorage->m_LevelWindowFilter->SetInputData(localStorage->m_ReslicedImage); } // check for texture interpolation property bool textureInterpolation = false; GetDataNode()->GetBoolProperty( "texture interpolation", textureInterpolation, renderer ); //set the interpolation modus according to the property localStorage->m_Texture->SetInterpolate(textureInterpolation); // connect the texture with the output of the levelwindow filter localStorage->m_Texture->SetInputConnection(localStorage->m_LevelWindowFilter->GetOutputPort()); this->TransformActor( renderer ); vtkActor* contourShadowActor = dynamic_cast (localStorage->m_Actors->GetParts()->GetItemAsObject(0)); if(binary && binaryOutline) //connect the mapper with the polyData which contains the lines { //We need the contour for the binary outline property as actor localStorage->m_Mapper->SetInputData(localStorage->m_OutlinePolyData); localStorage->m_Actor->SetTexture(NULL); //no texture for contours bool binaryOutlineShadow( false ); datanode->GetBoolProperty( "outline binary shadow", binaryOutlineShadow, renderer ); if ( binaryOutlineShadow ) contourShadowActor->SetVisibility( true ); else contourShadowActor->SetVisibility( false ); } else { //Connect the mapper with the input texture. This is the standard case. //setup the textured plane this->GeneratePlane( renderer, sliceBounds ); //set the plane as input for the mapper localStorage->m_Mapper->SetInputConnection(localStorage->m_Plane->GetOutputPort()); //set the texture for the actor localStorage->m_Actor->SetTexture(localStorage->m_Texture); contourShadowActor->SetVisibility( false ); } // We have been modified => save this for next Update() localStorage->m_LastUpdateTime.Modified(); } void mitk::ImageVtkMapper2D::ApplyLevelWindow(mitk::BaseRenderer *renderer) { LocalStorage *localStorage = this->GetLocalStorage( renderer ); LevelWindow levelWindow; this->GetDataNode()->GetLevelWindow( levelWindow, renderer, "levelwindow" ); localStorage->m_LevelWindowFilter->GetLookupTable()->SetRange( levelWindow.GetLowerWindowBound(), levelWindow.GetUpperWindowBound() ); mitk::LevelWindow opacLevelWindow; if( this->GetDataNode()->GetLevelWindow( opacLevelWindow, renderer, "opaclevelwindow" ) ) { //pass the opaque level window to the filter localStorage->m_LevelWindowFilter->SetMinOpacity(opacLevelWindow.GetLowerWindowBound()); localStorage->m_LevelWindowFilter->SetMaxOpacity(opacLevelWindow.GetUpperWindowBound()); } else { //no opaque level window localStorage->m_LevelWindowFilter->SetMinOpacity(0.0); localStorage->m_LevelWindowFilter->SetMaxOpacity(255.0); } } void mitk::ImageVtkMapper2D::ApplyColor( mitk::BaseRenderer* renderer ) { LocalStorage *localStorage = this->GetLocalStorage( renderer ); float rgb[3]= { 1.0f, 1.0f, 1.0f }; // check for color prop and use it for rendering if it exists // binary image hovering & binary image selection bool hover = false; bool selected = false; GetDataNode()->GetBoolProperty("binaryimage.ishovering", hover, renderer); GetDataNode()->GetBoolProperty("selected", selected, renderer); if(hover && !selected) { mitk::ColorProperty::Pointer colorprop = dynamic_cast(GetDataNode()->GetProperty ("binaryimage.hoveringcolor", renderer)); if(colorprop.IsNotNull()) { memcpy(rgb, colorprop->GetColor().GetDataPointer(), 3*sizeof(float)); } else { GetDataNode()->GetColor( rgb, renderer, "color" ); } } if(selected) { mitk::ColorProperty::Pointer colorprop = dynamic_cast(GetDataNode()->GetProperty ("binaryimage.selectedcolor", renderer)); if(colorprop.IsNotNull()) { memcpy(rgb, colorprop->GetColor().GetDataPointer(), 3*sizeof(float)); } else { GetDataNode()->GetColor(rgb, renderer, "color"); } } if(!hover && !selected) { GetDataNode()->GetColor( rgb, renderer, "color" ); } double rgbConv[3] = {(double)rgb[0], (double)rgb[1], (double)rgb[2]}; //conversion to double for VTK dynamic_cast (localStorage->m_Actors->GetParts()->GetItemAsObject(0))->GetProperty()->SetColor(rgbConv); localStorage->m_Actor->GetProperty()->SetColor(rgbConv); if ( localStorage->m_Actors->GetParts()->GetNumberOfItems() > 1 ) { float rgb[3]= { 1.0f, 1.0f, 1.0f }; mitk::ColorProperty::Pointer colorprop = dynamic_cast(GetDataNode()->GetProperty ("outline binary shadow color", renderer)); if(colorprop.IsNotNull()) { memcpy(rgb, colorprop->GetColor().GetDataPointer(), 3*sizeof(float)); } double rgbConv[3] = {(double)rgb[0], (double)rgb[1], (double)rgb[2]}; //conversion to double for VTK dynamic_cast( localStorage->m_Actors->GetParts()->GetItemAsObject(0) )->GetProperty()->SetColor(rgbConv); } } void mitk::ImageVtkMapper2D::ApplyOpacity( mitk::BaseRenderer* renderer ) { LocalStorage* localStorage = this->GetLocalStorage( renderer ); float opacity = 1.0f; // check for opacity prop and use it for rendering if it exists GetDataNode()->GetOpacity( opacity, renderer, "opacity" ); //set the opacity according to the properties localStorage->m_Actor->GetProperty()->SetOpacity(opacity); if ( localStorage->m_Actors->GetParts()->GetNumberOfItems() > 1 ) { dynamic_cast( localStorage->m_Actors->GetParts()->GetItemAsObject(0) )->GetProperty()->SetOpacity(opacity); } } void mitk::ImageVtkMapper2D::ApplyRenderingMode( mitk::BaseRenderer* renderer ) { LocalStorage* localStorage = m_LSH.GetLocalStorage(renderer); bool binary = false; this->GetDataNode()->GetBoolProperty( "binary", binary, renderer ); if(binary) // is it a binary image? { //for binary images, we always use our default LuT and map every value to (0,1) //the opacity of 0 will always be 0.0. We never a apply a LuT/TfF nor a level window. localStorage->m_LevelWindowFilter->SetLookupTable(localStorage->m_BinaryLookupTable); } else { //all other image types can make use of the rendering mode int renderingMode = mitk::RenderingModeProperty::LEVELWINDOW_COLOR; mitk::RenderingModeProperty::Pointer mode = dynamic_cast(this->GetDataNode()->GetProperty( "Image Rendering.Mode", renderer )); if(mode.IsNotNull()) { renderingMode = mode->GetRenderingMode(); } switch(renderingMode) { case mitk::RenderingModeProperty::LEVELWINDOW_COLOR: MITK_DEBUG << "'Image Rendering.Mode' = LevelWindow_Color"; localStorage->m_LevelWindowFilter->SetLookupTable( localStorage->m_DefaultLookupTable ); this->ApplyLevelWindow( renderer ); break; case mitk::RenderingModeProperty::LOOKUPTABLE_LEVELWINDOW_COLOR: MITK_DEBUG << "'Image Rendering.Mode' = LevelWindow_LookupTable_Color"; this->ApplyLookuptable( renderer ); this->ApplyLevelWindow( renderer ); break; case mitk::RenderingModeProperty::COLORTRANSFERFUNCTION_LEVELWINDOW_COLOR: MITK_DEBUG << "'Image Rendering.Mode' = LevelWindow_ColorTransferFunction_Color"; this->ApplyColorTransferFunction( renderer ); this->ApplyLevelWindow( renderer ); break; case mitk::RenderingModeProperty::LOOKUPTABLE_COLOR: MITK_DEBUG << "'Image Rendering.Mode' = LookupTable_Color"; this->ApplyLookuptable( renderer ); break; case mitk::RenderingModeProperty::COLORTRANSFERFUNCTION_COLOR: MITK_DEBUG << "'Image Rendering.Mode' = ColorTransferFunction_Color"; this->ApplyColorTransferFunction( renderer ); break; default: MITK_ERROR << "No valid 'Image Rendering.Mode' set"; break; } } //we apply color for all images (including binaries). this->ApplyColor( renderer ); } void mitk::ImageVtkMapper2D::ApplyLookuptable( mitk::BaseRenderer* renderer ) { LocalStorage* localStorage = m_LSH.GetLocalStorage(renderer); vtkLookupTable* usedLookupTable = localStorage->m_ColorLookupTable; // If lookup table or transferfunction use is requested... mitk::LookupTableProperty::Pointer lookupTableProp = dynamic_cast(this->GetDataNode()->GetProperty("LookupTable")); if( lookupTableProp.IsNotNull() ) // is a lookuptable set? { usedLookupTable = lookupTableProp->GetLookupTable()->GetVtkLookupTable(); } else { //"Image Rendering.Mode was set to use a lookup table but there is no property 'LookupTable'. //A default (rainbow) lookup table will be used. //Here have to do nothing. Warning for the user has been removed, due to unwanted console output //in every interation of the rendering. } localStorage->m_LevelWindowFilter->SetLookupTable(usedLookupTable); } void mitk::ImageVtkMapper2D::ApplyColorTransferFunction(mitk::BaseRenderer *renderer) { mitk::TransferFunctionProperty::Pointer transferFunctionProp = dynamic_cast(this->GetDataNode()->GetProperty("Image Rendering.Transfer Function",renderer )); if( transferFunctionProp.IsNull() ) { MITK_ERROR << "'Image Rendering.Mode'' was set to use a color transfer function but there is no property 'Image Rendering.Transfer Function'. Nothing will be done."; return; } LocalStorage* localStorage = m_LSH.GetLocalStorage(renderer); //pass the transfer function to our level window filter localStorage->m_LevelWindowFilter->SetLookupTable(transferFunctionProp->GetValue()->GetColorTransferFunction()); } void mitk::ImageVtkMapper2D::Update(mitk::BaseRenderer* renderer) { bool visible = true; GetDataNode()->GetVisibility(visible, renderer, "visible"); if ( !visible ) { return; } mitk::Image* data = const_cast( this->GetInput() ); if ( data == NULL ) { return; } // Calculate time step of the input data for the specified renderer (integer value) this->CalculateTimeStep( renderer ); // Check if time step is valid const TimeGeometry *dataTimeGeometry = data->GetTimeGeometry(); if ( ( dataTimeGeometry == NULL ) || ( dataTimeGeometry->CountTimeSteps() == 0 ) || ( !dataTimeGeometry->IsValidTimeStep( this->GetTimestep() ) ) ) { return; } const DataNode *node = this->GetDataNode(); data->UpdateOutputInformation(); LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); //check if something important has changed and we need to rerender if ( (localStorage->m_LastUpdateTime < node->GetMTime()) //was the node modified? || (localStorage->m_LastUpdateTime < data->GetPipelineMTime()) //Was the data modified? || (localStorage->m_LastUpdateTime < renderer->GetCurrentWorldGeometry2DUpdateTime()) //was the geometry modified? || (localStorage->m_LastUpdateTime < renderer->GetCurrentWorldGeometry2D()->GetMTime()) || (localStorage->m_LastUpdateTime < node->GetPropertyList()->GetMTime()) //was a property modified? || (localStorage->m_LastUpdateTime < node->GetPropertyList(renderer)->GetMTime()) ) { this->GenerateDataForRenderer( renderer ); } // since we have checked that nothing important has changed, we can set // m_LastUpdateTime to the current time localStorage->m_LastUpdateTime.Modified(); } void mitk::ImageVtkMapper2D::SetDefaultProperties(mitk::DataNode* node, mitk::BaseRenderer* renderer, bool overwrite) { mitk::Image::Pointer image = dynamic_cast(node->GetData()); // Properties common for both images and segmentations node->AddProperty( "depthOffset", mitk::FloatProperty::New( 0.0 ), renderer, overwrite ); node->AddProperty( "outline binary", mitk::BoolProperty::New( false ), renderer, overwrite ); node->AddProperty( "outline width", mitk::FloatProperty::New( 1.0 ), renderer, overwrite ); node->AddProperty( "outline binary shadow", mitk::BoolProperty::New( false ), renderer, overwrite ); node->AddProperty( "outline binary shadow color", ColorProperty::New(0.0,0.0,0.0), renderer, overwrite ); node->AddProperty( "outline shadow width", mitk::FloatProperty::New( 1.5 ), renderer, overwrite ); if(image->IsRotated()) node->AddProperty( "reslice interpolation", mitk::VtkResliceInterpolationProperty::New(VTK_RESLICE_CUBIC) ); else node->AddProperty( "reslice interpolation", mitk::VtkResliceInterpolationProperty::New() ); node->AddProperty( "texture interpolation", mitk::BoolProperty::New( mitk::DataNodeFactory::m_TextureInterpolationActive ) ); // set to user configurable default value (see global options) node->AddProperty( "in plane resample extent by geometry", mitk::BoolProperty::New( false ) ); node->AddProperty( "bounding box", mitk::BoolProperty::New( false ) ); mitk::RenderingModeProperty::Pointer renderingModeProperty = mitk::RenderingModeProperty::New(); node->AddProperty( "Image Rendering.Mode", renderingModeProperty); std::string photometricInterpretation; // DICOM tag telling us how pixel values should be displayed if ( node->GetStringProperty( "dicom.pixel.PhotometricInterpretation", photometricInterpretation ) ) { // modality provided by DICOM or other reader if ( photometricInterpretation.find("MONOCHROME1") != std::string::npos ) // meaning: display MINIMUM pixels as WHITE { // generate LUT (white to black) mitk::LookupTable::Pointer mitkLut = mitk::LookupTable::New(); vtkLookupTable* bwLut = mitkLut->GetVtkLookupTable(); bwLut->SetTableRange (0, 1); bwLut->SetSaturationRange (0, 0); bwLut->SetHueRange (0, 0); bwLut->SetValueRange (1, 0); bwLut->SetAlphaRange (1, 1); bwLut->SetRampToLinear(); bwLut->Build(); mitk::LookupTableProperty::Pointer mitkLutProp = mitk::LookupTableProperty::New(); mitkLutProp->SetLookupTable(mitkLut); node->SetProperty( "LookupTable", mitkLutProp ); } else if ( photometricInterpretation.find("MONOCHROME2") != std::string::npos ) // meaning: display MINIMUM pixels as BLACK { // apply default LUT (black to white) node->SetProperty( "color", mitk::ColorProperty::New( 1,1,1 ), renderer ); } // PALETTE interpretation should be handled ok by RGB loading } bool isBinaryImage(false); if ( ! node->GetBoolProperty("binary", isBinaryImage) ) { // ok, property is not set, use heuristic to determine if this // is a binary image mitk::Image::Pointer centralSliceImage; ScalarType minValue = 0.0; ScalarType maxValue = 0.0; ScalarType min2ndValue = 0.0; ScalarType max2ndValue = 0.0; mitk::ImageSliceSelector::Pointer sliceSelector = mitk::ImageSliceSelector::New(); sliceSelector->SetInput(image); sliceSelector->SetSliceNr(image->GetDimension(2)/2); sliceSelector->SetTimeNr(image->GetDimension(3)/2); sliceSelector->SetChannelNr(image->GetDimension(4)/2); sliceSelector->Update(); centralSliceImage = sliceSelector->GetOutput(); if ( centralSliceImage.IsNotNull() && centralSliceImage->IsInitialized() ) { minValue = centralSliceImage->GetStatistics()->GetScalarValueMin(); maxValue = centralSliceImage->GetStatistics()->GetScalarValueMax(); min2ndValue = centralSliceImage->GetStatistics()->GetScalarValue2ndMin(); max2ndValue = centralSliceImage->GetStatistics()->GetScalarValue2ndMax(); } if ((maxValue == min2ndValue && minValue == max2ndValue) || minValue == maxValue) { // centralSlice is strange, lets look at all data minValue = image->GetStatistics()->GetScalarValueMin(); maxValue = image->GetStatistics()->GetScalarValueMaxNoRecompute(); min2ndValue = image->GetStatistics()->GetScalarValue2ndMinNoRecompute(); max2ndValue = image->GetStatistics()->GetScalarValue2ndMaxNoRecompute(); } isBinaryImage = ( maxValue == min2ndValue && minValue == max2ndValue ); } // some more properties specific for a binary... if (isBinaryImage) { node->AddProperty( "opacity", mitk::FloatProperty::New(0.3f), renderer, overwrite ); node->AddProperty( "color", ColorProperty::New(1.0,0.0,0.0), renderer, overwrite ); node->AddProperty( "binaryimage.selectedcolor", ColorProperty::New(1.0,0.0,0.0), renderer, overwrite ); node->AddProperty( "binaryimage.selectedannotationcolor", ColorProperty::New(1.0,0.0,0.0), renderer, overwrite ); node->AddProperty( "binaryimage.hoveringcolor", ColorProperty::New(1.0,0.0,0.0), renderer, overwrite ); node->AddProperty( "binaryimage.hoveringannotationcolor", ColorProperty::New(1.0,0.0,0.0), renderer, overwrite ); node->AddProperty( "binary", mitk::BoolProperty::New( true ), renderer, overwrite ); node->AddProperty("layer", mitk::IntProperty::New(10), renderer, overwrite); } else //...or image type object { node->AddProperty( "opacity", mitk::FloatProperty::New(1.0f), renderer, overwrite ); node->AddProperty( "color", ColorProperty::New(1.0,1.0,1.0), renderer, overwrite ); node->AddProperty( "binary", mitk::BoolProperty::New( false ), renderer, overwrite ); node->AddProperty("layer", mitk::IntProperty::New(0), renderer, overwrite); std::string className = image->GetNameOfClass(); if (className != "TensorImage" && className != "QBallImage") { PixelType pixelType = image->GetPixelType(); size_t numComponents = pixelType.GetNumberOfComponents(); if ((pixelType.GetPixelTypeAsString() == "vector" && numComponents > 1) || numComponents == 2 || numComponents > 4) node->AddProperty("Image.Displayed Component", mitk::IntProperty::New(0), renderer, overwrite); } } if(image.IsNotNull() && image->IsInitialized()) { if((overwrite) || (node->GetProperty("levelwindow", renderer)==NULL)) { /* initialize level/window from DICOM tags */ std::string sLevel; std::string sWindow; if ( image->GetPropertyList()->GetStringProperty( "dicom.voilut.WindowCenter", sLevel ) && image->GetPropertyList()->GetStringProperty( "dicom.voilut.WindowWidth", sWindow ) ) { float level = atof( sLevel.c_str() ); float window = atof( sWindow.c_str() ); mitk::LevelWindow contrast; std::string sSmallestPixelValueInSeries; std::string sLargestPixelValueInSeries; if ( image->GetPropertyList()->GetStringProperty( "dicom.series.SmallestPixelValueInSeries", sSmallestPixelValueInSeries ) && image->GetPropertyList()->GetStringProperty( "dicom.series.LargestPixelValueInSeries", sLargestPixelValueInSeries ) ) { float smallestPixelValueInSeries = atof( sSmallestPixelValueInSeries.c_str() ); float largestPixelValueInSeries = atof( sLargestPixelValueInSeries.c_str() ); contrast.SetRangeMinMax( smallestPixelValueInSeries-1, largestPixelValueInSeries+1 ); // why not a little buffer? // might remedy some l/w widget challenges } else { contrast.SetAuto( static_cast(node->GetData()), false, true ); // we need this as a fallback } contrast.SetLevelWindow( level, window, true ); node->SetProperty( "levelwindow", LevelWindowProperty::New( contrast ), renderer ); } } if(((overwrite) || (node->GetProperty("opaclevelwindow", renderer)==NULL)) && (image->GetPixelType().GetPixelType() == itk::ImageIOBase::RGBA) && (image->GetPixelType().GetComponentType() == itk::ImageIOBase::UCHAR) ) { mitk::LevelWindow opaclevwin; opaclevwin.SetRangeMinMax(0,255); opaclevwin.SetWindowBounds(0,255); mitk::LevelWindowProperty::Pointer prop = mitk::LevelWindowProperty::New(opaclevwin); node->SetProperty( "opaclevelwindow", prop, renderer ); } } Superclass::SetDefaultProperties(node, renderer, overwrite); } mitk::ImageVtkMapper2D::LocalStorage* mitk::ImageVtkMapper2D::GetLocalStorage(mitk::BaseRenderer* renderer) { return m_LSH.GetLocalStorage(renderer); } vtkSmartPointer mitk::ImageVtkMapper2D::CreateOutlinePolyData(mitk::BaseRenderer* renderer ){ LocalStorage* localStorage = this->GetLocalStorage(renderer); //get the min and max index values of each direction int* extent = localStorage->m_ReslicedImage->GetExtent(); int xMin = extent[0]; int xMax = extent[1]; int yMin = extent[2]; int yMax = extent[3]; int* dims = localStorage->m_ReslicedImage->GetDimensions(); //dimensions of the image int line = dims[0]; //how many pixels per line? int x = xMin; //pixel index x int y = yMin; //pixel index y char* currentPixel; //get the depth for each contour float depth = CalculateLayerDepth(renderer); vtkSmartPointer points = vtkSmartPointer::New(); //the points to draw vtkSmartPointer lines = vtkSmartPointer::New(); //the lines to connect the points // We take the pointer to the first pixel of the image currentPixel = static_cast(localStorage->m_ReslicedImage->GetScalarPointer() ); while (y <= yMax) { //if the current pixel value is set to something if ((currentPixel) && (*currentPixel != 0)) { //check in which direction a line is necessary //a line is added if the neighbor of the current pixel has the value 0 //and if the pixel is located at the edge of the image //if vvvvv not the first line vvvvv if (y > yMin && *(currentPixel-line) == 0) { //x direction - bottom edge of the pixel //add the 2 points vtkIdType p1 = points->InsertNextPoint(x*localStorage->m_mmPerPixel[0], y*localStorage->m_mmPerPixel[1], depth); vtkIdType p2 = points->InsertNextPoint((x+1)*localStorage->m_mmPerPixel[0], y*localStorage->m_mmPerPixel[1], depth); //add the line between both points lines->InsertNextCell(2); lines->InsertCellPoint(p1); lines->InsertCellPoint(p2); } //if vvvvv not the last line vvvvv if (y < yMax && *(currentPixel+line) == 0) { //x direction - top edge of the pixel vtkIdType p1 = points->InsertNextPoint(x*localStorage->m_mmPerPixel[0], (y+1)*localStorage->m_mmPerPixel[1], depth); vtkIdType p2 = points->InsertNextPoint((x+1)*localStorage->m_mmPerPixel[0], (y+1)*localStorage->m_mmPerPixel[1], depth); lines->InsertNextCell(2); lines->InsertCellPoint(p1); lines->InsertCellPoint(p2); } //if vvvvv not the first pixel vvvvv if ( (x > xMin || y > yMin) && *(currentPixel-1) == 0) { //y direction - left edge of the pixel vtkIdType p1 = points->InsertNextPoint(x*localStorage->m_mmPerPixel[0], y*localStorage->m_mmPerPixel[1], depth); vtkIdType p2 = points->InsertNextPoint(x*localStorage->m_mmPerPixel[0], (y+1)*localStorage->m_mmPerPixel[1], depth); lines->InsertNextCell(2); lines->InsertCellPoint(p1); lines->InsertCellPoint(p2); } //if vvvvv not the last pixel vvvvv if ( (y < yMax || (x < xMax) ) && *(currentPixel+1) == 0) { //y direction - right edge of the pixel vtkIdType p1 = points->InsertNextPoint((x+1)*localStorage->m_mmPerPixel[0], y*localStorage->m_mmPerPixel[1], depth); vtkIdType p2 = points->InsertNextPoint((x+1)*localStorage->m_mmPerPixel[0], (y+1)*localStorage->m_mmPerPixel[1], depth); lines->InsertNextCell(2); lines->InsertCellPoint(p1); lines->InsertCellPoint(p2); } /* now consider pixels at the edge of the image */ //if vvvvv left edge of image vvvvv if (x == xMin) { //draw left edge of the pixel vtkIdType p1 = points->InsertNextPoint(x*localStorage->m_mmPerPixel[0], y*localStorage->m_mmPerPixel[1], depth); vtkIdType p2 = points->InsertNextPoint(x*localStorage->m_mmPerPixel[0], (y+1)*localStorage->m_mmPerPixel[1], depth); lines->InsertNextCell(2); lines->InsertCellPoint(p1); lines->InsertCellPoint(p2); } //if vvvvv right edge of image vvvvv if (x == xMax) { //draw right edge of the pixel vtkIdType p1 = points->InsertNextPoint((x+1)*localStorage->m_mmPerPixel[0], y*localStorage->m_mmPerPixel[1], depth); vtkIdType p2 = points->InsertNextPoint((x+1)*localStorage->m_mmPerPixel[0], (y+1)*localStorage->m_mmPerPixel[1], depth); lines->InsertNextCell(2); lines->InsertCellPoint(p1); lines->InsertCellPoint(p2); } //if vvvvv bottom edge of image vvvvv if (y == yMin) { //draw bottom edge of the pixel vtkIdType p1 = points->InsertNextPoint(x*localStorage->m_mmPerPixel[0], y*localStorage->m_mmPerPixel[1], depth); vtkIdType p2 = points->InsertNextPoint((x+1)*localStorage->m_mmPerPixel[0], y*localStorage->m_mmPerPixel[1], depth); lines->InsertNextCell(2); lines->InsertCellPoint(p1); lines->InsertCellPoint(p2); } //if vvvvv top edge of image vvvvv if (y == yMax) { //draw top edge of the pixel vtkIdType p1 = points->InsertNextPoint(x*localStorage->m_mmPerPixel[0], (y+1)*localStorage->m_mmPerPixel[1], depth); vtkIdType p2 = points->InsertNextPoint((x+1)*localStorage->m_mmPerPixel[0], (y+1)*localStorage->m_mmPerPixel[1], depth); lines->InsertNextCell(2); lines->InsertCellPoint(p1); lines->InsertCellPoint(p2); } }//end if currentpixel is set x++; if (x > xMax) { //reached end of line x = xMin; y++; } // Increase the pointer-position to the next pixel. // This is safe, as the while-loop and the x-reset logic above makes // sure we do not exceed the bounds of the image currentPixel++; }//end of while // Create a polydata to store everything in vtkSmartPointer polyData = vtkSmartPointer::New(); // Add the points to the dataset polyData->SetPoints(points); // Add the lines to the dataset polyData->SetLines(lines); return polyData; } void mitk::ImageVtkMapper2D::TransformActor(mitk::BaseRenderer* renderer) { LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); //get the transformation matrix of the reslicer in order to render the slice as axial, coronal or saggital vtkSmartPointer trans = vtkSmartPointer::New(); vtkSmartPointer matrix = localStorage->m_Reslicer->GetResliceAxes(); trans->SetMatrix(matrix); //transform the plane/contour (the actual actor) to the corresponding view (axial, coronal or saggital) localStorage->m_Actor->SetUserTransform(trans); //transform the origin to center based coordinates, because MITK is center based. localStorage->m_Actor->SetPosition( -0.5*localStorage->m_mmPerPixel[0], -0.5*localStorage->m_mmPerPixel[1], 0.0); if ( localStorage->m_Actors->GetNumberOfPaths() > 1 ) { vtkActor* secondaryActor = dynamic_cast( localStorage->m_Actors->GetParts()->GetItemAsObject(0) ); secondaryActor->SetUserTransform(trans); secondaryActor->SetPosition( -0.5*localStorage->m_mmPerPixel[0], -0.5*localStorage->m_mmPerPixel[1], 0.0); } } -bool mitk::ImageVtkMapper2D::RenderingGeometryIntersectsImage( const Geometry2D* renderingGeometry, SlicedGeometry3D* imageGeometry ) +bool mitk::ImageVtkMapper2D::RenderingGeometryIntersectsImage( const PlaneGeometry* renderingGeometry, SlicedGeometry3D* imageGeometry ) { // if either one of the two geometries is NULL we return true // for safety reasons if ( renderingGeometry == NULL || imageGeometry == NULL ) return true; // get the distance for the first cornerpoint ScalarType initialDistance = renderingGeometry->SignedDistance( imageGeometry->GetCornerPoint( 0 ) ); for( int i=1; i<8; i++ ) { mitk::Point3D cornerPoint = imageGeometry->GetCornerPoint( i ); // get the distance to the other cornerpoints ScalarType distance = renderingGeometry->SignedDistance( cornerPoint ); // if it has not the same signing as the distance of the first point if ( initialDistance * distance < 0 ) { // we have an intersection and return true return true; } } // all distances have the same sign, no intersection and we return false return false; } mitk::ImageVtkMapper2D::LocalStorage::~LocalStorage() { } mitk::ImageVtkMapper2D::LocalStorage::LocalStorage() : m_VectorComponentExtractor(vtkSmartPointer::New()) { m_LevelWindowFilter = vtkSmartPointer::New(); //Do as much actions as possible in here to avoid double executions. m_Plane = vtkSmartPointer::New(); m_Texture = vtkSmartPointer::New().GetPointer(); m_DefaultLookupTable = vtkSmartPointer::New(); m_BinaryLookupTable = vtkSmartPointer::New(); m_ColorLookupTable = vtkSmartPointer::New(); m_Mapper = vtkSmartPointer::New(); m_Actor = vtkSmartPointer::New(); m_Actors = vtkSmartPointer::New(); m_Reslicer = mitk::ExtractSliceFilter::New(); m_TSFilter = vtkSmartPointer::New(); m_OutlinePolyData = vtkSmartPointer::New(); m_ReslicedImage = vtkSmartPointer::New(); m_EmptyPolyData = vtkSmartPointer::New(); //the following actions are always the same and thus can be performed //in the constructor for each image (i.e. the image-corresponding local storage) m_TSFilter->ReleaseDataFlagOn(); //built a default lookuptable m_DefaultLookupTable->SetRampToLinear(); m_DefaultLookupTable->SetSaturationRange( 0.0, 0.0 ); m_DefaultLookupTable->SetHueRange( 0.0, 0.0 ); m_DefaultLookupTable->SetValueRange( 0.0, 1.0 ); m_DefaultLookupTable->Build(); m_BinaryLookupTable->SetRampToLinear(); m_BinaryLookupTable->SetSaturationRange( 0.0, 0.0 ); m_BinaryLookupTable->SetHueRange( 0.0, 0.0 ); m_BinaryLookupTable->SetValueRange( 0.0, 1.0 ); m_BinaryLookupTable->SetRange(0.0, 1.0); m_BinaryLookupTable->Build(); // add a default rainbow lookup table for color mapping m_ColorLookupTable->SetRampToLinear(); m_ColorLookupTable->SetHueRange(0.6667, 0.0); m_ColorLookupTable->SetTableRange(0.0, 20.0); m_ColorLookupTable->Build(); // make first value transparent { double rgba[4]; m_BinaryLookupTable->GetTableValue(0, rgba); m_BinaryLookupTable->SetTableValue(0, rgba[0], rgba[1], rgba[2], 0.0); // background to 0 } //do not repeat the texture (the image) m_Texture->RepeatOff(); //set the mapper for the actor m_Actor->SetMapper( m_Mapper ); vtkSmartPointer outlineShadowActor = vtkSmartPointer::New(); outlineShadowActor->SetMapper( m_Mapper ); m_Actors->AddPart( outlineShadowActor ); m_Actors->AddPart( m_Actor ); } diff --git a/Core/Code/Rendering/mitkImageVtkMapper2D.h b/Core/Code/Rendering/mitkImageVtkMapper2D.h index 3643d989aa..20675ea6f4 100644 --- a/Core/Code/Rendering/mitkImageVtkMapper2D.h +++ b/Core/Code/Rendering/mitkImageVtkMapper2D.h @@ -1,306 +1,306 @@ /*=================================================================== 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 MITKIMAGEVTKMAPPER2D_H_HEADER_INCLUDED_C10E906E #define MITKIMAGEVTKMAPPER2D_H_HEADER_INCLUDED_C10E906E //MITK #include //MITK Rendering #include "mitkBaseRenderer.h" #include "mitkVtkMapper.h" #include "mitkExtractSliceFilter.h" //VTK #include #include class vtkActor; class vtkPolyDataMapper; class vtkPlaneSource; class vtkImageData; class vtkLookupTable; class vtkImageExtractComponents; class vtkImageReslice; class vtkImageChangeInformation; class vtkPoints; class vtkMitkThickSlicesFilter; class vtkPolyData; class vtkMitkApplyLevelWindowToRGBFilter; class vtkMitkLevelWindowFilter; namespace mitk { /** \brief Mapper to resample and display 2D slices of a 3D image. * * The following image gives a brief overview of the mapping and the involved parts. * * \image html imageVtkMapper2Darchitecture.png * * First, the image is resliced by means of vtkImageReslice. The volume image * serves as input to the mapper in addition to spatial placement of the slice and a few other * properties such as thick slices. This code was already present in the old version * (mitkImageMapperGL2D). * * Next, the obtained slice (m_ReslicedImage) is put into a vtkMitkLevelWindowFilter * and the scalar levelwindow, opacity levelwindow and optional clipping to * local image bounds are applied * * Next, the output of the vtkMitkLevelWindowFilter is used to create a texture * (m_Texture) and a plane onto which the texture is rendered (m_Plane). For * mapping purposes, a vtkPolyDataMapper (m_Mapper) is utilized. Orthographic * projection is applied to create the effect of a 2D image. The mapper and the * texture are assigned to the actor (m_Actor) which is passed to the VTK rendering * pipeline via the method GetVtkProp(). * * In order to transform the textured plane to the correct position in space, the * same transformation as used for reslicing is applied to both the camera and the * vtkActor. All important steps are explained in more detail below. The resulting * 2D image (by reslicing the underlying 3D input image appropriately) can either * be directly rendered in a 2D view or just be calculated to be used later by another * rendering entity, e.g. in texture mapping in a 3D view. * * Properties that can be set for images and influence the imageMapper2D are: * * - \b "opacity": (FloatProperty) Opacity of the image * - \b "color": (ColorProperty) Color of the image * - \b "LookupTable": (mitkLookupTableProperty) If this property is set, * the default lookuptable will be ignored and the "LookupTable" value * will be used instead. * - \b "Image Rendering.Mode": This property decides which mode is used to render images. (E.g. if a lookup table or a transferfunction is applied). Detailed documentation about the modes can be found here: \link mitk::RenderingerModeProperty \endlink * - \b "Image Rendering.Transfer Function": (mitkTransferFunctionProperty) If this * property is set, a color transferfunction will be used to color the image. * - \b "binary": (BoolProperty) is the image a binary image or not * - \b "outline binary": (BoolProperty) show outline of the image or not * - \b "texture interpolation": (BoolProperty) texture interpolation of the image * - \b "reslice interpolation": (VtkResliceInterpolationProperty) reslice interpolation of the image * - \b "in plane resample extent by geometry": (BoolProperty) Do it or not * - \b "bounding box": (BoolProperty) Is the Bounding Box of the image shown or not * - \b "layer": (IntProperty) Layer of the image * - \b "volume annotation color": (ColorProperty) color of the volume annotation, TODO has to be reimplemented * - \b "volume annotation unit": (StringProperty) annotation unit as string (does not implicit convert the unit!) unit is ml or cm3, TODO has to be reimplemented * The default properties are: * - \b "opacity", mitk::FloatProperty::New(0.3f), renderer, overwrite ) * - \b "color", ColorProperty::New(1.0,0.0,0.0), renderer, overwrite ) * - \b "binary", mitk::BoolProperty::New( true ), renderer, overwrite ) * - \b "outline binary", mitk::BoolProperty::New( false ), renderer, overwrite ) * - \b "texture interpolation", mitk::BoolProperty::New( mitk::DataNodeFactory::m_TextureInterpolationActive ) ) * - \b "reslice interpolation", mitk::VtkResliceInterpolationProperty::New() ) * - \b "in plane resample extent by geometry", mitk::BoolProperty::New( false ) ) * - \b "bounding box", mitk::BoolProperty::New( false ) ) * - \b "layer", mitk::IntProperty::New(10), renderer, overwrite) * - \b "Image Rendering.Transfer Function": Default color transfer function for CTs * - \b "LookupTable": Rainbow color. * If the modality-property is set for an image, the mapper uses modality-specific default properties, * e.g. color maps, if they are defined. * \ingroup Mapper */ class MITK_CORE_EXPORT ImageVtkMapper2D : public VtkMapper { public: /** Standard class typedefs. */ mitkClassMacro( ImageVtkMapper2D,VtkMapper ); /** Method for creation through the object factory. */ itkNewMacro(Self); /** \brief Get the Image to map */ const mitk::Image *GetInput(void); /** \brief Checks whether this mapper needs to update itself and generate * data. */ virtual void Update(mitk::BaseRenderer * renderer); //### methods of MITK-VTK rendering pipeline virtual vtkProp* GetVtkProp(mitk::BaseRenderer* renderer); //### end of methods of MITK-VTK rendering pipeline /** \brief Internal class holding the mapper, actor, etc. for each of the 3 2D render windows */ /** * To render transveral, coronal, and sagittal, the mapper is called three times. * For performance reasons, the corresponding data for each view is saved in the * internal helper class LocalStorage. This allows rendering n views with just * 1 mitkMapper using n vtkMapper. * */ class MITK_CORE_EXPORT LocalStorage : public mitk::Mapper::BaseLocalStorage { public: /** \brief Actor of a 2D render window. */ vtkSmartPointer m_Actor; vtkSmartPointer m_Actors; /** \brief Mapper of a 2D render window. */ vtkSmartPointer m_Mapper; vtkSmartPointer m_VectorComponentExtractor; /** \brief Current slice of a 2D render window.*/ vtkSmartPointer m_ReslicedImage; /** \brief Empty vtkPolyData that is set when rendering geometry does not * intersect the image geometry. * \warning This member variable is set to NULL, * if no image geometry is inside the plane geometry * of the respective render window. Any user of this * slice has to check whether it is set to NULL! */ vtkSmartPointer m_EmptyPolyData; /** \brief Plane on which the slice is rendered as texture. */ vtkSmartPointer m_Plane; /** \brief The texture which is used to render the current slice. */ vtkSmartPointer m_Texture; /** \brief The lookuptables for colors and level window */ vtkSmartPointer m_DefaultLookupTable; vtkSmartPointer m_BinaryLookupTable; vtkSmartPointer m_ColorLookupTable; /** \brief The actual reslicer (one per renderer) */ mitk::ExtractSliceFilter::Pointer m_Reslicer; /** \brief Filter for thick slices */ vtkSmartPointer m_TSFilter; /** \brief PolyData object containg all lines/points needed for outlining the contour. This container is used to save a computed contour for the next rendering execution. For instance, if you zoom or pann, there is no need to recompute the contour. */ vtkSmartPointer m_OutlinePolyData; /** \brief Timestamp of last update of stored data. */ itk::TimeStamp m_LastUpdateTime; /** \brief mmPerPixel relation between pixel and mm. (World spacing).*/ mitk::ScalarType* m_mmPerPixel; /** \brief This filter is used to apply the level window to Grayvalue and RBG(A) images. */ vtkSmartPointer m_LevelWindowFilter; /** \brief Default constructor of the local storage. */ LocalStorage(); /** \brief Default deconstructor of the local storage. */ ~LocalStorage(); }; /** \brief The LocalStorageHandler holds all (three) LocalStorages for the three 2D render windows. */ mitk::LocalStorageHandler m_LSH; /** \brief Get the LocalStorage corresponding to the current renderer. */ LocalStorage* GetLocalStorage(mitk::BaseRenderer* renderer); /** \brief Set the default properties for general image rendering. */ static void SetDefaultProperties(mitk::DataNode* node, mitk::BaseRenderer* renderer = NULL, bool overwrite = false); /** \brief This method switches between different rendering modes (e.g. use a lookup table or a transfer function). * Detailed documentation about the modes can be found here: \link mitk::RenderingerModeProperty \endlink */ void ApplyRenderingMode(mitk::BaseRenderer *renderer); protected: /** \brief Transforms the actor to the actual position in 3D. * \param renderer The current renderer corresponding to the render window. */ void TransformActor(mitk::BaseRenderer* renderer); /** \brief Generates a plane according to the size of the resliced image in milimeters. * * \image html texturedPlane.png * * In VTK a vtkPlaneSource is defined through three points. The origin and two * points defining the axes of the plane (see VTK documentation). The origin is * set to (xMin; yMin; Z), where xMin and yMin are the minimal bounds of the * resliced image in space. Z is relevant for blending and the layer property. * The center of the plane (C) is also the center of the view plane (cf. the image above). * * \note For the standard MITK view with three 2D render windows showing three * different slices, three such planes are generated. All these planes are generated * in the XY-plane (even if they depict a YZ-slice of the volume). * */ void GeneratePlane(mitk::BaseRenderer* renderer, double planeBounds[6]); /** \brief Generates a vtkPolyData object containing the outline of a given binary slice. \param renderer: Pointer to the renderer containing the needed information \note This code is based on code from the iil library. */ vtkSmartPointer CreateOutlinePolyData(mitk::BaseRenderer* renderer); /** Default constructor */ ImageVtkMapper2D(); /** Default deconstructor */ virtual ~ImageVtkMapper2D(); /** \brief Does the actual resampling, without rendering the image yet. * All the data is generated inside this method. The vtkProp (or Actor) * is filled with content (i.e. the resliced image). * * After generation, a 4x4 transformation matrix(t) of the current slice is obtained * from the vtkResliceImage object via GetReslicesAxis(). This matrix is * applied to each textured plane (actor->SetUserTransform(t)) to transform everything * to the actual 3D position (cf. the following image). * * \image html cameraPositioning3D.png * */ virtual void GenerateDataForRenderer(mitk::BaseRenderer *renderer); /** \brief This method uses the vtkCamera clipping range and the layer property * to calcualte the depth of the object (e.g. image or contour). The depth is used * to keep the correct order for the final VTK rendering.*/ float CalculateLayerDepth(mitk::BaseRenderer* renderer); /** \brief This method applies (or modifies) the lookuptable for all types of images. * \warning To use the lookup table, the property 'Lookup Table' must be set and a 'Image Rendering.Mode' * which uses the lookup table must be set. */ void ApplyLookuptable(mitk::BaseRenderer* renderer); /** \brief This method applies a color transfer function. * Internally, a vtkColorTransferFunction is used. This is usefull for coloring continous * images (e.g. float) * \warning To use the color transfer function, the property 'Image Rendering.Transfer Function' must be set and a 'Image Rendering.Mode' which uses the color transfer function must be set. */ void ApplyColorTransferFunction(mitk::BaseRenderer* renderer); /** * @brief ApplyLevelWindow Apply the level window for the given renderer. * \warning To use the level window, the property 'LevelWindow' must be set and a 'Image Rendering.Mode' which uses the level window must be set. * @param renderer Level window for which renderer? */ void ApplyLevelWindow(mitk::BaseRenderer* renderer); /** \brief Set the color of the image/polydata */ void ApplyColor( mitk::BaseRenderer* renderer ); /** \brief Set the opacity of the actor. */ void ApplyOpacity( mitk::BaseRenderer* renderer ); /** * \brief Calculates whether the given rendering geometry intersects the * given SlicedGeometry3D. * - * This method checks if the given Geometry2D intersects the given - * SlicedGeometry3D. It calculates the distance of the Geometry2D to all + * This method checks if the given PlaneGeometry intersects the given + * SlicedGeometry3D. It calculates the distance of the PlaneGeometry to all * 8 cornerpoints of the SlicedGeometry3D. If all distances have the same * sign (all positive or all negative) there is no intersection. * If the distances have different sign, there is an intersection. **/ - bool RenderingGeometryIntersectsImage( const Geometry2D* renderingGeometry, SlicedGeometry3D* imageGeometry ); + bool RenderingGeometryIntersectsImage( const PlaneGeometry* renderingGeometry, SlicedGeometry3D* imageGeometry ); }; } // namespace mitk #endif /* MITKIMAGEVTKMAPPER2D_H_HEADER_INCLUDED_C10E906E */ diff --git a/Core/Code/Rendering/mitkPointSetGLMapper2D.cpp b/Core/Code/Rendering/mitkPointSetGLMapper2D.cpp index 66b91d9360..73842710f5 100644 --- a/Core/Code/Rendering/mitkPointSetGLMapper2D.cpp +++ b/Core/Code/Rendering/mitkPointSetGLMapper2D.cpp @@ -1,524 +1,524 @@ /*=================================================================== 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 "mitkPointSetGLMapper2D.h" #include "mitkPointSet.h" #include "mitkPlaneGeometry.h" #include "mitkColorProperty.h" #include "mitkProperties.h" #include "vtkLinearTransform.h" #include "mitkStringProperty.h" #include "mitkPointSet.h" #include "mitkVtkPropRenderer.h" #include "mitkGL.h" //const float selectedColor[]={1.0,0.0,0.6}; //for selected! mitk::PointSetGLMapper2D::PointSetGLMapper2D() : m_Polygon(false), m_ShowPoints(true), m_ShowDistances(false), m_DistancesDecimalDigits(1), m_ShowAngles(false), m_ShowDistantLines(true), m_LineWidth(1) { } mitk::PointSetGLMapper2D::~PointSetGLMapper2D() { } const mitk::PointSet *mitk::PointSetGLMapper2D::GetInput(void) { return static_cast ( GetDataNode()->GetData() ); } void mitk::PointSetGLMapper2D::ApplyAllProperties(mitk::BaseRenderer* renderer) { GLMapper::ApplyColorAndOpacityProperties( renderer ); const mitk::DataNode* node=GetDataNode(); if( node == NULL ) return; node->GetBoolProperty("show contour", m_Polygon); node->GetBoolProperty("close contour", m_PolygonClosed); node->GetBoolProperty("show points", m_ShowPoints); node->GetBoolProperty("show distances", m_ShowDistances); node->GetIntProperty("distance decimal digits", m_DistancesDecimalDigits); node->GetBoolProperty("show angles", m_ShowAngles); node->GetBoolProperty("show distant lines", m_ShowDistantLines); node->GetIntProperty("line width", m_LineWidth); node->GetIntProperty("point line width", m_PointLineWidth); node->GetIntProperty("point 2D size", m_Point2DSize); } static bool makePerpendicularVector2D(const mitk::Vector2D& in, mitk::Vector2D& out) { if((fabs(in[0])>0) && ( (fabs(in[0])>fabs(in[1])) || (in[1] == 0) ) ) { out[0]=-in[1]/in[0]; out[1]=1; out.Normalize(); return true; } else if(fabs(in[1])>0) { out[0]=1; out[1]=-in[0]/in[1]; out.Normalize(); return true; } else return false; } void mitk::PointSetGLMapper2D::Paint( mitk::BaseRenderer *renderer ) { const mitk::DataNode* node=GetDataNode(); if( node == NULL ) return; const int text2dDistance = 10; bool visible = true; GetDataNode()->GetVisibility(visible, renderer, "visible"); if ( !visible) return; // @FIXME: Logik fuer update bool updateNeccesary=true; if (updateNeccesary) { // ok, das ist aus GenerateData kopiert mitk::PointSet::Pointer input = const_cast(this->GetInput()); // Get the TimeGeometry of the input object const TimeGeometry* inputTimeGeometry = input->GetTimeGeometry(); if (( inputTimeGeometry == NULL ) || ( inputTimeGeometry->CountTimeSteps() == 0 ) ) { return; } // // get the world time // - const Geometry2D* worldGeometry = renderer->GetCurrentWorldGeometry2D(); + const PlaneGeometry* worldGeometry = renderer->GetCurrentWorldGeometry2D(); assert( worldGeometry != NULL ); ScalarType time = worldGeometry->GetTimeBounds()[ 0 ]; // // convert the world time in time steps of the input object // int timeStep=0; if ( time > ScalarTypeNumericTraits::NonpositiveMin() ) timeStep = inputTimeGeometry->TimePointToTimeStep( time ); if ( inputTimeGeometry->IsValidTimeStep( timeStep ) == false ) { return; } mitk::PointSet::DataType::Pointer itkPointSet = input->GetPointSet( timeStep ); if ( itkPointSet.GetPointer() == NULL) { return; } mitk::DisplayGeometry::Pointer displayGeometry = renderer->GetDisplayGeometry(); assert(displayGeometry.IsNotNull()); //apply color and opacity read from the PropertyList this->ApplyAllProperties(renderer); vtkLinearTransform* transform = GetDataNode()->GetVtkTransform(); //List of the Points PointSet::DataType::PointsContainerConstIterator it, end; it = itkPointSet->GetPoints()->Begin(); end = itkPointSet->GetPoints()->End(); //iterator on the additional data of each point PointSet::DataType::PointDataContainerIterator selIt, selEnd; bool pointDataBroken = (itkPointSet->GetPointData()->Size() != itkPointSet->GetPoints()->Size()); selIt = itkPointSet->GetPointData()->Begin(); selEnd = itkPointSet->GetPointData()->End(); int counter = 0; //for writing text int j = 0; //for switching back to old color after using selected color float recallColor[4]; glGetFloatv(GL_CURRENT_COLOR,recallColor); //get the properties for coloring the points float unselectedColor[4] = {1.0, 1.0, 0.0, 1.0};//yellow //check if there is an unselected property if (dynamic_cast(node->GetPropertyList(renderer)->GetProperty("unselectedcolor")) != NULL) { mitk::Color tmpColor = dynamic_cast(this->GetDataNode()->GetPropertyList(renderer)->GetProperty("unselectedcolor"))->GetValue(); unselectedColor[0] = tmpColor[0]; unselectedColor[1] = tmpColor[1]; unselectedColor[2] = tmpColor[2]; unselectedColor[3] = 1.0f; //!!define a new ColorProp to be able to pass alpha value } else if (dynamic_cast(node->GetPropertyList(NULL)->GetProperty("unselectedcolor")) != NULL) { mitk::Color tmpColor = dynamic_cast(this->GetDataNode()->GetPropertyList(NULL)->GetProperty("unselectedcolor"))->GetValue(); unselectedColor[0] = tmpColor[0]; unselectedColor[1] = tmpColor[1]; unselectedColor[2] = tmpColor[2]; unselectedColor[3] = 1.0f; //!!define a new ColorProp to be able to pass alpha value } else { //get the color from the dataNode node->GetColor(unselectedColor, NULL); } //get selected property float selectedColor[4] = {1.0, 0.0, 0.6, 1.0}; if (dynamic_cast(node->GetPropertyList(renderer)->GetProperty("selectedcolor")) != NULL) { mitk::Color tmpColor = dynamic_cast(this->GetDataNode()->GetPropertyList(renderer)->GetProperty("selectedcolor"))->GetValue(); selectedColor[0] = tmpColor[0]; selectedColor[1] = tmpColor[1]; selectedColor[2] = tmpColor[2]; selectedColor[3] = 1.0f; } else if (dynamic_cast(node->GetPropertyList(NULL)->GetProperty("selectedcolor")) != NULL) { mitk::Color tmpColor = dynamic_cast(this->GetDataNode()->GetPropertyList(NULL)->GetProperty("selectedcolor"))->GetValue(); selectedColor[0] = tmpColor[0]; selectedColor[1] = tmpColor[1]; selectedColor[2] = tmpColor[2]; selectedColor[3] = 1.0f; } //check if there is an pointLineWidth property if (dynamic_cast(node->GetPropertyList(renderer)->GetProperty("point line width")) != NULL) { m_PointLineWidth = dynamic_cast(this->GetDataNode()->GetPropertyList(renderer)->GetProperty("point line width"))->GetValue(); } else if (dynamic_cast(node->GetPropertyList(NULL)->GetProperty("point line width")) != NULL) { m_PointLineWidth = dynamic_cast(this->GetDataNode()->GetPropertyList(NULL)->GetProperty("point line width"))->GetValue(); } //check if there is an point 2D size property if (dynamic_cast(node->GetPropertyList(renderer)->GetProperty("point 2D size")) != NULL) { m_Point2DSize = dynamic_cast(this->GetDataNode()->GetPropertyList(renderer)->GetProperty("point 2D size"))->GetValue(); } else if (dynamic_cast(node->GetPropertyList(NULL)->GetProperty("point 2D size")) != NULL) { m_Point2DSize = dynamic_cast(this->GetDataNode()->GetPropertyList(NULL)->GetProperty("point 2D size"))->GetValue(); } Point3D p; // currently visited point Point3D lastP; // last visited point Vector3D vec; // p - lastP Vector3D lastVec; // lastP - point before lastP vec.Fill(0); mitk::Point3D projected_p; // p projected on viewplane Point2D pt2d; // projected_p in display coordinates Point2D lastPt2d; // last projected_p in display coordinates Point2D preLastPt2d;// projected_p in display coordinates before lastPt2d Point2D lastPt2DInPointSet; // The last point in the pointset in display coordinates mitk::PointSet::DataType::PointType plob; plob.Fill(0); itkPointSet->GetPoint( itkPointSet->GetNumberOfPoints()-1, &plob); //map lastPt2DInPointSet to display coordinates float vtkp[3]; itk2vtk(plob, vtkp); transform->TransformPoint(vtkp, vtkp); vtk2itk(vtkp,p); displayGeometry->Project(p, projected_p); displayGeometry->Map(projected_p, lastPt2DInPointSet); displayGeometry->WorldToDisplay(lastPt2DInPointSet, lastPt2DInPointSet); while(it!=end) // iterate over all points { lastP = p; // valid only for counter > 0 lastVec = vec; // valid only for counter > 1 preLastPt2d = lastPt2d; // valid only for counter > 1 lastPt2d = pt2d; // valid only for counter > 0 itk2vtk(it->Value(), vtkp); transform->TransformPoint(vtkp, vtkp); vtk2itk(vtkp,p); vec = p-lastP; // valid only for counter > 0 displayGeometry->Project(p, projected_p); Vector3D diff=p-projected_p; ScalarType scalardiff = diff.GetSquaredNorm(); //MouseOrientation bool isInputDevice=false; bool isRendererSlice = scalardiff < 0.00001; //cause roundoff error if(this->GetDataNode()->GetBoolProperty("inputdevice",isInputDevice) && isInputDevice && !isRendererSlice ) { displayGeometry->Map(projected_p, pt2d); displayGeometry->WorldToDisplay(pt2d, pt2d); //Point size depending of distance to slice /*float p_size = (1/scalardiff)*10*m_Point2DSize; if(p_size < m_Point2DSize * 0.6 ) p_size = m_Point2DSize * 0.6 ; else if ( p_size > m_Point2DSize ) p_size = m_Point2DSize;*/ float p_size = (1/scalardiff)*100.0; if(p_size < 6.0 ) p_size = 6.0 ; else if ( p_size > 10.0 ) p_size = 10.0; //draw Point float opacity = (p_size<8)?0.3:1.0;//don't get the opacity from the node? Feature not a bug! Otehrwise the 2D cross is hardly seen. glColor4f(unselectedColor[0],unselectedColor[1],unselectedColor[2],opacity); glPointSize(p_size); //glShadeModel(GL_FLAT); glBegin (GL_POINTS); glVertex2dv(&pt2d[0]); glEnd (); } //for point set if(!isInputDevice && ( (scalardiff<4.0) || (m_Polygon))) { Point2D tmp; displayGeometry->Map(projected_p, pt2d); displayGeometry->WorldToDisplay(pt2d, pt2d); Vector2D horz,vert; horz[0]=(float)m_Point2DSize-scalardiff*2; horz[1]=0; vert[0]=0; vert[1]=(float)m_Point2DSize-scalardiff*2; // now paint text if available if (dynamic_cast(this->GetDataNode() ->GetProperty("label")) != NULL) { const char * pointLabel = dynamic_cast( this->GetDataNode()->GetProperty("label"))->GetValue(); std::string l = pointLabel; if (input->GetSize()>1) { // char buffer[20]; // sprintf(buffer,"%d",it->Index()); std::stringstream ss; ss << it->Index(); l.append(ss.str()); } if (unselectedColor != NULL) { mitk::VtkPropRenderer* OpenGLrenderer = dynamic_cast( renderer ); float rgb[3];//yellow rgb[0] = unselectedColor[0]; rgb[1] = unselectedColor[1]; rgb[2] = unselectedColor[2]; OpenGLrenderer->WriteSimpleText(l, pt2d[0] + text2dDistance, pt2d[1] + text2dDistance,rgb[0], rgb[1],rgb[2]); } else { mitk::VtkPropRenderer* OpenGLrenderer = dynamic_cast( renderer ); OpenGLrenderer->WriteSimpleText(l, pt2d[0] + text2dDistance, pt2d[1] + text2dDistance,0.0,1.0,0.0); } } if((m_ShowPoints) && (scalardiff<4.0)) { //check if the point is to be marked as selected if(selIt != selEnd || pointDataBroken) { bool addAsSelected = false; if (pointDataBroken) addAsSelected = false; else if (selIt->Value().selected) addAsSelected = true; else addAsSelected = false; if (addAsSelected) { horz[0]=(float)m_Point2DSize; vert[1]=(float)m_Point2DSize; glColor3f(selectedColor[0],selectedColor[1],selectedColor[2]); glLineWidth(m_PointLineWidth); //a diamond around the point with the selected color glBegin (GL_LINE_LOOP); tmp=pt2d-horz; glVertex2dv(&tmp[0]); tmp=pt2d+vert; glVertex2dv(&tmp[0]); tmp=pt2d+horz; glVertex2dv(&tmp[0]); tmp=pt2d-vert; glVertex2dv(&tmp[0]); glEnd (); glLineWidth(1); //the actual point in the specified color to see the usual color of the point glColor3f(unselectedColor[0],unselectedColor[1],unselectedColor[2]); glPointSize(1); glBegin (GL_POINTS); tmp=pt2d; glVertex2dv(&tmp[0]); glEnd (); } else //if not selected { glColor3f(unselectedColor[0],unselectedColor[1],unselectedColor[2]); glLineWidth(m_PointLineWidth); //drawing crosses glBegin (GL_LINES); tmp=pt2d-horz; glVertex2dv(&tmp[0]); tmp=pt2d+horz; glVertex2dv(&tmp[0]); tmp=pt2d-vert; glVertex2dv(&tmp[0]); tmp=pt2d+vert; glVertex2dv(&tmp[0]); glEnd (); glLineWidth(1); } } } bool drawLinesEtc = true; if (!m_ShowDistantLines && counter > 0) // check, whether this line should be drawn { ScalarType currentDistance = displayGeometry->GetWorldGeometry()->SignedDistance(p); ScalarType lastDistance = displayGeometry->GetWorldGeometry()->SignedDistance(lastP); if ( currentDistance * lastDistance > 0.5 ) // points on same side of plane drawLinesEtc = false; } // draw a line if ((m_Polygon && counter>0 && drawLinesEtc) || (m_Polygon && m_PolygonClosed && drawLinesEtc)) { if ((counter == 0) && ( m_PolygonClosed)) { lastPt2d = lastPt2DInPointSet; } //get contour color property float contourColor[4] = {unselectedColor[0], unselectedColor[1], unselectedColor[2], unselectedColor[3]};//so if no property set, then use unselected color if (dynamic_cast(node->GetPropertyList(renderer)->GetProperty("contourcolor")) != NULL) { mitk::Color tmpColor = dynamic_cast(this->GetDataNode()->GetPropertyList(renderer)->GetProperty("contourcolor"))->GetValue(); contourColor[0] = tmpColor[0]; contourColor[1] = tmpColor[1]; contourColor[2] = tmpColor[2]; contourColor[3] = 1.0f; } else if (dynamic_cast(node->GetPropertyList(NULL)->GetProperty("contourcolor")) != NULL) { mitk::Color tmpColor = dynamic_cast(this->GetDataNode()->GetPropertyList(NULL)->GetProperty("contourcolor"))->GetValue(); contourColor[0] = tmpColor[0]; contourColor[1] = tmpColor[1]; contourColor[2] = tmpColor[2]; contourColor[3] = 1.0f; } //set this color glColor3f(contourColor[0],contourColor[1],contourColor[2]); glLineWidth( m_LineWidth ); glBegin (GL_LINES); glVertex2dv(&pt2d[0]); glVertex2dv(&lastPt2d[0]); glEnd (); glLineWidth(1.0); if(m_ShowDistances) // calculate and print a distance { std::stringstream buffer; float distance = vec.GetNorm(); buffer<( renderer ); OpenGLrenderer->WriteSimpleText(buffer.str(), pos2d[0], pos2d[1]); //this->WriteTextXY(pos2d[0], pos2d[1], buffer.str(),renderer); } if(m_ShowAngles && counter > 1 ) // calculate and print the angle btw. two lines { std::stringstream buffer; //buffer << angle(vec.Get_vnl_vector(), -lastVec.Get_vnl_vector())*180/vnl_math::pi << "�"; buffer << angle(vec.GetVnlVector(), -lastVec.GetVnlVector())*180/vnl_math::pi << (char)176; Vector2D vec2d = pt2d-lastPt2d; vec2d.Normalize(); Vector2D lastVec2d = lastPt2d-preLastPt2d; lastVec2d.Normalize(); vec2d=vec2d-lastVec2d; vec2d.Normalize(); Vector2D pos2d = lastPt2d.GetVectorFromOrigin()+vec2d*text2dDistance*text2dDistance; mitk::VtkPropRenderer* OpenGLrenderer = dynamic_cast( renderer ); OpenGLrenderer->WriteSimpleText(buffer.str(), pos2d[0], pos2d[1]); //this->WriteTextXY(pos2d[0], pos2d[1], buffer.str(),renderer); } } counter++; } ++it; if(selIt != selEnd && !pointDataBroken) ++selIt; j++; } //recall the color to the same color before this drawing glColor3f(recallColor[0],recallColor[1],recallColor[2]); } } void mitk::PointSetGLMapper2D::SetDefaultProperties(mitk::DataNode* node, mitk::BaseRenderer* renderer, bool overwrite) { node->AddProperty( "line width", mitk::IntProperty::New(2), renderer, overwrite ); // width of the line from one point to another node->AddProperty( "point line width", mitk::IntProperty::New(1), renderer, overwrite ); //width of the cross marking a point node->AddProperty( "point 2D size", mitk::IntProperty::New(8), renderer, overwrite ); // length of the cross marking a point // length of an edge of the box marking a point node->AddProperty( "show contour", mitk::BoolProperty::New(false), renderer, overwrite ); // contour of the line between points node->AddProperty( "close contour", mitk::BoolProperty::New(false), renderer, overwrite ); node->AddProperty( "show points", mitk::BoolProperty::New(true), renderer, overwrite ); //show or hide points node->AddProperty( "show distances", mitk::BoolProperty::New(false), renderer, overwrite ); //show or hide distance measure (not always available) node->AddProperty( "distance decimal digits", mitk::IntProperty::New(2), renderer, overwrite ); //set the number of decimal digits to be shown node->AddProperty( "show angles", mitk::BoolProperty::New(false), renderer, overwrite ); //show or hide angle measurement (not always available) node->AddProperty( "show distant lines", mitk::BoolProperty::New(false), renderer, overwrite ); //show the line between to points from a distant view (equals "always on top" option) node->AddProperty( "layer", mitk::IntProperty::New(1), renderer, overwrite ); // default to draw pointset above images (they have a default layer of 0) Superclass::SetDefaultProperties(node, renderer, overwrite); } diff --git a/Core/Code/Rendering/mitkPolyDataGLMapper2D.cpp b/Core/Code/Rendering/mitkPolyDataGLMapper2D.cpp index 8c43e1e306..e8d078e062 100644 --- a/Core/Code/Rendering/mitkPolyDataGLMapper2D.cpp +++ b/Core/Code/Rendering/mitkPolyDataGLMapper2D.cpp @@ -1,275 +1,275 @@ /*=================================================================== 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 #include "mitkPolyDataGLMapper2D.h" #include "mitkBaseRenderer.h" #include "mitkPlaneGeometry.h" #include "mitkSurface.h" #include "mitkColorProperty.h" #include "mitkProperties.h" #include "mitkAbstractTransformGeometry.h" #include "mitkVtkMapper3D.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void mitk::PolyDataGLMapper2D::Paint( mitk::BaseRenderer * renderer ) { bool visible = true; GetDataNode()->GetVisibility(visible, renderer, "visible"); if ( !visible ) return; // ok, das ist aus GenerateData kopiert mitk::BaseData::Pointer input = const_cast( GetDataNode()->GetData() ); assert( input ); input->Update(); vtkPolyData * vtkpolydata = this->GetVtkPolyData(); assert( vtkpolydata ); vtkLinearTransform * vtktransform = GetDataNode() ->GetVtkTransform(); if (vtktransform) { vtkLinearTransform * inversetransform = vtktransform->GetLinearInverse(); - Geometry2D::ConstPointer worldGeometry = renderer->GetCurrentWorldGeometry2D(); + PlaneGeometry::ConstPointer worldGeometry = renderer->GetCurrentWorldGeometry2D(); PlaneGeometry::ConstPointer worldPlaneGeometry = dynamic_cast( worldGeometry.GetPointer() ); if ( vtkpolydata != NULL ) { Point3D point; Vector3D normal; if(worldPlaneGeometry.IsNotNull()) { // set up vtkPlane according to worldGeometry point=worldPlaneGeometry->GetOrigin(); normal=worldPlaneGeometry->GetNormal(); normal.Normalize(); m_Plane->SetTransform((vtkAbstractTransform*)NULL); } else { //@FIXME: does not work correctly. Does m_Plane->SetTransform really transforms a "plane plane" into a "curved plane"? return; AbstractTransformGeometry::ConstPointer worldAbstractGeometry = dynamic_cast(renderer->GetCurrentWorldGeometry2D()); if(worldAbstractGeometry.IsNotNull()) { // set up vtkPlane according to worldGeometry point=const_cast(worldAbstractGeometry->GetParametricBoundingBox())->GetMinimum(); FillVector3D(normal, 0, 0, 1); m_Plane->SetTransform(worldAbstractGeometry->GetVtkAbstractTransform()->GetInverse()); } else return; } double vp[ 3 ], vnormal[ 3 ]; vnl2vtk(point.GetVnlVector(), vp); vnl2vtk(normal.GetVnlVector(), vnormal); //normally, we would need to transform the surface and cut the transformed surface with the cutter. //This might be quite slow. Thus, the idea is, to perform an inverse transform of the plane instead. //@todo It probably does not work for scaling operations yet:scaling operations have to be //dealed with after the cut is performed by scaling the contour. inversetransform->TransformPoint( vp, vp ); inversetransform->TransformNormalAtPoint( vp, vnormal, vnormal ); m_Plane->SetOrigin( vp ); m_Plane->SetNormal( vnormal ); // set data into cutter m_Cutter->SetInputData( vtkpolydata ); // m_Cutter->GenerateCutScalarsOff(); // m_Cutter->SetSortByToSortByCell(); // calculate the cut m_Cutter->Update(); // fetch geometry mitk::DisplayGeometry::Pointer displayGeometry = renderer->GetDisplayGeometry(); assert( displayGeometry ); // float toGL=displayGeometry->GetSizeInDisplayUnits()[1]; //apply color and opacity read from the PropertyList ApplyColorAndOpacityProperties( renderer ); // traverse the cut contour vtkPolyData * contour = m_Cutter->GetOutput(); vtkPoints *vpoints = contour->GetPoints(); vtkCellArray *vpolys = contour->GetLines(); vtkPointData *vpointdata = contour->GetPointData(); vtkDataArray* vscalars = vpointdata->GetScalars(); vtkCellData *vcelldata = contour->GetCellData(); vtkDataArray* vcellscalars = vcelldata->GetScalars(); int i, numberOfCells = vpolys->GetNumberOfCells(); Point3D p; Point2D p2d, last, first; vpolys->InitTraversal(); vtkScalarsToColors* lut = GetVtkLUT(); assert ( lut != NULL ); for ( i = 0;i < numberOfCells;++i ) { vtkIdType *cell(NULL); vtkIdType cellSize(0); vpolys->GetNextCell( cellSize, cell ); if ( m_ColorByCellData ) { // color each cell according to cell data double* color = lut->GetColor( vcellscalars->GetComponent( i, 0 ) ); glColor3f( color[ 0 ], color[ 1 ], color[ 2 ] ); } if ( m_ColorByPointData ) { double* color = lut->GetColor( vscalars->GetComponent( cell[0], 0 ) ); glColor3f( color[ 0 ], color[ 1 ], color[ 2 ] ); } glBegin ( GL_LINE_LOOP ); for ( int j = 0;j < cellSize;++j ) { vpoints->GetPoint( cell[ j ], vp ); //take transformation via vtktransform into account vtktransform->TransformPoint( vp, vp ); vtk2itk( vp, p ); //convert 3D point (in mm) to 2D point on slice (also in mm) worldGeometry->Map( p, p2d ); //convert point (until now mm and in worldcoordinates) to display coordinates (units ) displayGeometry->WorldToDisplay( p2d, p2d ); //convert display coordinates ( (0,0) is top-left ) in GL coordinates ( (0,0) is bottom-left ) //p2d[1]=toGL-p2d[1]; //add the current vertex to the line glVertex2f( p2d[0], p2d[1] ); } glEnd (); } } } } vtkPolyDataMapper* mitk::PolyDataGLMapper2D::GetVtkPolyDataMapper() { return NULL; /* mitk::DataNode::ConstPointer node = this->GetDataNode(); if ( node.IsNull() ) return NULL; mitk::VtkMapper3D::Pointer mitkMapper = dynamic_cast< mitk::VtkMapper3D* > ( node->GetMapper( 2 ) ); if ( mitkMapper.IsNull() ) return NULL; mitkMapper->Update(NULL); vtkActor* actor = dynamic_cast( mitkMapper->GetVtkProp(0) ); if ( actor == NULL ) return NULL; return dynamic_cast( actor->GetMapper() ); */ } vtkPolyData* mitk::PolyDataGLMapper2D::GetVtkPolyData( ) { vtkPolyDataMapper * polyDataMapper = GetVtkPolyDataMapper(); if ( polyDataMapper == NULL ) return NULL; else return polyDataMapper->GetInput(); } vtkScalarsToColors* mitk::PolyDataGLMapper2D::GetVtkLUT( ) { vtkPolyDataMapper * polyDataMapper = GetVtkPolyDataMapper(); if ( polyDataMapper == NULL ) return NULL; else return polyDataMapper->GetLookupTable(); } bool mitk::PolyDataGLMapper2D::IsConvertibleToVtkPolyData() { return ( GetVtkPolyDataMapper() != NULL ); } mitk::PolyDataGLMapper2D::PolyDataGLMapper2D() { m_Plane = vtkPlane::New(); m_Cutter = vtkCutter::New(); m_Cutter->SetCutFunction( m_Plane ); m_Cutter->GenerateValues( 1, 0, 1 ); m_ColorByCellData = false; m_ColorByPointData = false; //m_LUT = vtkLookupTable::New(); //m_LUT->SetTableRange( 0, 255 ); //m_LUT->SetNumberOfColors( 255 ); //m_LUT->SetRampToLinear (); //m_LUT->Build(); } mitk::PolyDataGLMapper2D::~PolyDataGLMapper2D() {} diff --git a/Core/Code/Rendering/mitkSurfaceGLMapper2D.cpp b/Core/Code/Rendering/mitkSurfaceGLMapper2D.cpp index d0617e7a9f..1602739f07 100644 --- a/Core/Code/Rendering/mitkSurfaceGLMapper2D.cpp +++ b/Core/Code/Rendering/mitkSurfaceGLMapper2D.cpp @@ -1,544 +1,544 @@ /*=================================================================== 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 #include "mitkSurfaceGLMapper2D.h" #include "mitkBaseRenderer.h" #include "mitkPlaneGeometry.h" #include "mitkSurface.h" #include "mitkColorProperty.h" #include "mitkProperties.h" #include "mitkVtkScalarModeProperty.h" #include "mitkAbstractTransformGeometry.h" #include "mitkLookupTableProperty.h" #include #include #include #include #include #include #include #include #include #include #include #include #include mitk::SurfaceGLMapper2D::SurfaceGLMapper2D() : m_Plane( vtkPlane::New() ), m_Cutter( vtkCutter::New() ), m_LUT( vtkLookupTable::New() ), m_PointLocator( vtkPKdTree::New() ), m_Stripper( vtkStripper::New() ), m_DrawNormals(false), m_FrontNormalLengthInPixels(10.0), m_BackNormalLengthInPixels(10.0) { // default for normals on front side = green m_FrontSideColor[0] = 0.0; m_FrontSideColor[1] = 1.0; m_FrontSideColor[2] = 0.0; m_FrontSideColor[3] = 1.0; // default for normals on back side = red m_BackSideColor[0] = 1.0; m_BackSideColor[1] = 0.0; m_BackSideColor[2] = 0.0; m_BackSideColor[3] = 1.0; // default for line color = yellow m_LineColor[0] = 1.0; m_LineColor[1] = 1.0; m_LineColor[2] = 0.0; m_LineColor[3] = 1.0; m_Cutter->SetCutFunction(m_Plane); m_Cutter->GenerateValues(1,0,1); m_LUT->SetTableRange(0,255); m_LUT->SetNumberOfColors(255); m_LUT->SetRampToLinear(); m_LUT->Build(); } mitk::SurfaceGLMapper2D::~SurfaceGLMapper2D() { m_Plane->Delete(); m_Cutter->Delete(); m_LUT->Delete(); m_PointLocator->Delete(); m_Stripper->Delete(); } const mitk::Surface *mitk::SurfaceGLMapper2D::GetInput(void) { if(m_Surface.IsNotNull()) return m_Surface; return static_cast ( GetDataNode()->GetData() ); } void mitk::SurfaceGLMapper2D::SetDataNode( mitk::DataNode* node ) { Superclass::SetDataNode( node ); bool useCellData; if (dynamic_cast(node->GetProperty("deprecated useCellDataForColouring")) == NULL) useCellData = false; else useCellData = dynamic_cast(node->GetProperty("deprecated useCellDataForColouring"))->GetValue(); if (!useCellData) { // search min/max point scalars over all time steps double dataRange[2] = {0,0}; double range[2]; Surface::Pointer input = const_cast< Surface* >(dynamic_cast( this->GetDataNode()->GetData() )); if(input.IsNull()) return; const TimeGeometry::Pointer inputTimeGeometry = input->GetTimeGeometry(); if(( inputTimeGeometry.IsNull() ) || ( inputTimeGeometry->CountTimeSteps() == 0 ) ) return; for (unsigned int timestep=0; timestepCountTimeSteps(); timestep++) { vtkPolyData * vtkpolydata = input->GetVtkPolyData( timestep ); if((vtkpolydata==NULL) || (vtkpolydata->GetNumberOfPoints() < 1 )) continue; vtkDataArray *vpointscalars = vtkpolydata->GetPointData()->GetScalars(); if (vpointscalars) { vpointscalars->GetRange( range, 0 ); if (dataRange[0]==0 && dataRange[1]==0) { dataRange[0] = range[0]; dataRange[1] = range[1]; } else { if (range[0] < dataRange[0]) dataRange[0] = range[0]; if (range[1] > dataRange[1]) dataRange[1] = range[1]; } } } if (dataRange[1] - dataRange[0] > 0) { m_LUT->SetTableRange( dataRange ); m_LUT->Build(); } } } void mitk::SurfaceGLMapper2D::Paint(mitk::BaseRenderer * renderer) { bool visible = true; GetDataNode()->GetVisibility(visible, renderer, "visible"); if(!visible) return; Surface::Pointer input = const_cast(this->GetInput()); if(input.IsNull()) return; // // get the TimeGeometry of the input object // const TimeGeometry* inputTimeGeometry = input->GetTimeGeometry(); if(( inputTimeGeometry == NULL ) || ( inputTimeGeometry->CountTimeSteps() == 0 ) ) return; if (dynamic_cast(this->GetDataNode()->GetProperty("line width")) == NULL) m_LineWidth = 1; else m_LineWidth = dynamic_cast(this->GetDataNode()->GetProperty("line width"))->GetValue(); // // get the world time // - Geometry2D::ConstPointer worldGeometry = renderer->GetCurrentWorldGeometry2D(); + PlaneGeometry::ConstPointer worldGeometry = renderer->GetCurrentWorldGeometry2D(); assert( worldGeometry.IsNotNull() ); ScalarType time = worldGeometry->GetTimeBounds()[ 0 ]; int timestep=0; if( time > ScalarTypeNumericTraits::NonpositiveMin() ) timestep = inputTimeGeometry->TimePointToTimeStep( time ); // int timestep = this->GetTimestep(); if( inputTimeGeometry->IsValidTimeStep( timestep ) == false ) return; vtkPolyData * vtkpolydata = input->GetVtkPolyData( timestep ); if((vtkpolydata==NULL) || (vtkpolydata->GetNumberOfPoints() < 1 )) return; PlaneGeometry::ConstPointer worldPlaneGeometry = dynamic_cast(worldGeometry.GetPointer()); //apply color and opacity read from the PropertyList this->ApplyAllProperties(renderer); if (m_DrawNormals) { m_PointLocator->SetDataSet( vtkpolydata ); m_PointLocator->BuildLocatorFromPoints( vtkpolydata->GetPoints() ); } if(vtkpolydata!=NULL) { Point3D point; Vector3D normal; //Check if Lookup-Table is already given, else use standard one. double* scalarLimits = m_LUT->GetTableRange(); double scalarsMin = scalarLimits[0], scalarsMax = scalarLimits[1]; vtkLookupTable *lut;// = vtkLookupTable::New(); LookupTableProperty::Pointer lookupTableProp; this->GetDataNode()->GetProperty(lookupTableProp, "LookupTable", renderer); if (lookupTableProp.IsNotNull() ) { lut = lookupTableProp->GetLookupTable()->GetVtkLookupTable(); if (dynamic_cast(this->GetDataNode()->GetProperty("ScalarsRangeMinimum")) != NULL) scalarsMin = dynamic_cast(this->GetDataNode()->GetProperty("ScalarsRangeMinimum"))->GetValue(); if (dynamic_cast(this->GetDataNode()->GetProperty("ScalarsRangeMaximum")) != NULL) scalarsMax = dynamic_cast(this->GetDataNode()->GetProperty("ScalarsRangeMaximum"))->GetValue(); // check if the scalar range has been changed, e.g. manually, for the data tree node, and rebuild the LUT if necessary. double* oldRange = lut->GetTableRange(); if( oldRange[0] != scalarsMin || oldRange[1] != scalarsMax ) { lut->SetTableRange(scalarsMin, scalarsMax); lut->Build(); } } else { lut = m_LUT; } vtkLinearTransform * vtktransform = GetDataNode()->GetVtkTransform(timestep); if(worldPlaneGeometry.IsNotNull()) { // set up vtkPlane according to worldGeometry point=worldPlaneGeometry->GetOrigin(); normal=worldPlaneGeometry->GetNormal(); normal.Normalize(); m_Plane->SetTransform((vtkAbstractTransform*)NULL); } else { AbstractTransformGeometry::ConstPointer worldAbstractGeometry = dynamic_cast(renderer->GetCurrentWorldGeometry2D()); if(worldAbstractGeometry.IsNotNull()) { AbstractTransformGeometry::ConstPointer surfaceAbstractGeometry = dynamic_cast(input->GetTimeGeometry()->GetGeometryForTimeStep(0).GetPointer()); if(surfaceAbstractGeometry.IsNotNull()) //@todo substitude by operator== after implementation, see bug id 28 { PaintCells(renderer, vtkpolydata, worldGeometry, renderer->GetDisplayGeometry(), vtktransform, lut); return; } else { //@FIXME: does not work correctly. Does m_Plane->SetTransform really transforms a "flat plane" into a "curved plane"? return; // set up vtkPlane according to worldGeometry point=const_cast(worldAbstractGeometry->GetParametricBoundingBox())->GetMinimum(); FillVector3D(normal, 0, 0, 1); m_Plane->SetTransform(worldAbstractGeometry->GetVtkAbstractTransform()->GetInverse()); } } else return; } double vp[3], vnormal[3]; vnl2vtk(point.GetVnlVector(), vp); vnl2vtk(normal.GetVnlVector(), vnormal); //normally, we would need to transform the surface and cut the transformed surface with the cutter. //This might be quite slow. Thus, the idea is, to perform an inverse transform of the plane instead. //@todo It probably does not work for scaling operations yet:scaling operations have to be //dealed with after the cut is performed by scaling the contour. vtkLinearTransform * inversetransform = vtktransform->GetLinearInverse(); inversetransform->TransformPoint(vp, vp); inversetransform->TransformNormalAtPoint(vp, vnormal, vnormal); m_Plane->SetOrigin(vp); m_Plane->SetNormal(vnormal); //set data into cutter m_Cutter->SetInputData(vtkpolydata); m_Cutter->Update(); // m_Cutter->GenerateCutScalarsOff(); // m_Cutter->SetSortByToSortByCell(); if (m_DrawNormals) { m_Stripper->SetInputData( m_Cutter->GetOutput() ); // calculate the cut m_Stripper->Update(); PaintCells(renderer, m_Stripper->GetOutput(), worldGeometry, renderer->GetDisplayGeometry(), vtktransform, lut, vtkpolydata); } else { PaintCells(renderer, m_Cutter->GetOutput(), worldGeometry, renderer->GetDisplayGeometry(), vtktransform, lut, vtkpolydata); } } } void mitk::SurfaceGLMapper2D::PaintCells(mitk::BaseRenderer* renderer, vtkPolyData* contour, - const Geometry2D* worldGeometry, + const PlaneGeometry* worldGeometry, const DisplayGeometry* displayGeometry, vtkLinearTransform * vtktransform, vtkLookupTable *lut, vtkPolyData* original3DObject) { // deprecated settings bool usePointData = false; bool useCellData = false; this->GetDataNode()->GetBoolProperty("deprecated useCellDataForColouring", useCellData); bool scalarVisibility = false; this->GetDataNode()->GetBoolProperty("scalar visibility", scalarVisibility); if(scalarVisibility) { VtkScalarModeProperty* scalarMode; if(this->GetDataNode()->GetProperty(scalarMode, "scalar mode", renderer)) { if( (scalarMode->GetVtkScalarMode() == VTK_SCALAR_MODE_USE_POINT_DATA) || (scalarMode->GetVtkScalarMode() == VTK_SCALAR_MODE_DEFAULT) ) { usePointData = true; } if(scalarMode->GetVtkScalarMode() == VTK_SCALAR_MODE_USE_CELL_DATA) { useCellData = true; } } else { usePointData = true; } } vtkPoints *vpoints = contour->GetPoints(); vtkDataArray *vpointscalars = contour->GetPointData()->GetScalars(); vtkCellArray *vlines = contour->GetLines(); vtkDataArray* vcellscalars = contour->GetCellData()->GetScalars(); Point3D p; Point2D p2d, last; int i, j; int numberOfLines = vlines->GetNumberOfCells(); glLineWidth( m_LineWidth ); glBegin (GL_LINES); glColor4fv(m_LineColor); double distanceSinceLastNormal(0.0); vlines->InitTraversal(); for(i=0;iGetNextCell(cellSize, cell); vpoints->GetPoint(cell[0], vp); //take transformation via vtktransform into account vtktransform->TransformPoint(vp, vp); vtk2itk(vp, p); //convert 3D point (in mm) to 2D point on slice (also in mm) worldGeometry->Map(p, p2d); //convert point (until now mm and in world coordinates) to display coordinates (units ) displayGeometry->WorldToDisplay(p2d, p2d); last=p2d; for(j=1; jGetPoint(cell[j], vp); Point3D originalPoint; vtk2itk(vp, originalPoint); //take transformation via vtktransform into account vtktransform->TransformPoint(vp, vp); vtk2itk(vp, p); //convert 3D point (in mm) to 2D point on slice (also in mm) worldGeometry->Map(p, p2d); //convert point (until now mm and in world coordinates) to display coordinates (units ) displayGeometry->WorldToDisplay(p2d, p2d); double color[3]; if (useCellData && vcellscalars != NULL ) { // color each cell according to cell data lut->GetColor( vcellscalars->GetComponent(i,0),color); glColor3f(color[0],color[1],color[2]); glVertex2f(last[0], last[1]); glVertex2f(p2d[0], p2d[1]); } else if (usePointData && vpointscalars != NULL ) { lut->GetColor( vpointscalars->GetComponent(cell[j-1],0),color); glColor3f(color[0],color[1],color[2]); glVertex2f(last[0], last[1]); lut->GetColor( vpointscalars->GetComponent(cell[j],0),color); glColor3f(color[0],color[1],color[2]); glVertex2f(p2d[0], p2d[1]); } else { glVertex2f(last[0], last[1]); glVertex2f(p2d[0], p2d[1]); // draw normals ? if (m_DrawNormals && original3DObject) { distanceSinceLastNormal += sqrt((p2d[0]-last[0])*(p2d[0]-last[0]) + (p2d[1]-last[1])*(p2d[1]-last[1])); if (distanceSinceLastNormal >= 5.0) { distanceSinceLastNormal = 0.0; vtkPointData* pointData = original3DObject->GetPointData(); if (!pointData) break; vtkDataArray* normalsArray = pointData->GetNormals(); if (!normalsArray) break; // find 3D point closest to the currently drawn point double distance(0.0); vtkIdType closestPointId = m_PointLocator->FindClosestPoint(originalPoint[0], originalPoint[1], originalPoint[2], distance); if (closestPointId >= 0) { // find normal of 3D object at this 3D point double* normal = normalsArray->GetTuple3(closestPointId); double transformedNormal[3]; vtktransform->TransformNormal(normal, transformedNormal); Vector3D normalITK; vtk2itk(transformedNormal, normalITK); normalITK.Normalize(); // calculate a point (point from the cut 3D object) + (normal vector of closest point) Point3D tip3D = p + normalITK; // map this point into our 2D coordinate system Point2D tip2D; worldGeometry->Map(tip3D, tip2D); displayGeometry->WorldToDisplay(tip2D, tip2D); // calculate 2D vector from point to point+normal, normalize it to standard length Vector2D tipVectorGLFront = tip2D - p2d; tipVectorGLFront.Normalize(); tipVectorGLFront *= m_FrontNormalLengthInPixels; Vector2D tipVectorGLBack = p2d - tip2D; tipVectorGLBack.Normalize(); tipVectorGLBack *= m_BackNormalLengthInPixels; Point2D tipPoint2D = p2d + tipVectorGLFront; Point2D backTipPoint2D = p2d + tipVectorGLBack; // draw normalized mapped normal vector glColor4f(m_BackSideColor[0], m_BackSideColor[1], m_BackSideColor[2], m_BackSideColor[3]); // red backside glVertex2f(p2d[0], p2d[1]); glVertex2f(tipPoint2D[0], tipPoint2D[1]); glColor4f(m_FrontSideColor[0], m_FrontSideColor[1], m_FrontSideColor[2], m_FrontSideColor[3]); // green backside glVertex2f(p2d[0], p2d[1]); glVertex2f(backTipPoint2D[0], backTipPoint2D[1]); glColor4fv(m_LineColor); // back to line color } } } } last=p2d; } } glEnd(); glLineWidth(1.0); } void mitk::SurfaceGLMapper2D::SetDefaultProperties(mitk::DataNode* node, mitk::BaseRenderer* renderer, bool overwrite) { node->AddProperty( "line width", IntProperty::New(2), renderer, overwrite ); node->AddProperty( "scalar mode", VtkScalarModeProperty::New(), renderer, overwrite ); node->AddProperty( "draw normals 2D", BoolProperty::New(false), renderer, overwrite ); node->AddProperty( "invert normals", BoolProperty::New(false), renderer, overwrite ); node->AddProperty( "front color", ColorProperty::New(0.0, 1.0, 0.0), renderer, overwrite ); node->AddProperty( "back color", ColorProperty::New(1.0, 0.0, 0.0), renderer, overwrite ); node->AddProperty( "front normal lenth (px)", FloatProperty::New(10.0), renderer, overwrite ); node->AddProperty( "back normal lenth (px)", FloatProperty::New(10.0), renderer, overwrite ); node->AddProperty( "layer", mitk::IntProperty::New(100), renderer, overwrite); Superclass::SetDefaultProperties(node, renderer, overwrite); } void mitk::SurfaceGLMapper2D::ApplyAllProperties(mitk::BaseRenderer* renderer) { ApplyColorAndOpacityProperties(renderer); DataNode * node = GetDataNode(); if(node == NULL) { return; } node->GetBoolProperty("draw normals 2D", m_DrawNormals, renderer); // check for color and opacity properties, use it for rendering if they exists node->GetColor(m_LineColor, renderer, "color"); node->GetOpacity(m_LineColor[3], renderer, "opacity"); bool invertNormals(false); node->GetBoolProperty("invert normals", invertNormals, renderer); if (!invertNormals) { node->GetColor(m_FrontSideColor, renderer, "front color"); node->GetOpacity(m_FrontSideColor[3], renderer, "opacity"); node->GetColor(m_BackSideColor, renderer, "back color"); node->GetOpacity(m_BackSideColor[3], renderer, "opacity"); node->GetFloatProperty( "front normal lenth (px)", m_FrontNormalLengthInPixels, renderer ); node->GetFloatProperty( "back normal lenth (px)", m_BackNormalLengthInPixels, renderer ); } else { node->GetColor(m_FrontSideColor, renderer, "back color"); node->GetOpacity(m_FrontSideColor[3], renderer, "opacity"); node->GetColor(m_BackSideColor, renderer, "front color"); node->GetOpacity(m_BackSideColor[3], renderer, "opacity"); node->GetFloatProperty( "back normal lenth (px)", m_FrontNormalLengthInPixels, renderer ); node->GetFloatProperty( "front normal lenth (px)", m_BackNormalLengthInPixels, renderer ); } } diff --git a/Core/Code/Rendering/mitkSurfaceGLMapper2D.h b/Core/Code/Rendering/mitkSurfaceGLMapper2D.h index 04630969d1..cc0ac02fa2 100644 --- a/Core/Code/Rendering/mitkSurfaceGLMapper2D.h +++ b/Core/Code/Rendering/mitkSurfaceGLMapper2D.h @@ -1,155 +1,155 @@ /*=================================================================== 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 MITKSURFACEDATAMAPPER2D_H_HEADER_INCLUDED_C10EB2E8 #define MITKSURFACEDATAMAPPER2D_H_HEADER_INCLUDED_C10EB2E8 #include #include "mitkGLMapper.h" #include "mitkSurface.h" class vtkCutter; class vtkPlane; class vtkLookupTable; class vtkLinearTransform; class vtkPKdTree; class vtkStripper; namespace mitk { class BaseRenderer; -class Geometry2D; +class PlaneGeometry; class DisplayGeometry; /** * @brief OpenGL-based mapper to display a Surface in a 2D window. * * Displays a 2D cut through a Surface object (vtkPolyData). This * is basically done in two steps: * * 1. Cut a slice out of a (input) vtkPolyData object. The slice may be a flat plane (PlaneGeometry) * or a curved plane (ThinPlateSplineCurvedGeometry). The actual cutting is done by a vtkCutter. * The result of cutting is a (3D) vtkPolyData object, which contains only points and lines * describing the cut. * * 2. Paint the cut out slice by means of OpenGL. To do this, all lines of the cut object are traversed. * For each line segment, both end points are transformed from 3D into the 2D system of the associated * renderer and then drawn by OpenGL. * * There is a mode to display normals of the input surface object (see properties below). If this mode * is on, then the drawing of the 2D cut is slightly more complicated. For each line segment of the cut, * we take the end point (p2d) of this line and search the input vtkPolyData object for the closest point to p2d (p3D-input). * We then read out the surface normal for p3D-input. We map this normal into our 2D coordinate system and * then draw a line from p2d to (p2d+mapped normal). This drawing of surface normals will only work if the * input vtkPolyData actually HAS normals. If you have a vtkPolyData without normals, use the vtkPolyDataNormals * filter to generate normals. * * Properties that influence rendering are: * * - \b "color": (ColorProperty) Color of surface object * - \b "line width": (IntProperty) Width in pixels of the lines drawn. * - \b "scalar visibility": (BoolProperty) Whether point/cell data values (from vtkPolyData) should be used to influence colors * - \b "scalar mode": (BoolProperty) If "scalar visibility" is on, whether to use point data or cell data for coloring. * - \b "LookupTable": (LookupTableProperty) A lookup table to translate point/cell data values (from vtkPolyData) to colors * - \b "ScalarsRangeMinimum": (FloatProperty) Range of the lookup table * - \b "ScalarsRangeMaximum": (FloatProperty) Range of the lookup table * - \b "draw normals 2D": (BoolProperty) If true, normals are drawn (if present in vtkPolyData) * - \b "invert normals": (BoolProperty) Inverts front/back for display. * - \b "front color": (ColorProperty) Color for normals display on front side of the plane * - \b "front normal length (px)": (FloatProperty) Length of the front side normals in pixels. * - \b "back color": (ColorProperty) Color for normals display on back side of the plane * - \b "back normal length (px)": (FloatProperty) Length of the back side normals in pixels. * */ class MITK_CORE_EXPORT SurfaceGLMapper2D : public GLMapper { public: mitkClassMacro(SurfaceGLMapper2D, GLMapper); itkNewMacro(Self); const Surface* GetInput(void); virtual void Paint(BaseRenderer* renderer); /** * @brief The Surface to map can be explicitly set by this method. * * If it is set, it is used instead of the data stored in the DataNode. * This enables to use the mapper also internally from other mappers. */ itkSetConstObjectMacro(Surface, Surface); /** * @brief Get the Surface set explicitly. * * @return NULL is returned if no Surface is set to be used instead of DataNode::GetData(). * @sa SetSurface */ itkGetConstObjectMacro(Surface, Surface); /** *\brief Overwritten to initialize lookup table for point scalar data */ void SetDataNode( DataNode* node ); /** * \brief Generate OpenGL primitives for the VTK contour held in contour. */ void PaintCells(BaseRenderer* renderer, vtkPolyData* contour, - const Geometry2D* worldGeometry, + const PlaneGeometry* worldGeometry, const DisplayGeometry* displayGeometry, vtkLinearTransform* vtktransform, vtkLookupTable* lut = NULL, vtkPolyData* original3DObject = NULL); static void SetDefaultProperties(DataNode* node, BaseRenderer* renderer = NULL, bool overwrite = false); virtual void ApplyAllProperties(BaseRenderer* renderer); protected: SurfaceGLMapper2D(); virtual ~SurfaceGLMapper2D(); vtkPlane* m_Plane; vtkCutter* m_Cutter; Surface::ConstPointer m_Surface; vtkLookupTable* m_LUT; int m_LineWidth; vtkPKdTree* m_PointLocator; vtkStripper* m_Stripper; bool m_DrawNormals; float m_FrontSideColor[4]; float m_BackSideColor[4]; float m_LineColor[4]; float m_FrontNormalLengthInPixels; float m_BackNormalLengthInPixels; }; } // namespace mitk #endif /* MITKSURFACEDATAMAPPER2D_H_HEADER_INCLUDED_C10EB2E8 */ diff --git a/Core/Code/Testing/mitkClippedSurfaceBoundsCalculatorTest.cpp b/Core/Code/Testing/mitkClippedSurfaceBoundsCalculatorTest.cpp index ebf8d1c31d..186dbda875 100644 --- a/Core/Code/Testing/mitkClippedSurfaceBoundsCalculatorTest.cpp +++ b/Core/Code/Testing/mitkClippedSurfaceBoundsCalculatorTest.cpp @@ -1,484 +1,484 @@ /*=================================================================== 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 "mitkTestingMacros.h" #include #include "mitkClippedSurfaceBoundsCalculator.h" #include "mitkGeometry3D.h" -#include "mitkGeometry2D.h" +#include "mitkPlaneGeometry.h" #include "mitkVector.h" static void CheckPlanesInsideBoundingBoxOnlyOnOneSlice(mitk::BaseGeometry::Pointer geometry3D) { //Check planes which are inside the bounding box mitk::ClippedSurfaceBoundsCalculator* calculator = new mitk::ClippedSurfaceBoundsCalculator(); mitk::Image::Pointer image = mitk::Image::New(); image->Initialize( mitk::MakePixelType(), *(geometry3D.GetPointer()) ); //Check planes which are only on one slice: //Slice 0 mitk::Point3D origin; origin[0] = 511; origin[1] = 0; origin[2] = 0; mitk::Vector3D normal; mitk::FillVector3D(normal, 0, 0, 1); mitk::PlaneGeometry::Pointer planeOnSliceZero = mitk::PlaneGeometry::New(); planeOnSliceZero->InitializePlane(origin, normal); calculator->SetInput( planeOnSliceZero , image); calculator->Update(); mitk::ClippedSurfaceBoundsCalculator::OutputType minMax = calculator->GetMinMaxSpatialDirectionZ(); MITK_TEST_CONDITION(minMax.first == minMax.second, "Check if plane is only on one slice"); MITK_TEST_CONDITION(minMax.first == 0 && minMax.second == 0, "Check if plane is on slice 0"); //Slice 3 origin[2] = 3; mitk::PlaneGeometry::Pointer planeOnSliceThree = mitk::PlaneGeometry::New(); planeOnSliceThree->InitializePlane(origin, normal); planeOnSliceThree->SetImageGeometry(false); calculator->SetInput( planeOnSliceThree , image); calculator->Update(); minMax = calculator->GetMinMaxSpatialDirectionZ(); MITK_TEST_CONDITION(minMax.first == minMax.second, "Check if plane is only on one slice"); MITK_TEST_CONDITION(minMax.first == 3 && minMax.second == 3, "Check if plane is on slice 3"); //Slice 17 origin[2] = 17; mitk::PlaneGeometry::Pointer planeOnSliceSeventeen = mitk::PlaneGeometry::New(); planeOnSliceSeventeen->InitializePlane(origin, normal); calculator->SetInput( planeOnSliceSeventeen , image); calculator->Update(); minMax = calculator->GetMinMaxSpatialDirectionZ(); MITK_TEST_CONDITION(minMax.first == minMax.second, "Check if plane is only on one slice"); MITK_TEST_CONDITION(minMax.first == 17 && minMax.second == 17, "Check if plane is on slice 17"); //Slice 20 origin[2] = 19; mitk::PlaneGeometry::Pointer planeOnSliceTwenty = mitk::PlaneGeometry::New(); planeOnSliceTwenty->InitializePlane(origin, normal); calculator->SetInput( planeOnSliceTwenty , image); calculator->Update(); minMax = calculator->GetMinMaxSpatialDirectionZ(); MITK_TEST_CONDITION(minMax.first == minMax.second, "Check if plane is only on one slice"); MITK_TEST_CONDITION(minMax.first == 19 && minMax.second == 19, "Check if plane is on slice 19"); delete calculator; } static void CheckPlanesInsideBoundingBox(mitk::BaseGeometry::Pointer geometry3D) { //Check planes which are inside the bounding box mitk::ClippedSurfaceBoundsCalculator* calculator = new mitk::ClippedSurfaceBoundsCalculator(); mitk::Image::Pointer image = mitk::Image::New(); image->Initialize( mitk::MakePixelType(), *(geometry3D.GetPointer()) ); //Check planes which are only on one slice: //Slice 0 mitk::Point3D origin; origin[0] = 511; // Set to 511.9 so that the intersection point is inside the bounding box origin[1] = 0; origin[2] = 0; mitk::Vector3D normal; mitk::FillVector3D(normal, 1, 0, 0); mitk::PlaneGeometry::Pointer planeSagittalOne = mitk::PlaneGeometry::New(); planeSagittalOne->InitializePlane(origin, normal); calculator->SetInput( planeSagittalOne , image); calculator->Update(); mitk::ClippedSurfaceBoundsCalculator::OutputType minMax = calculator->GetMinMaxSpatialDirectionZ(); MITK_TEST_CONDITION(minMax.first == 0 && minMax.second == 19, "Check if plane is from slice 0 to slice 19"); //Slice 3 origin[0] = 256; MITK_INFO << "Case1 origin: " << origin; mitk::PlaneGeometry::Pointer planeSagittalTwo = mitk::PlaneGeometry::New(); planeSagittalTwo->InitializePlane(origin, normal); MITK_INFO << "PlaneNormal: " << planeSagittalTwo->GetNormal(); MITK_INFO << "PlaneOrigin: " << planeSagittalTwo->GetOrigin(); calculator->SetInput( planeSagittalTwo , image); calculator->Update(); minMax = calculator->GetMinMaxSpatialDirectionZ(); MITK_INFO << "min: " << minMax.first << " max: " << minMax.second; MITK_TEST_CONDITION(minMax.first == 0 && minMax.second == 19, "Check if plane is from slice 0 to slice 19"); //Slice 17 origin[0] = 0; // Set to 0.1 so that the intersection point is inside the bounding box mitk::PlaneGeometry::Pointer planeOnSliceSeventeen = mitk::PlaneGeometry::New(); planeOnSliceSeventeen->InitializePlane(origin, normal); calculator->SetInput( planeOnSliceSeventeen , image); calculator->Update(); minMax = calculator->GetMinMaxSpatialDirectionZ(); MITK_TEST_CONDITION(minMax.first == 0 && minMax.second == 19, "Check if plane is from slice 0 to slice 19"); //Crooked planes: origin[0] = 0; origin[1] = 507; origin[2] = 0; normal[0] = 1; normal[1] = -1; normal[2] = 1; mitk::PlaneGeometry::Pointer planeCrookedOne = mitk::PlaneGeometry::New(); planeCrookedOne->InitializePlane(origin, normal); calculator->SetInput( planeCrookedOne , image); calculator->Update(); minMax = calculator->GetMinMaxSpatialDirectionZ(); MITK_INFO << "min: " << minMax.first << " max: " << minMax.second; MITK_TEST_CONDITION(minMax.first == 0 && minMax.second == 4, "Check if plane is from slice 0 to slice 4 with inclined plane"); origin[0] = 512; origin[1] = 0; origin[2] = 16; mitk::PlaneGeometry::Pointer planeCrookedTwo = mitk::PlaneGeometry::New(); planeCrookedTwo->InitializePlane(origin, normal); calculator->SetInput( planeCrookedTwo , image); calculator->Update(); minMax = calculator->GetMinMaxSpatialDirectionZ(); MITK_INFO << "min: " << minMax.first << " max: " << minMax.second; MITK_TEST_CONDITION(minMax.first == 17 && minMax.second == 19, "Check if plane is from slice 17 to slice 19 with inclined plane"); origin[0] = 511; origin[1] = 0; origin[2] = 0; normal[1] = 0; normal[2] = 0.04; mitk::PlaneGeometry::Pointer planeCrookedThree = mitk::PlaneGeometry::New(); planeCrookedThree->InitializePlane(origin, normal); calculator->SetInput( planeCrookedThree , image); calculator->Update(); minMax = calculator->GetMinMaxSpatialDirectionZ(); MITK_INFO << "min: " << minMax.first << " max: " << minMax.second; MITK_TEST_CONDITION(minMax.first == 0 && minMax.second == 19, "Check if plane is from slice 0 to slice 19 with inclined plane"); delete calculator; } static void CheckPlanesOutsideOfBoundingBox(mitk::BaseGeometry::Pointer geometry3D) { //Check planes which are outside of the bounding box mitk::ClippedSurfaceBoundsCalculator* calculator = new mitk::ClippedSurfaceBoundsCalculator(); mitk::Image::Pointer image = mitk::Image::New(); image->Initialize( mitk::MakePixelType(), *(geometry3D.GetPointer()) ); //In front of the bounding box mitk::Point3D origin; origin[0] = 511; origin[1] = 0; origin[2] = -5; mitk::Vector3D normal; mitk::FillVector3D(normal, 0, 0, 1); mitk::PlaneGeometry::Pointer planeInFront = mitk::PlaneGeometry::New(); planeInFront->InitializePlane(origin, normal); calculator->SetInput( planeInFront , image); calculator->Update(); mitk::ClippedSurfaceBoundsCalculator::OutputType minMax = calculator->GetMinMaxSpatialDirectionZ(); MITK_TEST_CONDITION(minMax.first == std::numeric_limits::max(), "Check if min value hasn't been set"); MITK_TEST_CONDITION(minMax.second == std::numeric_limits::min(), "Check if max value hasn't been set"); //Behind the bounding box origin[2] = 515; mitk::PlaneGeometry::Pointer planeBehind = mitk::PlaneGeometry::New(); planeBehind->InitializePlane(origin, normal); calculator->SetInput( planeBehind , image); calculator->Update(); minMax = calculator->GetMinMaxSpatialDirectionZ(); MITK_TEST_CONDITION(minMax.first == std::numeric_limits::max(), "Check if min value hasn't been set"); MITK_TEST_CONDITION(minMax.second == std::numeric_limits::min(), "Check if max value hasn't been set"); //Above origin[1] = 515; mitk::FillVector3D(normal, 0, 1, 0); mitk::PlaneGeometry::Pointer planeAbove = mitk::PlaneGeometry::New(); planeAbove->InitializePlane(origin, normal); calculator->SetInput( planeAbove , image); calculator->Update(); minMax = calculator->GetMinMaxSpatialDirectionZ(); MITK_TEST_CONDITION(minMax.first == std::numeric_limits::max(), "Check if min value hasn't been set"); MITK_TEST_CONDITION(minMax.second == std::numeric_limits::min(), "Check if max value hasn't been set"); //Below origin[1] = -5; mitk::PlaneGeometry::Pointer planeBelow = mitk::PlaneGeometry::New(); planeBelow->InitializePlane(origin, normal); calculator->SetInput( planeBelow , image); calculator->Update(); minMax = calculator->GetMinMaxSpatialDirectionZ(); MITK_TEST_CONDITION(minMax.first == std::numeric_limits::max(), "Check if min value hasn't been set"); MITK_TEST_CONDITION(minMax.second == std::numeric_limits::min(), "Check if max value hasn't been set"); //Left side origin[0] = -5; mitk::FillVector3D(normal, 1, 0, 0); mitk::PlaneGeometry::Pointer planeLeftSide = mitk::PlaneGeometry::New(); planeLeftSide->InitializePlane(origin, normal); calculator->SetInput( planeLeftSide , image); calculator->Update(); minMax = calculator->GetMinMaxSpatialDirectionZ(); MITK_TEST_CONDITION(minMax.first == std::numeric_limits::max(), "Check if min value hasn't been set"); MITK_TEST_CONDITION(minMax.second == std::numeric_limits::min(), "Check if max value hasn't been set"); //Right side origin[1] = 515; mitk::PlaneGeometry::Pointer planeRightSide = mitk::PlaneGeometry::New(); planeRightSide->InitializePlane(origin, normal); calculator->SetInput( planeRightSide , image); calculator->Update(); minMax = calculator->GetMinMaxSpatialDirectionZ(); MITK_TEST_CONDITION(minMax.first == std::numeric_limits::max(), "Check if min value hasn't been set"); MITK_TEST_CONDITION(minMax.second == std::numeric_limits::min(), "Check if max value hasn't been set"); delete calculator; } static void CheckIntersectionPointsOfTwoGeometry3D(mitk::BaseGeometry::Pointer firstGeometry3D, mitk::BaseGeometry::Pointer secondGeometry3D) { mitk::ClippedSurfaceBoundsCalculator* calculator = new mitk::ClippedSurfaceBoundsCalculator(); mitk::Image::Pointer firstImage = mitk::Image::New(); firstImage->Initialize( mitk::MakePixelType(), *(firstGeometry3D.GetPointer()) ); calculator->SetInput( secondGeometry3D, firstImage); calculator->Update(); mitk::ClippedSurfaceBoundsCalculator::OutputType minMax = calculator->GetMinMaxSpatialDirectionZ(); minMax = calculator->GetMinMaxSpatialDirectionZ(); MITK_INFO << "min: " << minMax.first << " max: " << minMax.second; MITK_TEST_CONDITION(minMax.first == 0 && minMax.second == 19, "Check if plane is from slice 0 to slice 19"); } static void CheckIntersectionWithPointCloud( mitk::BaseGeometry::Pointer geometry3D ) { //Check planes which are inside the bounding box mitk::Image::Pointer image = mitk::Image::New(); image->Initialize( mitk::MakePixelType(), *(geometry3D.GetPointer()) ); { mitk::Point3D pnt1, pnt2; pnt1[0] = 3; pnt1[1] = 5; pnt1[2] = 3; pnt2[0] = 8; pnt2[1] = 3; pnt2[2] = 8; mitk::ClippedSurfaceBoundsCalculator::PointListType pointlist; pointlist.push_back( pnt1 ); pointlist.push_back( pnt2 ); mitk::ClippedSurfaceBoundsCalculator calculator; calculator.SetInput( pointlist, image ); calculator.Update(); mitk::ClippedSurfaceBoundsCalculator::OutputType minMaxZ = calculator.GetMinMaxSpatialDirectionZ(); MITK_TEST_CONDITION(minMaxZ.first == 3 && minMaxZ.second == 8, "Check if points span from slice 3 to slice 8 in axial"); mitk::ClippedSurfaceBoundsCalculator::OutputType minMaxX = calculator.GetMinMaxSpatialDirectionX(); MITK_TEST_CONDITION(minMaxX.first == 3 && minMaxX.second == 5, "Check if points span from slice 3 to slice 5 in sagittal"); } { mitk::Point3D pnt1, pnt2; pnt1.Fill( -3 ); pnt2.Fill( 600 ); mitk::ClippedSurfaceBoundsCalculator::PointListType pointlist; pointlist.push_back( pnt1 ); pointlist.push_back( pnt2 ); mitk::ClippedSurfaceBoundsCalculator calculator; calculator.SetInput( pointlist, image ); calculator.Update(); mitk::ClippedSurfaceBoundsCalculator::OutputType minMaxZ = calculator.GetMinMaxSpatialDirectionZ(); MITK_TEST_CONDITION(minMaxZ.first == 0 && minMaxZ.second == 19, "Check if points are correctly clipped to slice 0 and slice 19 in axial"); mitk::ClippedSurfaceBoundsCalculator::OutputType minMaxX = calculator.GetMinMaxSpatialDirectionX(); MITK_TEST_CONDITION(minMaxX.first == 0 && minMaxX.second == 511, "Check if points are correctly clipped to slice 0 and slice 511 in sagittal"); } } int mitkClippedSurfaceBoundsCalculatorTest(int, char* []) { // always start with this! MITK_TEST_BEGIN("ClippedSurfaceBoundsCalculator"); /** The class mitkClippedSurfaceBoundsCalculator calculates the intersection points of a PlaneGeometry and a Geometry3D. * This unittest checks if the correct min and max values for the three spatial directions (x, y, z) * are calculated. To test this we define artifical PlaneGeometries and Geometry3Ds and test different * scenarios: * * 1. planes which are inside the bounding box of a 3D geometry but only on one slice * 2. planes which are outside of the bounding box * 3. planes which are inside the bounding box but over more than one slice * * Note: Currently rotated geometries are not tested! */ /********************* Define Geometry3D ***********************/ //Define origin: mitk::Point3D origin; origin[0] = 511; origin[1] = 0; origin[2] = 0; //Define normal: mitk::Vector3D normal; mitk::FillVector3D(normal, 0, 0, 1); //Initialize PlaneGeometry: mitk::PlaneGeometry::Pointer planeGeometry = mitk::PlaneGeometry::New(); planeGeometry->InitializePlane(origin, normal); //Set Bounds: mitk::BoundingBox::BoundsArrayType bounds = planeGeometry->GetBounds(); bounds[0] = 0; bounds[1] = 512; bounds[2] = 0; bounds[3] = 512; bounds[4] = 0; bounds[5] = 1; planeGeometry->SetBounds(bounds); //Initialize SlicedGeometry3D: mitk::SlicedGeometry3D::Pointer slicedGeometry3D = mitk::SlicedGeometry3D::New(); - slicedGeometry3D->InitializeEvenlySpaced(dynamic_cast(planeGeometry.GetPointer()), 20); + slicedGeometry3D->InitializeEvenlySpaced(dynamic_cast(planeGeometry.GetPointer()), 20); mitk::BaseGeometry::Pointer geometry3D = dynamic_cast< mitk::BaseGeometry* > ( slicedGeometry3D.GetPointer() ); geometry3D->SetImageGeometry(true); //Define origin for second Geometry3D; mitk::Point3D origin2; origin2[0] = 511; origin2[1] = 60; origin2[2] = 0; //Define normal: mitk::Vector3D normal2; mitk::FillVector3D(normal2, 0, 1, 0); //Initialize PlaneGeometry: mitk::PlaneGeometry::Pointer planeGeometry2 = mitk::PlaneGeometry::New(); planeGeometry2->InitializePlane(origin2, normal2); //Initialize SlicedGeometry3D: mitk::SlicedGeometry3D::Pointer secondSlicedGeometry3D = mitk::SlicedGeometry3D::New(); - secondSlicedGeometry3D->InitializeEvenlySpaced(dynamic_cast(planeGeometry2.GetPointer()), 20); + secondSlicedGeometry3D->InitializeEvenlySpaced(dynamic_cast(planeGeometry2.GetPointer()), 20); mitk::BaseGeometry::Pointer secondGeometry3D = dynamic_cast< mitk::BaseGeometry* > ( secondSlicedGeometry3D.GetPointer() ); secondGeometry3D->SetImageGeometry(true); /***************************************************************/ CheckPlanesInsideBoundingBoxOnlyOnOneSlice(geometry3D); CheckPlanesOutsideOfBoundingBox(geometry3D); CheckPlanesInsideBoundingBox(geometry3D); CheckIntersectionPointsOfTwoGeometry3D(geometry3D, secondGeometry3D); CheckIntersectionWithPointCloud( geometry3D ); /** ToDo: * test also rotated 3D geometry! */ MITK_TEST_END(); } diff --git a/Core/Code/Testing/mitkGeometry2DTest.cpp b/Core/Code/Testing/mitkGeometry2DTest.cpp index eb340562c4..a99c42438d 100644 --- a/Core/Code/Testing/mitkGeometry2DTest.cpp +++ b/Core/Code/Testing/mitkGeometry2DTest.cpp @@ -1,157 +1,157 @@ /*=================================================================== 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 "mitkGeometry3D.h" #include #include #include "mitkRotationOperation.h" #include "mitkInteractionConst.h" #include #include #include "mitkTestingMacros.h" #include #include -mitk::Geometry2D::Pointer createGeometry2D() +mitk::PlaneGeometry::Pointer createGeometry2D() { mitk::Vector3D mySpacing; mySpacing[0] = 31; mySpacing[1] = 0.1; mySpacing[2] = 5.4; mitk::Point3D myOrigin; myOrigin[0] = 8; myOrigin[1] = 9; myOrigin[2] = 10; mitk::AffineTransform3D::Pointer myTransform = mitk::AffineTransform3D::New(); itk::Matrix transMatrix; transMatrix.Fill(0); transMatrix[0][0] = 1; transMatrix[1][1] = 2; transMatrix[2][2] = 4; myTransform->SetMatrix(transMatrix); - mitk::Geometry2D::Pointer geometry2D = mitk::Geometry2D::New(); + mitk::PlaneGeometry::Pointer geometry2D = mitk::PlaneGeometry::New(); geometry2D->SetIndexToWorldTransform(myTransform); geometry2D->SetSpacing(mySpacing); geometry2D->SetOrigin(myOrigin); return geometry2D; } int testGeometry2DCloning() { - mitk::Geometry2D::Pointer geometry2D = createGeometry2D(); + mitk::PlaneGeometry::Pointer geometry2D = createGeometry2D(); try { - mitk::Geometry2D::Pointer clone = geometry2D->Clone(); + mitk::PlaneGeometry::Pointer clone = geometry2D->Clone(); itk::Matrix matrix = clone->GetIndexToWorldTransform()->GetMatrix(); MITK_TEST_CONDITION(matrix[0][0] == 31, "Test if matrix element exists..."); double origin = geometry2D->GetOrigin()[0]; MITK_TEST_CONDITION(mitk::Equal(origin, 8),"First Point of origin as expected..."); double spacing = geometry2D->GetSpacing()[0]; MITK_TEST_CONDITION(mitk::Equal(spacing, 31),"First Point of spacing as expected..."); } catch (...) { MITK_TEST_CONDITION(false, "Error during access on a member of cloned geometry"); } // direction [row] [coloum] MITK_TEST_OUTPUT( << "Casting a rotated 2D ITK Image to a MITK Image and check if Geometry is still same" ); return EXIT_SUCCESS; } bool compareMatrix(itk::Matrix left, itk::Matrix right) { bool equal = true; for (int i = 0; i < 3; ++i) for (int j = 0; j < 3; ++j) equal &= mitk::Equal(left[i][j], right[i][j]); return equal; } int testGeometry2DInitializeOrder() { mitk::Vector3D mySpacing; mySpacing[0] = 31; mySpacing[1] = 0.1; mySpacing[2] = 5.4; mitk::Point3D myOrigin; myOrigin[0] = 8; myOrigin[1] = 9; myOrigin[2] = 10; mitk::AffineTransform3D::Pointer myTransform = mitk::AffineTransform3D::New(); itk::Matrix transMatrix; transMatrix.Fill(0); transMatrix[0][0] = 1; transMatrix[1][1] = 2; transMatrix[2][2] = 4; myTransform->SetMatrix(transMatrix); - mitk::Geometry2D::Pointer geometry2D1 = mitk::Geometry2D::New(); + mitk::PlaneGeometry::Pointer geometry2D1 = mitk::PlaneGeometry::New(); geometry2D1->SetIndexToWorldTransform(myTransform); geometry2D1->SetSpacing(mySpacing); geometry2D1->SetOrigin(myOrigin); - mitk::Geometry2D::Pointer geometry2D2 = mitk::Geometry2D::New(); + mitk::PlaneGeometry::Pointer geometry2D2 = mitk::PlaneGeometry::New(); geometry2D2->SetSpacing(mySpacing); geometry2D2->SetOrigin(myOrigin); geometry2D2->SetIndexToWorldTransform(myTransform); - mitk::Geometry2D::Pointer geometry2D3 = mitk::Geometry2D::New(); + mitk::PlaneGeometry::Pointer geometry2D3 = mitk::PlaneGeometry::New(); geometry2D3->SetIndexToWorldTransform(myTransform); geometry2D3->SetSpacing(mySpacing); geometry2D3->SetOrigin(myOrigin); geometry2D3->SetIndexToWorldTransform(myTransform); MITK_TEST_CONDITION(mitk::Equal(geometry2D1->GetOrigin(), geometry2D2->GetOrigin()),"Origin of Geometry 1 match those of Geometry 2."); MITK_TEST_CONDITION(mitk::Equal(geometry2D1->GetOrigin(), geometry2D3->GetOrigin()),"Origin of Geometry 1 match those of Geometry 3."); MITK_TEST_CONDITION(mitk::Equal(geometry2D2->GetOrigin(), geometry2D3->GetOrigin()),"Origin of Geometry 2 match those of Geometry 3."); MITK_TEST_CONDITION(mitk::Equal(geometry2D1->GetSpacing(), geometry2D2->GetSpacing()),"Spacing of Geometry 1 match those of Geometry 2."); MITK_TEST_CONDITION(mitk::Equal(geometry2D1->GetSpacing(), geometry2D3->GetSpacing()),"Spacing of Geometry 1 match those of Geometry 3."); MITK_TEST_CONDITION(mitk::Equal(geometry2D2->GetSpacing(), geometry2D3->GetSpacing()),"Spacing of Geometry 2 match those of Geometry 3."); MITK_TEST_CONDITION(compareMatrix(geometry2D1->GetIndexToWorldTransform()->GetMatrix(), geometry2D2->GetIndexToWorldTransform()->GetMatrix()),"Transformation of Geometry 1 match those of Geometry 2."); MITK_TEST_CONDITION(compareMatrix(geometry2D1->GetIndexToWorldTransform()->GetMatrix(), geometry2D3->GetIndexToWorldTransform()->GetMatrix()),"Transformation of Geometry 1 match those of Geometry 3."); MITK_TEST_CONDITION(compareMatrix(geometry2D2->GetIndexToWorldTransform()->GetMatrix(), geometry2D3->GetIndexToWorldTransform()->GetMatrix()),"Transformation of Geometry 2 match those of Geometry 3."); return EXIT_SUCCESS; } int mitkGeometry2DTest(int /*argc*/, char* /*argv*/[]) { MITK_TEST_BEGIN(mitkGeometry3DTest); int result; MITK_TEST_CONDITION_REQUIRED ( (result = testGeometry2DCloning()) == EXIT_SUCCESS, ""); // See bug 15990 // MITK_TEST_CONDITION_REQUIRED ( (result = testGeometry2DInitializeOrder()) == EXIT_SUCCESS, ""); MITK_TEST_END(); return EXIT_SUCCESS; } diff --git a/Core/Code/Testing/mitkGeometryDataToSurfaceFilterTest.cpp b/Core/Code/Testing/mitkGeometryDataToSurfaceFilterTest.cpp index 86fe52a33e..9407e0fda3 100644 --- a/Core/Code/Testing/mitkGeometryDataToSurfaceFilterTest.cpp +++ b/Core/Code/Testing/mitkGeometryDataToSurfaceFilterTest.cpp @@ -1,253 +1,253 @@ /*=================================================================== 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 "mitkGeometry2DDataToSurfaceFilter.h" #include "mitkSurface.h" #include "mitkPlaneGeometry.h" #include "mitkGeometry2DData.h" #include "vtkPolyData.h" #include template int testExpectedIndexBoundingBox(mitk::BaseGeometry* geometry, TScalarType expectedIndexBounds[6]) { mitk::BoundingBox* bb = const_cast(geometry->GetBoundingBox()); mitk::BoundingBox::BoundsArrayType bounds = bb->GetBounds(); int i; for(i=0;i<6;++i) { if( mitk::Equal(bounds[i], expectedIndexBounds[i]) == false) { std::cout<<"[FAILED]"< int testExpectedAxisParallelBoundingBox(mitk::BaseGeometry* geometry, TScalarType expectedAxisParallelBounds[6]) { mitk::BoundingBox::Pointer bb = geometry->CalculateBoundingBoxRelativeToTransform(NULL); mitk::BoundingBox::BoundsArrayType bounds = bb->GetBounds(); int i; for(i=0;i<6;++i) { if( mitk::Equal(bounds[i], expectedAxisParallelBounds[i]) == false) { std::cout<<"[FAILED]"<GetVtkPolyData() == NULL) || (surface->GetVtkPolyData()->GetNumberOfPoints() == 0 )) { std::cout<<"[FAILED]"<GetVtkPolyData(); polys->ComputeBounds(); polys->GetBounds( bounds ); int i; if(expectIdentityTransform == false) { - mitk::Geometry2D::Pointer geometry = mitk::Geometry2D::New(); + mitk::PlaneGeometry::Pointer geometry = mitk::PlaneGeometry::New(); geometry->SetFloatBounds(bounds); geometry->SetIndexToWorldTransform(surface->GetGeometry()->GetIndexToWorldTransform()); mitk::BoundingBox::BoundsArrayType bb = const_cast(geometry->GetBoundingBox())->GetBounds(); for(i=0;i<6;++i) bounds[i]=bb[i]; } std::cout << " Testing GetBoundingBox() "; if((result=testExpectedIndexBoundingBox(surface->GetGeometry(), bounds)) != EXIT_SUCCESS) { std::cout<<"[FAILED]"<GetOutput()->SetRequestedRegionToLargestPossibleRegion(); std::cout<<"[PASSED]"<UpdateOutputInformation(); std::cout<<"[PASSED]"<GetOutput()->GetGeometry(), expectedIndexBounds)) != EXIT_SUCCESS) { return result; } std::cout << "Testing correctness of axis-parallel bounding-box after UpdateOutputInformation(): "; if((result=testExpectedAxisParallelBoundingBox(geometryToSurfaceFilter->GetOutput()->GetGeometry(), expectedAxisParallelBounds)) != EXIT_SUCCESS) { return result; } std::cout << "Testing Update(): "; geometryToSurfaceFilter->Update(); std::cout<<"[PASSED]"<GetOutput()->GetGeometry(), expectedIndexBounds)) != EXIT_SUCCESS) { return result; } std::cout << "Testing correctness of axis-parallel bounding-box after UpdateOutputInformation(): "; if((result=testExpectedAxisParallelBoundingBox(geometryToSurfaceFilter->GetOutput()->GetGeometry(), expectedAxisParallelBounds)) != EXIT_SUCCESS) { return result; } std::cout << "Testing bounding-box consistency: "<GetOutput(), expectIdentityTransform)) != EXIT_SUCCESS) { std::cout<<"[FAILED]"<InitializeStandardPlane(50, 100); mitk::FillVector3D(origin, 1.0, 2.0, 3.0); plane->SetOrigin(origin); mitk::Geometry2DData::Pointer geometryData = mitk::Geometry2DData::New(); geometryData->SetGeometry2D(plane); std::cout << "Testing SetInput(): "; geometryToSurfaceFilter->SetInput(geometryData); std::cout<<"[PASSED]"<GetInput() != geometryData ) { std::cout<<"[FAILED]"<GetPlaceByGeometry() != false ) { std::cout<<"[FAILED]"<SetPlaceByGeometry(true); if (geometryToSurfaceFilter->GetPlaceByGeometry() != true ) { std::cout<<"[FAILED]"<InsertElement( 0, bbMin ); pointsContainer->InsertElement( 1, bbMax ); boundingBox->SetPoints( pointsContainer ); boundingBox->ComputeBoundingBox(); geometryToSurfaceFilter->SetPlaceByGeometry( true ); geometryToSurfaceFilter->SetBoundingBox( boundingBox ); mitk::ScalarType expectedIndexBoundsWithBB[6] = {9.0, 39.0, 8.0, 88.0, 0.0, 0.0}; mitk::ScalarType expectedAxisParallelBoundsWithBB[6] = {10.0, 40.0, 10.0, 90.0, 3.0, 3.0}; if((result=testGeometryDataToSurfaceFilter(geometryToSurfaceFilter, expectedIndexBoundsWithBB, expectedAxisParallelBoundsWithBB, true)) != EXIT_SUCCESS) { return result; } std::cout<<"[TEST DONE]"< #include #include #include "mitkItkImageFileReader.h" #include #include #include "mitkImageGenerator.h" #include "mitkImageReadAccessor.h" #include "mitkException.h" #include "mitkPixelTypeMultiplex.h" #include "mitkImagePixelReadAccessor.h" #include "mitkImageSliceSelector.h" // itk includes #include #include // stl includes #include // vtk includes #include // Checks if reference count is correct after using GetVtkImageData() bool ImageVtkDataReferenceCheck(const char* fname) { const std::string filename = std::string(fname); mitk::ItkImageFileReader::Pointer imageReader = mitk::ItkImageFileReader::New(); try { imageReader->SetFileName(filename); imageReader->Update(); } catch(...) { MITK_TEST_FAILED_MSG(<< "Could not read file for testing: " << filename); return false; } { mitk::Image::Pointer image = imageReader->GetOutput(); vtkImageData* vtk = image->GetVtkImageData(); if(vtk == NULL) return false; } return true; } template void TestRandomPixelAccess( const mitk::PixelType ptype, mitk::Image::Pointer image, mitk::Point3D & point, mitk::ScalarType & value ) { // generate a random point in world coordinates mitk::Point3D xMax, yMax, zMax, xMaxIndex, yMaxIndex, zMaxIndex; xMaxIndex.Fill(0.0f); yMaxIndex.Fill(0.0f); zMaxIndex.Fill(0.0f); xMaxIndex[0] = image->GetLargestPossibleRegion().GetSize()[0]; yMaxIndex[1] = image->GetLargestPossibleRegion().GetSize()[1]; zMaxIndex[2] = image->GetLargestPossibleRegion().GetSize()[2]; image->GetGeometry()->IndexToWorld(xMaxIndex, xMax); image->GetGeometry()->IndexToWorld(yMaxIndex, yMax); image->GetGeometry()->IndexToWorld(zMaxIndex, zMax); MITK_INFO << "Origin " << image->GetGeometry()->GetOrigin()[0] << " "<< image->GetGeometry()->GetOrigin()[1] << " "<< image->GetGeometry()->GetOrigin()[2] << ""; MITK_INFO << "MaxExtend " << xMax[0] << " "<< yMax[1] << " "<< zMax[2] << ""; itk::Statistics::MersenneTwisterRandomVariateGenerator::Pointer randomGenerator = itk::Statistics::MersenneTwisterRandomVariateGenerator::New(); randomGenerator->Initialize( std::rand() ); // initialize with random value, to get sensible random points for the image point[0] = randomGenerator->GetUniformVariate( image->GetGeometry()->GetOrigin()[0], xMax[0]); point[1] = randomGenerator->GetUniformVariate( image->GetGeometry()->GetOrigin()[1], yMax[1]); point[2] = randomGenerator->GetUniformVariate( image->GetGeometry()->GetOrigin()[2], zMax[2]); MITK_INFO << "RandomPoint " << point[0] << " "<< point[1] << " "<< point[2] << ""; // test values and max/min mitk::ScalarType imageMin = image->GetStatistics()->GetScalarValueMin(); mitk::ScalarType imageMax = image->GetStatistics()->GetScalarValueMax(); // test accessing PixelValue with coordinate leading to a negative index const mitk::Point3D geom_origin = image->GetGeometry()->GetOrigin(); const mitk::Point3D geom_center = image->GetGeometry()->GetCenter(); // shift position from origin outside of the image ( in the opposite direction to [center-origin] vector which points in the inside) mitk::Point3D position = geom_origin + (geom_origin - geom_center); MITK_INFO << "Testing access outside of the image"; unsigned int dim = image->GetDimension(); if(dim == 3 || dim == 4){ mitk::ImagePixelReadAccessor imAccess3(image,image->GetVolumeData(0)); // Comparison ?>=0 not needed since all position[i] and timestep are unsigned int // (position[0]>=0 && position[1] >=0 && position[2]>=0 && timestep>=0) // bug-11978 : we still need to catch index with negative values if ( point[0] < 0 || point[1] < 0 || point[2] < 0 ) { MITK_WARN << "Given position ("<< point << ") is out of image range, returning 0." ; } else { value = static_cast(imAccess3.GetPixelByWorldCoordinates(point)); MITK_TEST_CONDITION( (value >= imageMin && value <= imageMax), "Value returned is between max/min"); } mitk::Index3D itkIndex; image->GetGeometry()->WorldToIndex(position, itkIndex); MITK_TEST_FOR_EXCEPTION_BEGIN(mitk::Exception); imAccess3.GetPixelByIndexSafe(itkIndex); MITK_TEST_FOR_EXCEPTION_END(mitk::Exception); } MITK_INFO << imageMin << " "<< imageMax << " "<< value << ""; } class mitkImageTestClass { public: void SetClonedGeometry_None_ClonedEqualInput() { mitk::Image::Pointer image = mitk::ImageGenerator::GenerateRandomImage(100, 100, 100, 1, 0.2, 0.3, 0.4); //----------------- // geometry information for image mitk::Point3D origin; mitk::Vector3D right, bottom; mitk::Vector3D spacing; mitk::FillVector3D(origin, 17.0, 19.92, 7.83); mitk::FillVector3D(right, 1.0, 2.0, 3.0); mitk::FillVector3D(bottom, 0.0, -3.0, 2.0); mitk::FillVector3D(spacing, 0.78, 0.91, 2.23); //InitializeStandardPlane(rightVector, downVector, spacing) mitk::PlaneGeometry::Pointer planegeometry = mitk::PlaneGeometry::New(); planegeometry->InitializeStandardPlane(100, 100, right, bottom, &spacing); planegeometry->SetOrigin(origin); planegeometry->ChangeImageGeometryConsideringOriginOffset(true); image->SetClonedGeometry(planegeometry); mitk::BaseGeometry::Pointer imageGeometry = image->GetGeometry(); itk::ScalableAffineTransform* frameNew = imageGeometry->GetIndexToWorldTransform(); itk::ScalableAffineTransform* frameOld = planegeometry->GetIndexToWorldTransform(); bool matrixEqual = true; for (int i = 0; i < 16; ++i) { double valueNew = *(frameNew->GetMatrix()[i]); double valueOld = *(frameOld->GetMatrix()[i]); //MITK_INFO << "Index: " << i << " Old: " << valueOld << " New: " << valueNew << " Difference:" << valueOld-valueNew<< std::endl; matrixEqual = matrixEqual && mitk::Equal(valueNew, valueOld, mitk::eps); } // Disabled because this test fails on the dashboard. Does not fail on my machine. // See Bug 6505 // MITK_TEST_CONDITION(matrixEqual, "Matrix elements of cloned matrix equal original matrix"); } }; int mitkImageTest(int argc, char* argv[]) { MITK_TEST_BEGIN(mitkImageTest); mitkImageTestClass tester; tester.SetClonedGeometry_None_ClonedEqualInput(); //Create Image out of nowhere mitk::Image::Pointer imgMem = mitk::Image::New(); mitk::PixelType pt = mitk::MakeScalarPixelType(); unsigned int dim[]={100,100,20}; MITK_TEST_CONDITION_REQUIRED( imgMem.IsNotNull(), "An image was created. "); // Initialize image imgMem->Initialize( pt, 3, dim); MITK_TEST_CONDITION_REQUIRED( imgMem->IsInitialized(), "Image::IsInitialized() ?"); MITK_TEST_CONDITION_REQUIRED( imgMem->GetPixelType() == pt, "PixelType was set correctly."); int *p = NULL; int *p2 = NULL; try { mitk::ImageReadAccessor imgMemAcc(imgMem); p = (int*)imgMemAcc.GetData(); } catch (mitk::Exception& e) { MITK_ERROR << e.what(); } MITK_TEST_CONDITION( p != NULL, "GetData() returned not-NULL pointer."); // filling image const unsigned int size = dim[0]*dim[1]*dim[2]; for(unsigned int i=0; iGetSliceData(dim[2]/2)); p2 = (int*)imgMemAcc.GetData(); } catch (mitk::Exception& e) { MITK_ERROR << e.what(); } MITK_TEST_CONDITION_REQUIRED( p2 != NULL, "Valid slice data returned"); unsigned int xy_size = dim[0]*dim[1]; unsigned int start_mid_slice = (dim[2]/2)*xy_size; isEqual = true; for(unsigned int i=0; i(); imgMem->Initialize( pType , 3, dim); MITK_TEST_CONDITION_REQUIRED(imgMem->GetDimension()== 3, "Testing initialization parameter dimension!"); MITK_TEST_CONDITION_REQUIRED(imgMem->GetPixelType() == pType, "Testing initialization parameter pixeltype!"); MITK_TEST_CONDITION_REQUIRED(imgMem->GetDimension(0) == dim[0] && imgMem->GetDimension(1)== dim[1] && imgMem->GetDimension(2)== dim[2], "Testing initialization of dimensions!"); MITK_TEST_CONDITION( imgMem->IsInitialized(), "Image is initialized."); // Setting volume again: try { mitk::ImageReadAccessor imgMemAcc(imgMem); imgMem->SetVolume(imgMemAcc.GetData()); } catch (mitk::Exception& e) { MITK_ERROR << e.what(); } //----------------- // geometry information for image mitk::Point3D origin; mitk::Vector3D right, bottom; mitk::Vector3D spacing; mitk::FillVector3D(origin, 17.0, 19.92, 7.83); mitk::FillVector3D(right, 1.0, 2.0, 3.0); mitk::FillVector3D(bottom, 0.0, -3.0, 2.0); mitk::FillVector3D(spacing, 0.78, 0.91, 2.23); //InitializeStandardPlane(rightVector, downVector, spacing) mitk::PlaneGeometry::Pointer planegeometry = mitk::PlaneGeometry::New(); planegeometry->InitializeStandardPlane(100, 100, right, bottom, &spacing); planegeometry->SetOrigin(origin); // Testing Initialize(const mitk::PixelType& type, const mitk::Geometry3D& geometry, unsigned int slices) with PlaneGeometry and GetData(): "; imgMem->Initialize( mitk::MakePixelType(), *planegeometry); MITK_TEST_CONDITION_REQUIRED( imgMem->GetGeometry()->GetOrigin() == static_cast(planegeometry)->GetOrigin(), "Testing correct setting of geometry via initialize!"); try { mitk::ImageReadAccessor imgMemAcc(imgMem); p = (int*)imgMemAcc.GetData(); } catch (mitk::Exception& e) { MITK_ERROR << e.what(); } MITK_TEST_CONDITION_REQUIRED( p!=NULL, "GetData() returned valid pointer."); // Testing Initialize(const mitk::PixelType& type, int sDim, const mitk::PlaneGeometry& geometry) and GetData(): "; imgMem->Initialize( mitk::MakePixelType() , 40, *planegeometry); try { mitk::ImageReadAccessor imgMemAcc(imgMem); p = (int*)imgMemAcc.GetData(); } catch (mitk::Exception& e) { MITK_ERROR << e.what(); } MITK_TEST_CONDITION_REQUIRED( p != NULL, "GetData() returned valid pointer."); //----------------- // testing origin information and methods MITK_TEST_CONDITION_REQUIRED( mitk::Equal(imgMem->GetGeometry()->GetOrigin(), origin), "Testing correctness of origin via GetGeometry()->GetOrigin(): "); // Setting origin via SetOrigin(origin): "; mitk::FillVector3D(origin, 37.0, 17.92, 27.83); imgMem->SetOrigin(origin); // Test origin MITK_TEST_CONDITION_REQUIRED( mitk::Equal(imgMem->GetGeometry()->GetOrigin(), origin), "Testing correctness of changed origin via GetGeometry()->GetOrigin(): "); MITK_TEST_CONDITION_REQUIRED( mitk::Equal(imgMem->GetSlicedGeometry()->GetGeometry2D(0)->GetOrigin(), origin), "Testing correctness of changed origin via GetSlicedGeometry()->GetGeometry2D(0)->GetOrigin(): "); //----------------- // testing spacing information and methodsunsigned int dim[]={100,100,20}; MITK_TEST_CONDITION_REQUIRED(mitk::Equal(imgMem->GetGeometry()->GetSpacing(), spacing), "Testing correct spacing from Geometry3D!"); mitk::FillVector3D(spacing, 7.0, 0.92, 1.83); imgMem->SetSpacing(spacing); MITK_TEST_CONDITION_REQUIRED( mitk::Equal(imgMem->GetGeometry()->GetSpacing(), spacing), "Testing correctness of changed spacing via GetGeometry()->GetSpacing(): "); MITK_TEST_CONDITION_REQUIRED( mitk::Equal(imgMem->GetSlicedGeometry()->GetGeometry2D(0)->GetSpacing(), spacing), "Testing correctness of changed spacing via GetSlicedGeometry()->GetGeometry2D(0)->GetSpacing(): "); mitk::Image::Pointer vecImg = mitk::Image::New(); try { mitk::ImageReadAccessor imgMemAcc(imgMem); vecImg->Initialize( imgMem->GetPixelType(), *imgMem->GetGeometry(), 2 /* #channels */, 0 /*tDim*/ ); vecImg->SetImportChannel(const_cast(imgMemAcc.GetData()), 0, mitk::Image::CopyMemory ); vecImg->SetImportChannel(const_cast(imgMemAcc.GetData()), 1, mitk::Image::CopyMemory ); mitk::ImageReadAccessor vecImgAcc(vecImg); mitk::ImageReadAccessor vecImgAcc0(vecImg, vecImg->GetChannelData(0)); mitk::ImageReadAccessor vecImgAcc1(vecImg, vecImg->GetChannelData(1)); MITK_TEST_CONDITION_REQUIRED(vecImgAcc0.GetData() != NULL && vecImgAcc1.GetData() != NULL, "Testing set and return of channel data!"); MITK_TEST_CONDITION_REQUIRED( vecImg->IsValidSlice(0,0,1) , ""); MITK_TEST_OUTPUT(<< " Testing whether CopyMemory worked"); MITK_TEST_CONDITION_REQUIRED(imgMemAcc.GetData() != vecImgAcc.GetData(), ""); MITK_TEST_OUTPUT(<< " Testing destruction after SetImportChannel"); vecImg = NULL; MITK_TEST_CONDITION_REQUIRED(vecImg.IsNull() , "testing destruction!"); } catch (mitk::Exception& e) { MITK_ERROR << e.what(); } //----------------- MITK_TEST_OUTPUT(<< "Testing initialization via vtkImageData"); MITK_TEST_OUTPUT(<< " Setting up vtkImageData"); vtkImageData* vtkimage = vtkImageData::New(); vtkimage->Initialize(); vtkimage->SetDimensions( 2, 3, 4); double vtkorigin[] = {-350,-358.203, -1363.5}; vtkimage->SetOrigin(vtkorigin); mitk::Point3D vtkoriginAsMitkPoint; mitk::vtk2itk(vtkorigin, vtkoriginAsMitkPoint); double vtkspacing[] = {1.367, 1.367, 2}; vtkimage->SetSpacing(vtkspacing); vtkimage->AllocateScalars(VTK_SHORT,1); std::cout<<"[PASSED]"<Initialize(vtkimage); MITK_TEST_CONDITION_REQUIRED(mitkByVtkImage->IsInitialized(), ""); vtkimage->Delete(); MITK_TEST_OUTPUT(<< " Testing whether spacing has been correctly initialized from vtkImageData"); mitk::Vector3D spacing2 = mitkByVtkImage->GetGeometry()->GetSpacing(); mitk::Vector3D vtkspacingAsMitkVector; mitk::vtk2itk(vtkspacing, vtkspacingAsMitkVector); MITK_TEST_CONDITION_REQUIRED(mitk::Equal(spacing2,vtkspacingAsMitkVector), ""); MITK_TEST_OUTPUT(<< " Testing whether GetSlicedGeometry(0)->GetOrigin() has been correctly initialized from vtkImageData"); mitk::Point3D origin2 = mitkByVtkImage->GetSlicedGeometry(0)->GetOrigin(); MITK_TEST_CONDITION_REQUIRED(mitk::Equal(origin2,vtkoriginAsMitkPoint), ""); MITK_TEST_OUTPUT(<< " Testing whether GetGeometry()->GetOrigin() has been correctly initialized from vtkImageData"); origin2 = mitkByVtkImage->GetGeometry()->GetOrigin(); MITK_TEST_CONDITION_REQUIRED(mitk::Equal(origin2,vtkoriginAsMitkPoint), ""); // TODO test the following initializers on channel-incorporation // void mitk::Image::Initialize(const mitk::PixelType& type, unsigned int dimension, unsigned int *dimensions, unsigned int channels) - // void mitk::Image::Initialize(const mitk::PixelType& type, int sDim, const mitk::Geometry2D& geometry2d, bool flipped, unsigned int channels, int tDim ) + // void mitk::Image::Initialize(const mitk::PixelType& type, int sDim, const mitk::PlaneGeometry& geometry2d, bool flipped, unsigned int channels, int tDim ) // void mitk::Image::Initialize(const mitk::Image* image) // void mitk::Image::Initialize(const mitkIpPicDescriptor* pic, int channels, int tDim, int sDim) //mitk::Image::Pointer vecImg = mitk::Image::New(); //vecImg->Initialize(PixelType(typeid(float), 6, itk::ImageIOBase::SYMMETRICSECONDRANKTENSOR), *imgMem->GetGeometry(), 2 /* #channels */, 0 /*tDim*/, false /*shiftBoundingBoxMinimumToZero*/ ); //vecImg->Initialize(PixelType(typeid(itk::Vector)), *imgMem->GetGeometry(), 2 /* #channels */, 0 /*tDim*/, false /*shiftBoundingBoxMinimumToZero*/ ); // testing access by index coordinates and by world coordinates MITK_TEST_CONDITION_REQUIRED(argc == 2, "Check if test image is accessible!"); const std::string filename = std::string(argv[1]); mitk::ItkImageFileReader::Pointer imageReader = mitk::ItkImageFileReader::New(); try { imageReader->SetFileName(filename); imageReader->Update(); } catch(...) { MITK_TEST_FAILED_MSG(<< "Could not read file for testing: " << filename); return 0; } mitk::Image::Pointer image = imageReader->GetOutput(); mitk::Point3D point; mitk::ScalarType value = -1.; mitkPixelTypeMultiplex3(TestRandomPixelAccess,image->GetImageDescriptor()->GetChannelTypeById(0),image,point,value) { // testing the clone method of mitk::Image mitk::Image::Pointer cloneImage = image->Clone(); MITK_TEST_CONDITION_REQUIRED(cloneImage->GetDimension() == image->GetDimension(), "Clone (testing dimension)"); MITK_TEST_CONDITION_REQUIRED(cloneImage->GetPixelType() == image->GetPixelType(), "Clone (testing pixel type)"); // After cloning an image the geometry of both images should be equal too MITK_TEST_CONDITION_REQUIRED(cloneImage->GetGeometry()->GetOrigin() == image->GetGeometry()->GetOrigin(), "Clone (testing origin)"); MITK_TEST_CONDITION_REQUIRED(cloneImage->GetGeometry()->GetSpacing() == image->GetGeometry()->GetSpacing(), "Clone (testing spacing)"); MITK_TEST_CONDITION_REQUIRED(mitk::MatrixEqualElementWise(cloneImage->GetGeometry()->GetIndexToWorldTransform()->GetMatrix(), image->GetGeometry()->GetIndexToWorldTransform()->GetMatrix()), "Clone (testing transformation matrix)"); MITK_TEST_CONDITION_REQUIRED(mitk::MatrixEqualElementWise(cloneImage->GetTimeGeometry()->GetGeometryForTimeStep(cloneImage->GetDimension(3)-1)->GetIndexToWorldTransform()->GetMatrix(), cloneImage->GetTimeGeometry()->GetGeometryForTimeStep(image->GetDimension(3)-1)->GetIndexToWorldTransform()->GetMatrix()), "Clone(testing time sliced geometry)"); for (unsigned int i = 0u; i < cloneImage->GetDimension(); ++i) { MITK_TEST_CONDITION_REQUIRED(cloneImage->GetDimension(i) == image->GetDimension(i), "Clone (testing dimension " << i << ")"); } } //access via itk if(image->GetDimension()> 3) // CastToItk only works with 3d images so we need to check for 4d images { mitk::ImageTimeSelector::Pointer selector = mitk::ImageTimeSelector::New(); selector->SetTimeNr(0); selector->SetInput(image); selector->Update(); image = selector->GetOutput(); } if(image->GetDimension()==3) { typedef itk::Image ItkFloatImage3D; ItkFloatImage3D::Pointer itkimage; try { mitk::CastToItkImage(image, itkimage); MITK_TEST_CONDITION_REQUIRED(itkimage.IsNotNull(), "Test conversion to itk::Image!"); } catch (std::exception& e) { MITK_INFO << e.what(); } mitk::Point3D itkPhysicalPoint; image->GetGeometry()->WorldToItkPhysicalPoint(point, itkPhysicalPoint); MITK_INFO << "ITKPoint " << itkPhysicalPoint[0] << " "<< itkPhysicalPoint[1] << " "<< itkPhysicalPoint[2] << ""; mitk::Point3D backTransformedPoint; image->GetGeometry()->ItkPhysicalPointToWorld(itkPhysicalPoint, backTransformedPoint); MITK_TEST_CONDITION_REQUIRED( mitk::Equal(point,backTransformedPoint), "Testing world->itk-physical->world consistency"); itk::Index<3> idx; bool status = itkimage->TransformPhysicalPointToIndex(itkPhysicalPoint, idx); MITK_INFO << "ITK Index " << idx[0] << " "<< idx[1] << " "<< idx[2] << ""; if(status && value != -1.) { float valByItk = itkimage->GetPixel(idx); MITK_TEST_CONDITION_REQUIRED( mitk::Equal(valByItk, value), "Compare value of pixel returned by mitk in comparison to itk"); } else { MITK_WARN<< "Index is out buffered region!"; } } else { MITK_INFO << "Image does not contain three dimensions, some test cases are skipped!"; } // clone generated 3D image with one slice in z direction (cf. bug 11058) unsigned int* threeDdim = new unsigned int[3]; threeDdim[0] = 100; threeDdim[1] = 200; threeDdim[2] = 1; mitk::Image::Pointer threeDImage = mitk::Image::New(); threeDImage->Initialize(mitk::MakeScalarPixelType(), 3, threeDdim); mitk::Image::Pointer cloneThreeDImage = threeDImage->Clone(); // check that the clone image has the same dimensionality as the source image MITK_TEST_CONDITION_REQUIRED( cloneThreeDImage->GetDimension() == 3, "Testing if the clone image initializes with 3D!"); MITK_TEST_CONDITION_REQUIRED( ImageVtkDataReferenceCheck(argv[1]), "Checking reference count of Image after using GetVtkImageData()"); MITK_TEST_END(); } diff --git a/Core/Code/Testing/mitkSliceNavigationControllerTest.cpp b/Core/Code/Testing/mitkSliceNavigationControllerTest.cpp index dafd58a116..13f828fc7c 100644 --- a/Core/Code/Testing/mitkSliceNavigationControllerTest.cpp +++ b/Core/Code/Testing/mitkSliceNavigationControllerTest.cpp @@ -1,572 +1,572 @@ /*=================================================================== 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 "mitkSliceNavigationController.h" #include "mitkPlaneGeometry.h" #include "mitkSlicedGeometry3D.h" #include "mitkRotationOperation.h" #include "mitkInteractionConst.h" #include "mitkPlanePositionManager.h" #include "mitkTestingMacros.h" #include "usGetModuleContext.h" #include "usModuleContext.h" #include "usServiceReference.h" #include #include #include bool operator==(const mitk::Geometry3D & left, const mitk::Geometry3D & right) { mitk::BoundingBox::BoundsArrayType leftbounds, rightbounds; leftbounds =left.GetBounds(); rightbounds=right.GetBounds(); unsigned int i; for(i=0;i<6;++i) if(mitk::Equal(leftbounds[i],rightbounds[i])==false) return false; const mitk::Geometry3D::TransformType::MatrixType & leftmatrix = left.GetIndexToWorldTransform()->GetMatrix(); const mitk::Geometry3D::TransformType::MatrixType & rightmatrix = right.GetIndexToWorldTransform()->GetMatrix(); unsigned int j; for(i=0;i<3;++i) { const mitk::Geometry3D::TransformType::MatrixType::ValueType* leftvector = leftmatrix[i]; const mitk::Geometry3D::TransformType::MatrixType::ValueType* rightvector = rightmatrix[i]; for(j=0;j<3;++j) if(mitk::Equal(leftvector[i],rightvector[i])==false) return false; } const mitk::Geometry3D::TransformType::OffsetType & leftoffset = left.GetIndexToWorldTransform()->GetOffset(); const mitk::Geometry3D::TransformType::OffsetType & rightoffset = right.GetIndexToWorldTransform()->GetOffset(); for(i=0;i<3;++i) if(mitk::Equal(leftoffset[i],rightoffset[i])==false) return false; return true; } int compareGeometry(const mitk::TimeGeometry & timeGeometry, const mitk::ScalarType& width, const mitk::ScalarType& height, const mitk::ScalarType& numSlices, const mitk::ScalarType& widthInMM, const mitk::ScalarType& heightInMM, const mitk::ScalarType& thicknessInMM, const mitk::Point3D& cornerpoint0, const mitk::Vector3D& right, const mitk::Vector3D& bottom, const mitk::Vector3D& normal) { //Probleme durch umstellung von Time-SlicedGeometry auf TimeGeometry? //Eventuell gibt es keine Entsprechung mehr. const mitk::BaseGeometry::Pointer geometry= timeGeometry.GetGeometryForTimeStep(0); std::cout << "Testing width, height and thickness (in units): "; if((mitk::Equal(geometry->GetExtent(0),width)==false) || (mitk::Equal(geometry->GetExtent(1),height)==false) || (mitk::Equal(geometry->GetExtent(2),numSlices)==false) ) { std::cout<<"[FAILED]"<GetExtentInMM(0),widthInMM)==false) || (mitk::Equal(geometry->GetExtentInMM(1),heightInMM)==false) || (mitk::Equal(geometry->GetExtentInMM(2),thicknessInMM)==false) ) { std::cout<<"[FAILED]"<GetAxisVector(0), dv)==false)) { std::cout<<"[FAILED]"<GetAxisVector(1), dv)==false)) { std::cout<<"[FAILED]"<GetAxisVector(2), dv)==false)) { std::cout<<"[FAILED]"<GetCornerPoint(0),cornerpoint0, (double) vnl_math::float_eps)==false)) { std::cout<<"[FAILED]"<GetCornerPoint(0), cornerpoint0)==false) { std::cout<<"[FAILED]"<SetInputWorldGeometry3D(geometry); std::cout<<"[PASSED]"<SetViewDirection(mitk::SliceNavigationController::Axial); std::cout<<"[PASSED]"<Update(); std::cout<<"[PASSED]"<GetCreatedWorldGeometry(), width, height, numSlices, widthInMM, heightInMM, thicknessInMM*numSlices, axialcornerpoint0, right, bottom*(-1.0), normal*(-1.0)); if(result!=EXIT_SUCCESS) { std::cout<<"[FAILED]"<SetViewDirection(mitk::SliceNavigationController::Frontal); std::cout<<"[PASSED]"<Update(); std::cout<<"[PASSED]"<GetAxisVector(1)*(+0.5/geometry->GetExtent(1)); result = compareGeometry(*sliceCtrl->GetCreatedWorldGeometry(), width, numSlices, height, widthInMM, thicknessInMM*numSlices, heightInMM, frontalcornerpoint0, right, normal, bottom); if(result!=EXIT_SUCCESS) { std::cout<<"[FAILED]"<SetViewDirection(mitk::SliceNavigationController::Sagittal); std::cout<<"[PASSED]"<Update(); std::cout<<"[PASSED]"<GetAxisVector(0)*(+0.5/geometry->GetExtent(0)); result = compareGeometry(*sliceCtrl->GetCreatedWorldGeometry(), height, numSlices, width, heightInMM, thicknessInMM*numSlices, widthInMM, sagittalcornerpoint0, bottom, normal, right); if(result!=EXIT_SUCCESS) { std::cout<<"[FAILED]"<InitializeStandardPlane(right.GetVnlVector(), bottom.GetVnlVector(), &spacing); planegeometry->SetOrigin(origin); //Create SlicedGeometry3D out of planeGeometry mitk::SlicedGeometry3D::Pointer slicedgeometry1 = mitk::SlicedGeometry3D::New(); unsigned int numSlices = 20; slicedgeometry1->InitializeEvenlySpaced(planegeometry, thicknessInMM, numSlices, false); //Create another slicedgeo which will be rotated mitk::SlicedGeometry3D::Pointer slicedgeometry2 = mitk::SlicedGeometry3D::New(); slicedgeometry2->InitializeEvenlySpaced(planegeometry, thicknessInMM, numSlices, false); //Create geo3D as reference mitk::Geometry3D::Pointer geometry = mitk::Geometry3D::New(); geometry->SetBounds(slicedgeometry1->GetBounds()); geometry->SetIndexToWorldTransform(slicedgeometry1->GetIndexToWorldTransform()); //Initialize planes for (int i=0; i < (int)numSlices; i++) { mitk::PlaneGeometry::Pointer geo2d = mitk::PlaneGeometry::New(); geo2d->Initialize(); geo2d->SetReferenceGeometry(geometry); slicedgeometry1->SetGeometry2D(geo2d,i); } for (int i=0; i < (int)numSlices; i++) { mitk::PlaneGeometry::Pointer geo2d = mitk::PlaneGeometry::New(); geo2d->Initialize(); geo2d->SetReferenceGeometry(geometry); slicedgeometry2->SetGeometry2D(geo2d,i); } slicedgeometry1->SetReferenceGeometry(geometry); slicedgeometry2->SetReferenceGeometry(geometry); //Create SNC mitk::SliceNavigationController::Pointer sliceCtrl1 = mitk::SliceNavigationController::New(); sliceCtrl1->SetInputWorldGeometry3D(slicedgeometry1); sliceCtrl1->Update(); mitk::SliceNavigationController::Pointer sliceCtrl2 = mitk::SliceNavigationController::New(); sliceCtrl2->SetInputWorldGeometry3D(slicedgeometry2); sliceCtrl2->Update(); slicedgeometry1->SetSliceNavigationController(sliceCtrl1); slicedgeometry2->SetSliceNavigationController(sliceCtrl2); // Whats current geometry? MITK_INFO << "center: " << sliceCtrl1->GetCurrentPlaneGeometry()->GetCenter(); MITK_INFO << "normal: " << sliceCtrl1->GetCurrentPlaneGeometry()->GetNormal(); MITK_INFO << "origin: " << sliceCtrl1->GetCurrentPlaneGeometry()->GetOrigin(); MITK_INFO << "axis0 : " << sliceCtrl1->GetCurrentPlaneGeometry()->GetAxisVector(0); MITK_INFO << "aixs1 : " << sliceCtrl1->GetCurrentPlaneGeometry()->GetAxisVector(1); // // Now reorient slices (ONE POINT, ONE NORMAL) mitk::Point3D oldCenter, oldOrigin; mitk::Vector3D oldAxis0, oldAxis1; oldCenter = sliceCtrl1->GetCurrentPlaneGeometry()->GetCenter(); oldOrigin = sliceCtrl1->GetCurrentPlaneGeometry()->GetOrigin(); oldAxis0 = sliceCtrl1->GetCurrentPlaneGeometry()->GetAxisVector(0); oldAxis1 = sliceCtrl1->GetCurrentPlaneGeometry()->GetAxisVector(1); mitk::Point3D orientCenter; mitk::Vector3D orientNormal; orientCenter = oldCenter; mitk::FillVector3D(orientNormal, 0.3, 0.1, 0.8); orientNormal.Normalize(); sliceCtrl1->ReorientSlices(orientCenter,orientNormal); mitk::Point3D newCenter, newOrigin; mitk::Vector3D newNormal; newCenter = sliceCtrl1->GetCurrentPlaneGeometry()->GetCenter(); newOrigin = sliceCtrl1->GetCurrentPlaneGeometry()->GetOrigin(); newNormal = sliceCtrl1->GetCurrentPlaneGeometry()->GetNormal(); newNormal.Normalize(); itk::Index<3> orientCenterIdx; itk::Index<3> newCenterIdx; sliceCtrl1->GetCurrentGeometry3D()->WorldToIndex(orientCenter, orientCenterIdx); sliceCtrl1->GetCurrentGeometry3D()->WorldToIndex(newCenter, newCenterIdx); if ( (newCenterIdx != orientCenterIdx) || ( !mitk::Equal(orientNormal, newNormal) ) ) { MITK_INFO << "Reorient Planes (1 point, 1 vector) not working as it should"; MITK_INFO << "orientCenterIdx: " << orientCenterIdx; MITK_INFO << "newCenterIdx: " << newCenterIdx; MITK_INFO << "orientNormal: " << orientNormal; MITK_INFO << "newNormal: " << newNormal; return EXIT_FAILURE; } // // Now reorient slices (center, vec0, vec1 ) mitk::Vector3D orientAxis0, orientAxis1, newAxis0, newAxis1; mitk::FillVector3D(orientAxis0, 1.0, 0.0, 0.0); mitk::FillVector3D(orientAxis1, 0.0, 1.0, 0.0); orientAxis0.Normalize(); orientAxis1.Normalize(); sliceCtrl1->ReorientSlices(orientCenter,orientAxis0, orientAxis1); newAxis0 = sliceCtrl1->GetCurrentPlaneGeometry()->GetAxisVector(0); newAxis1 = sliceCtrl1->GetCurrentPlaneGeometry()->GetAxisVector(1); newCenter = sliceCtrl1->GetCurrentPlaneGeometry()->GetCenter(); newAxis0.Normalize(); newAxis1.Normalize(); sliceCtrl1->GetCurrentGeometry3D()->WorldToIndex(orientCenter, orientCenterIdx); sliceCtrl1->GetCurrentGeometry3D()->WorldToIndex(newCenter, newCenterIdx); if ( (newCenterIdx != orientCenterIdx) || ( !mitk::Equal(orientAxis0, newAxis0, 1E-12) ) || ( !mitk::Equal(orientAxis1, newAxis1, 1E-12 )) ) { MITK_INFO << "Reorient Planes (point, vec, vec) not working as it should"; MITK_INFO << "orientCenterIdx: " << orientCenterIdx; MITK_INFO << "newCenterIdx: " << newCenterIdx; MITK_INFO << "orientAxis0: " << orientAxis0; MITK_INFO << "newAxis0: " << newAxis0; MITK_INFO << "orientAxis1: " << orientAxis1; MITK_INFO << "newAxis1: " << newAxis1; return EXIT_FAILURE; } return EXIT_SUCCESS; } int testRestorePlanePostionOperation () { //Create PlaneGeometry mitk::PlaneGeometry::Pointer planegeometry = mitk::PlaneGeometry::New(); mitk::Point3D origin; mitk::Vector3D right, bottom, normal; mitk::ScalarType width, height; mitk::ScalarType widthInMM, heightInMM, thicknessInMM; width = 100; widthInMM = width; height = 200; heightInMM = height; thicknessInMM = 1.5; mitk::FillVector3D(origin, 4.5, 7.3, 11.2); mitk::FillVector3D(right, widthInMM, 0, 0); mitk::FillVector3D(bottom, 0, heightInMM, 0); mitk::FillVector3D(normal, 0, 0, thicknessInMM); mitk::Vector3D spacing; normal.Normalize(); normal *= thicknessInMM; mitk::FillVector3D(spacing, 1.0, 1.0, thicknessInMM); planegeometry->InitializeStandardPlane(right.GetVnlVector(), bottom.GetVnlVector(), &spacing); planegeometry->SetOrigin(origin); //Create SlicedGeometry3D out of planeGeometry mitk::SlicedGeometry3D::Pointer slicedgeometry1 = mitk::SlicedGeometry3D::New(); unsigned int numSlices = 300; slicedgeometry1->InitializeEvenlySpaced(planegeometry, thicknessInMM, numSlices, false); //Create another slicedgeo which will be rotated mitk::SlicedGeometry3D::Pointer slicedgeometry2 = mitk::SlicedGeometry3D::New(); slicedgeometry2->InitializeEvenlySpaced(planegeometry, thicknessInMM, numSlices, false); //Create geo3D as reference mitk::Geometry3D::Pointer geometry = mitk::Geometry3D::New(); geometry->SetBounds(slicedgeometry1->GetBounds()); geometry->SetIndexToWorldTransform(slicedgeometry1->GetIndexToWorldTransform()); //Initialize planes for (int i=0; i < (int)numSlices; i++) { mitk::PlaneGeometry::Pointer geo2d = mitk::PlaneGeometry::New(); geo2d->Initialize(); geo2d->SetReferenceGeometry(geometry); slicedgeometry1->SetGeometry2D(geo2d,i); } for (int i=0; i < (int)numSlices; i++) { mitk::PlaneGeometry::Pointer geo2d = mitk::PlaneGeometry::New(); geo2d->Initialize(); geo2d->SetReferenceGeometry(geometry); slicedgeometry2->SetGeometry2D(geo2d,i); } slicedgeometry1->SetReferenceGeometry(geometry); slicedgeometry2->SetReferenceGeometry(geometry); //Create SNC mitk::SliceNavigationController::Pointer sliceCtrl1 = mitk::SliceNavigationController::New(); sliceCtrl1->SetInputWorldGeometry3D(slicedgeometry1); sliceCtrl1->Update(); mitk::SliceNavigationController::Pointer sliceCtrl2 = mitk::SliceNavigationController::New(); sliceCtrl2->SetInputWorldGeometry3D(slicedgeometry2); sliceCtrl2->Update(); slicedgeometry1->SetSliceNavigationController(sliceCtrl1); slicedgeometry2->SetSliceNavigationController(sliceCtrl2); //Rotate slicedgeo2 double angle = 63.84; mitk::Vector3D rotationVector; mitk::FillVector3D( rotationVector, 0.5, 0.95, 0.23 ); mitk::Point3D center = slicedgeometry2->GetCenter(); mitk::RotationOperation* op = new mitk::RotationOperation( mitk::OpROTATE, center, rotationVector, angle ); slicedgeometry2->ExecuteOperation(op); sliceCtrl2->Update(); us::ServiceReference serviceRef = us::GetModuleContext()->GetServiceReference(); mitk::PlanePositionManagerService* service = us::GetModuleContext()->GetService(serviceRef); service->AddNewPlanePosition(slicedgeometry2->GetGeometry2D(0), 178); sliceCtrl1->ExecuteOperation(service->GetPlanePosition(0)); sliceCtrl1->Update(); - mitk::Geometry2D* planeRotated = slicedgeometry2->GetGeometry2D(178); - mitk::Geometry2D* planeRestored = dynamic_cast< const mitk::SlicedGeometry3D*>(sliceCtrl1->GetCurrentGeometry3D())->GetGeometry2D(178); + mitk::PlaneGeometry* planeRotated = slicedgeometry2->GetGeometry2D(178); + mitk::PlaneGeometry* planeRestored = dynamic_cast< const mitk::SlicedGeometry3D*>(sliceCtrl1->GetCurrentGeometry3D())->GetGeometry2D(178); try{ MITK_TEST_CONDITION_REQUIRED(mitk::MatrixEqualElementWise(planeRotated->GetIndexToWorldTransform()->GetMatrix(), planeRestored->GetIndexToWorldTransform()->GetMatrix()),"Testing for IndexToWorld"); MITK_TEST_CONDITION_REQUIRED(mitk::Equal(planeRotated->GetOrigin(), planeRestored->GetOrigin(),2*mitk::eps),"Testing for origin"); MITK_TEST_CONDITION_REQUIRED(mitk::Equal(planeRotated->GetSpacing(), planeRestored->GetSpacing()),"Testing for spacing"); MITK_TEST_CONDITION_REQUIRED(mitk::Equal(slicedgeometry2->GetDirectionVector(), dynamic_cast< const mitk::SlicedGeometry3D*>(sliceCtrl1->GetCurrentGeometry3D())->GetDirectionVector()),"Testing for directionvector"); MITK_TEST_CONDITION_REQUIRED(mitk::Equal(slicedgeometry2->GetSlices(), dynamic_cast< const mitk::SlicedGeometry3D*>(sliceCtrl1->GetCurrentGeometry3D())->GetSlices()),"Testing for numslices"); MITK_TEST_CONDITION_REQUIRED(mitk::MatrixEqualElementWise(slicedgeometry2->GetIndexToWorldTransform()->GetMatrix(), dynamic_cast< const mitk::SlicedGeometry3D*>(sliceCtrl1->GetCurrentGeometry3D())->GetIndexToWorldTransform()->GetMatrix()),"Testing for IndexToWorld"); } catch(...) { return EXIT_FAILURE; } return EXIT_SUCCESS; } int mitkSliceNavigationControllerTest(int /*argc*/, char* /*argv*/[]) { int result=EXIT_FAILURE; std::cout << "Creating and initializing a PlaneGeometry: "; mitk::PlaneGeometry::Pointer planegeometry = mitk::PlaneGeometry::New(); mitk::Point3D origin; mitk::Vector3D right, bottom, normal; mitk::ScalarType width, height; mitk::ScalarType widthInMM, heightInMM, thicknessInMM; width = 100; widthInMM = width; height = 200; heightInMM = height; thicknessInMM = 1.5; // mitk::FillVector3D(origin, 0, 0, thicknessInMM*0.5); mitk::FillVector3D(origin, 4.5, 7.3, 11.2); mitk::FillVector3D(right, widthInMM, 0, 0); mitk::FillVector3D(bottom, 0, heightInMM, 0); mitk::FillVector3D(normal, 0, 0, thicknessInMM); mitk::Vector3D spacing; normal.Normalize(); normal *= thicknessInMM; mitk::FillVector3D(spacing, 1.0, 1.0, thicknessInMM); planegeometry->InitializeStandardPlane(right.GetVnlVector(), bottom.GetVnlVector(), &spacing); planegeometry->SetOrigin(origin); std::cout<<"[PASSED]"<InitializeEvenlySpaced(planegeometry, thicknessInMM, numSlices, false); std::cout<<"[PASSED]"<SetBounds(slicedgeometry->GetBounds()); geometry->SetIndexToWorldTransform(slicedgeometry->GetIndexToWorldTransform()); std::cout<<"[PASSED]"<GetCornerPoint(0); result=testGeometry(geometry, width, height, numSlices, widthInMM, heightInMM, thicknessInMM, cornerpoint0, right, bottom, normal); if(result!=EXIT_SUCCESS) return result; mitk::AffineTransform3D::Pointer transform = mitk::AffineTransform3D::New(); transform->SetMatrix(geometry->GetIndexToWorldTransform()->GetMatrix()); mitk::BoundingBox::Pointer boundingbox = geometry->CalculateBoundingBoxRelativeToTransform(transform); geometry->SetBounds(boundingbox->GetBounds()); cornerpoint0 = geometry->GetCornerPoint(0); result=testGeometry(geometry, width, height, numSlices, widthInMM, heightInMM, thicknessInMM, cornerpoint0, right, bottom, normal); if(result!=EXIT_SUCCESS) return result; std::cout << "Changing the IndexToWorldTransform of the geometry to a rotated version by SetIndexToWorldTransform() (keep cornerpoint0): "; transform = mitk::AffineTransform3D::New(); mitk::AffineTransform3D::MatrixType::InternalMatrixType vnlmatrix; vnlmatrix = planegeometry->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix(); mitk::VnlVector axis(3); mitk::FillVector3D(axis, 1.0, 1.0, 1.0); axis.normalize(); vnl_quaternion rotation(axis, 0.223); vnlmatrix = rotation.rotation_matrix_transpose()*vnlmatrix; mitk::Matrix3D matrix; matrix = vnlmatrix; transform->SetMatrix(matrix); transform->SetOffset(cornerpoint0.GetVectorFromOrigin()); right.SetVnlVector( rotation.rotation_matrix_transpose()*right.GetVnlVector() ); bottom.SetVnlVector(rotation.rotation_matrix_transpose()*bottom.GetVnlVector()); normal.SetVnlVector(rotation.rotation_matrix_transpose()*normal.GetVnlVector()); geometry->SetIndexToWorldTransform(transform); std::cout<<"[PASSED]"<GetCornerPoint(0); result = testGeometry(geometry, width, height, numSlices, widthInMM, heightInMM, thicknessInMM, cornerpoint0, right, bottom, normal); if(result!=EXIT_SUCCESS) return result; //Testing Execute RestorePlanePositionOperation result = testRestorePlanePostionOperation(); if(result!=EXIT_SUCCESS) return result; // Re-Orient planes not working as it should on windows // However, this might be adjusted during a geometry redesign. /* //Testing ReorientPlanes result = testReorientPlanes(); if(result!=EXIT_SUCCESS) return result; */ std::cout<<"[TEST DONE]"< #include #include static const mitk::ScalarType slicedGeometryEps = 1E-9; // epsilon for this testfile. Set to float precision. void mitkSlicedGeometry3D_ChangeImageGeometryConsideringOriginOffset_Test() { //Tests for Offset MITK_TEST_OUTPUT( << "====== NOW RUNNING: Tests for pixel-center-based offset concerns ========"); // create a SlicedGeometry3D mitk::SlicedGeometry3D::Pointer slicedGeo3D=mitk::SlicedGeometry3D::New(); int num_slices = 5; slicedGeo3D->InitializeSlicedGeometry(num_slices); // 5 slices mitk::Point3D newOrigin; newOrigin[0] = 91.3; newOrigin[1] = -13.3; newOrigin[2] = 0; slicedGeo3D->SetOrigin(newOrigin); mitk::Vector3D newSpacing; newSpacing[0] = 1.0f; newSpacing[1] = 0.9f; newSpacing[2] = 0.3f; slicedGeo3D->SetSpacing(newSpacing); // create subslices as well for (int i=0; i < num_slices; i++) { - mitk::Geometry2D::Pointer geo2d = mitk::Geometry2D::New(); + mitk::PlaneGeometry::Pointer geo2d = mitk::PlaneGeometry::New(); geo2d->Initialize(); slicedGeo3D->SetGeometry2D(geo2d,i); } // now run tests MITK_TEST_OUTPUT( << "Testing whether slicedGeo3D->GetImageGeometry() is false by default"); MITK_TEST_CONDITION_REQUIRED( slicedGeo3D->GetImageGeometry()==false, ""); MITK_TEST_OUTPUT( << "Testing whether first and last geometry in the SlicedGeometry3D have GetImageGeometry()==false by default"); mitk::BaseGeometry* subSliceGeo2D_first = slicedGeo3D->GetGeometry2D(0); mitk::BaseGeometry* subSliceGeo2D_last = slicedGeo3D->GetGeometry2D(num_slices-1); MITK_TEST_CONDITION_REQUIRED( subSliceGeo2D_first->GetImageGeometry()==false, ""); MITK_TEST_CONDITION_REQUIRED( subSliceGeo2D_last->GetImageGeometry()==false, ""); // Save some Origins and cornerpoints mitk::Point3D OriginSlicedGeo( slicedGeo3D->GetOrigin() ); mitk::Point3D OriginFirstGeo( subSliceGeo2D_first->GetOrigin() ); mitk::Point3D OriginLastGeo( subSliceGeo2D_last->GetOrigin() ); mitk::Point3D CornerPoint0SlicedGeo(slicedGeo3D->GetCornerPoint(0)); mitk::Point3D CornerPoint1FirstGeo(subSliceGeo2D_first->GetCornerPoint(1)); mitk::Point3D CornerPoint2LastGeo(subSliceGeo2D_last->GetCornerPoint(2)); MITK_TEST_OUTPUT( << "Calling slicedGeo3D->ChangeImageGeometryConsideringOriginOffset(true)"); //std::cout << "vorher Origin: " << subSliceGeo2D_first->GetOrigin() << std::endl; //std::cout << "vorher Corner: " << subSliceGeo2D_first->GetCornerPoint(0) << std::endl; slicedGeo3D->ChangeImageGeometryConsideringOriginOffset(true); //std::cout << "nachher Origin: " << subSliceGeo2D_first->GetOrigin() << std::endl; //std::cout << "nachher Corner: " << subSliceGeo2D_first->GetCornerPoint(0) << std::endl; MITK_TEST_OUTPUT( << "Testing whether slicedGeo3D->GetImageGeometry() is now true"); MITK_TEST_CONDITION_REQUIRED( slicedGeo3D->GetImageGeometry()==true, ""); MITK_TEST_OUTPUT( << "Testing whether first and last geometry in the SlicedGeometry3D have GetImageGeometry()==true now"); MITK_TEST_CONDITION_REQUIRED( subSliceGeo2D_first->GetImageGeometry()==true, ""); MITK_TEST_CONDITION_REQUIRED( subSliceGeo2D_last->GetImageGeometry()==true, ""); MITK_TEST_OUTPUT( << "Testing wether offset has been added to origins"); // Manually adding Offset. OriginSlicedGeo[0] += (slicedGeo3D->GetSpacing()[0]) / 2; OriginSlicedGeo[1] += (slicedGeo3D->GetSpacing()[1]) / 2; OriginSlicedGeo[2] += (slicedGeo3D->GetSpacing()[2]) / 2; OriginFirstGeo[0] += (subSliceGeo2D_first->GetSpacing()[0]) / 2; OriginFirstGeo[1] += (subSliceGeo2D_first->GetSpacing()[1]) / 2; OriginFirstGeo[2] += (subSliceGeo2D_first->GetSpacing()[2]) / 2; OriginLastGeo[0] += (subSliceGeo2D_last->GetSpacing()[0]) / 2; OriginLastGeo[1] += (subSliceGeo2D_last->GetSpacing()[1]) / 2; OriginLastGeo[2] += (subSliceGeo2D_last->GetSpacing()[2]) / 2; MITK_TEST_CONDITION_REQUIRED( subSliceGeo2D_first->GetCornerPoint(1)==CornerPoint1FirstGeo, ""); MITK_TEST_CONDITION_REQUIRED( subSliceGeo2D_last->GetCornerPoint(2)==CornerPoint2LastGeo, ""); MITK_TEST_CONDITION_REQUIRED( slicedGeo3D->GetCornerPoint(0)==CornerPoint0SlicedGeo, ""); MITK_TEST_CONDITION_REQUIRED( slicedGeo3D->GetOrigin()==OriginSlicedGeo, ""); MITK_TEST_CONDITION_REQUIRED( subSliceGeo2D_first->GetOrigin()==OriginFirstGeo, ""); MITK_TEST_CONDITION_REQUIRED( subSliceGeo2D_last->GetOrigin()==OriginLastGeo, ""); MITK_TEST_OUTPUT( << "Calling slicedGeo3D->ChangeImageGeometryConsideringOriginOffset(false)"); slicedGeo3D->ChangeImageGeometryConsideringOriginOffset(false); MITK_TEST_OUTPUT( << "Testing whether slicedGeo3D->GetImageGeometry() is now false"); MITK_TEST_CONDITION_REQUIRED( slicedGeo3D->GetImageGeometry()==false, ""); MITK_TEST_OUTPUT( << "Testing whether first and last geometry in the SlicedGeometry3D have GetImageGeometry()==false now"); MITK_TEST_CONDITION_REQUIRED( subSliceGeo2D_first->GetImageGeometry()==false, ""); MITK_TEST_CONDITION_REQUIRED( subSliceGeo2D_last->GetImageGeometry()==false, ""); MITK_TEST_OUTPUT( << "Testing wether offset has been added to origins of geometry"); // Manually substracting Offset. OriginSlicedGeo[0] -= (slicedGeo3D->GetSpacing()[0]) / 2; OriginSlicedGeo[1] -= (slicedGeo3D->GetSpacing()[1]) / 2; OriginSlicedGeo[2] -= (slicedGeo3D->GetSpacing()[2]) / 2; OriginFirstGeo[0] -= (subSliceGeo2D_first->GetSpacing()[0]) / 2; OriginFirstGeo[1] -= (subSliceGeo2D_first->GetSpacing()[1]) / 2; OriginFirstGeo[2] -= (subSliceGeo2D_first->GetSpacing()[2]) / 2; OriginLastGeo[0] -= (subSliceGeo2D_last->GetSpacing()[0]) / 2; OriginLastGeo[1] -= (subSliceGeo2D_last->GetSpacing()[1]) / 2; OriginLastGeo[2] -= (subSliceGeo2D_last->GetSpacing()[2]) / 2; MITK_TEST_CONDITION_REQUIRED( subSliceGeo2D_first->GetCornerPoint(1)==CornerPoint1FirstGeo, ""); MITK_TEST_CONDITION_REQUIRED( subSliceGeo2D_last->GetCornerPoint(2)==CornerPoint2LastGeo, ""); MITK_TEST_CONDITION_REQUIRED( slicedGeo3D->GetCornerPoint(0)==CornerPoint0SlicedGeo, ""); MITK_TEST_CONDITION_REQUIRED( slicedGeo3D->GetOrigin()==OriginSlicedGeo, ""); MITK_TEST_CONDITION_REQUIRED( subSliceGeo2D_first->GetOrigin()==OriginFirstGeo, ""); MITK_TEST_CONDITION_REQUIRED( subSliceGeo2D_last->GetOrigin()==OriginLastGeo, ""); MITK_TEST_OUTPUT( << "ALL SUCCESSFULLY!"); } int mitkSlicedGeometry3DTest(int /*argc*/, char* /*argv*/[]) { mitk::PlaneGeometry::Pointer planegeometry1 = mitk::PlaneGeometry::New(); mitk::Point3D origin; mitk::Vector3D right, bottom, normal; mitk::ScalarType width, height; mitk::ScalarType widthInMM, heightInMM, thicknessInMM; width = 100; widthInMM = width; height = 200; heightInMM = height; thicknessInMM = 3.5; mitk::FillVector3D(origin, 4.5, 7.3, 11.2); mitk::FillVector3D(right, widthInMM, 0, 0); mitk::FillVector3D(bottom, 0, heightInMM, 0); mitk::FillVector3D(normal, 0, 0, thicknessInMM); std::cout << "Initializing planegeometry1 by InitializeStandardPlane(rightVector, downVector, spacing = NULL): "<InitializeStandardPlane(right.GetVnlVector(), bottom.GetVnlVector()); std::cout << "Setting planegeometry2 to a cloned version of planegeometry1: "<(planegeometry1->Clone().GetPointer());; std::cout << "Changing the IndexToWorldTransform of planegeometry2 to a rotated version by SetIndexToWorldTransform() (keep origin): "<GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix(); mitk::VnlVector axis(3); mitk::FillVector3D(axis, 1.0, 1.0, 1.0); axis.normalize(); vnl_quaternion rotation(axis, 0.123); vnlmatrix = rotation.rotation_matrix_transpose()*vnlmatrix; mitk::Matrix3D matrix; matrix = vnlmatrix; transform->SetMatrix(matrix); transform->SetOffset(planegeometry2->GetIndexToWorldTransform()->GetOffset()); right.SetVnlVector( rotation.rotation_matrix_transpose()*right.GetVnlVector() ); bottom.SetVnlVector(rotation.rotation_matrix_transpose()*bottom.GetVnlVector()); normal.SetVnlVector(rotation.rotation_matrix_transpose()*normal.GetVnlVector()); planegeometry2->SetIndexToWorldTransform(transform); std::cout << "Setting planegeometry3 to the backside of planegeometry2: " <InitializeStandardPlane(planegeometry2, mitk::PlaneGeometry::Axial, 0, false); std::cout << "Testing SlicedGeometry3D::InitializeEvenlySpaced(planegeometry3, zSpacing = 1, slices = 5, flipped = false): " <InitializeEvenlySpaced(planegeometry3, 1, numSlices, false); std::cout << "Testing availability and type (PlaneGeometry) of first geometry in the SlicedGeometry3D: "; mitk::PlaneGeometry* accessedplanegeometry3 = dynamic_cast(slicedWorldGeometry->GetGeometry2D(0)); if(accessedplanegeometry3==NULL) { std::cout<<"[FAILED]"<GetAxisVector(0), planegeometry3->GetAxisVector(0), slicedGeometryEps)==false) || (mitk::Equal(accessedplanegeometry3->GetAxisVector(1), planegeometry3->GetAxisVector(1), slicedGeometryEps)==false) || (mitk::Equal(accessedplanegeometry3->GetAxisVector(2), planegeometry3->GetAxisVector(2), slicedGeometryEps)==false) || (mitk::Equal(accessedplanegeometry3->GetOrigin(), planegeometry3->GetOrigin(), slicedGeometryEps)==false)) { std::cout<<"[FAILED]"<(slicedWorldGeometry->GetGeometry2D(numSlices-1)); mitk::Point3D origin3last; origin3last = planegeometry3->GetOrigin()+slicedWorldGeometry->GetDirectionVector()*(numSlices-1); if(accessedplanegeometry3last==NULL) { std::cout<<"[FAILED]"<GetAxisVector(0), planegeometry3->GetAxisVector(0), slicedGeometryEps)==false) || (mitk::Equal(accessedplanegeometry3last->GetAxisVector(1), planegeometry3->GetAxisVector(1), slicedGeometryEps)==false) || (mitk::Equal(accessedplanegeometry3last->GetAxisVector(2), planegeometry3->GetAxisVector(2), slicedGeometryEps)==false) || (mitk::Equal(accessedplanegeometry3last->GetOrigin(), origin3last, slicedGeometryEps)==false) || (mitk::Equal(accessedplanegeometry3last->GetIndexToWorldTransform()->GetOffset(), origin3last.GetVectorFromOrigin(), slicedGeometryEps)==false)) { std::cout<<"[FAILED]"<(slicedWorldGeometry->GetGeometry2D(0)); if(accessedplanegeometry3==NULL) { std::cout<<"[FAILED]"<GetAxisVector(0), planegeometry3->GetAxisVector(0), slicedGeometryEps)==false) || (mitk::Equal(accessedplanegeometry3->GetAxisVector(1), planegeometry3->GetAxisVector(1), slicedGeometryEps)==false) || (mitk::Equal(accessedplanegeometry3->GetAxisVector(2), planegeometry3->GetAxisVector(2), slicedGeometryEps)==false) || (mitk::Equal(accessedplanegeometry3->GetOrigin(), planegeometry3->GetOrigin(), slicedGeometryEps)==false) || (mitk::Equal(accessedplanegeometry3->GetIndexToWorldTransform()->GetOffset(), planegeometry3->GetOrigin().GetVectorFromOrigin(), slicedGeometryEps)==false)) { std::cout<<"[FAILED]"< #include #include #include #include #include #include #include #include #include #include mitk::ContourModelMapper2D::ContourModelMapper2D() { } mitk::ContourModelMapper2D::~ContourModelMapper2D() { } const mitk::ContourModel* mitk::ContourModelMapper2D::GetInput( void ) { //convient way to get the data from the dataNode return static_cast< const mitk::ContourModel * >( GetDataNode()->GetData() ); } vtkProp* mitk::ContourModelMapper2D::GetVtkProp(mitk::BaseRenderer* renderer) { //return the actor corresponding to the renderer return m_LSH.GetLocalStorage(renderer)->m_Actor; } void mitk::ContourModelMapper2D::GenerateDataForRenderer( mitk::BaseRenderer *renderer ) { /*++ convert the contour to vtkPolyData and set it as input for our mapper ++*/ LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); mitk::ContourModel* inputContour = static_cast< mitk::ContourModel* >( GetDataNode()->GetData() ); unsigned int timestep = renderer->GetTimeStep(); //if there's something to be rendered if( inputContour->GetNumberOfVertices(timestep) > 0) { localStorage->m_OutlinePolyData = this->CreateVtkPolyDataFromContour(inputContour, renderer); } this->ApplyContourProperties(renderer); localStorage->m_Mapper->SetInputData(localStorage->m_OutlinePolyData); } void mitk::ContourModelMapper2D::Update(mitk::BaseRenderer* renderer) { bool visible = true; GetDataNode()->GetVisibility(visible, renderer, "visible"); if ( !visible ) return; //check if there is something to be rendered mitk::ContourModel* data = static_cast< mitk::ContourModel*>( GetDataNode()->GetData() ); if ( data == NULL ) { return; } // Calculate time step of the input data for the specified renderer (integer value) this->CalculateTimeStep( renderer ); LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); // Check if time step is valid const TimeGeometry *dataTimeGeometry = data->GetTimeGeometry(); if ( ( dataTimeGeometry == NULL ) || ( dataTimeGeometry->CountTimeSteps() == 0 ) || ( !dataTimeGeometry->IsValidTimeStep( renderer->GetTimeStep() ) ) ) { //clear the rendered polydata localStorage->m_Mapper->RemoveAllInputs();//SetInput(vtkSmartPointer::New()); return; } const DataNode *node = this->GetDataNode(); data->UpdateOutputInformation(); //check if something important has changed and we need to rerender if ( (localStorage->m_LastUpdateTime < node->GetMTime()) //was the node modified? || (localStorage->m_LastUpdateTime < data->GetPipelineMTime()) //Was the data modified? || (localStorage->m_LastUpdateTime < renderer->GetCurrentWorldGeometry2DUpdateTime()) //was the geometry modified? || (localStorage->m_LastUpdateTime < renderer->GetCurrentWorldGeometry2D()->GetMTime()) || (localStorage->m_LastUpdateTime < node->GetPropertyList()->GetMTime()) //was a property modified? || (localStorage->m_LastUpdateTime < node->GetPropertyList(renderer)->GetMTime()) ) { this->GenerateDataForRenderer( renderer ); } // since we have checked that nothing important has changed, we can set // m_LastUpdateTime to the current time localStorage->m_LastUpdateTime.Modified(); } vtkSmartPointer mitk::ContourModelMapper2D::CreateVtkPolyDataFromContour(mitk::ContourModel* inputContour, mitk::BaseRenderer* renderer) { unsigned int timestep = this->GetTimestep(); // Create a polydata to store everything in vtkSmartPointer resultingPolyData = vtkSmartPointer::New(); //check for the worldgeometry from the current render window - mitk::PlaneGeometry* currentWorldGeometry = dynamic_cast( const_cast(renderer->GetCurrentWorldGeometry2D())); + mitk::PlaneGeometry* currentWorldGeometry = dynamic_cast( const_cast(renderer->GetCurrentWorldGeometry2D())); if(currentWorldGeometry) { //origin and normal of vtkPlane mitk::Point3D origin = currentWorldGeometry->GetOrigin(); mitk::Vector3D normal = currentWorldGeometry->GetNormal(); //the implicit function to slice through the polyData vtkSmartPointer plane = vtkSmartPointer::New(); plane->SetOrigin(origin[0], origin[1], origin[2]); plane->SetNormal(normal[0], normal[1], normal[2]); /* First of all convert the control points of the contourModel to vtk points * and add lines in between them */ //the points to draw vtkSmartPointer points = vtkSmartPointer::New(); //the lines to connect the points vtkSmartPointer lines = vtkSmartPointer::New(); // Create a polydata to store everything in vtkSmartPointer polyDataIn3D = vtkSmartPointer::New(); vtkSmartPointer appendPoly = vtkSmartPointer::New(); mitk::ContourModel::Pointer renderingContour = mitk::ContourModel::New(); renderingContour = inputContour; bool subdivision = false; this->GetDataNode()->GetBoolProperty( "subdivision curve", subdivision, renderer ); if (subdivision) { mitk::ContourModel::Pointer subdivContour = mitk::ContourModel::New(); mitk::ContourModelSubDivisionFilter::Pointer subdivFilter = mitk::ContourModelSubDivisionFilter::New(); subdivFilter->SetInput(inputContour); subdivFilter->Update(); subdivContour = subdivFilter->GetOutput(); if(subdivContour->GetNumberOfVertices() == 0 ) { subdivContour = inputContour; } renderingContour = subdivContour; } //iterate over all control points mitk::ContourModel::VertexIterator current = renderingContour->IteratorBegin(timestep); mitk::ContourModel::VertexIterator next = renderingContour->IteratorBegin(timestep); if(next != renderingContour->IteratorEnd(timestep)) { next++; mitk::ContourModel::VertexIterator end = renderingContour->IteratorEnd(timestep); while(next != end) { mitk::ContourModel::VertexType* currentControlPoint = *current; mitk::ContourModel::VertexType* nextControlPoint = *next; vtkIdType p1 = points->InsertNextPoint(currentControlPoint->Coordinates[0], currentControlPoint->Coordinates[1], currentControlPoint->Coordinates[2]); vtkIdType p2 = points->InsertNextPoint(nextControlPoint->Coordinates[0], nextControlPoint->Coordinates[1], nextControlPoint->Coordinates[2]); //add the line between both contorlPoints lines->InsertNextCell(2); lines->InsertCellPoint(p1); lines->InsertCellPoint(p2); if ( currentControlPoint->IsControlPoint ) { double coordinates[3]; coordinates[0] = currentControlPoint->Coordinates[0]; coordinates[1] = currentControlPoint->Coordinates[1]; coordinates[2] = currentControlPoint->Coordinates[2]; double distance = plane->DistanceToPlane(coordinates); if(distance < 0.1) { vtkSmartPointer sphere = vtkSmartPointer::New(); sphere->SetRadius(1.2); sphere->SetCenter(coordinates[0], coordinates[1], coordinates[2]); sphere->Update(); appendPoly->AddInputConnection(sphere->GetOutputPort()); } } current++; next++; }//end while (it!=end) //check if last control point is enabled to draw it if ( (*current)->IsControlPoint ) { double coordinates[3]; coordinates[0] = (*current)->Coordinates[0]; coordinates[1] = (*current)->Coordinates[1]; coordinates[2] = (*current)->Coordinates[2]; double distance = plane->DistanceToPlane(coordinates); if(distance < 0.1) { vtkSmartPointer sphere = vtkSmartPointer::New(); sphere->SetRadius(1.2); sphere->SetCenter(coordinates[0], coordinates[1], coordinates[2]); sphere->Update(); appendPoly->AddInputConnection(sphere->GetOutputPort()); } } /* If the contour is closed an additional line has to be created between the very first point * and the last point */ if(renderingContour->IsClosed(timestep)) { //add a line from the last to the first control point mitk::ContourModel::VertexType* firstControlPoint = *(renderingContour->IteratorBegin(timestep)); mitk::ContourModel::VertexType* lastControlPoint = *(--(renderingContour->IteratorEnd(timestep))); vtkIdType p2 = points->InsertNextPoint(lastControlPoint->Coordinates[0], lastControlPoint->Coordinates[1], lastControlPoint->Coordinates[2]); vtkIdType p1 = points->InsertNextPoint(firstControlPoint->Coordinates[0], firstControlPoint->Coordinates[1], firstControlPoint->Coordinates[2]); //add the line between both contorlPoints lines->InsertNextCell(2); lines->InsertCellPoint(p1); lines->InsertCellPoint(p2); }//end if(isClosed) // Add the points to the dataset polyDataIn3D->SetPoints(points); // Add the lines to the dataset polyDataIn3D->SetLines(lines); //cut through polyData bool useCuttingPlane = false; this->GetDataNode()->GetBoolProperty( "use cutting plane", useCuttingPlane, renderer ); if (useCuttingPlane) { //slice through the data to get a 2D representation of the (possible) 3D contour //needed because currently there is no outher solution if the contour is within the plane vtkSmartPointer tubeFilter = vtkSmartPointer::New(); tubeFilter->SetInputData(polyDataIn3D); tubeFilter->SetRadius(0.05); //cuts through vtkPolyData with a given implicit function. In our case a plane vtkSmartPointer cutter = vtkSmartPointer::New(); cutter->SetCutFunction(plane); cutter->SetInputConnection(tubeFilter->GetOutputPort()); //we want the scalars of the input - so turn off generating the scalars within vtkCutter cutter->GenerateCutScalarsOff(); cutter->Update(); //set to 2D representation of the contour resultingPolyData= cutter->GetOutput(); }//end if(project contour) else { //set to 3D polyData resultingPolyData = polyDataIn3D; } }//end if (it != end) appendPoly->AddInputData(resultingPolyData); appendPoly->Update(); //return contour with control points return appendPoly->GetOutput(); }else { //return empty polyData return resultingPolyData; } } void mitk::ContourModelMapper2D::ApplyContourProperties(mitk::BaseRenderer* renderer) { LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); float lineWidth(1.0); if (this->GetDataNode()->GetFloatProperty( "width", lineWidth, renderer )) { localStorage->m_Actor->GetProperty()->SetLineWidth(lineWidth); } mitk::ColorProperty::Pointer colorprop = dynamic_cast(GetDataNode()->GetProperty("color", renderer)); if(colorprop) { //set the color of the contour double red = colorprop->GetColor().GetRed(); double green = colorprop->GetColor().GetGreen(); double blue = colorprop->GetColor().GetBlue(); localStorage->m_Actor->GetProperty()->SetColor(red, green, blue); } //make sure that directional lighting isn't used for our contour localStorage->m_Actor->GetProperty()->SetAmbient(1.0); localStorage->m_Actor->GetProperty()->SetDiffuse(0.0); localStorage->m_Actor->GetProperty()->SetSpecular(0.0); } /*+++++++++++++++++++ LocalStorage part +++++++++++++++++++++++++*/ mitk::ContourModelMapper2D::LocalStorage* mitk::ContourModelMapper2D::GetLocalStorage(mitk::BaseRenderer* renderer) { return m_LSH.GetLocalStorage(renderer); } mitk::ContourModelMapper2D::LocalStorage::LocalStorage() { m_Mapper = vtkSmartPointer::New(); m_Actor = vtkSmartPointer::New(); m_OutlinePolyData = vtkSmartPointer::New(); //set the mapper for the actor m_Actor->SetMapper(m_Mapper); } void mitk::ContourModelMapper2D::SetDefaultProperties(mitk::DataNode* node, mitk::BaseRenderer* renderer, bool overwrite) { node->AddProperty( "color", ColorProperty::New(0.9, 1.0, 0.1), renderer, overwrite ); node->AddProperty( "width", mitk::FloatProperty::New( 1.0 ), renderer, overwrite ); node->AddProperty( "use cutting plane", mitk::BoolProperty::New( true ), renderer, overwrite ); node->AddProperty( "subdivision curve", mitk::BoolProperty::New( false ), renderer, overwrite ); Superclass::SetDefaultProperties(node, renderer, overwrite); } diff --git a/Modules/ImageExtraction/mitkExtractDirectedPlaneImageFilter.h b/Modules/ImageExtraction/mitkExtractDirectedPlaneImageFilter.h index c6ab81a772..4e2449a342 100644 --- a/Modules/ImageExtraction/mitkExtractDirectedPlaneImageFilter.h +++ b/Modules/ImageExtraction/mitkExtractDirectedPlaneImageFilter.h @@ -1,124 +1,124 @@ /*=================================================================== 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 mitkExtractDirectedPlaneImageFilter_h_Included #define mitkExtractDirectedPlaneImageFilter_h_Included #include "ImageExtractionExports.h" #include "mitkImageToImageFilter.h" #include "vtkImageReslice.h" #include "mitkVtkResliceInterpolationProperty.h" #define setMacro(name,type) \ virtual void Set##name (type _arg) \ { \ if (this->m_##name != _arg) \ { \ this->m_##name = _arg; \ } \ } #define getMacro(name,type) \ virtual type Get##name () \ { \ return m_##name; \ } class vtkPoints; namespace mitk { /** \deprecated This class is deprecated. Use mitk::ExtractSliceFilter instead. \sa ExtractSliceFilter \brief Extracts a 2D slice of arbitrary geometry from a 3D or 4D image. \sa mitkImageMapper2D \ingroup ImageToImageFilter This class takes a 3D or 4D mitk::Image as input and tries to extract one slice from it. This slice can be arbitrary oriented in space. The 2D slice is resliced by a vtk::ResliceImage filter if not perpendicular to the input image. The world geometry of the plane to be extracted image must be given as an input to the filter in order to correctly calculate world coordinates of the extracted slice. Setting a timestep from which the plane should be extracted is optional. Output will not be set if there was a problem extracting the desired slice. Last contributor: $Author: T. Schwarz$ */ class ImageExtraction_EXPORT ExtractDirectedPlaneImageFilter : public ImageToImageFilter { public: mitkClassMacro(ExtractDirectedPlaneImageFilter, ImageToImageFilter); itkNewMacro(ExtractDirectedPlaneImageFilter); - itkSetMacro( WorldGeometry, Geometry2D* ); + itkSetMacro( WorldGeometry, PlaneGeometry* ); // The Reslicer is accessible to configure the desired interpolation; // (See vtk::ImageReslice class for documentation). // Misusage is at your own risk... itkGetMacro( Reslicer, vtkImageReslice* ); // The target timestep in a 4D image from which the 2D plane is supposed // to be extracted. itkSetMacro( TargetTimestep, unsigned int ); itkGetMacro( TargetTimestep, unsigned int ); itkSetMacro( InPlaneResampleExtentByGeometry, bool ); itkGetMacro( InPlaneResampleExtentByGeometry, bool ); setMacro( ResliceInterpolationProperty, VtkResliceInterpolationProperty* ); itkGetMacro( ResliceInterpolationProperty, VtkResliceInterpolationProperty* ); setMacro( IsMapperMode, bool ); getMacro( IsMapperMode, bool ); protected: ExtractDirectedPlaneImageFilter(); // purposely hidden virtual ~ExtractDirectedPlaneImageFilter(); virtual void GenerateData(); virtual void GenerateOutputInformation(); bool CalculateClippedPlaneBounds( const BaseGeometry *boundingGeometry, const PlaneGeometry *planeGeometry, double *bounds ); bool LineIntersectZero( vtkPoints *points, int p1, int p2, double *bounds ); - const Geometry2D* m_WorldGeometry; + const PlaneGeometry* m_WorldGeometry; vtkImageReslice * m_Reslicer; unsigned int m_TargetTimestep; bool m_InPlaneResampleExtentByGeometry; int m_ThickSlicesMode; int m_ThickSlicesNum; bool m_IsMapperMode; VtkResliceInterpolationProperty* m_ResliceInterpolationProperty; }; } // namespace mitk #endif // mitkExtractDirectedPlaneImageFilter_h_Included diff --git a/Modules/ImageStatistics/Testing/mitkImageStatisticsCalculatorTest.cpp b/Modules/ImageStatistics/Testing/mitkImageStatisticsCalculatorTest.cpp index 9f57aa9705..2311d5a0d9 100644 --- a/Modules/ImageStatistics/Testing/mitkImageStatisticsCalculatorTest.cpp +++ b/Modules/ImageStatistics/Testing/mitkImageStatisticsCalculatorTest.cpp @@ -1,409 +1,409 @@ /*=================================================================== 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 "mitkTestFixture.h" #include "mitkTestingMacros.h" #include "mitkImageStatisticsCalculator.h" #include "mitkPlanarPolygon.h" #include "mitkDicomSeriesReader.h" /** * \brief Test class for mitkImageStatisticsCalculator * * This test covers: * - instantiation of an ImageStatisticsCalculator class * - correctness of statistics when using PlanarFigures for masking */ class mitkImageStatisticsCalculatorTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkImageStatisticsCalculatorTestSuite); MITK_TEST(TestUninitializedImage); MITK_TEST(TestCase1); MITK_TEST(TestCase2); MITK_TEST(TestCase3); MITK_TEST(TestCase4); MITK_TEST(TestCase5); MITK_TEST(TestCase6); MITK_TEST(TestCase7); MITK_TEST(TestCase8); MITK_TEST(TestCase9); MITK_TEST(TestCase10); MITK_TEST(TestCase11); MITK_TEST(TestCase12); CPPUNIT_TEST_SUITE_END(); public: void setUp(); void TestUninitializedImage(); void TestCase1(); void TestCase2(); void TestCase3(); void TestCase4(); void TestCase5(); void TestCase6(); void TestCase7(); void TestCase8(); void TestCase9(); void TestCase10(); void TestCase11(); void TestCase12(); private: mitk::Image::Pointer m_Image; - mitk::Geometry2D::Pointer m_Geometry; + mitk::PlaneGeometry::Pointer m_Geometry; // calculate statistics for the given image and planarpolygon const mitk::ImageStatisticsCalculator::Statistics ComputeStatistics( mitk::Image::Pointer image, mitk::PlanarFigure::Pointer polygon ); void VerifyStatistics(const mitk::ImageStatisticsCalculator::Statistics& stats, double testMean, double testSD); }; void mitkImageStatisticsCalculatorTestSuite::setUp() { std::string filename = this->GetTestDataFilePath("ImageStatistics/testimage.dcm"); if (filename.empty()) { MITK_TEST_FAILED_MSG( << "Could not find test file" ) } mitk::DicomSeriesReader::StringContainer file; file.push_back( filename ); mitk::DicomSeriesReader* reader = new mitk::DicomSeriesReader; mitk::DataNode::Pointer node = reader->LoadDicomSeries( file, false, false ); m_Image = dynamic_cast( node->GetData() ); MITK_TEST_CONDITION_REQUIRED( m_Image.IsNotNull(), "Loading test image" ) m_Geometry = m_Image->GetSlicedGeometry()->GetGeometry2D(0); MITK_TEST_CONDITION_REQUIRED( m_Geometry.IsNotNull(), "Getting image geometry" ) } void mitkImageStatisticsCalculatorTestSuite::TestCase1() { /***************************** * one whole white pixel * -> mean of 255 expected ******************************/ mitk::PlanarPolygon::Pointer figure1 = mitk::PlanarPolygon::New(); figure1->SetGeometry2D( m_Geometry ); mitk::Point2D pnt1; pnt1[0] = 10.5 ; pnt1[1] = 3.5; figure1->PlaceFigure( pnt1 ); mitk::Point2D pnt2; pnt2[0] = 9.5; pnt2[1] = 3.5; figure1->SetControlPoint( 1, pnt2, true ); mitk::Point2D pnt3; pnt3[0] = 9.5; pnt3[1] = 4.5; figure1->SetControlPoint( 2, pnt3, true ); mitk::Point2D pnt4; pnt4[0] = 10.5; pnt4[1] = 4.5; figure1->SetControlPoint( 3, pnt4, true ); figure1->GetPolyLine(0); this->VerifyStatistics(ComputeStatistics(m_Image, figure1.GetPointer()), 255.0, 0.0); } void mitkImageStatisticsCalculatorTestSuite::TestCase2() { /***************************** * half pixel in x-direction (white) * -> mean of 255 expected ******************************/ mitk::PlanarPolygon::Pointer figure1 = mitk::PlanarPolygon::New(); figure1->SetGeometry2D( m_Geometry ); mitk::Point2D pnt1; pnt1[0] = 10.0 ; pnt1[1] = 3.5; figure1->PlaceFigure( pnt1 ); mitk::Point2D pnt2; pnt2[0] = 9.5; pnt2[1] = 3.5; figure1->SetControlPoint( 1, pnt2, true ); mitk::Point2D pnt3; pnt3[0] = 9.5; pnt3[1] = 4.5; figure1->SetControlPoint( 2, pnt3, true ); mitk::Point2D pnt4; pnt4[0] = 10.0; pnt4[1] = 4.5; figure1->SetControlPoint( 3, pnt4, true ); figure1->GetPolyLine(0); this->VerifyStatistics(ComputeStatistics(m_Image, figure1.GetPointer()), 255.0, 0.0); } void mitkImageStatisticsCalculatorTestSuite::TestCase3() { /***************************** * half pixel in diagonal-direction (white) * -> mean of 255 expected ******************************/ mitk::PlanarPolygon::Pointer figure1 = mitk::PlanarPolygon::New(); figure1->SetGeometry2D( m_Geometry ); mitk::Point2D pnt1; pnt1[0] = 10.5 ; pnt1[1] = 3.5; figure1->PlaceFigure( pnt1 ); mitk::Point2D pnt2; pnt2[0] = 9.5; pnt2[1] = 3.5; figure1->SetControlPoint( 1, pnt2, true ); mitk::Point2D pnt3; pnt3[0] = 9.5; pnt3[1] = 4.5; figure1->SetControlPoint( 2, pnt3, true ); figure1->GetPolyLine(0); this->VerifyStatistics(ComputeStatistics(m_Image, figure1.GetPointer()), 255.0, 0.0); } void mitkImageStatisticsCalculatorTestSuite::TestCase4() { /***************************** * one pixel (white) + 2 half pixels (white) + 1 half pixel (black) * -> mean of 191.25 expected ******************************/ mitk::PlanarPolygon::Pointer figure1 = mitk::PlanarPolygon::New(); figure1->SetGeometry2D( m_Geometry ); mitk::Point2D pnt1; pnt1[0] = 1.1; pnt1[1] = 1.1; figure1->PlaceFigure( pnt1 ); mitk::Point2D pnt2; pnt2[0] = 2.0; pnt2[1] = 2.0; figure1->SetControlPoint( 1, pnt2, true ); mitk::Point2D pnt3; pnt3[0] = 3.0; pnt3[1] = 1.0; figure1->SetControlPoint( 2, pnt3, true ); mitk::Point2D pnt4; pnt4[0] = 2.0; pnt4[1] = 0.0; figure1->SetControlPoint( 3, pnt4, true ); figure1->GetPolyLine(0); this->VerifyStatistics(ComputeStatistics(m_Image, figure1.GetPointer()), 191.25, 127.5); } void mitkImageStatisticsCalculatorTestSuite::TestCase5() { /***************************** * whole pixel (white) + half pixel (gray) in x-direction * -> mean of 191.5 expected ******************************/ mitk::PlanarPolygon::Pointer figure1 = mitk::PlanarPolygon::New(); figure1->SetGeometry2D( m_Geometry ); mitk::Point2D pnt1; pnt1[0] = 11.0; pnt1[1] = 3.5; figure1->PlaceFigure( pnt1 ); mitk::Point2D pnt2; pnt2[0] = 9.5; pnt2[1] = 3.5; figure1->SetControlPoint( 1, pnt2, true ); mitk::Point2D pnt3; pnt3[0] = 9.5; pnt3[1] = 4.5; figure1->SetControlPoint( 2, pnt3, true ); mitk::Point2D pnt4; pnt4[0] = 11.0; pnt4[1] = 4.5; figure1->SetControlPoint( 3, pnt4, true ); figure1->GetPolyLine(0); this->VerifyStatistics(ComputeStatistics(m_Image, figure1.GetPointer()), 191.50, 89.80); } void mitkImageStatisticsCalculatorTestSuite::TestCase6() { /***************************** * quarter pixel (black) + whole pixel (white) + half pixel (gray) in x-direction * -> mean of 191.5 expected ******************************/ mitk::PlanarPolygon::Pointer figure1 = mitk::PlanarPolygon::New(); figure1->SetGeometry2D( m_Geometry ); mitk::Point2D pnt1; pnt1[0] = 11.0; pnt1[1] = 3.5; figure1->PlaceFigure( pnt1 ); mitk::Point2D pnt2; pnt2[0] = 9.25; pnt2[1] = 3.5; figure1->SetControlPoint( 1, pnt2, true ); mitk::Point2D pnt3; pnt3[0] = 9.25; pnt3[1] = 4.5; figure1->SetControlPoint( 2, pnt3, true ); mitk::Point2D pnt4; pnt4[0] = 11.0; pnt4[1] = 4.5; figure1->SetControlPoint( 3, pnt4, true ); figure1->GetPolyLine(0); this->VerifyStatistics(ComputeStatistics(m_Image, figure1.GetPointer()), 191.5, 89.80); } void mitkImageStatisticsCalculatorTestSuite::TestCase7() { /***************************** * half pixel (black) + whole pixel (white) + half pixel (gray) in x-direction * -> mean of 127.66 expected ******************************/ mitk::PlanarPolygon::Pointer figure1 = mitk::PlanarPolygon::New(); figure1->SetGeometry2D( m_Geometry ); mitk::Point2D pnt1; pnt1[0] = 11.0; pnt1[1] = 3.5; figure1->PlaceFigure( pnt1 ); mitk::Point2D pnt2; pnt2[0] = 9.0; pnt2[1] = 3.5; figure1->SetControlPoint( 1, pnt2, true ); mitk::Point2D pnt3; pnt3[0] = 9.0; pnt3[1] = 4.0; figure1->SetControlPoint( 2, pnt3, true ); mitk::Point2D pnt4; pnt4[0] = 11.0; pnt4[1] = 4.0; figure1->SetControlPoint( 3, pnt4, true ); figure1->GetPolyLine(0); this->VerifyStatistics(ComputeStatistics(m_Image, figure1.GetPointer()), 127.66, 127.5); } void mitkImageStatisticsCalculatorTestSuite::TestCase8() { /***************************** * whole pixel (gray) * -> mean of 128 expected ******************************/ mitk::PlanarPolygon::Pointer figure2 = mitk::PlanarPolygon::New(); figure2->SetGeometry2D( m_Geometry ); mitk::Point2D pnt1; pnt1[0] = 11.5; pnt1[1] = 10.5; figure2->PlaceFigure( pnt1 ); mitk::Point2D pnt2; pnt2[0] = 11.5; pnt2[1] = 11.5; figure2->SetControlPoint( 1, pnt2, true ); mitk::Point2D pnt3; pnt3[0] = 12.5; pnt3[1] = 11.5; figure2->SetControlPoint( 2, pnt3, true ); mitk::Point2D pnt4; pnt4[0] = 12.5; pnt4[1] = 10.5; figure2->SetControlPoint( 3, pnt4, true ); figure2->GetPolyLine(0); this->VerifyStatistics(ComputeStatistics(m_Image, figure2.GetPointer()), 128.0, 0.0); } void mitkImageStatisticsCalculatorTestSuite::TestCase9() { /***************************** * whole pixel (gray) + half pixel (white) in y-direction * -> mean of 191.5 expected ******************************/ mitk::PlanarPolygon::Pointer figure2 = mitk::PlanarPolygon::New(); figure2->SetGeometry2D( m_Geometry ); mitk::Point2D pnt1; pnt1[0] = 11.5; pnt1[1] = 10.5; figure2->PlaceFigure( pnt1 ); mitk::Point2D pnt2; pnt2[0] = 11.5; pnt2[1] = 12.0; figure2->SetControlPoint( 1, pnt2, true ); mitk::Point2D pnt3; pnt3[0] = 12.5; pnt3[1] = 12.0; figure2->SetControlPoint( 2, pnt3, true ); mitk::Point2D pnt4; pnt4[0] = 12.5; pnt4[1] = 10.5; figure2->SetControlPoint( 3, pnt4, true ); figure2->GetPolyLine(0); this->VerifyStatistics(ComputeStatistics(m_Image, figure2.GetPointer()), 191.5, 89.80); } void mitkImageStatisticsCalculatorTestSuite::TestCase10() { /***************************** * 2 whole pixel (white) + 2 whole pixel (black) in y-direction * -> mean of 127.66 expected ******************************/ mitk::PlanarPolygon::Pointer figure2 = mitk::PlanarPolygon::New(); figure2->SetGeometry2D( m_Geometry ); mitk::Point2D pnt1; pnt1[0] = 11.5; pnt1[1] = 10.5; figure2->PlaceFigure( pnt1 ); mitk::Point2D pnt2; pnt2[0] = 11.5; pnt2[1] = 13.5; figure2->SetControlPoint( 1, pnt2, true ); mitk::Point2D pnt3; pnt3[0] = 12.5; pnt3[1] = 13.5; figure2->SetControlPoint( 2, pnt3, true ); mitk::Point2D pnt4; pnt4[0] = 12.5; pnt4[1] = 10.5; figure2->SetControlPoint( 3, pnt4, true ); figure2->GetPolyLine(0); this->VerifyStatistics(ComputeStatistics(m_Image, figure2.GetPointer()), 127.66, 127.5); } void mitkImageStatisticsCalculatorTestSuite::TestCase11() { /***************************** * 9 whole pixels (white) + 3 half pixels (white) * + 3 whole pixel (black) [ + 3 slightly less than half pixels (black)] * -> mean of 204.0 expected ******************************/ mitk::PlanarPolygon::Pointer figure2 = mitk::PlanarPolygon::New(); figure2->SetGeometry2D( m_Geometry ); mitk::Point2D pnt1; pnt1[0] = 0.5; pnt1[1] = 0.5; figure2->PlaceFigure( pnt1 ); mitk::Point2D pnt2; pnt2[0] = 3.5; pnt2[1] = 3.5; figure2->SetControlPoint( 1, pnt2, true ); mitk::Point2D pnt3; pnt3[0] = 8.4999; pnt3[1] = 3.5; figure2->SetControlPoint( 2, pnt3, true ); mitk::Point2D pnt4; pnt4[0] = 5.4999; pnt4[1] = 0.5; figure2->SetControlPoint( 3, pnt4, true ); figure2->GetPolyLine(0); this->VerifyStatistics(ComputeStatistics(m_Image, figure2.GetPointer()), 204.0, 105.58 ); } void mitkImageStatisticsCalculatorTestSuite::TestCase12() { /***************************** * half pixel (white) + whole pixel (white) + half pixel (black) * -> mean of 212.66 expected ******************************/ mitk::PlanarPolygon::Pointer figure2 = mitk::PlanarPolygon::New(); figure2->SetGeometry2D( m_Geometry ); mitk::Point2D pnt1; pnt1[0] = 9.5; pnt1[1] = 0.5; figure2->PlaceFigure( pnt1 ); mitk::Point2D pnt2; pnt2[0] = 9.5; pnt2[1] = 2.5; figure2->SetControlPoint( 1, pnt2, true ); mitk::Point2D pnt3; pnt3[0] = 11.5; pnt3[1] = 2.5; figure2->SetControlPoint( 2, pnt3, true ); figure2->GetPolyLine(0); this->VerifyStatistics(ComputeStatistics(m_Image, figure2.GetPointer()), 212.66, 73.32); } const mitk::ImageStatisticsCalculator::Statistics mitkImageStatisticsCalculatorTestSuite::ComputeStatistics( mitk::Image::Pointer image, mitk::PlanarFigure::Pointer polygon ) { mitk::ImageStatisticsCalculator::Pointer statisticsCalculator = mitk::ImageStatisticsCalculator::New(); statisticsCalculator->SetImage( image ); statisticsCalculator->SetMaskingModeToPlanarFigure(); statisticsCalculator->SetPlanarFigure( polygon ); statisticsCalculator->ComputeStatistics(); return statisticsCalculator->GetStatistics(); } void mitkImageStatisticsCalculatorTestSuite::VerifyStatistics(const mitk::ImageStatisticsCalculator::Statistics& stats, double testMean, double testSD) { int tmpMean = stats.Mean * 100; double calculatedMean = tmpMean / 100.0; MITK_TEST_CONDITION( calculatedMean == testMean, "Calculated mean grayvalue '" << calculatedMean << "' is equal to the desired value '" << testMean << "'" ); int tmpSD = stats.Sigma * 100; double calculatedSD = tmpSD / 100.0; MITK_TEST_CONDITION( calculatedSD == testSD, "Calculated grayvalue sd '" << calculatedSD << "' is equal to the desired value '" << testSD <<"'" ); } void mitkImageStatisticsCalculatorTestSuite::TestUninitializedImage() { /***************************** * loading uninitialized image to datastorage ******************************/ MITK_TEST_FOR_EXCEPTION_BEGIN(mitk::Exception) mitk::Image::Pointer image = mitk::Image::New(); mitk::DataNode::Pointer node = mitk::DataNode::New(); node->SetData(image); mitk::ImageStatisticsCalculator::Pointer is = mitk::ImageStatisticsCalculator::New(); is->ComputeStatistics(); MITK_TEST_FOR_EXCEPTION_END(mitk::Exception) } MITK_TEST_SUITE_REGISTRATION(mitkImageStatisticsCalculator) diff --git a/Modules/ImageStatistics/mitkImageStatisticsCalculator.cpp b/Modules/ImageStatistics/mitkImageStatisticsCalculator.cpp index e1c38bb974..a23a469b8d 100644 --- a/Modules/ImageStatistics/mitkImageStatisticsCalculator.cpp +++ b/Modules/ImageStatistics/mitkImageStatisticsCalculator.cpp @@ -1,1220 +1,1220 @@ /*=================================================================== 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 "mitkImageStatisticsCalculator.h" #include "mitkImageAccessByItk.h" #include "mitkImageCast.h" #include "mitkExtractImageFilter.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace mitk { ImageStatisticsCalculator::ImageStatisticsCalculator() : m_MaskingMode( MASKING_MODE_NONE ), m_MaskingModeChanged( false ), m_IgnorePixelValue(0.0), m_DoIgnorePixelValue(false), m_IgnorePixelValueChanged(false), m_PlanarFigureAxis (0), m_PlanarFigureSlice (0), m_PlanarFigureCoordinate0 (0), m_PlanarFigureCoordinate1 (0) { m_EmptyHistogram = HistogramType::New(); m_EmptyHistogram->SetMeasurementVectorSize(1); HistogramType::SizeType histogramSize(1); histogramSize.Fill( 256 ); m_EmptyHistogram->Initialize( histogramSize ); m_EmptyStatistics.Reset(); } ImageStatisticsCalculator::~ImageStatisticsCalculator() { } void ImageStatisticsCalculator::SetImage( const mitk::Image *image ) { if ( m_Image != image ) { m_Image = image; this->Modified(); unsigned int numberOfTimeSteps = image->GetTimeSteps(); // Initialize vectors to time-size of this image m_ImageHistogramVector.resize( numberOfTimeSteps ); m_MaskedImageHistogramVector.resize( numberOfTimeSteps ); m_PlanarFigureHistogramVector.resize( numberOfTimeSteps ); m_ImageStatisticsVector.resize( numberOfTimeSteps ); m_MaskedImageStatisticsVector.resize( numberOfTimeSteps ); m_PlanarFigureStatisticsVector.resize( numberOfTimeSteps ); m_ImageStatisticsTimeStampVector.resize( numberOfTimeSteps ); m_MaskedImageStatisticsTimeStampVector.resize( numberOfTimeSteps ); m_PlanarFigureStatisticsTimeStampVector.resize( numberOfTimeSteps ); m_ImageStatisticsCalculationTriggerVector.resize( numberOfTimeSteps ); m_MaskedImageStatisticsCalculationTriggerVector.resize( numberOfTimeSteps ); m_PlanarFigureStatisticsCalculationTriggerVector.resize( numberOfTimeSteps ); for ( unsigned int t = 0; t < image->GetTimeSteps(); ++t ) { m_ImageStatisticsTimeStampVector[t].Modified(); m_ImageStatisticsCalculationTriggerVector[t] = true; } } } void ImageStatisticsCalculator::SetImageMask( const mitk::Image *imageMask ) { if ( m_Image.IsNull() ) { itkExceptionMacro( << "Image needs to be set first!" ); } if ( m_ImageMask != imageMask ) { m_ImageMask = imageMask; this->Modified(); for ( unsigned int t = 0; t < m_Image->GetTimeSteps(); ++t ) { m_MaskedImageStatisticsTimeStampVector[t].Modified(); m_MaskedImageStatisticsCalculationTriggerVector[t] = true; } } } void ImageStatisticsCalculator::SetPlanarFigure( mitk::PlanarFigure *planarFigure ) { if ( m_Image.IsNull() ) { itkExceptionMacro( << "Image needs to be set first!" ); } if ( m_PlanarFigure != planarFigure ) { m_PlanarFigure = planarFigure; this->Modified(); for ( unsigned int t = 0; t < m_Image->GetTimeSteps(); ++t ) { m_PlanarFigureStatisticsTimeStampVector[t].Modified(); m_PlanarFigureStatisticsCalculationTriggerVector[t] = true; } } } void ImageStatisticsCalculator::SetMaskingMode( unsigned int mode ) { if ( m_MaskingMode != mode ) { m_MaskingMode = mode; m_MaskingModeChanged = true; this->Modified(); } } void ImageStatisticsCalculator::SetMaskingModeToNone() { if ( m_MaskingMode != MASKING_MODE_NONE ) { m_MaskingMode = MASKING_MODE_NONE; m_MaskingModeChanged = true; this->Modified(); } } void ImageStatisticsCalculator::SetMaskingModeToImage() { if ( m_MaskingMode != MASKING_MODE_IMAGE ) { m_MaskingMode = MASKING_MODE_IMAGE; m_MaskingModeChanged = true; this->Modified(); } } void ImageStatisticsCalculator::SetMaskingModeToPlanarFigure() { if ( m_MaskingMode != MASKING_MODE_PLANARFIGURE ) { m_MaskingMode = MASKING_MODE_PLANARFIGURE; m_MaskingModeChanged = true; this->Modified(); } } void ImageStatisticsCalculator::SetIgnorePixelValue(double value) { if ( m_IgnorePixelValue != value ) { m_IgnorePixelValue = value; if(m_DoIgnorePixelValue) { m_IgnorePixelValueChanged = true; } this->Modified(); } } double ImageStatisticsCalculator::GetIgnorePixelValue() { return m_IgnorePixelValue; } void ImageStatisticsCalculator::SetDoIgnorePixelValue(bool value) { if ( m_DoIgnorePixelValue != value ) { m_DoIgnorePixelValue = value; m_IgnorePixelValueChanged = true; this->Modified(); } } bool ImageStatisticsCalculator::GetDoIgnorePixelValue() { return m_DoIgnorePixelValue; } bool ImageStatisticsCalculator::ComputeStatistics( unsigned int timeStep ) { if (m_Image.IsNull() ) { mitkThrow() << "Image not set!"; } if (!m_Image->IsInitialized()) { mitkThrow() << "Image not initialized!"; } if ( m_Image->GetReferenceCount() == 1 ) { // Image no longer valid; we are the only ones to still hold a reference on it return false; } if ( timeStep >= m_Image->GetTimeSteps() ) { throw std::runtime_error( "Error: invalid time step!" ); } // If a mask was set but we are the only ones to still hold a reference on // it, delete it. if ( m_ImageMask.IsNotNull() && (m_ImageMask->GetReferenceCount() == 1) ) { m_ImageMask = NULL; } // Check if statistics is already up-to-date unsigned long imageMTime = m_ImageStatisticsTimeStampVector[timeStep].GetMTime(); unsigned long maskedImageMTime = m_MaskedImageStatisticsTimeStampVector[timeStep].GetMTime(); unsigned long planarFigureMTime = m_PlanarFigureStatisticsTimeStampVector[timeStep].GetMTime(); bool imageStatisticsCalculationTrigger = m_ImageStatisticsCalculationTriggerVector[timeStep]; bool maskedImageStatisticsCalculationTrigger = m_MaskedImageStatisticsCalculationTriggerVector[timeStep]; bool planarFigureStatisticsCalculationTrigger = m_PlanarFigureStatisticsCalculationTriggerVector[timeStep]; if ( !m_IgnorePixelValueChanged && ((m_MaskingMode != MASKING_MODE_NONE) || (imageMTime > m_Image->GetMTime() && !imageStatisticsCalculationTrigger)) && ((m_MaskingMode != MASKING_MODE_IMAGE) || (maskedImageMTime > m_ImageMask->GetMTime() && !maskedImageStatisticsCalculationTrigger)) && ((m_MaskingMode != MASKING_MODE_PLANARFIGURE) || (planarFigureMTime > m_PlanarFigure->GetMTime() && !planarFigureStatisticsCalculationTrigger)) ) { // Statistics is up to date! if ( m_MaskingModeChanged ) { m_MaskingModeChanged = false; return true; } else { return false; } } // Reset state changed flag m_MaskingModeChanged = false; m_IgnorePixelValueChanged = false; // Depending on masking mode, extract and/or generate the required image // and mask data from the user input this->ExtractImageAndMask( timeStep ); StatisticsContainer *statisticsContainer; HistogramContainer *histogramContainer; switch ( m_MaskingMode ) { case MASKING_MODE_NONE: default: if(!m_DoIgnorePixelValue) { statisticsContainer = &m_ImageStatisticsVector[timeStep]; histogramContainer = &m_ImageHistogramVector[timeStep]; m_ImageStatisticsTimeStampVector[timeStep].Modified(); m_ImageStatisticsCalculationTriggerVector[timeStep] = false; } else { statisticsContainer = &m_MaskedImageStatisticsVector[timeStep]; histogramContainer = &m_MaskedImageHistogramVector[timeStep]; m_MaskedImageStatisticsTimeStampVector[timeStep].Modified(); m_MaskedImageStatisticsCalculationTriggerVector[timeStep] = false; } break; case MASKING_MODE_IMAGE: statisticsContainer = &m_MaskedImageStatisticsVector[timeStep]; histogramContainer = &m_MaskedImageHistogramVector[timeStep]; m_MaskedImageStatisticsTimeStampVector[timeStep].Modified(); m_MaskedImageStatisticsCalculationTriggerVector[timeStep] = false; break; case MASKING_MODE_PLANARFIGURE: statisticsContainer = &m_PlanarFigureStatisticsVector[timeStep]; histogramContainer = &m_PlanarFigureHistogramVector[timeStep]; m_PlanarFigureStatisticsTimeStampVector[timeStep].Modified(); m_PlanarFigureStatisticsCalculationTriggerVector[timeStep] = false; break; } // Calculate statistics and histogram(s) if ( m_InternalImage->GetDimension() == 3 ) { if ( m_MaskingMode == MASKING_MODE_NONE && !m_DoIgnorePixelValue ) { AccessFixedDimensionByItk_2( m_InternalImage, InternalCalculateStatisticsUnmasked, 3, statisticsContainer, histogramContainer ); } else { AccessFixedDimensionByItk_3( m_InternalImage, InternalCalculateStatisticsMasked, 3, m_InternalImageMask3D.GetPointer(), statisticsContainer, histogramContainer ); } } else if ( m_InternalImage->GetDimension() == 2 ) { if ( m_MaskingMode == MASKING_MODE_NONE && !m_DoIgnorePixelValue ) { AccessFixedDimensionByItk_2( m_InternalImage, InternalCalculateStatisticsUnmasked, 2, statisticsContainer, histogramContainer ); } else { AccessFixedDimensionByItk_3( m_InternalImage, InternalCalculateStatisticsMasked, 2, m_InternalImageMask2D.GetPointer(), statisticsContainer, histogramContainer ); } } else { MITK_ERROR << "ImageStatistics: Image dimension not supported!"; } // Release unused image smart pointers to free memory m_InternalImage = mitk::Image::ConstPointer(); m_InternalImageMask3D = MaskImage3DType::Pointer(); m_InternalImageMask2D = MaskImage2DType::Pointer(); return true; } const ImageStatisticsCalculator::HistogramType * ImageStatisticsCalculator::GetHistogram( unsigned int timeStep, unsigned int label ) const { if ( m_Image.IsNull() || (timeStep >= m_Image->GetTimeSteps()) ) { return NULL; } switch ( m_MaskingMode ) { case MASKING_MODE_NONE: default: { if(m_DoIgnorePixelValue) return m_MaskedImageHistogramVector[timeStep][label]; return m_ImageHistogramVector[timeStep][label]; } case MASKING_MODE_IMAGE: return m_MaskedImageHistogramVector[timeStep][label]; case MASKING_MODE_PLANARFIGURE: return m_PlanarFigureHistogramVector[timeStep][label]; } } const ImageStatisticsCalculator::HistogramContainer & ImageStatisticsCalculator::GetHistogramVector( unsigned int timeStep ) const { if ( m_Image.IsNull() || (timeStep >= m_Image->GetTimeSteps()) ) { return m_EmptyHistogramContainer; } switch ( m_MaskingMode ) { case MASKING_MODE_NONE: default: { if(m_DoIgnorePixelValue) return m_MaskedImageHistogramVector[timeStep]; return m_ImageHistogramVector[timeStep]; } case MASKING_MODE_IMAGE: return m_MaskedImageHistogramVector[timeStep]; case MASKING_MODE_PLANARFIGURE: return m_PlanarFigureHistogramVector[timeStep]; } } const ImageStatisticsCalculator::Statistics & ImageStatisticsCalculator::GetStatistics( unsigned int timeStep, unsigned int label ) const { if ( m_Image.IsNull() || (timeStep >= m_Image->GetTimeSteps()) ) { return m_EmptyStatistics; } switch ( m_MaskingMode ) { case MASKING_MODE_NONE: default: { if(m_DoIgnorePixelValue) return m_MaskedImageStatisticsVector[timeStep][label]; return m_ImageStatisticsVector[timeStep][label]; } case MASKING_MODE_IMAGE: return m_MaskedImageStatisticsVector[timeStep][label]; case MASKING_MODE_PLANARFIGURE: return m_PlanarFigureStatisticsVector[timeStep][label]; } } const ImageStatisticsCalculator::StatisticsContainer & ImageStatisticsCalculator::GetStatisticsVector( unsigned int timeStep ) const { if ( m_Image.IsNull() || (timeStep >= m_Image->GetTimeSteps()) ) { return m_EmptyStatisticsContainer; } switch ( m_MaskingMode ) { case MASKING_MODE_NONE: default: { if(m_DoIgnorePixelValue) return m_MaskedImageStatisticsVector[timeStep]; return m_ImageStatisticsVector[timeStep]; } case MASKING_MODE_IMAGE: return m_MaskedImageStatisticsVector[timeStep]; case MASKING_MODE_PLANARFIGURE: return m_PlanarFigureStatisticsVector[timeStep]; } } void ImageStatisticsCalculator::ExtractImageAndMask( unsigned int timeStep ) { if ( m_Image.IsNull() ) { throw std::runtime_error( "Error: image empty!" ); } if ( timeStep >= m_Image->GetTimeSteps() ) { throw std::runtime_error( "Error: invalid time step!" ); } ImageTimeSelector::Pointer imageTimeSelector = ImageTimeSelector::New(); imageTimeSelector->SetInput( m_Image ); imageTimeSelector->SetTimeNr( timeStep ); imageTimeSelector->UpdateLargestPossibleRegion(); mitk::Image *timeSliceImage = imageTimeSelector->GetOutput(); switch ( m_MaskingMode ) { case MASKING_MODE_NONE: { m_InternalImage = timeSliceImage; m_InternalImageMask2D = NULL; m_InternalImageMask3D = NULL; if(m_DoIgnorePixelValue) { if( m_InternalImage->GetDimension() == 3 ) { CastToItkImage( timeSliceImage, m_InternalImageMask3D ); m_InternalImageMask3D->FillBuffer(1); } if( m_InternalImage->GetDimension() == 2 ) { CastToItkImage( timeSliceImage, m_InternalImageMask2D ); m_InternalImageMask2D->FillBuffer(1); } } break; } case MASKING_MODE_IMAGE: { if ( m_ImageMask.IsNotNull() && (m_ImageMask->GetReferenceCount() > 1) ) { if ( timeStep >= m_ImageMask->GetTimeSteps() ) { // Use the last mask time step in case the current time step is bigger than the total // number of mask time steps. // It makes more sense setting this to the last mask time step than to 0. // For instance if you have a mask with 2 time steps and an image with 5: // If time step 0 is selected, the mask will use time step 0. // If time step 1 is selected, the mask will use time step 1. // If time step 2+ is selected, the mask will use time step 1. // If you have a mask with only one time step instead, this will always default to 0. timeStep = m_ImageMask->GetTimeSteps() - 1; } ImageTimeSelector::Pointer maskedImageTimeSelector = ImageTimeSelector::New(); maskedImageTimeSelector->SetInput( m_ImageMask ); maskedImageTimeSelector->SetTimeNr( timeStep ); maskedImageTimeSelector->UpdateLargestPossibleRegion(); mitk::Image *timeSliceMaskedImage = maskedImageTimeSelector->GetOutput(); m_InternalImage = timeSliceImage; CastToItkImage( timeSliceMaskedImage, m_InternalImageMask3D ); } else { throw std::runtime_error( "Error: image mask empty!" ); } break; } case MASKING_MODE_PLANARFIGURE: { m_InternalImageMask2D = NULL; if ( m_PlanarFigure.IsNull() ) { throw std::runtime_error( "Error: planar figure empty!" ); } if ( !m_PlanarFigure->IsClosed() ) { throw std::runtime_error( "Masking not possible for non-closed figures" ); } const BaseGeometry *imageGeometry = timeSliceImage->GetGeometry(); if ( imageGeometry == NULL ) { throw std::runtime_error( "Image geometry invalid!" ); } - const Geometry2D *planarFigureGeometry2D = m_PlanarFigure->GetGeometry2D(); + const PlaneGeometry *planarFigureGeometry2D = m_PlanarFigure->GetGeometry2D(); if ( planarFigureGeometry2D == NULL ) { throw std::runtime_error( "Planar-Figure not yet initialized!" ); } const PlaneGeometry *planarFigureGeometry = dynamic_cast< const PlaneGeometry * >( planarFigureGeometry2D ); if ( planarFigureGeometry == NULL ) { throw std::runtime_error( "Non-planar planar figures not supported!" ); } // Find principal direction of PlanarFigure in input image unsigned int axis; if ( !this->GetPrincipalAxis( imageGeometry, planarFigureGeometry->GetNormal(), axis ) ) { throw std::runtime_error( "Non-aligned planar figures not supported!" ); } m_PlanarFigureAxis = axis; // Find slice number corresponding to PlanarFigure in input image MaskImage3DType::IndexType index; imageGeometry->WorldToIndex( planarFigureGeometry->GetOrigin(), index ); unsigned int slice = index[axis]; m_PlanarFigureSlice = slice; // Extract slice with given position and direction from image unsigned int dimension = timeSliceImage->GetDimension(); if (dimension != 2) { ExtractImageFilter::Pointer imageExtractor = ExtractImageFilter::New(); imageExtractor->SetInput( timeSliceImage ); imageExtractor->SetSliceDimension( axis ); imageExtractor->SetSliceIndex( slice ); imageExtractor->Update(); m_InternalImage = imageExtractor->GetOutput(); } else { m_InternalImage = timeSliceImage; } // Compute mask from PlanarFigure AccessFixedDimensionByItk_1( m_InternalImage, InternalCalculateMaskFromPlanarFigure, 2, axis ); } } if(m_DoIgnorePixelValue) { if ( m_InternalImage->GetDimension() == 3 ) { AccessFixedDimensionByItk_1( m_InternalImage, InternalMaskIgnoredPixels, 3, m_InternalImageMask3D.GetPointer() ); } else if ( m_InternalImage->GetDimension() == 2 ) { AccessFixedDimensionByItk_1( m_InternalImage, InternalMaskIgnoredPixels, 2, m_InternalImageMask2D.GetPointer() ); } } } bool ImageStatisticsCalculator::GetPrincipalAxis( const BaseGeometry *geometry, Vector3D vector, unsigned int &axis ) { vector.Normalize(); for ( unsigned int i = 0; i < 3; ++i ) { Vector3D axisVector = geometry->GetAxisVector( i ); axisVector.Normalize(); if ( fabs( fabs( axisVector * vector ) - 1.0) < mitk::eps ) { axis = i; return true; } } return false; } template < typename TPixel, unsigned int VImageDimension > void ImageStatisticsCalculator::InternalCalculateStatisticsUnmasked( const itk::Image< TPixel, VImageDimension > *image, StatisticsContainer *statisticsContainer, HistogramContainer* histogramContainer ) { typedef itk::Image< TPixel, VImageDimension > ImageType; typedef itk::Image< unsigned short, VImageDimension > MaskImageType; typedef typename ImageType::IndexType IndexType; typedef itk::Statistics::ScalarImageToHistogramGenerator< ImageType > HistogramGeneratorType; statisticsContainer->clear(); histogramContainer->clear(); // Progress listening... typedef itk::SimpleMemberCommand< ImageStatisticsCalculator > ITKCommandType; ITKCommandType::Pointer progressListener; progressListener = ITKCommandType::New(); progressListener->SetCallbackFunction( this, &ImageStatisticsCalculator::UnmaskedStatisticsProgressUpdate ); // Issue 100 artificial progress events since ScalarIMageToHistogramGenerator // does not (yet?) support progress reporting this->InvokeEvent( itk::StartEvent() ); for ( unsigned int i = 0; i < 100; ++i ) { this->UnmaskedStatisticsProgressUpdate(); } // Calculate statistics (separate filter) typedef itk::StatisticsImageFilter< ImageType > StatisticsFilterType; typename StatisticsFilterType::Pointer statisticsFilter = StatisticsFilterType::New(); statisticsFilter->SetInput( image ); unsigned long observerTag = statisticsFilter->AddObserver( itk::ProgressEvent(), progressListener ); statisticsFilter->Update(); statisticsFilter->RemoveObserver( observerTag ); this->InvokeEvent( itk::EndEvent() ); // Calculate minimum and maximum typedef itk::MinimumMaximumImageCalculator< ImageType > MinMaxFilterType; typename MinMaxFilterType::Pointer minMaxFilter = MinMaxFilterType::New(); minMaxFilter->SetImage( image ); unsigned long observerTag2 = minMaxFilter->AddObserver( itk::ProgressEvent(), progressListener ); minMaxFilter->Compute(); minMaxFilter->RemoveObserver( observerTag2 ); this->InvokeEvent( itk::EndEvent() ); Statistics statistics; statistics.Reset(); statistics.Label = 1; statistics.N = image->GetBufferedRegion().GetNumberOfPixels(); statistics.Min = statisticsFilter->GetMinimum(); statistics.Max = statisticsFilter->GetMaximum(); statistics.Mean = statisticsFilter->GetMean(); statistics.Median = 0.0; statistics.Sigma = statisticsFilter->GetSigma(); statistics.RMS = sqrt( statistics.Mean * statistics.Mean + statistics.Sigma * statistics.Sigma ); statistics.MinIndex.set_size(image->GetImageDimension()); statistics.MaxIndex.set_size(image->GetImageDimension()); for (unsigned int i=0; iGetIndexOfMaximum()[i]; statistics.MinIndex[i] = minMaxFilter->GetIndexOfMinimum()[i]; } statisticsContainer->push_back( statistics ); // Calculate histogram typename HistogramGeneratorType::Pointer histogramGenerator = HistogramGeneratorType::New(); histogramGenerator->SetInput( image ); histogramGenerator->SetMarginalScale( 100 ); histogramGenerator->SetNumberOfBins( 768 ); histogramGenerator->SetHistogramMin( statistics.Min ); histogramGenerator->SetHistogramMax( statistics.Max ); histogramGenerator->Compute(); histogramContainer->push_back( histogramGenerator->GetOutput() ); } template < typename TPixel, unsigned int VImageDimension > void ImageStatisticsCalculator::InternalMaskIgnoredPixels( const itk::Image< TPixel, VImageDimension > *image, itk::Image< unsigned short, VImageDimension > *maskImage ) { typedef itk::Image< TPixel, VImageDimension > ImageType; typedef itk::Image< unsigned short, VImageDimension > MaskImageType; itk::ImageRegionIterator itmask(maskImage, maskImage->GetLargestPossibleRegion()); itk::ImageRegionConstIterator itimage(image, image->GetLargestPossibleRegion()); itmask.GoToBegin(); itimage.GoToBegin(); while( !itmask.IsAtEnd() ) { if(m_IgnorePixelValue == itimage.Get()) { itmask.Set(0); } ++itmask; ++itimage; } } template < typename TPixel, unsigned int VImageDimension > void ImageStatisticsCalculator::InternalCalculateStatisticsMasked( const itk::Image< TPixel, VImageDimension > *image, itk::Image< unsigned short, VImageDimension > *maskImage, StatisticsContainer* statisticsContainer, HistogramContainer* histogramContainer ) { typedef itk::Image< TPixel, VImageDimension > ImageType; typedef itk::Image< unsigned short, VImageDimension > MaskImageType; typedef typename ImageType::IndexType IndexType; typedef typename ImageType::PointType PointType; typedef typename ImageType::SpacingType SpacingType; typedef itk::LabelStatisticsImageFilter< ImageType, MaskImageType > LabelStatisticsFilterType; typedef itk::ChangeInformationImageFilter< MaskImageType > ChangeInformationFilterType; typedef itk::ExtractImageFilter< ImageType, ImageType > ExtractImageFilterType; statisticsContainer->clear(); histogramContainer->clear(); // Make sure that mask is set if ( maskImage == NULL ) { itkExceptionMacro( << "Mask image needs to be set!" ); } // Make sure that spacing of mask and image are the same SpacingType imageSpacing = image->GetSpacing(); SpacingType maskSpacing = maskImage->GetSpacing(); PointType zeroPoint; zeroPoint.Fill( 0.0 ); if ( (zeroPoint + imageSpacing).SquaredEuclideanDistanceTo( (zeroPoint + maskSpacing) ) > mitk::eps ) { itkExceptionMacro( << "Mask needs to have same spacing as image! (Image spacing: " << imageSpacing << "; Mask spacing: " << maskSpacing << ")" ); } // Make sure that orientation of mask and image are the same typedef typename ImageType::DirectionType DirectionType; DirectionType imageDirection = image->GetDirection(); DirectionType maskDirection = maskImage->GetDirection(); for( int i = 0; i < imageDirection.ColumnDimensions; ++i ) { for( int j = 0; j < imageDirection.ColumnDimensions; ++j ) { double differenceDirection = imageDirection[i][j] - maskDirection[i][j]; if ( fabs( differenceDirection ) > mitk::eps ) { itkExceptionMacro( << "Mask needs to have same direction as image! (Image direction: " << imageDirection << "; Mask direction: " << maskDirection << ")" ); } } } // Make sure that the voxels of mask and image are correctly "aligned", i.e., voxel boundaries are the same in both images PointType imageOrigin = image->GetOrigin(); PointType maskOrigin = maskImage->GetOrigin(); long offset[ImageType::ImageDimension]; typedef itk::ContinuousIndex ContinousIndexType; ContinousIndexType maskOriginContinousIndex, imageOriginContinousIndex; image->TransformPhysicalPointToContinuousIndex(maskOrigin, maskOriginContinousIndex); image->TransformPhysicalPointToContinuousIndex(imageOrigin, imageOriginContinousIndex); for ( unsigned int i = 0; i < ImageType::ImageDimension; ++i ) { double misalignment = maskOriginContinousIndex[i] - floor( maskOriginContinousIndex[i] + 0.5 ); if ( fabs( misalignment ) > mitk::eps ) { itkExceptionMacro( << "Pixels/voxels of mask and image are not sufficiently aligned! (Misalignment: " << misalignment << ")" ); } double indexCoordDistance = maskOriginContinousIndex[i] - imageOriginContinousIndex[i]; offset[i] = (int) indexCoordDistance + image->GetBufferedRegion().GetIndex()[i]; } // Adapt the origin and region (index/size) of the mask so that the origin of both are the same typename ChangeInformationFilterType::Pointer adaptMaskFilter; adaptMaskFilter = ChangeInformationFilterType::New(); adaptMaskFilter->ChangeOriginOn(); adaptMaskFilter->ChangeRegionOn(); adaptMaskFilter->SetInput( maskImage ); adaptMaskFilter->SetOutputOrigin( image->GetOrigin() ); adaptMaskFilter->SetOutputOffset( offset ); adaptMaskFilter->Update(); typename MaskImageType::Pointer adaptedMaskImage = adaptMaskFilter->GetOutput(); // Make sure that mask region is contained within image region if ( !image->GetLargestPossibleRegion().IsInside( adaptedMaskImage->GetLargestPossibleRegion() ) ) { itkExceptionMacro( << "Mask region needs to be inside of image region! (Image region: " << image->GetLargestPossibleRegion() << "; Mask region: " << adaptedMaskImage->GetLargestPossibleRegion() << ")" ); } // If mask region is smaller than image region, extract the sub-sampled region from the original image typename ImageType::SizeType imageSize = image->GetBufferedRegion().GetSize(); typename ImageType::SizeType maskSize = maskImage->GetBufferedRegion().GetSize(); bool maskSmallerImage = false; for ( unsigned int i = 0; i < ImageType::ImageDimension; ++i ) { if ( maskSize[i] < imageSize[i] ) { maskSmallerImage = true; } } typename ImageType::ConstPointer adaptedImage; if ( maskSmallerImage ) { typename ExtractImageFilterType::Pointer extractImageFilter = ExtractImageFilterType::New(); extractImageFilter->SetInput( image ); extractImageFilter->SetExtractionRegion( adaptedMaskImage->GetBufferedRegion() ); extractImageFilter->Update(); adaptedImage = extractImageFilter->GetOutput(); } else { adaptedImage = image; } // Initialize Filter typedef itk::StatisticsImageFilter< ImageType > StatisticsFilterType; typename StatisticsFilterType::Pointer statisticsFilter = StatisticsFilterType::New(); statisticsFilter->SetInput( adaptedImage ); statisticsFilter->Update(); int numberOfBins = ( m_DoIgnorePixelValue && (m_MaskingMode == MASKING_MODE_NONE) ) ? 768 : 384; typename LabelStatisticsFilterType::Pointer labelStatisticsFilter; labelStatisticsFilter = LabelStatisticsFilterType::New(); labelStatisticsFilter->SetInput( adaptedImage ); labelStatisticsFilter->SetLabelInput( adaptedMaskImage ); labelStatisticsFilter->UseHistogramsOn(); labelStatisticsFilter->SetHistogramParameters( numberOfBins, statisticsFilter->GetMinimum(), statisticsFilter->GetMaximum() ); // Add progress listening typedef itk::SimpleMemberCommand< ImageStatisticsCalculator > ITKCommandType; ITKCommandType::Pointer progressListener; progressListener = ITKCommandType::New(); progressListener->SetCallbackFunction( this, &ImageStatisticsCalculator::MaskedStatisticsProgressUpdate ); unsigned long observerTag = labelStatisticsFilter->AddObserver( itk::ProgressEvent(), progressListener ); // Execute filter this->InvokeEvent( itk::StartEvent() ); // Make sure that only the mask region is considered (otherwise, if the mask region is smaller // than the image region, the Update() would result in an exception). labelStatisticsFilter->GetOutput()->SetRequestedRegion( adaptedMaskImage->GetLargestPossibleRegion() ); // Execute the filter labelStatisticsFilter->Update(); this->InvokeEvent( itk::EndEvent() ); labelStatisticsFilter->RemoveObserver( observerTag ); // Find all relevant labels of mask (other than 0) std::list< int > relevantLabels; bool maskNonEmpty = false; unsigned int i; for ( i = 1; i < 4096; ++i ) { if ( labelStatisticsFilter->HasLabel( i ) ) { relevantLabels.push_back( i ); maskNonEmpty = true; } } if ( maskNonEmpty ) { std::list< int >::iterator it; for ( it = relevantLabels.begin(), i = 0; it != relevantLabels.end(); ++it, ++i ) { histogramContainer->push_back( HistogramType::ConstPointer( labelStatisticsFilter->GetHistogram( (*it) ) ) ); Statistics statistics; statistics.Label = (*it); statistics.N = labelStatisticsFilter->GetCount( *it ); statistics.Min = labelStatisticsFilter->GetMinimum( *it ); statistics.Max = labelStatisticsFilter->GetMaximum( *it ); statistics.Mean = labelStatisticsFilter->GetMean( *it ); statistics.Median = labelStatisticsFilter->GetMedian( *it ); statistics.Sigma = labelStatisticsFilter->GetSigma( *it ); statistics.RMS = sqrt( statistics.Mean * statistics.Mean + statistics.Sigma * statistics.Sigma ); // restrict image to mask area for min/max index calculation typedef itk::MaskImageFilter< ImageType, MaskImageType, ImageType > MaskImageFilterType; typename MaskImageFilterType::Pointer masker = MaskImageFilterType::New(); masker->SetOutsideValue( (statistics.Min+statistics.Max)/2 ); masker->SetInput1(adaptedImage); masker->SetInput2(adaptedMaskImage); masker->Update(); // get index of minimum and maximum typedef itk::MinimumMaximumImageCalculator< ImageType > MinMaxFilterType; typename MinMaxFilterType::Pointer minMaxFilter = MinMaxFilterType::New(); minMaxFilter->SetImage( masker->GetOutput() ); unsigned long observerTag2 = minMaxFilter->AddObserver( itk::ProgressEvent(), progressListener ); minMaxFilter->Compute(); minMaxFilter->RemoveObserver( observerTag2 ); this->InvokeEvent( itk::EndEvent() ); statistics.MinIndex.set_size(adaptedImage->GetImageDimension()); statistics.MaxIndex.set_size(adaptedImage->GetImageDimension()); typename MinMaxFilterType::IndexType tempMaxIndex = minMaxFilter->GetIndexOfMaximum(); typename MinMaxFilterType::IndexType tempMinIndex = minMaxFilter->GetIndexOfMinimum(); // FIX BUG 14644 //If a PlanarFigure is used for segmentation the //adaptedImage is a single slice (2D). Adding the // 3. dimension. if (m_MaskingMode == MASKING_MODE_PLANARFIGURE && m_Image->GetDimension()==3) { statistics.MaxIndex.set_size(m_Image->GetDimension()); statistics.MaxIndex[m_PlanarFigureCoordinate0]=tempMaxIndex[0]; statistics.MaxIndex[m_PlanarFigureCoordinate1]=tempMaxIndex[1]; statistics.MaxIndex[m_PlanarFigureAxis]=m_PlanarFigureSlice; statistics.MinIndex.set_size(m_Image->GetDimension()); statistics.MinIndex[m_PlanarFigureCoordinate0]=tempMinIndex[0]; statistics.MinIndex[m_PlanarFigureCoordinate1]=tempMinIndex[1]; statistics.MinIndex[m_PlanarFigureAxis]=m_PlanarFigureSlice; } else { for (unsigned int i = 0; ipush_back( statistics ); } } else { histogramContainer->push_back( HistogramType::ConstPointer( m_EmptyHistogram ) ); statisticsContainer->push_back( Statistics() ); } } template < typename TPixel, unsigned int VImageDimension > void ImageStatisticsCalculator::InternalCalculateMaskFromPlanarFigure( const itk::Image< TPixel, VImageDimension > *image, unsigned int axis ) { typedef itk::Image< TPixel, VImageDimension > ImageType; typedef itk::CastImageFilter< ImageType, MaskImage2DType > CastFilterType; // Generate mask image as new image with same header as input image and // initialize with "1". typename CastFilterType::Pointer castFilter = CastFilterType::New(); castFilter->SetInput( image ); castFilter->Update(); castFilter->GetOutput()->FillBuffer( 1 ); // all PolylinePoints of the PlanarFigure are stored in a vtkPoints object. // These points are used by the vtkLassoStencilSource to create // a vtkImageStencil. - const mitk::Geometry2D *planarFigureGeometry2D = m_PlanarFigure->GetGeometry2D(); + const mitk::PlaneGeometry *planarFigureGeometry2D = m_PlanarFigure->GetGeometry2D(); const typename PlanarFigure::PolyLineType planarFigurePolyline = m_PlanarFigure->GetPolyLine( 0 ); const mitk::BaseGeometry *imageGeometry3D = m_Image->GetGeometry( 0 ); // Determine x- and y-dimensions depending on principal axis int i0, i1; switch ( axis ) { case 0: i0 = 1; i1 = 2; break; case 1: i0 = 0; i1 = 2; break; case 2: default: i0 = 0; i1 = 1; break; } m_PlanarFigureCoordinate0= i0; m_PlanarFigureCoordinate1= i1; // store the polyline contour as vtkPoints object bool outOfBounds = false; vtkSmartPointer points = vtkSmartPointer::New(); typename PlanarFigure::PolyLineType::const_iterator it; for ( it = planarFigurePolyline.begin(); it != planarFigurePolyline.end(); ++it ) { Point3D point3D; // Convert 2D point back to the local index coordinates of the selected // image planarFigureGeometry2D->Map( it->Point, point3D ); // Polygons (partially) outside of the image bounds can not be processed // further due to a bug in vtkPolyDataToImageStencil if ( !imageGeometry3D->IsInside( point3D ) ) { outOfBounds = true; } imageGeometry3D->WorldToIndex( point3D, point3D ); points->InsertNextPoint( point3D[i0], point3D[i1], 0 ); } // mark a malformed 2D planar figure ( i.e. area = 0 ) as out of bounds // this can happen when all control points of a rectangle lie on the same line = two of the three extents are zero double bounds[6] = {0, 0, 0, 0, 0, 0}; points->GetBounds( bounds ); bool extent_x = (fabs(bounds[0] - bounds[1])) < mitk::eps; bool extent_y = (fabs(bounds[2] - bounds[3])) < mitk::eps; bool extent_z = (fabs(bounds[4] - bounds[5])) < mitk::eps; // throw an exception if a closed planar figure is deformed, i.e. has only one non-zero extent if ( m_PlanarFigure->IsClosed() && ((extent_x && extent_y) || (extent_x && extent_z) || (extent_y && extent_z))) { mitkThrow() << "Figure has a zero area and cannot be used for masking."; } if ( outOfBounds ) { throw std::runtime_error( "Figure at least partially outside of image bounds!" ); } // create a vtkLassoStencilSource and set the points of the Polygon vtkSmartPointer lassoStencil = vtkSmartPointer::New(); lassoStencil->SetShapeToPolygon(); lassoStencil->SetPoints( points ); // Export from ITK to VTK (to use a VTK filter) typedef itk::VTKImageImport< MaskImage2DType > ImageImportType; typedef itk::VTKImageExport< MaskImage2DType > ImageExportType; typename ImageExportType::Pointer itkExporter = ImageExportType::New(); itkExporter->SetInput( castFilter->GetOutput() ); vtkSmartPointer vtkImporter = vtkSmartPointer::New(); this->ConnectPipelines( itkExporter, vtkImporter ); // Apply the generated image stencil to the input image vtkSmartPointer imageStencilFilter = vtkSmartPointer::New(); imageStencilFilter->SetInputConnection( vtkImporter->GetOutputPort() ); imageStencilFilter->SetStencilConnection(lassoStencil->GetOutputPort()); imageStencilFilter->ReverseStencilOff(); imageStencilFilter->SetBackgroundValue( 0 ); imageStencilFilter->Update(); // Export from VTK back to ITK vtkSmartPointer vtkExporter = vtkImageExport::New(); // TODO: this is WRONG, should be vtkSmartPointer::New(), but bug # 14455 //vtkSmartPointer vtkExporter = vtkSmartPointer::New(); vtkExporter->SetInputConnection( imageStencilFilter->GetOutputPort() ); vtkExporter->Update(); typename ImageImportType::Pointer itkImporter = ImageImportType::New(); this->ConnectPipelines( vtkExporter, itkImporter ); itkImporter->Update(); // Store mask m_InternalImageMask2D = itkImporter->GetOutput(); } void ImageStatisticsCalculator::UnmaskedStatisticsProgressUpdate() { // Need to throw away every second progress event to reach a final count of // 100 since two consecutive filters are used in this case static int updateCounter = 0; if ( updateCounter++ % 2 == 0 ) { this->InvokeEvent( itk::ProgressEvent() ); } } void ImageStatisticsCalculator::MaskedStatisticsProgressUpdate() { this->InvokeEvent( itk::ProgressEvent() ); } } diff --git a/Modules/IpPicSupport/Testing/mitkPicFileReaderTest.cpp b/Modules/IpPicSupport/Testing/mitkPicFileReaderTest.cpp index 01cda35ab7..9ea804837b 100644 --- a/Modules/IpPicSupport/Testing/mitkPicFileReaderTest.cpp +++ b/Modules/IpPicSupport/Testing/mitkPicFileReaderTest.cpp @@ -1,189 +1,189 @@ /*=================================================================== 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 "mitkTestingMacros.h" #include "mitkImage.h" #include "mitkPicFileReader.h" #include "mitkPicHelper.h" #include "mitkSlicedGeometry3D.h" #include #include #include #include int mitkPicFileReaderTest(int argc, char* argv[]) { MITK_TEST_BEGIN(mitkPicFileReaderTest) if(argc>=1) { if(itksys::SystemTools::FileLength(argv[1]) == 0) { mitk::PicFileReader::Pointer emptyFileReader = mitk::PicFileReader::New(); emptyFileReader->SetFileName(argv[1]); MITK_TEST_FOR_EXCEPTION(itk::ImageFileReaderException,emptyFileReader->Update()); } else { //independently read header of pic file mitkIpPicDescriptor *picheader=NULL; if(itksys::SystemTools::LowerCase(itksys::SystemTools::GetFilenameExtension(argv[1])).find(".pic")!=std::string::npos) { picheader = mitkIpPicGetHeader(argv[1], NULL); } if(picheader==NULL) { std::cout<<"file not found/not a pic-file - test not applied [PASSED]"<SetFileName(argv[1]); reader->Update(); std::cout << "Testing IsInitialized(): "; if(reader->GetOutput()->IsInitialized()==false) { std::cout<<"[FAILED]"<GetOutput()->IsSliceSet(0)==false) { std::cout<<"[FAILED]"<GetOutput()->GetGeometry()==NULL) { std::cout<<"[FAILED]"<GetOutput()->GetTimeGeometry(); if(timeGeometry==NULL) { std::cout<<"[FAILED]"<GetGeometryForTimeStep(0).IsNull()) { std::cout<<"[FAILED]"<(timeGeometry->GetGeometryForTimeStep(0).GetPointer()); if(slicedgeometry==NULL) { std::cout<<"[FAILED]"<GetGeometry2D(0); + mitk::PlaneGeometry* geometry2d = slicedgeometry->GetGeometry2D(0); if(geometry2d==NULL) { std::cout<<"[FAILED]"<GetExtent(0)-picheader->n[0])>mitk::eps) || (fabs(geometry2d->GetExtent(1)-picheader->n[1])>mitk::eps)) { std::cout<<"[FAILED]"<GetExtent(0)-picheader->n[0])>mitk::eps) || (fabs(slicedgeometry->GetExtent(1)-picheader->n[1])>mitk::eps) || (picheader->dim>2 && (fabs(slicedgeometry->GetExtent(2)-picheader->n[2])>mitk::eps)) ) { std::cout<<"[FAILED]"<GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(0).two_norm(); spacing[1] = slicedgeometry->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(1).two_norm(); spacing[2] = slicedgeometry->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(2).two_norm(); mitk::Vector3D readspacing=slicedgeometry->GetSpacing(); mitk::Vector3D dist = spacing-readspacing; if(dist.GetSquaredNorm()>mitk::eps) { std::cout<<"[FAILED]"<mitk::eps) { std::cout<<"[FAILED]"<dim==4) { std::cout << "4D dataset: Testing that timebounds are not infinite: "; if((slicedgeometry->GetTimeBounds()[0] == mitk::ScalarTypeNumericTraits::NonpositiveMin()) && (slicedgeometry->GetTimeBounds()[1] == mitk::ScalarTypeNumericTraits::max()) ) { std::cout<<"[FAILED]"< mitk::GeometryClipImageFilter::GeometryClipImageFilter() : m_ClippingGeometry(NULL), m_ClipPartAboveGeometry(true), m_OutsideValue(0), m_AutoOutsideValue(false), m_LabelBothSides(false), m_AutoOrientLabels(false), m_AboveGeometryLabel(1), m_BelowGeometryLabel(2) { this->SetNumberOfIndexedInputs(2); this->SetNumberOfRequiredInputs(2); m_InputTimeSelector = mitk::ImageTimeSelector::New(); m_OutputTimeSelector = mitk::ImageTimeSelector::New(); m_ClippingGeometryData = mitk::GeometryData::New(); } mitk::GeometryClipImageFilter::~GeometryClipImageFilter() { } void mitk::GeometryClipImageFilter::SetClippingGeometry(const mitk::TimeGeometry* timeClippingGeometry) { m_TimeClippingGeometry = timeClippingGeometry; SetClippingGeometry(timeClippingGeometry->GetGeometryForTimeStep(0)); } void mitk::GeometryClipImageFilter::SetClippingGeometry(const mitk::BaseGeometry* aClippingGeometry) { if(aClippingGeometry != m_ClippingGeometry.GetPointer()) { m_ClippingGeometry = aClippingGeometry; m_ClippingGeometryData->SetGeometry(const_cast(aClippingGeometry)); SetNthInput(1, m_ClippingGeometryData); Modified(); } } const mitk::BaseGeometry* mitk::GeometryClipImageFilter::GetClippingGeometry() const { return m_ClippingGeometry; } const mitk::TimeGeometry* mitk::GeometryClipImageFilter::GetClippingTimeGeometry() const { return m_TimeClippingGeometry; } void mitk::GeometryClipImageFilter::GenerateInputRequestedRegion() { Superclass::GenerateInputRequestedRegion(); mitk::Image* output = this->GetOutput(); mitk::Image* input = const_cast< mitk::Image * > ( this->GetInput() ); if((output->IsInitialized()==false) || (m_ClippingGeometry.IsNull())) return; input->SetRequestedRegionToLargestPossibleRegion(); GenerateTimeInInputRegion(output, input); } void mitk::GeometryClipImageFilter::GenerateOutputInformation() { mitk::Image::ConstPointer input = this->GetInput(); mitk::Image::Pointer output = this->GetOutput(); if ((output->IsInitialized()) && (this->GetMTime() <= m_TimeOfHeaderInitialization.GetMTime())) return; itkDebugMacro(<<"GenerateOutputInformation()"); unsigned int i; unsigned int *tmpDimensions = new unsigned int[input->GetDimension()]; for(i=0;iGetDimension();++i) tmpDimensions[i]=input->GetDimension(i); output->Initialize(input->GetPixelType(), input->GetDimension(), tmpDimensions, input->GetNumberOfChannels()); delete [] tmpDimensions; output->SetGeometry(static_cast(input->GetGeometry()->Clone().GetPointer())); output->SetPropertyList(input->GetPropertyList()->Clone()); m_TimeOfHeaderInitialization.Modified(); } template < typename TPixel, unsigned int VImageDimension > -void mitk::_InternalComputeClippedImage(itk::Image* inputItkImage, mitk::GeometryClipImageFilter* geometryClipper, const mitk::Geometry2D* clippingGeometry2D) +void mitk::_InternalComputeClippedImage(itk::Image* inputItkImage, mitk::GeometryClipImageFilter* geometryClipper, const mitk::PlaneGeometry* clippingGeometry2D) { typedef itk::Image ItkInputImageType; typedef itk::Image ItkOutputImageType; typedef itk::ImageRegionConstIteratorWithIndex< ItkInputImageType > ItkInputImageIteratorType; typedef itk::ImageRegionIteratorWithIndex< ItkOutputImageType > ItkOutputImageIteratorType; typename mitk::ImageToItk::Pointer outputimagetoitk = mitk::ImageToItk::New(); outputimagetoitk->SetInput(geometryClipper->m_OutputTimeSelector->GetOutput()); outputimagetoitk->Update(); typename ItkOutputImageType::Pointer outputItkImage = outputimagetoitk->GetOutput(); // create the iterators typename ItkInputImageType::RegionType inputRegionOfInterest = inputItkImage->GetLargestPossibleRegion(); ItkInputImageIteratorType inputIt( inputItkImage, inputRegionOfInterest ); ItkOutputImageIteratorType outputIt( outputItkImage, inputRegionOfInterest ); typename ItkOutputImageType::PixelType outsideValue; if(geometryClipper->m_AutoOutsideValue) outsideValue = itk::NumericTraits::min(); else outsideValue = (typename ItkOutputImageType::PixelType) geometryClipper->m_OutsideValue; mitk::BaseGeometry* inputGeometry = geometryClipper->m_InputTimeSelector->GetOutput()->GetGeometry(); typedef itk::Index IndexType; Point3D indexPt; indexPt.Fill(0); int i, dim=IndexType::GetIndexDimension(); Point3D pointInMM; bool above = geometryClipper->m_ClipPartAboveGeometry; bool labelBothSides = geometryClipper->GetLabelBothSides(); if (geometryClipper->GetAutoOrientLabels()) { Point3D leftMostPoint; leftMostPoint.Fill( std::numeric_limits::min() / 2.0 ); if(clippingGeometry2D->IsAbove(pointInMM) != above) { // invert meaning of above --> left is always the "above" side above = !above; MITK_INFO << leftMostPoint << " is BELOW geometry. Inverting meaning of above" << std::endl; } else MITK_INFO << leftMostPoint << " is above geometry" << std::endl; } typename ItkOutputImageType::PixelType aboveLabel = (typename ItkOutputImageType::PixelType)geometryClipper->GetAboveGeometryLabel(); typename ItkOutputImageType::PixelType belowLabel = (typename ItkOutputImageType::PixelType)geometryClipper->GetBelowGeometryLabel(); for ( inputIt.GoToBegin(), outputIt.GoToBegin(); !inputIt.IsAtEnd(); ++inputIt, ++outputIt) { if((typename ItkOutputImageType::PixelType)inputIt.Get() == outsideValue) { outputIt.Set(outsideValue); } else { for(i=0;iIndexToWorld(indexPt, pointInMM); if(clippingGeometry2D->IsAbove(pointInMM) == above) { if ( labelBothSides ) outputIt.Set( aboveLabel ); else outputIt.Set( outsideValue ); } else { if ( labelBothSides) outputIt.Set( belowLabel ); else outputIt.Set( inputIt.Get() ); } } } } #include "mitkImageAccessByItk.h" void mitk::GeometryClipImageFilter::GenerateData() { Image::ConstPointer input = this->GetInput(); Image::Pointer output = this->GetOutput(); if((output->IsInitialized()==false) || (m_ClippingGeometry.IsNull())) return; - const Geometry2D * clippingGeometryOfCurrentTimeStep = NULL; + const PlaneGeometry * clippingGeometryOfCurrentTimeStep = NULL; if(m_TimeClippingGeometry.IsNull()) { - clippingGeometryOfCurrentTimeStep = dynamic_cast(m_ClippingGeometry.GetPointer()); + clippingGeometryOfCurrentTimeStep = dynamic_cast(m_ClippingGeometry.GetPointer()); } else { - clippingGeometryOfCurrentTimeStep = dynamic_cast(m_TimeClippingGeometry->GetGeometryForTimeStep(0).GetPointer()); + clippingGeometryOfCurrentTimeStep = dynamic_cast(m_TimeClippingGeometry->GetGeometryForTimeStep(0).GetPointer()); } if(clippingGeometryOfCurrentTimeStep == NULL) return; m_InputTimeSelector->SetInput(input); m_OutputTimeSelector->SetInput(this->GetOutput()); mitk::Image::RegionType outputRegion = output->GetRequestedRegion(); const mitk::TimeGeometry *outputTimeGeometry = output->GetTimeGeometry(); const mitk::TimeGeometry *inputTimeGeometry = input->GetTimeGeometry(); ScalarType timeInMS; int timestep=0; int tstart=outputRegion.GetIndex(3); int tmax=tstart+outputRegion.GetSize(3); int t; for(t=tstart;tTimeStepToTimePoint( t ); timestep = inputTimeGeometry->TimePointToTimeStep( timeInMS ); m_InputTimeSelector->SetTimeNr(timestep); m_InputTimeSelector->UpdateLargestPossibleRegion(); m_OutputTimeSelector->SetTimeNr(t); m_OutputTimeSelector->UpdateLargestPossibleRegion(); if(m_TimeClippingGeometry.IsNotNull()) { timestep = m_TimeClippingGeometry->TimePointToTimeStep( timeInMS ); if(m_TimeClippingGeometry->IsValidTimeStep(timestep) == false) continue; - clippingGeometryOfCurrentTimeStep = dynamic_cast(m_TimeClippingGeometry->GetGeometryForTimeStep(timestep).GetPointer()); + clippingGeometryOfCurrentTimeStep = dynamic_cast(m_TimeClippingGeometry->GetGeometryForTimeStep(timestep).GetPointer()); } AccessByItk_2(m_InputTimeSelector->GetOutput(),_InternalComputeClippedImage,this,clippingGeometryOfCurrentTimeStep); } m_TimeOfHeaderInitialization.Modified(); } diff --git a/Modules/MitkAlgorithmsExt/mitkGeometryClipImageFilter.h b/Modules/MitkAlgorithmsExt/mitkGeometryClipImageFilter.h index 4870d2fab0..c1fc85a5ec 100644 --- a/Modules/MitkAlgorithmsExt/mitkGeometryClipImageFilter.h +++ b/Modules/MitkAlgorithmsExt/mitkGeometryClipImageFilter.h @@ -1,180 +1,180 @@ /*=================================================================== 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 MITKGEOMETRYCLIPIMAGEFILTER_H_HEADER_INCLUDED_C1F48A22 #define MITKGEOMETRYCLIPIMAGEFILTER_H_HEADER_INCLUDED_C1F48A22 #include "mitkCommon.h" #include "MitkAlgorithmsExtExports.h" #include "mitkImageToImageFilter.h" #include "mitkImageTimeSelector.h" #include "mitkGeometryData.h" namespace itk { template class ITK_EXPORT Image; } namespace mitk { //##Documentation -//## @brief Filter for clipping an image with a Geometry2D +//## @brief Filter for clipping an image with a PlaneGeometry //## -//## The given geometry for clipping can be either a Geometry2D +//## The given geometry for clipping can be either a PlaneGeometry //## or a TimeGeometry containing multiple instances -//## of Geometry2D +//## of PlaneGeometry //## //## \todo add AutoOrientLabels, which makes the "left" side (minimum X value) side of the image get one defined label. //## left-most because vtkPolyDataNormals uses the same definition and this filter is used for visualization of //## front/back side of curved planes //## //## @ingroup Process class MitkAlgorithmsExt_EXPORT GeometryClipImageFilter : public ImageToImageFilter { public: mitkClassMacro(GeometryClipImageFilter, ImageToImageFilter); itkNewMacro(Self); /** * Set the geometry to be used for clipping * - * The given geometry for clipping must be a Geometry2D. + * The given geometry for clipping must be a PlaneGeometry. */ void SetClippingGeometry(const mitk::BaseGeometry* aClippingGeometry); /** * Set the geometry to be used for clipping * * The given geometry for clipping must a * TimeGeometry containing multiple instances - * of Geometry2D + * of PlaneGeometry */ void SetClippingGeometry(const mitk::TimeGeometry* aClippingGeometry); const mitk::BaseGeometry* GetClippingGeometry() const; const mitk::TimeGeometry* GetClippingTimeGeometry() const; //##Description //## @brief Get whether the part above or below the geometry //## shall be clipped (default: @a true) itkGetConstMacro(ClipPartAboveGeometry, bool); //## @brief Set whether the part above or below the geometry //## shall be clipped (default: @a true) itkSetMacro(ClipPartAboveGeometry, bool); //## @brief Set whether the part above or below the geometry //## shall be clipped (default: @a true) itkBooleanMacro(ClipPartAboveGeometry); //##Description //## @brief Set value for outside pixels (default: 0), //## used when m_AutoOutsideValue is \a false itkSetMacro(OutsideValue, ScalarType); itkGetConstMacro(OutsideValue, ScalarType); //##Description //## @brief If set to \a true the minimum of the ouput pixel type is //## used as outside value (default: \a false) itkSetMacro(AutoOutsideValue, bool); itkGetConstMacro(AutoOutsideValue, bool); itkBooleanMacro(AutoOutsideValue); itkSetMacro(AutoOrientLabels, bool); itkGetConstMacro(AutoOrientLabels, bool); //##Description //## @brief If set to \a true both sides of the clipping //## geometry will be labeld using m_AboveGeometryLabel and //## m_BelowGeometryLabel itkSetMacro(LabelBothSides, bool); itkGetConstMacro(LabelBothSides, bool); itkBooleanMacro(LabelBothSides); //##Description //## @brief Set for voxels above the clipping geometry. //## This value is only used, if m_LabelBothSides is set to true. itkSetMacro(AboveGeometryLabel, ScalarType); itkGetConstMacro(AboveGeometryLabel, ScalarType); //##Description //## @brief Set for voxels below the clipping geometry. //## This value is only used, if m_LabelBothSides is set to true. itkSetMacro(BelowGeometryLabel, ScalarType); itkGetConstMacro(BelowGeometryLabel, ScalarType); protected: GeometryClipImageFilter(); ~GeometryClipImageFilter(); virtual void GenerateInputRequestedRegion(); virtual void GenerateOutputInformation(); virtual void GenerateData(); template < typename TPixel, unsigned int VImageDimension > - friend void _InternalComputeClippedImage(itk::Image* itkImage, mitk::GeometryClipImageFilter* geometryClipper, const mitk::Geometry2D* clippingGeometry2D); + friend void _InternalComputeClippedImage(itk::Image* itkImage, mitk::GeometryClipImageFilter* geometryClipper, const mitk::PlaneGeometry* clippingGeometry2D); mitk::BaseGeometry::ConstPointer m_ClippingGeometry; mitk::GeometryData::Pointer m_ClippingGeometryData; mitk::TimeGeometry::ConstPointer m_TimeClippingGeometry; mitk::ImageTimeSelector::Pointer m_InputTimeSelector; mitk::ImageTimeSelector::Pointer m_OutputTimeSelector; //##Description //## @brief Defines whether the part above or below the geometry //## shall be clipped (default: @a true) bool m_ClipPartAboveGeometry; //##Description //## @brief Value for outside pixels (default: 0) //## //## Used only if m_AutoOutsideValue is \a false. ScalarType m_OutsideValue; //##Description //## @brief If \a true the minimum of the ouput pixel type is //## used as outside value (default: \a false) bool m_AutoOutsideValue; //##Description //## @brief If \a true all pixels above and below the geometry //## are labeled with m_AboveGeometryLabel and m_BelowGeometryLabel bool m_LabelBothSides; /** * \brief Orient above like vtkPolyDataNormals does with AutoOrientNormals */ bool m_AutoOrientLabels; //##Description //## @brief Is used for labeling all pixels above the geometry //## when m_LabelBothSides is on ScalarType m_AboveGeometryLabel; //##Description //## @brief Is used for labeling all pixels below the geometry //## when m_LabelBothSides is on ScalarType m_BelowGeometryLabel; //##Description //## @brief Time when Header was last initialized itk::TimeStamp m_TimeOfHeaderInitialization; }; } // namespace mitk #endif /* MITKGEOMETRYCLIPIMAGEFILTER_H_HEADER_INCLUDED_C1F48A22 */ diff --git a/Modules/MitkAlgorithmsExt/mitkHeightFieldSurfaceClipImageFilter.cpp b/Modules/MitkAlgorithmsExt/mitkHeightFieldSurfaceClipImageFilter.cpp index 55e85258b1..f52d9dbef1 100644 --- a/Modules/MitkAlgorithmsExt/mitkHeightFieldSurfaceClipImageFilter.cpp +++ b/Modules/MitkAlgorithmsExt/mitkHeightFieldSurfaceClipImageFilter.cpp @@ -1,465 +1,465 @@ /*=================================================================== 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 "mitkHeightFieldSurfaceClipImageFilter.h" #include "mitkImageTimeSelector.h" #include "mitkTimeHelper.h" #include "mitkProperties.h" #include "mitkImageToItk.h" #include "mitkImageAccessByItk.h" #include #include #include #include #include #include namespace mitk { HeightFieldSurfaceClipImageFilter::HeightFieldSurfaceClipImageFilter() : m_ClippingMode( CLIPPING_MODE_CONSTANT ), m_ClippingConstant( 0.0 ), m_MultiplicationFactor( 2.0 ), m_MultiPlaneValue(2), m_HeightFieldResolutionX( 256 ), m_HeightFieldResolutionY( 256 ), m_MaxHeight( 1024.0 ) { this->SetNumberOfIndexedInputs(8); this->SetNumberOfRequiredInputs(2); m_InputTimeSelector = ImageTimeSelector::New(); m_OutputTimeSelector = ImageTimeSelector::New(); } HeightFieldSurfaceClipImageFilter::~HeightFieldSurfaceClipImageFilter() { } void HeightFieldSurfaceClipImageFilter::SetClippingSurface( Surface *clippingSurface ) { this->SetNthInput( 1, clippingSurface ); } void HeightFieldSurfaceClipImageFilter::SetClippingSurfaces(ClippingPlaneList planeList) { if(planeList.size() > 7) { MITK_WARN<<"Only 7 clipping planes are allowed!"; } for (unsigned int i = 0; i < planeList.size(); ++i) { this->SetNthInput(i+1, planeList.at(i)); } } const Surface* HeightFieldSurfaceClipImageFilter::GetClippingSurface() const { return dynamic_cast< const Surface * >( itk::ProcessObject::GetInput( 1 ) ); } void HeightFieldSurfaceClipImageFilter::SetClippingMode( int mode ) { m_ClippingMode = mode; } int HeightFieldSurfaceClipImageFilter::GetClippingMode() { return m_ClippingMode; } void HeightFieldSurfaceClipImageFilter::SetClippingModeToConstant() { m_ClippingMode = CLIPPING_MODE_CONSTANT; } void HeightFieldSurfaceClipImageFilter::SetClippingModeToMultiplyByFactor() { m_ClippingMode = CLIPPING_MODE_MULTIPLYBYFACTOR; } void HeightFieldSurfaceClipImageFilter::SetClippingModeToMultiPlaneValue() { m_ClippingMode = CLIPPING_MODE_MULTIPLANE; } void HeightFieldSurfaceClipImageFilter::GenerateInputRequestedRegion() { Image *outputImage = this->GetOutput(); Image *inputImage = const_cast< Image * >( this->GetInput( 0 ) ); const Surface *inputSurface = dynamic_cast< const Surface * >( this->GetInput( 1 ) ); if ( !outputImage->IsInitialized() || inputSurface == NULL ) { return; } inputImage->SetRequestedRegionToLargestPossibleRegion(); GenerateTimeInInputRegion( outputImage, inputImage ); } void HeightFieldSurfaceClipImageFilter::GenerateOutputInformation() { const Image *inputImage = this->GetInput( 0 ); Image *outputImage = this->GetOutput(); if ( outputImage->IsInitialized() && (this->GetMTime() <= m_TimeOfHeaderInitialization.GetMTime()) ) { return; } itkDebugMacro(<<"GenerateOutputInformation()"); unsigned int i; unsigned int *tmpDimensions = new unsigned int[inputImage->GetDimension()]; for ( i = 0; i < inputImage->GetDimension(); ++i ) { tmpDimensions[i] = inputImage->GetDimension( i ); } outputImage->Initialize( inputImage->GetPixelType(), inputImage->GetDimension(), tmpDimensions, inputImage->GetNumberOfChannels() ); delete[] tmpDimensions; outputImage->SetGeometry( static_cast< Geometry3D * >( inputImage->GetGeometry()->Clone().GetPointer() ) ); outputImage->SetPropertyList( inputImage->GetPropertyList()->Clone() ); m_TimeOfHeaderInitialization.Modified(); } template < typename TPixel, unsigned int VImageDimension > void HeightFieldSurfaceClipImageFilter::_InternalComputeClippedImage( itk::Image< TPixel, VImageDimension > *inputItkImage, HeightFieldSurfaceClipImageFilter *clipImageFilter, vtkPolyData *clippingPolyData, AffineTransform3D *imageToPlaneTransform ) { typedef itk::Image< TPixel, VImageDimension > ItkInputImageType; typedef itk::Image< TPixel, VImageDimension > ItkOutputImageType; typedef itk::ImageSliceConstIteratorWithIndex< ItkInputImageType > ItkInputImageIteratorType; typedef itk::ImageRegionIteratorWithIndex< ItkOutputImageType > ItkOutputImageIteratorType; typename ImageToItk::Pointer outputimagetoitk = ImageToItk::New(); outputimagetoitk->SetInput( clipImageFilter->m_OutputTimeSelector->GetOutput() ); outputimagetoitk->Update(); typename ItkOutputImageType::Pointer outputItkImage = outputimagetoitk->GetOutput(); std::vector< double > test; // create the iterators typename ItkInputImageType::RegionType inputRegionOfInterest = inputItkImage->GetLargestPossibleRegion(); ItkInputImageIteratorType inputIt( inputItkImage, inputRegionOfInterest ); ItkOutputImageIteratorType outputIt( outputItkImage, inputRegionOfInterest ); // Get bounds of clipping data clippingPolyData->ComputeBounds(); double *bounds = clippingPolyData->GetBounds(); double xWidth = bounds[1] - bounds[0]; double yWidth = bounds[3] - bounds[2]; // Create vtkCellLocator for clipping poly data vtkCellLocator *cellLocator = vtkCellLocator::New(); cellLocator->SetDataSet( clippingPolyData ); cellLocator->CacheCellBoundsOn(); cellLocator->AutomaticOn(); cellLocator->BuildLocator(); // Allocate memory for 2D image to hold the height field generated by // projecting the clipping data onto the plane double *heightField = new double[m_HeightFieldResolutionX * m_HeightFieldResolutionY]; // Walk through height field and for each entry calculate height of the // clipping poly data at this point by means of vtkCellLocator. The // clipping data x/y bounds are used for converting from poly data space to // image (height-field) space. MITK_INFO << "Calculating Height Field..." << std::endl; for ( unsigned int y = 0; y < m_HeightFieldResolutionY; ++y ) { for ( unsigned int x = 0; x < m_HeightFieldResolutionX; ++x ) { double p0[3], p1[3], surfacePoint[3], pcoords[3]; p0[0] = bounds[0] + xWidth * x / (double) m_HeightFieldResolutionX; p0[1] = bounds[2] + yWidth * y / (double) m_HeightFieldResolutionY; p0[2] = -m_MaxHeight; p1[0] = p0[0]; p1[1] = p0[1]; p1[2] = m_MaxHeight; double t, distance; int subId; if ( cellLocator->IntersectWithLine( p0, p1, 0.1, t, surfacePoint, pcoords, subId ) ) { distance = (2.0 * t - 1.0) * m_MaxHeight; } else { distance = -65536.0; } heightField[y * m_HeightFieldResolutionX + x] = distance; itk::Image::IndexType index; index[0] = x; index[1] = y; } } // Walk through entire input image and for each point determine its distance // from the x/y plane. MITK_INFO << "Performing clipping..." << std::endl; TPixel factor = static_cast< TPixel >( clipImageFilter->m_MultiplicationFactor ); TPixel clippingConstant = clipImageFilter->m_ClippingConstant; inputIt.SetFirstDirection( 0 ); inputIt.SetSecondDirection( 1 ); //through all slices for ( inputIt.GoToBegin(), outputIt.GoToBegin(); !inputIt.IsAtEnd(); inputIt.NextSlice() ) { //through all lines of a slice for ( ; !inputIt.IsAtEndOfSlice(); inputIt.NextLine() ) { //Transform the start(line) point from the image to the plane Point3D imageP0, planeP0; imageP0[0] = inputIt.GetIndex()[0]; imageP0[1] = inputIt.GetIndex()[1]; imageP0[2] = inputIt.GetIndex()[2]; planeP0 = imageToPlaneTransform->TransformPoint( imageP0 ); //Transform the end point (line) from the image to the plane Point3D imageP1, planeP1; imageP1[0] = imageP0[0] + inputRegionOfInterest.GetSize( 0 ); imageP1[1] = imageP0[1]; imageP1[2] = imageP0[2]; planeP1 = imageToPlaneTransform->TransformPoint( imageP1 ); //calculate the step size (if the plane is rotate, you go "crossway" through the image) Vector3D step = (planeP1 - planeP0) / (double) inputRegionOfInterest.GetSize( 0 ); //over all pixel for ( ; !inputIt.IsAtEndOfLine(); ++inputIt, ++outputIt, planeP0 += step ) { //Only ConstantMode: if image pixel value == constant mode value-->set output pixel value directly if ( (clipImageFilter->m_ClippingMode == CLIPPING_MODE_CONSTANT) && ((TPixel)inputIt.Get() == clippingConstant ) ) { outputIt.Set( clippingConstant ); } else { int x0 = (int) ((double)(m_HeightFieldResolutionX) * (planeP0[0] - bounds[0]) / xWidth); int y0 = (int) ((double)(m_HeightFieldResolutionY) * (planeP0[1] - bounds[2]) / yWidth); bool clip; //if the current point is outside of the plane region (RegionOfInterest)-->clip the pixel allways if ( (x0 < 0) || (x0 >= (int)m_HeightFieldResolutionX) || (y0 < 0) || (y0 >= (int)m_HeightFieldResolutionY) ) { clip = true; } else { // Calculate bilinearly interpolated height field value at plane point int x1 = x0 + 1; int y1 = y0 + 1; if ( x1 >= (int)m_HeightFieldResolutionX ) { x1 = x0; } if ( y1 >= (int)m_HeightFieldResolutionY ) { y1 = y0; } //Get the neighbour points for the interpolation ScalarType q00, q01, q10, q11; q00 = heightField[y0 * m_HeightFieldResolutionX + x0]; q01 = heightField[y0 * m_HeightFieldResolutionX + x1]; q10 = heightField[y1 * m_HeightFieldResolutionX + x0]; q11 = heightField[y1 * m_HeightFieldResolutionX + x1]; double p00 = ((double)(m_HeightFieldResolutionX) * (planeP0[0] - bounds[0]) / xWidth); double p01 = ((double)(m_HeightFieldResolutionY) * (planeP0[1] - bounds[2]) / yWidth); ScalarType q = q00 * ((double) x1 - p00) * ((double) y1 - p01) + q01 * (p00 - (double) x0) * ((double) y1 - p01) + q10 * ((double) x1 - p00) * (p01 - (double) y0) + q11 * (p00 - (double) x0) * (p01 - (double) y0); if ( q - planeP0[2] < 0 ) { clip = true; } else { clip = false; } } //different modes: differnt values for the clipped pixel if ( clip ) { if ( clipImageFilter->m_ClippingMode == CLIPPING_MODE_CONSTANT ) { outputIt.Set( clipImageFilter->m_ClippingConstant ); } else if ( clipImageFilter->m_ClippingMode == CLIPPING_MODE_MULTIPLYBYFACTOR ) { outputIt.Set( inputIt.Get() * factor ); } else if ( clipImageFilter->m_ClippingMode == CLIPPING_MODE_MULTIPLANE ) { if(inputIt.Get() != 0) outputIt.Set( inputIt.Get() + m_MultiPlaneValue); else outputIt.Set( inputIt.Get() ); } } // the non-clipped pixel keeps his value else { outputIt.Set( inputIt.Get() ); } } } } } MITK_INFO << "DONE!" << std::endl; // Clean-up cellLocator->Delete(); } void HeightFieldSurfaceClipImageFilter::GenerateData() { const Image *inputImage = this->GetInput( 0 ); const Image *outputImage = this->GetOutput(); m_InputTimeSelector->SetInput( inputImage ); m_OutputTimeSelector->SetInput( outputImage ); Image::RegionType outputRegion = outputImage->GetRequestedRegion(); const TimeGeometry *outputTimeGeometry = outputImage->GetTimeGeometry(); const TimeGeometry *inputTimeGeometry = inputImage->GetTimeGeometry(); ScalarType timeInMS; int timestep = 0; int tstart = outputRegion.GetIndex( 3 ); int tmax = tstart + outputRegion.GetSize( 3 ); for (unsigned int i = 1; i < this->GetNumberOfInputs(); ++i) { Surface *inputSurface = const_cast< Surface * >( dynamic_cast< Surface * >( itk::ProcessObject::GetInput( i ) ) ); if ( !outputImage->IsInitialized() || inputSurface == NULL ) return; MITK_INFO<<"Plane: "<TimeStepToTimePoint( t ); timestep = inputTimeGeometry->TimePointToTimeStep( timeInMS ); m_InputTimeSelector->SetTimeNr( timestep ); m_InputTimeSelector->UpdateLargestPossibleRegion(); m_OutputTimeSelector->SetTimeNr( t ); m_OutputTimeSelector->UpdateLargestPossibleRegion(); // Compose IndexToWorld transform of image with WorldToIndexTransform of // clipping data for conversion from image index space to plane index space AffineTransform3D::Pointer planeWorldToIndexTransform = AffineTransform3D::New(); inputSurface->GetGeometry( t )->GetIndexToWorldTransform() ->GetInverse( planeWorldToIndexTransform ); AffineTransform3D::Pointer imageToPlaneTransform = AffineTransform3D::New(); imageToPlaneTransform->SetIdentity(); imageToPlaneTransform->Compose( inputTimeGeometry->GetGeometryForTimeStep( t )->GetIndexToWorldTransform() ); imageToPlaneTransform->Compose( planeWorldToIndexTransform ); MITK_INFO << "Accessing ITK function...\n"; if(i==1) { AccessByItk_3( m_InputTimeSelector->GetOutput(), _InternalComputeClippedImage, this, inputSurface->GetVtkPolyData( t ), imageToPlaneTransform ); } else { mitk::Image::Pointer extensionImage = m_OutputTimeSelector->GetOutput()->Clone(); AccessByItk_3( extensionImage, _InternalComputeClippedImage, this, inputSurface->GetVtkPolyData( t ), imageToPlaneTransform ); } if (m_ClippingMode == CLIPPING_MODE_MULTIPLANE) m_MultiPlaneValue = m_MultiPlaneValue*2; } } m_TimeOfHeaderInitialization.Modified(); } } // namespace diff --git a/Modules/MitkExt/Algorithms/mitkInterpolateLinesFilter.h b/Modules/MitkExt/Algorithms/mitkInterpolateLinesFilter.h index 3dc0158dff..2082ede460 100644 --- a/Modules/MitkExt/Algorithms/mitkInterpolateLinesFilter.h +++ b/Modules/MitkExt/Algorithms/mitkInterpolateLinesFilter.h @@ -1,104 +1,104 @@ /*=================================================================== 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 MITKINTERPOLATELINESFILTER_H_HEADER_INCLUDED_C10B22CD #define MITKINTERPOLATELINESFILTER_H_HEADER_INCLUDED_C10B22CD #include "mitkCommon.h" #include "MitkExtExports.h" #include "mitkSurfaceSource.h" #include "mitkMesh.h" -#include "mitkGeometry2D.h" +#include "mitkPlaneGeometry.h" class vtkPolyData; class vtkPoints; class vtkCardinalSpline; class vtkPoints; class vtkCellArray; namespace mitk { //##Documentation //## @brief Interpolate additional points on lines //## -//## If a Geometry2D is set (by SetGeometryForInterpolation), -//## we do an interpolation in the 2D-space of the Geometry2D: +//## If a PlaneGeometry is set (by SetGeometryForInterpolation), +//## we do an interpolation in the 2D-space of the PlaneGeometry: //## Map two neighboring original points on -//## the Geometry2D, resulting in two 2D-points, interpolate +//## the PlaneGeometry, resulting in two 2D-points, interpolate //## in 2D between them, and map them back via the -//## Geometry2D in the 3D-world. +//## PlaneGeometry in the 3D-world. //## @ingroup Process class MitkExt_EXPORT InterpolateLinesFilter : public SurfaceSource { public: mitkClassMacro(InterpolateLinesFilter, SurfaceSource); itkNewMacro(Self); virtual void GenerateOutputInformation(); virtual void GenerateData(); const mitk::Mesh *GetInput(void); virtual void SetInput(const mitk::Mesh *image); //##Documentation //## @brief Get spline resolution //## itkGetMacro(SplineResolution, unsigned int); //##Documentation //## @brief Set spline resolution //## itkSetMacro(SplineResolution, unsigned int); //##Documentation //## @brief Get/set geometry for interpolation //## //## If this is set (not NULL), we do an interpolation in the - //## 2D-space of the Geometry2D: Map two neighboring original - //## points on the Geometry2D, resulting in two 2D-points, + //## 2D-space of the PlaneGeometry: Map two neighboring original + //## points on the PlaneGeometry, resulting in two 2D-points, //## interpolate in 2D between them, and map them back via the - //## Geometry2D in the 3D-world. - itkGetConstObjectMacro(GeometryForInterpolation, mitk::Geometry2D); - itkSetObjectMacro(GeometryForInterpolation, mitk::Geometry2D); + //## PlaneGeometry in the 3D-world. + itkGetConstObjectMacro(GeometryForInterpolation, mitk::PlaneGeometry); + itkSetObjectMacro(GeometryForInterpolation, mitk::PlaneGeometry); //##Documentation //## @brief Get the overall length of the interpolated lines //## //## @warning valid only after Update() itkGetMacro(Length, ScalarType); protected: InterpolateLinesFilter(); virtual ~InterpolateLinesFilter(); void BuildPointAndVectorList(mitk::Mesh::CellType& cell, vtkPoints* points, vtkCellArray* cellarray); //##Documentation //## @brief Spline resolution of created Surface //## unsigned int m_SplineResolution; - mitk::Geometry2D::Pointer m_GeometryForInterpolation; + mitk::PlaneGeometry::Pointer m_GeometryForInterpolation; vtkCardinalSpline *m_SpX, *m_SpY, *m_SpZ; ScalarType m_Length; }; } // namespace mitk #endif /* MITKINTERPOLATELINESFILTER_H_HEADER_INCLUDED_C10B22CD */ diff --git a/Modules/MitkExt/Interactions/mitkAffineInteractor3D.cpp b/Modules/MitkExt/Interactions/mitkAffineInteractor3D.cpp index b6f33f2aa9..5e8478cb1c 100644 --- a/Modules/MitkExt/Interactions/mitkAffineInteractor3D.cpp +++ b/Modules/MitkExt/Interactions/mitkAffineInteractor3D.cpp @@ -1,488 +1,488 @@ /*=================================================================== 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 "mitkAffineInteractor3D.h" #include "mitkPointOperation.h" #include "mitkPositionEvent.h" #include "mitkStatusBar.h" #include "mitkDataNode.h" #include "mitkInteractionConst.h" #include "mitkAction.h" #include "mitkStateEvent.h" #include "mitkOperationEvent.h" #include "mitkUndoController.h" #include "mitkStateMachineFactory.h" #include "mitkStateTransitionOperation.h" #include "mitkBaseRenderer.h" #include "mitkRenderingManager.h" #include "mitkRotationOperation.h" #include #include #include #include #include #include #include #include namespace mitk { //how precise must the user pick the point //default value AffineInteractor3D ::AffineInteractor3D(const char * type, DataNode* dataNode, int /* n */ ) : Interactor( type, dataNode ), m_Precision( 6.5 ), m_InteractionMode( INTERACTION_MODE_TRANSLATION ) { m_OriginalGeometry = BaseGeometry::New(); // Initialize vector arithmetic m_ObjectNormal[0] = 0.0; m_ObjectNormal[1] = 0.0; m_ObjectNormal[2] = 1.0; } AffineInteractor3D::~AffineInteractor3D() { } void AffineInteractor3D::SetInteractionMode( unsigned int interactionMode ) { m_InteractionMode = interactionMode; } void AffineInteractor3D::SetInteractionModeToTranslation() { m_InteractionMode = INTERACTION_MODE_TRANSLATION; } void AffineInteractor3D::SetInteractionModeToRotation() { m_InteractionMode = INTERACTION_MODE_ROTATION; } unsigned int AffineInteractor3D::GetInteractionMode() const { return m_InteractionMode; } void AffineInteractor3D::SetPrecision( ScalarType precision ) { m_Precision = precision; } // Overwritten since this class can handle it better! float AffineInteractor3D ::CanHandleEvent(StateEvent const* stateEvent) const { float returnValue = 0.5; // If it is a key event that can be handled in the current state, // then return 0.5 DisplayPositionEvent const *disPosEvent = dynamic_cast (stateEvent->GetEvent()); // Key event handling: if (disPosEvent == NULL) { // Check if the current state has a transition waiting for that key event. if (this->GetCurrentState()->GetTransition(stateEvent->GetId())!=NULL) { return 0.5; } else { return 0.0; } } //on MouseMove do nothing! //if (stateEvent->GetEvent()->GetType() == Type_MouseMove) //{ // return 0.0; //} //if the event can be understood and if there is a transition waiting for that event if (this->GetCurrentState()->GetTransition(stateEvent->GetId())!=NULL) { returnValue = 0.5;//it can be understood } //int timeStep = disPosEvent->GetSender()->GetTimeStep(); //CurveModel *curveModel = dynamic_cast( // m_DataNode->GetData() ); //if ( curveModel != NULL ) //{ - // // Get the Geometry2D of the window the user interacts with (for 2D point + // // Get the PlaneGeometry of the window the user interacts with (for 2D point // // projection) // BaseRenderer *renderer = stateEvent->GetEvent()->GetSender(); - // const Geometry2D *projectionPlane = renderer->GetCurrentWorldGeometry2D(); + // const PlaneGeometry *projectionPlane = renderer->GetCurrentWorldGeometry2D(); // // For reading on the points, Ids etc // //CurveModel::PointSetType *pointSet = curveModel->GetPointSet( timeStep ); // //if ( pointSet == NULL ) // //{ // // return 0.0; // //} //} return returnValue; } bool AffineInteractor3D ::ExecuteAction( Action *action, StateEvent const *stateEvent ) { bool ok = false; // Get data object BaseData *data = m_DataNode->GetData(); if ( data == NULL ) { MITK_ERROR << "No data object present!"; return ok; } // Get Event and extract renderer const Event *event = stateEvent->GetEvent(); BaseRenderer *renderer = NULL; vtkRenderWindow *renderWindow = NULL; vtkRenderWindowInteractor *renderWindowInteractor = NULL; vtkRenderer *currentVtkRenderer = NULL; vtkCamera *camera = NULL; if ( event != NULL ) { renderer = event->GetSender(); if ( renderer != NULL ) { renderWindow = renderer->GetRenderWindow(); if ( renderWindow != NULL ) { renderWindowInteractor = renderWindow->GetInteractor(); if ( renderWindowInteractor != NULL ) { currentVtkRenderer = renderWindowInteractor ->GetInteractorStyle()->GetCurrentRenderer(); if ( currentVtkRenderer != NULL ) { camera = currentVtkRenderer->GetActiveCamera(); } } } } } // Check if we have a DisplayPositionEvent const DisplayPositionEvent *dpe = dynamic_cast< const DisplayPositionEvent * >( stateEvent->GetEvent() ); if ( dpe != NULL ) { m_CurrentPickedPoint = dpe->GetWorldPosition(); m_CurrentPickedDisplayPoint = dpe->GetDisplayPosition(); } // Get the timestep to also support 3D+t int timeStep = 0; ScalarType timeInMS = 0.0; if ( renderer != NULL ) { timeStep = renderer->GetTimeStep( data ); timeInMS = renderer->GetTime(); } // If data is an mitk::Surface, extract it Surface *surface = dynamic_cast< Surface * >( data ); vtkPolyData *polyData = NULL; if ( surface != NULL ) { polyData = surface->GetVtkPolyData( timeStep ); // Extract surface normal from surface (if existent, otherwise use default) vtkPointData *pointData = polyData->GetPointData(); if ( pointData != NULL ) { vtkDataArray *normal = polyData->GetPointData()->GetVectors( "planeNormal" ); if ( normal != NULL ) { m_ObjectNormal[0] = normal->GetComponent( 0, 0 ); m_ObjectNormal[1] = normal->GetComponent( 0, 1 ); m_ObjectNormal[2] = normal->GetComponent( 0, 2 ); } } } // Get geometry object m_Geometry = data->GetGeometry( timeStep ); // Make sure that the data (if time-resolved) has enough entries; // if not, create the required extra ones (empty) data->Expand( timeStep+1 ); switch (action->GetActionId()) { case AcDONOTHING: ok = true; break; case AcCHECKOBJECT: { // Re-enable VTK interactor (may have been disabled previously) if ( renderWindowInteractor != NULL ) { renderWindowInteractor->Enable(); } // Check if we have a DisplayPositionEvent const DisplayPositionEvent *dpe = dynamic_cast< const DisplayPositionEvent * >( stateEvent->GetEvent() ); if ( dpe == NULL ) { ok = true; break; } // Check if an object is present at the current mouse position DataNode *pickedNode = dpe->GetPickedObjectNode(); StateEvent *newStateEvent; if ( pickedNode == m_DataNode ) { // Yes: object will be selected newStateEvent = new StateEvent( EIDYES ); } else { // No: back to start state newStateEvent = new StateEvent( EIDNO ); } this->HandleEvent( newStateEvent ); ok = true; break; } case AcDESELECTOBJECT: { // Color object white m_DataNode->SetColor( 1.0, 1.0, 1.0 ); RenderingManager::GetInstance()->RequestUpdateAll(); // Colorize surface / wireframe as inactive this->ColorizeSurface( polyData, m_CurrentPickedPoint, -1.0 ); ok = true; break; } case AcSELECTPICKEDOBJECT: { // Color object red m_DataNode->SetColor( 1.0, 0.0, 0.0 ); RenderingManager::GetInstance()->RequestUpdateAll(); // Colorize surface / wireframe dependend on distance from picked point this->ColorizeSurface( polyData, m_CurrentPickedPoint, 0.0 ); ok = true; break; } case AcINITMOVE: { // Disable VTK interactor until MITK interaction has been completed if ( renderWindowInteractor != NULL ) { renderWindowInteractor->Disable(); } // Check if we have a DisplayPositionEvent const DisplayPositionEvent *dpe = dynamic_cast< const DisplayPositionEvent * >( stateEvent->GetEvent() ); if ( dpe == NULL ) { ok = true; break; } //DataNode *pickedNode = dpe->GetPickedObjectNode(); m_InitialPickedPoint = m_CurrentPickedPoint; m_InitialPickedDisplayPoint = m_CurrentPickedDisplayPoint; if ( currentVtkRenderer != NULL ) { vtkInteractorObserver::ComputeDisplayToWorld( currentVtkRenderer, m_InitialPickedDisplayPoint[0], m_InitialPickedDisplayPoint[1], 0.0, //m_InitialInteractionPickedPoint[2], m_InitialPickedPointWorld ); } // Make deep copy of current Geometry3D of the plane data->UpdateOutputInformation(); // make sure that the Geometry is up-to-date m_OriginalGeometry = static_cast< BaseGeometry * >( data->GetGeometry( timeStep )->Clone().GetPointer() ); ok = true; break; } case AcMOVE: { // Check if we have a DisplayPositionEvent const DisplayPositionEvent *dpe = dynamic_cast< const DisplayPositionEvent * >( stateEvent->GetEvent() ); if ( dpe == NULL ) { ok = true; break; } if ( currentVtkRenderer != NULL ) { vtkInteractorObserver::ComputeDisplayToWorld( currentVtkRenderer, m_CurrentPickedDisplayPoint[0], m_CurrentPickedDisplayPoint[1], 0.0, //m_InitialInteractionPickedPoint[2], m_CurrentPickedPointWorld ); } Vector3D interactionMove; interactionMove[0] = m_CurrentPickedPointWorld[0] - m_InitialPickedPointWorld[0]; interactionMove[1] = m_CurrentPickedPointWorld[1] - m_InitialPickedPointWorld[1]; interactionMove[2] = m_CurrentPickedPointWorld[2] - m_InitialPickedPointWorld[2]; if ( m_InteractionMode == INTERACTION_MODE_TRANSLATION ) { Point3D origin = m_OriginalGeometry->GetOrigin(); Vector3D transformedObjectNormal; data->GetGeometry( timeStep )->IndexToWorld( m_ObjectNormal, transformedObjectNormal ); data->GetGeometry( timeStep )->SetOrigin( origin + transformedObjectNormal * (interactionMove * transformedObjectNormal) ); } else if ( m_InteractionMode == INTERACTION_MODE_ROTATION ) { if ( camera ) { double vpn[3]; camera->GetViewPlaneNormal( vpn ); Vector3D viewPlaneNormal; viewPlaneNormal[0] = vpn[0]; viewPlaneNormal[1] = vpn[1]; viewPlaneNormal[2] = vpn[2]; Vector3D rotationAxis = itk::CrossProduct( viewPlaneNormal, interactionMove ); rotationAxis.Normalize(); int *size = currentVtkRenderer->GetSize(); double l2 = (m_CurrentPickedDisplayPoint[0] - m_InitialPickedDisplayPoint[0]) * (m_CurrentPickedDisplayPoint[0] - m_InitialPickedDisplayPoint[0]) + (m_CurrentPickedDisplayPoint[1] - m_InitialPickedDisplayPoint[1]) * (m_CurrentPickedDisplayPoint[1] - m_InitialPickedDisplayPoint[1]); double rotationAngle = 360.0 * sqrt(l2/(size[0]*size[0]+size[1]*size[1])); // Use center of data bounding box as center of rotation Point3D rotationCenter = m_OriginalGeometry->GetCenter();; // Reset current Geometry3D to original state (pre-interaction) and // apply rotation RotationOperation op( OpROTATE, rotationCenter, rotationAxis, rotationAngle ); BaseGeometry::Pointer newGeometry = static_cast< BaseGeometry * >( m_OriginalGeometry->Clone().GetPointer() ); newGeometry->ExecuteOperation( &op ); data->SetClonedGeometry(newGeometry, timeStep); } } RenderingManager::GetInstance()->RequestUpdateAll(); ok = true; break; } default: return Superclass::ExecuteAction( action, stateEvent ); } return ok; } bool AffineInteractor3D::ColorizeSurface( vtkPolyData *polyData, const Point3D & /*pickedPoint*/, double scalar ) { if ( polyData == NULL ) { return false; } //vtkPoints *points = polyData->GetPoints(); vtkPointData *pointData = polyData->GetPointData(); if ( pointData == NULL ) { return false; } vtkDataArray *scalars = pointData->GetScalars(); if ( scalars == NULL ) { return false; } for ( unsigned int i = 0; i < pointData->GetNumberOfTuples(); ++i ) { scalars->SetComponent( i, 0, scalar ); } polyData->Modified(); pointData->Update(); return true; } } // namespace diff --git a/Modules/MitkExt/Interactions/mitkSurfaceDeformationInteractor3D.cpp b/Modules/MitkExt/Interactions/mitkSurfaceDeformationInteractor3D.cpp index 749af5cc05..0089494a96 100644 --- a/Modules/MitkExt/Interactions/mitkSurfaceDeformationInteractor3D.cpp +++ b/Modules/MitkExt/Interactions/mitkSurfaceDeformationInteractor3D.cpp @@ -1,498 +1,498 @@ /*=================================================================== 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 "mitkSurfaceDeformationInteractor3D.h" #include "mitkPointOperation.h" #include "mitkDisplayPositionEvent.h" #include "mitkWheelEvent.h" #include "mitkStatusBar.h" #include "mitkDataNode.h" #include "mitkInteractionConst.h" #include "mitkAction.h" #include "mitkStateEvent.h" #include "mitkOperationEvent.h" #include "mitkUndoController.h" #include "mitkStateMachineFactory.h" #include "mitkStateTransitionOperation.h" #include "mitkBaseRenderer.h" #include "mitkRenderingManager.h" #include "mitkSurface.h" #include #include #include #include #include #include #include #include //how precise must the user pick the point //default value mitk::SurfaceDeformationInteractor3D ::SurfaceDeformationInteractor3D(const char * type, DataNode* dataNode, int /* n */ ) : Interactor( type, dataNode ), m_Precision( 6.5 ), m_PickedSurfaceNode( NULL ), m_PickedSurface( NULL ), m_GaussSigma( 30.0 ) { m_OriginalPolyData = vtkPolyData::New(); // Initialize vector arithmetic m_ObjectNormal[0] = 0.0; m_ObjectNormal[1] = 0.0; m_ObjectNormal[2] = 1.0; } mitk::SurfaceDeformationInteractor3D::~SurfaceDeformationInteractor3D() { m_OriginalPolyData->Delete(); } void mitk::SurfaceDeformationInteractor3D::SetPrecision( mitk::ScalarType precision ) { m_Precision = precision; } // Overwritten since this class can handle it better! float mitk::SurfaceDeformationInteractor3D ::CanHandleEvent(StateEvent const* stateEvent) const { float returnValue = 0.5; // If it is a key event that can be handled in the current state, // then return 0.5 mitk::DisplayPositionEvent const *disPosEvent = dynamic_cast (stateEvent->GetEvent()); // Key event handling: if (disPosEvent == NULL) { // Check if the current state has a transition waiting for that key event. if (this->GetCurrentState()->GetTransition(stateEvent->GetId())!=NULL) { return 0.5; } else { return 0.0; } } //on MouseMove do nothing! //if (stateEvent->GetEvent()->GetType() == mitk::Type_MouseMove) //{ // return 0.0; //} //if the event can be understood and if there is a transition waiting for that event if (this->GetCurrentState()->GetTransition(stateEvent->GetId())!=NULL) { returnValue = 0.5;//it can be understood } //int timeStep = disPosEvent->GetSender()->GetTimeStep(); //mitk::CurveModel *curveModel = dynamic_cast( // m_DataNode->GetData() ); //if ( curveModel != NULL ) //{ - // // Get the Geometry2D of the window the user interacts with (for 2D point + // // Get the PlaneGeometry of the window the user interacts with (for 2D point // // projection) // mitk::BaseRenderer *renderer = stateEvent->GetEvent()->GetSender(); - // const Geometry2D *projectionPlane = renderer->GetCurrentWorldGeometry2D(); + // const PlaneGeometry *projectionPlane = renderer->GetCurrentWorldGeometry2D(); // // For reading on the points, Ids etc // //mitk::CurveModel::PointSetType *pointSet = curveModel->GetPointSet( timeStep ); // //if ( pointSet == NULL ) // //{ // // return 0.0; // //} //} return returnValue; } bool mitk::SurfaceDeformationInteractor3D ::ExecuteAction( Action *action, mitk::StateEvent const *stateEvent ) { bool ok = false; // Get data object mitk::BaseData *data = m_DataNode->GetData(); if ( data == NULL ) { MITK_ERROR << "No data object present!"; return ok; } // Get mitk::Event and extract renderer const mitk::Event *event = stateEvent->GetEvent(); mitk::BaseRenderer *renderer = NULL; vtkRenderWindow *renderWindow = NULL; vtkRenderWindowInteractor *renderWindowInteractor = NULL; if ( event != NULL ) { renderer = event->GetSender(); if ( renderer != NULL ) { renderWindow = renderer->GetRenderWindow(); if ( renderWindow != NULL ) { renderWindowInteractor = renderWindow->GetInteractor(); } } } // Check if we have a DisplayPositionEvent const mitk::DisplayPositionEvent *dpe = dynamic_cast< const mitk::DisplayPositionEvent * >( stateEvent->GetEvent() ); if ( dpe != NULL ) { m_PickedSurfaceNode = dpe->GetPickedObjectNode(); m_CurrentPickedPoint = dpe->GetWorldPosition(); m_CurrentPickedDisplayPoint = dpe->GetDisplayPosition(); } // Get the timestep to also support 3D+t int timeStep = 0; mitk::ScalarType timeInMS = 0.0; if ( renderer != NULL ) { timeStep = renderer->GetTimeStep( data ); timeInMS = renderer->GetTime(); } // Extract surface m_Surface = dynamic_cast< Surface * >( data ); if ( m_Surface != NULL ) { m_PolyData = m_Surface->GetVtkPolyData( timeStep ); } else { m_PolyData = NULL; } // Extract surface normal from surface (if existent, otherwise use default) vtkPointData *pointData = m_PolyData->GetPointData(); if ( pointData != NULL ) { vtkDataArray *normal = m_PolyData->GetPointData()->GetVectors( "planeNormal" ); if ( normal != NULL ) { m_ObjectNormal[0] = normal->GetComponent( 0, 0 ); m_ObjectNormal[1] = normal->GetComponent( 0, 1 ); m_ObjectNormal[2] = normal->GetComponent( 0, 2 ); } } // Get geometry object m_Geometry = data->GetGeometry( timeStep ); // Make sure that the data (if time-resolved) has enough entries; // if not, create the required extra ones (empty) data->Expand( timeStep+1 ); switch (action->GetActionId()) { case AcDONOTHING: ok = true; break; case AcCHECKOBJECT: { // Check if an object is present at the current mouse position m_PickedSurface = NULL; m_PickedPolyData = NULL; if ( m_PickedSurfaceNode != NULL ) { m_PickedSurface = dynamic_cast< mitk::Surface * >( m_PickedSurfaceNode->GetData() ); if ( m_PickedSurface != NULL ) { m_PickedPolyData = m_PickedSurface->GetVtkPolyData( timeStep ); } } mitk::StateEvent *newStateEvent; if ( (m_PickedSurfaceNode == m_DataNode) && (m_PickedSurface != NULL) ) { // Yes: object will be selected newStateEvent = new mitk::StateEvent( EIDYES ); // Disable VTK interactor until MITK interaction has been completed if ( renderWindowInteractor != NULL ) { renderWindowInteractor->Disable(); } } else { // No: back to start state newStateEvent = new mitk::StateEvent( EIDNO ); // Re-enable VTK interactor (may have been disabled previously) if ( renderWindowInteractor != NULL ) { renderWindowInteractor->Enable(); } } this->HandleEvent( newStateEvent ); // Colorized surface at current picked position m_SurfaceColorizationCenter = m_CurrentPickedPoint; ok = true; break; } case AcDESELECTOBJECT: { // Color object white m_DataNode->SetColor( 1.0, 1.0, 1.0 ); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); // Colorize surface / wireframe as inactive this->ColorizeSurface( m_PolyData, m_SurfaceColorizationCenter, COLORIZATION_CONSTANT, -1.0 ); ok = true; break; } case AcSELECTPICKEDOBJECT: { // Color object red m_DataNode->SetColor( 1.0, 0.0, 0.0 ); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); // Colorize surface / wireframe dependend on distance from picked point this->ColorizeSurface( m_PolyData, m_SurfaceColorizationCenter, COLORIZATION_GAUSS ); ok = true; break; } case AcINITMOVE: { // Store current picked point m_InitialPickedPoint = m_CurrentPickedPoint; m_InitialPickedDisplayPoint = m_CurrentPickedDisplayPoint; if ( renderWindowInteractor != NULL ) { vtkInteractorObserver::ComputeDisplayToWorld( renderWindowInteractor->GetInteractorStyle()->GetCurrentRenderer(), m_InitialPickedDisplayPoint[0], m_InitialPickedDisplayPoint[1], 0.0, //m_InitialInteractionPickedPoint[2], m_InitialPickedPointWorld ); } // Make deep copy of vtkPolyData interacted on m_OriginalPolyData->DeepCopy( m_PolyData ); ok = true; break; } case AcMOVE: { if ( renderWindowInteractor != NULL ) { vtkInteractorObserver::ComputeDisplayToWorld( renderWindowInteractor->GetInteractorStyle()->GetCurrentRenderer(), m_CurrentPickedDisplayPoint[0], m_CurrentPickedDisplayPoint[1], 0.0, //m_InitialInteractionPickedPoint[2], m_CurrentPickedPointWorld ); } // Calculate mouse move in 3D space mitk::Vector3D interactionMove; interactionMove[0] = m_CurrentPickedPointWorld[0] - m_InitialPickedPointWorld[0]; interactionMove[1] = m_CurrentPickedPointWorld[1] - m_InitialPickedPointWorld[1]; interactionMove[2] = m_CurrentPickedPointWorld[2] - m_InitialPickedPointWorld[2]; // Transform mouse move into geometry space data->UpdateOutputInformation(); // make sure that the Geometry is up-to-date mitk::Point3D origin; origin.Fill( 0.0 ); mitk::Vector3D interactionMoveIndex; m_Geometry->WorldToIndex( interactionMove, interactionMoveIndex ); // Get picked point and transform into local coordinates mitk::Point3D pickedPoint; m_Geometry->WorldToIndex( m_InitialPickedPoint, pickedPoint ); mitk::Vector3D v1 = pickedPoint.GetVectorFromOrigin(); mitk::Vector3D v2 = m_ObjectNormal * (interactionMoveIndex * m_ObjectNormal); vtkPoints *originalPoints = m_OriginalPolyData->GetPoints(); vtkPoints *deformedPoints = m_PolyData->GetPoints(); double denom = m_GaussSigma * m_GaussSigma * 2; double point[3]; for ( unsigned int i = 0; i < deformedPoints->GetNumberOfPoints(); ++i ) { // Get original point double *originalPoint = originalPoints->GetPoint( i ); mitk::Vector3D v0; v0[0] = originalPoint[0]; v0[1] = originalPoint[1]; v0[2] = originalPoint[2]; // Calculate distance of this point from line through picked point double d = itk::CrossProduct( m_ObjectNormal, (v1 - v0) ).GetNorm(); mitk::Vector3D t = v2 * exp( - d * d / denom ); point[0] = originalPoint[0] + t[0]; point[1] = originalPoint[1] + t[1]; point[2] = originalPoint[2] + t[2]; deformedPoints->SetPoint( i, point ); } // Make sure that surface is colorized at initial picked position // as long as we are in deformation state m_SurfaceColorizationCenter = m_InitialPickedPoint; m_PolyData->Modified(); m_Surface->Modified(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); ok = false; break; } case AcMODIFY: { // Check if we have an mitk::WheelEvent const mitk::WheelEvent *we = dynamic_cast< const mitk::WheelEvent * >( stateEvent->GetEvent() ); if ( we == NULL ) { ok = true; break; } m_GaussSigma += (double) (we->GetDelta()) / 20;; if ( m_GaussSigma < 10.0 ) { m_GaussSigma = 10.0; } else if ( m_GaussSigma > 128.0 ) { m_GaussSigma = 128.0; } // Colorize surface / wireframe dependend on sigma and distance from picked point this->ColorizeSurface( m_PolyData, m_SurfaceColorizationCenter, COLORIZATION_GAUSS ); mitk::RenderingManager::GetInstance()->RequestUpdateAll( mitk::RenderingManager::REQUEST_UPDATE_3DWINDOWS ); ok = true; break; } default: return Superclass::ExecuteAction( action, stateEvent ); } return ok; } bool mitk::SurfaceDeformationInteractor3D::ColorizeSurface( vtkPolyData *polyData, const Point3D &pickedPoint, int mode, double scalar ) { if ( polyData == NULL ) { return false; } vtkPoints *points = polyData->GetPoints(); vtkPointData *pointData = polyData->GetPointData(); if ( pointData == NULL ) { return false; } vtkDataArray *scalars = pointData->GetScalars(); if ( scalars == NULL ) { return false; } if ( mode == COLORIZATION_GAUSS ) { // Get picked point and transform into local coordinates mitk::Point3D localPickedPoint; m_Geometry->WorldToIndex( pickedPoint, localPickedPoint ); mitk::Vector3D v1 = localPickedPoint.GetVectorFromOrigin(); double denom = m_GaussSigma * m_GaussSigma * 2; for ( unsigned int i = 0; i < points->GetNumberOfPoints(); ++i ) { // Get original point double *point = points->GetPoint( i ); mitk::Vector3D v0; v0[0] = point[0]; v0[1] = point[1]; v0[2] = point[2]; // Calculate distance of this point from line through picked point double d = itk::CrossProduct( m_ObjectNormal, (v1 - v0) ).GetNorm(); double t = exp( - d * d / denom ); scalars->SetComponent( i, 0, t ); } } else if ( mode == COLORIZATION_CONSTANT ) { for ( unsigned int i = 0; i < pointData->GetNumberOfTuples(); ++i ) { scalars->SetComponent( i, 0, scalar ); } } polyData->Modified(); pointData->Update(); return true; } diff --git a/Modules/MitkExt/Testing/mitkPlaneFitTest.cpp b/Modules/MitkExt/Testing/mitkPlaneFitTest.cpp index 2a6ee7501f..be5dc4a2a6 100644 --- a/Modules/MitkExt/Testing/mitkPlaneFitTest.cpp +++ b/Modules/MitkExt/Testing/mitkPlaneFitTest.cpp @@ -1,97 +1,97 @@ /*=================================================================== 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 #include -#include +#include #include #include #include int mitkPlaneFitTest(int, char*[] ) { //float bounds[]={0.0f,10.0f,0.0f,10.0f,0.0f,5.0f}; mitk::PlaneFit::Pointer PlaneFit = mitk::PlaneFit::New(); mitk::PointSet::Pointer PointSet = mitk::PointSet::New(); mitk::BaseGeometry::Pointer BaseGeometry = mitk::BaseGeometry::New(); mitk::Point3D Point; //first without any point, then incrementally add points within thre points there will be a plane geometry std::cout <<"Start PlaneFitTest "<GetPointSet()->GetPoints()->InsertElement(position, Point); } //Set Input PlaneFit->SetInput(PointSet); const mitk::PointSet* testPointSet = PlaneFit->GetInput(); std::cout<<" Size test of Input Method: "; if( testPointSet->GetSize() == PointSet->GetSize() ) { std::cout<<"[PASSED]"<Update(); const mitk::Point3D ¢roid = PlaneFit->GetCentroid(); mitk::Point3D expectedCentroid; expectedCentroid[0]=2.5; expectedCentroid[1]=3.75; expectedCentroid[2]=2.5; if ( centroid == expectedCentroid ) { std::cout<<"[PASSED]"<( PlaneFit->GetOutput()->GetGeometry()); - if( Geometry2D ) + mitk::PlaneGeometry* PlaneGeometry = dynamic_cast( PlaneFit->GetOutput()->GetGeometry()); + if( PlaneGeometry ) { std::cout<<"[PASSED]"< #include const float selectedColor[]={1.0,0.0,0.6}; //for selected! mitk::MeshMapper2D::MeshMapper2D() { } mitk::MeshMapper2D::~MeshMapper2D() { } const mitk::Mesh *mitk::MeshMapper2D::GetInput(void) { return static_cast ( GetDataNode()->GetData() ); } // Return whether a point is "smaller" than the second static bool point3DSmaller( const mitk::Point3D& elem1, const mitk::Point3D& elem2 ) { if(elem1[0]!=elem2[0]) return elem1[0] < elem2[0]; if(elem1[1]!=elem2[1]) return elem1[1] < elem2[1]; return elem1[2] < elem2[2]; } void mitk::MeshMapper2D::Paint( mitk::BaseRenderer *renderer ) { bool visible = true; GetDataNode()->GetVisibility(visible, renderer, "visible"); if(!visible) return; // @FIXME: Logik fuer update bool updateNeccesary = true; if (updateNeccesary) { //aus GenerateData mitk::Mesh::Pointer input = const_cast(this->GetInput()); // Get the TimeGeometry of the input object const TimeGeometry* inputTimeGeometry = input->GetTimeGeometry(); if (( inputTimeGeometry == NULL ) || ( inputTimeGeometry->CountTimeSteps() == 0 ) ) { return; } // // get the world time // - const Geometry2D* worldGeometry = renderer->GetCurrentWorldGeometry2D(); + const PlaneGeometry* worldGeometry = renderer->GetCurrentWorldGeometry2D(); assert( worldGeometry != NULL ); ScalarType time = worldGeometry->GetTimeBounds()[ 0 ]; // // convert the world time in time steps of the input object // int timeStep=0; if ( time > ScalarTypeNumericTraits::NonpositiveMin() ) timeStep = inputTimeGeometry->TimePointToTimeStep( time ); if ( inputTimeGeometry->IsValidTimeStep( timeStep ) == false ) { return; } mitk::Mesh::MeshType::Pointer itkMesh = input->GetMesh( timeStep ); if ( itkMesh.GetPointer() == NULL) { return; } mitk::DisplayGeometry::Pointer displayGeometry = renderer->GetDisplayGeometry(); assert(displayGeometry.IsNotNull()); const PlaneGeometry* worldplanegeometry = dynamic_cast(renderer->GetCurrentWorldGeometry2D()); //apply color and opacity read from the PropertyList ApplyColorAndOpacityProperties(renderer); vtkLinearTransform* transform = GetDataNode()->GetVtkTransform(); //List of the Points Mesh::DataType::PointsContainerConstIterator it, end; it=itkMesh->GetPoints()->Begin(); end=itkMesh ->GetPoints()->End(); //iterator on the additional data of each point Mesh::PointDataIterator dataIt;//, dataEnd; dataIt=itkMesh->GetPointData()->Begin(); //for switching back to old color after using selected color float unselectedColor[4]; glGetFloatv(GL_CURRENT_COLOR,unselectedColor); while(it!=end) { mitk::Point3D p, projected_p; float vtkp[3]; itk2vtk(it->Value(), vtkp); transform->TransformPoint(vtkp, vtkp); vtk2itk(vtkp,p); displayGeometry->Project(p, projected_p); Vector3D diff=p-projected_p; if(diff.GetSquaredNorm()<4.0) { Point2D pt2d, tmp; displayGeometry->Map(projected_p, pt2d); displayGeometry->WorldToDisplay(pt2d, pt2d); Vector2D horz,vert; horz[0]=5; horz[1]=0; vert[0]=0; vert[1]=5; //check if the point is to be marked as selected if (dataIt->Value().selected) { horz[0]=8; vert[1]=8; glColor3f(selectedColor[0],selectedColor[1],selectedColor[2]);//red switch (dataIt->Value().pointSpec) { case PTSTART: { //a quad glBegin (GL_LINE_LOOP); tmp=pt2d-horz+vert; glVertex2dv(&tmp[0]); tmp=pt2d+horz+vert; glVertex2dv(&tmp[0]); tmp=pt2d+horz-vert; glVertex2dv(&tmp[0]); tmp=pt2d-horz-vert; glVertex2dv(&tmp[0]); glEnd (); } break; case PTUNDEFINED: { //a diamond around the point glBegin (GL_LINE_LOOP); tmp=pt2d-horz; glVertex2dv(&tmp[0]); tmp=pt2d+vert; glVertex2dv(&tmp[0]); tmp=pt2d+horz; glVertex2dv(&tmp[0]); tmp=pt2d-vert; glVertex2dv(&tmp[0]); glEnd (); } break; default: break; }//switch //the actual point glBegin (GL_POINTS); tmp=pt2d; glVertex2dv(&tmp[0]); glEnd (); } else //if not selected { glColor3f(unselectedColor[0],unselectedColor[1],unselectedColor[2]); switch (dataIt->Value().pointSpec) { case PTSTART: { //a quad glBegin (GL_LINE_LOOP); tmp=pt2d-horz+vert; glVertex2dv(&tmp[0]); tmp=pt2d+horz+vert; glVertex2dv(&tmp[0]); tmp=pt2d+horz-vert; glVertex2dv(&tmp[0]); tmp=pt2d-horz-vert; glVertex2dv(&tmp[0]); glEnd (); } case PTUNDEFINED: { //drawing crosses glBegin (GL_LINES); tmp=pt2d-horz; glVertex2dv(&tmp[0]); tmp=pt2d+horz; glVertex2dv(&tmp[0]); tmp=pt2d-vert; glVertex2dv(&tmp[0]); tmp=pt2d+vert; glVertex2dv(&tmp[0]); glEnd (); } default: { break; } }//switch }//else } ++it; ++dataIt; } //now connect the lines inbetween mitk::Mesh::PointType thisPoint; thisPoint.Fill(0); Point2D *firstOfCell = NULL; Point2D *lastPoint = NULL; unsigned int lastPointId = 0; bool lineSelected = false; Point3D firstOfCell3D; Point3D lastPoint3D; bool first; mitk::Line line; std::vector intersectionPoints; double t; //iterate through all cells and then iterate through all indexes of points in that cell Mesh::CellIterator cellIt, cellEnd; Mesh::CellDataIterator cellDataIt;//, cellDataEnd; Mesh::PointIdIterator cellIdIt, cellIdEnd; cellIt = itkMesh->GetCells()->Begin(); cellEnd = itkMesh->GetCells()->End(); cellDataIt = itkMesh->GetCellData()->Begin(); while (cellIt != cellEnd) { unsigned int numOfPointsInCell = cellIt->Value()->GetNumberOfPoints(); if (numOfPointsInCell>1) { //iterate through all id's in the cell cellIdIt = cellIt->Value()->PointIdsBegin(); cellIdEnd = cellIt->Value()->PointIdsEnd(); firstOfCell3D = input->GetPoint(*cellIdIt,timeStep); intersectionPoints.clear(); intersectionPoints.reserve(numOfPointsInCell); first = true; while(cellIdIt != cellIdEnd) { lastPoint3D = thisPoint; thisPoint = input->GetPoint(*cellIdIt,timeStep); //search in data (vector<> selectedLines) if the index of the point is set. if so, then the line is selected. lineSelected = false; Mesh::SelectedLinesType selectedLines = cellDataIt->Value().selectedLines; //a line between 1(lastPoint) and 2(pt2d) has the Id 1, so look for the Id of lastPoint //since we only start, if we have more than one point in the cell, lastPointId is initiated with 0 Mesh::SelectedLinesIter position = std::find(selectedLines.begin(), selectedLines.end(), lastPointId); if (position != selectedLines.end()) { lineSelected = true; } mitk::Point3D p, projected_p; float vtkp[3]; itk2vtk(thisPoint, vtkp); transform->TransformPoint(vtkp, vtkp); vtk2itk(vtkp,p); displayGeometry->Project(p, projected_p); Vector3D diff=p-projected_p; if(diff.GetSquaredNorm()<4.0) { Point2D pt2d, tmp; displayGeometry->Map(projected_p, pt2d); displayGeometry->WorldToDisplay(pt2d, pt2d); if (lastPoint == NULL) { //set the first point in the cell. This point in needed to close the polygon firstOfCell = new Point2D; *firstOfCell = pt2d; lastPoint = new Point2D; *lastPoint = pt2d; lastPointId = *cellIdIt; } else { if (lineSelected) { glColor3f(selectedColor[0],selectedColor[1],selectedColor[2]);//red //a line from lastPoint to thisPoint glBegin (GL_LINES); glVertex2dv(&(*lastPoint)[0]); glVertex2dv(&pt2d[0]); glEnd (); } else //if not selected { glColor3f(unselectedColor[0],unselectedColor[1],unselectedColor[2]); //drawing crosses glBegin (GL_LINES); glVertex2dv(&(*lastPoint)[0]); glVertex2dv(&pt2d[0]); glEnd (); } //to draw the line to the next in iteration step *lastPoint = pt2d; //and to search for the selection state of the line lastPointId = *cellIdIt; }//if..else }//if <4.0 //fill off-plane polygon part 1 if((!first) && (worldplanegeometry!=NULL)) { line.SetPoints(lastPoint3D, thisPoint); if(worldplanegeometry->IntersectionPointParam(line, t) && ((t>=0) && (t<=1)) ) { intersectionPoints.push_back(line.GetPoint(t)); } } ++cellIdIt; first=false; }//while cellIdIter //closed polygon? if ( cellDataIt->Value().closed ) { //close the polygon if needed if( firstOfCell != NULL ) { lineSelected = false; Mesh::SelectedLinesType selectedLines = cellDataIt->Value().selectedLines; Mesh::SelectedLinesIter position = std::find(selectedLines.begin(), selectedLines.end(), lastPointId); if (position != selectedLines.end())//found the index { glColor3f(selectedColor[0],selectedColor[1],selectedColor[2]);//red //a line from lastPoint to firstPoint glBegin (GL_LINES); glVertex2dv(&(*lastPoint)[0]); glVertex2dv(&(*firstOfCell)[0]); glEnd (); } else { glColor3f(unselectedColor[0],unselectedColor[1],unselectedColor[2]); glBegin (GL_LINES); glVertex2dv(&(*lastPoint)[0]); glVertex2dv(&(*firstOfCell)[0]); glEnd (); } } }//if closed //Axis-aligned bounding box(AABB) around the cell if selected and set in Property bool showBoundingBox; if (dynamic_cast(this->GetDataNode()->GetProperty("showBoundingBox")) == NULL) showBoundingBox = false; else showBoundingBox = dynamic_cast(this->GetDataNode()->GetProperty("showBoundingBox"))->GetValue(); if(showBoundingBox) { if (cellDataIt->Value().selected) { mitk::Mesh::DataType::BoundingBoxPointer aABB = input->GetBoundingBoxFromCell(cellIt->Index()); if (aABB.IsNotNull()) { mitk::Mesh::PointType min, max; min = aABB->GetMinimum(); max = aABB->GetMaximum(); //project to the displayed geometry Point2D min2D, max2D; Point3D p, projected_p; float vtkp[3]; itk2vtk(min, vtkp); transform->TransformPoint(vtkp, vtkp); vtk2itk(vtkp,p); displayGeometry->Project(p, projected_p); displayGeometry->Map(projected_p, min2D); displayGeometry->WorldToDisplay(min2D, min2D); itk2vtk(max, vtkp); transform->TransformPoint(vtkp, vtkp); vtk2itk(vtkp,p); displayGeometry->Project(p, projected_p); Vector3D diff=p-projected_p; if(diff.GetSquaredNorm()<4.0) { displayGeometry->Map(projected_p, max2D); displayGeometry->WorldToDisplay(max2D, max2D); //draw the BoundingBox glColor3f(selectedColor[0],selectedColor[1],selectedColor[2]);//red //a line from lastPoint to firstPoint glBegin(GL_LINE_LOOP); glVertex2f(min2D[0], min2D[1]); glVertex2f(min2D[0], max2D[1]); glVertex2f(max2D[0], max2D[1]); glVertex2f(max2D[0], min2D[1]); glEnd(); }//draw bounding-box }//bounding-box exists }//cell selected }//show bounding-box //fill off-plane polygon part 2 if(worldplanegeometry!=NULL) { //consider line from last to first line.SetPoints(thisPoint, firstOfCell3D); if(worldplanegeometry->IntersectionPointParam(line, t) && ((t>=0) && (t<=1)) ) { intersectionPoints.push_back(line.GetPoint(t)); } std::sort(intersectionPoints.begin(), intersectionPoints.end(), point3DSmaller); std::vector::iterator it, end; end=intersectionPoints.end(); if((intersectionPoints.size()%2)!=0) { --end; //ensure even number of intersection-points } double p[2]; Point3D pt3d; Point2D pt2d; for ( it = intersectionPoints.begin( ); it != end; ++it ) { glBegin (GL_LINES); displayGeometry->Map(*it, pt2d); displayGeometry->WorldToDisplay(pt2d, pt2d); p[0] = pt2d[0]; p[1] = pt2d[1]; glVertex2dv(p); ++it; displayGeometry->Map(*it, pt2d); displayGeometry->WorldToDisplay(pt2d, pt2d); p[0] = pt2d[0]; p[1] = pt2d[1]; glVertex2dv(p); glEnd (); } if(it!=intersectionPoints.end()) { glBegin (GL_LINES); displayGeometry->Map(*it, pt2d); displayGeometry->WorldToDisplay(pt2d, pt2d); p[0] = pt2d[0]; p[1] = pt2d[1]; glVertex2dv(p); p[0] = pt2d[0]; p[1] = pt2d[1]; glVertex2dv(p); glEnd (); } }//fill off-plane polygon part 2 }//if numOfPointsInCell>1 delete firstOfCell; delete lastPoint; lastPoint = NULL; firstOfCell = NULL; lastPointId = 0; ++cellIt; ++cellDataIt; } } } diff --git a/Modules/MitkMapperExt/mitkUnstructuredGridMapper2D.cpp b/Modules/MitkMapperExt/mitkUnstructuredGridMapper2D.cpp index 1cd6675f7c..85b6fcfd85 100644 --- a/Modules/MitkMapperExt/mitkUnstructuredGridMapper2D.cpp +++ b/Modules/MitkMapperExt/mitkUnstructuredGridMapper2D.cpp @@ -1,574 +1,574 @@ /*=================================================================== 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 #include "mitkUnstructuredGridMapper2D.h" #include "mitkBaseRenderer.h" #include "mitkPlaneGeometry.h" #include "mitkUnstructuredGrid.h" #include "mitkTransferFunction.h" #include "mitkTransferFunctionProperty.h" #include "mitkColorProperty.h" #include "mitkVtkScalarModeProperty.h" #include "mitkProperties.h" #include "mitkAbstractTransformGeometry.h" #include "mitkVtkMapper3D.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "Internal/vtkPointSetSlicer.h" void mitk::UnstructuredGridMapper2D::GenerateDataForRenderer( mitk::BaseRenderer* renderer ) { BaseLocalStorage *ls = m_LSH.GetLocalStorage(renderer); bool needGenerateData = ls->IsGenerateDataRequired( renderer, this, GetDataNode() ); if(needGenerateData) { ls->UpdateGenerateDataTime(); mitk::DataNode::ConstPointer node = this->GetDataNode(); if ( node.IsNull() ) return; if (!node->GetProperty(m_ScalarMode, "scalar mode")) { m_ScalarMode = mitk::VtkScalarModeProperty::New(0); } if (!node->GetProperty(m_ScalarVisibility, "scalar visibility")) { m_ScalarVisibility = mitk::BoolProperty::New(true); } if (!node->GetProperty(m_Outline, "outline polygons")) { m_Outline = mitk::BoolProperty::New(false); } if (!node->GetProperty(m_Color, "color")) { m_Color = mitk::ColorProperty::New(1.0f, 1.0f, 1.0f); } if (!node->GetProperty(m_LineWidth, "line width")) { m_LineWidth = mitk::IntProperty::New(1); } } mitk::BaseData::Pointer input = const_cast( GetDataNode()->GetData() ); assert( input ); input->Update(); if (m_VtkPointSet) m_VtkPointSet->UnRegister(0); m_VtkPointSet = this->GetVtkPointSet(renderer); assert(m_VtkPointSet); m_VtkPointSet->Register(0); if (m_ScalarVisibility->GetValue()) { mitk::DataNode::ConstPointer node = this->GetDataNode(); mitk::TransferFunctionProperty::Pointer transferFuncProp; node->GetProperty(transferFuncProp, "TransferFunction", renderer); if (transferFuncProp.IsNotNull()) { mitk::TransferFunction::Pointer tf = transferFuncProp->GetValue(); if (m_ScalarsToColors) m_ScalarsToColors->UnRegister(0); m_ScalarsToColors = static_cast(tf->GetColorTransferFunction()); m_ScalarsToColors->Register(0); if (m_ScalarsToOpacity) m_ScalarsToOpacity->UnRegister(0); m_ScalarsToOpacity = tf->GetScalarOpacityFunction(); m_ScalarsToOpacity->Register(0); } else { if (m_ScalarsToColors) m_ScalarsToColors->UnRegister(0); m_ScalarsToColors = this->GetVtkLUT(renderer); assert(m_ScalarsToColors); m_ScalarsToColors->Register(0); float opacity; node->GetOpacity(opacity, renderer); if (m_ScalarsToOpacity) m_ScalarsToOpacity->UnRegister(0); m_ScalarsToOpacity = vtkPiecewiseFunction::New(); double range[2]; m_VtkPointSet->GetScalarRange(range); m_ScalarsToOpacity->AddSegment(range[0], opacity, range[1], opacity); } } } void mitk::UnstructuredGridMapper2D::Paint( mitk::BaseRenderer* renderer ) { bool visible = true; GetDataNode()->GetVisibility(visible, renderer, "visible"); if(!visible) return; vtkLinearTransform * vtktransform = GetDataNode()->GetVtkTransform(); vtkLinearTransform * inversetransform = vtktransform->GetLinearInverse(); - Geometry2D::ConstPointer worldGeometry = renderer->GetCurrentWorldGeometry2D(); + PlaneGeometry::ConstPointer worldGeometry = renderer->GetCurrentWorldGeometry2D(); PlaneGeometry::ConstPointer worldPlaneGeometry = dynamic_cast( worldGeometry.GetPointer() ); Point3D point; Vector3D normal; if(worldPlaneGeometry.IsNotNull()) { // set up vtkPlane according to worldGeometry point=worldPlaneGeometry->GetOrigin(); normal=worldPlaneGeometry->GetNormal(); normal.Normalize(); m_Plane->SetTransform((vtkAbstractTransform*)NULL); } else { //@FIXME: does not work correctly. Does m_Plane->SetTransform really transforms a "plane plane" into a "curved plane"? return; AbstractTransformGeometry::ConstPointer worldAbstractGeometry = dynamic_cast(renderer->GetCurrentWorldGeometry2D()); if(worldAbstractGeometry.IsNotNull()) { // set up vtkPlane according to worldGeometry point=const_cast(worldAbstractGeometry->GetParametricBoundingBox())->GetMinimum(); FillVector3D(normal, 0, 0, 1); m_Plane->SetTransform(worldAbstractGeometry->GetVtkAbstractTransform()->GetInverse()); } else return; } double vp[ 3 ], vnormal[ 3 ]; vnl2vtk(point.GetVnlVector(), vp); vnl2vtk(normal.GetVnlVector(), vnormal); //normally, we would need to transform the surface and cut the transformed surface with the cutter. //This might be quite slow. Thus, the idea is, to perform an inverse transform of the plane instead. //@todo It probably does not work for scaling operations yet:scaling operations have to be //dealed with after the cut is performed by scaling the contour. inversetransform->TransformPoint( vp, vp ); inversetransform->TransformNormalAtPoint( vp, vnormal, vnormal ); m_Plane->SetOrigin( vp ); m_Plane->SetNormal( vnormal ); // set data into cutter m_Slicer->SetInputData( m_VtkPointSet ); // m_Cutter->GenerateCutScalarsOff(); // m_Cutter->SetSortByToSortByCell(); // calculate the cut m_Slicer->Update(); // fetch geometry mitk::DisplayGeometry::Pointer displayGeometry = renderer->GetDisplayGeometry(); assert( displayGeometry ); // float toGL=displayGeometry->GetSizeInDisplayUnits()[1]; //apply color and opacity read from the PropertyList ApplyColorAndOpacityProperties( renderer ); // traverse the cut contour vtkPolyData * contour = m_Slicer->GetOutput(); vtkPoints *vpoints = contour->GetPoints(); vtkCellArray *vlines = contour->GetLines(); vtkCellArray *vpolys = contour->GetPolys(); vtkPointData *vpointdata = contour->GetPointData(); vtkDataArray* vscalars = vpointdata->GetScalars(); vtkCellData *vcelldata = contour->GetCellData(); vtkDataArray* vcellscalars = vcelldata->GetScalars(); const int numberOfLines = contour->GetNumberOfLines(); const int numberOfPolys = contour->GetNumberOfPolys(); const bool useCellData = m_ScalarMode->GetVtkScalarMode() == VTK_SCALAR_MODE_DEFAULT || m_ScalarMode->GetVtkScalarMode() == VTK_SCALAR_MODE_USE_CELL_DATA; const bool usePointData = m_ScalarMode->GetVtkScalarMode() == VTK_SCALAR_MODE_USE_POINT_DATA; Point3D p; Point2D p2d; vlines->InitTraversal(); vpolys->InitTraversal(); mitk::Color outlineColor = m_Color->GetColor(); glLineWidth((float)m_LineWidth->GetValue()); for (int i = 0;i < numberOfLines;++i ) { vtkIdType *cell(0); vtkIdType cellSize(0); vlines->GetNextCell( cellSize, cell ); float rgba[4] = {outlineColor[0], outlineColor[1], outlineColor[2], 1.0f}; if (m_ScalarVisibility->GetValue() && vcellscalars) { if ( useCellData ) { // color each cell according to cell data double scalar = vcellscalars->GetComponent( i, 0 ); double rgb[3] = { 1.0f, 1.0f, 1.0f }; m_ScalarsToColors->GetColor(scalar, rgb); rgba[0] = (float)rgb[0]; rgba[1] = (float)rgb[1]; rgba[2] = (float)rgb[2]; rgba[3] = (float)m_ScalarsToOpacity->GetValue(scalar); } else if ( usePointData ) { double scalar = vscalars->GetComponent( i, 0 ); double rgb[3] = { 1.0f, 1.0f, 1.0f }; m_ScalarsToColors->GetColor(scalar, rgb); rgba[0] = (float)rgb[0]; rgba[1] = (float)rgb[1]; rgba[2] = (float)rgb[2]; rgba[3] = (float)m_ScalarsToOpacity->GetValue(scalar); } } glColor4fv( rgba ); glBegin ( GL_LINE_LOOP ); for ( int j = 0;j < cellSize;++j ) { vpoints->GetPoint( cell[ j ], vp ); //take transformation via vtktransform into account vtktransform->TransformPoint( vp, vp ); vtk2itk( vp, p ); //convert 3D point (in mm) to 2D point on slice (also in mm) worldGeometry->Map( p, p2d ); //convert point (until now mm and in worldcoordinates) to display coordinates (units ) displayGeometry->WorldToDisplay( p2d, p2d ); //convert display coordinates ( (0,0) is top-left ) in GL coordinates ( (0,0) is bottom-left ) //p2d[1]=toGL-p2d[1]; //add the current vertex to the line glVertex2f( p2d[0], p2d[1] ); } glEnd (); } bool polyOutline = m_Outline->GetValue(); bool scalarVisibility = m_ScalarVisibility->GetValue(); // cache the transformed points // a fixed size array is way faster than 'new' // slices through 3d cells usually do not generated // polygons with more than 6 vertices const int maxPolySize = 10; Point2D* cachedPoints = new Point2D[maxPolySize*numberOfPolys]; glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // only draw polygons if there are cell scalars // or the outline property is set to true if (scalarVisibility && vcellscalars) { glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); for (int i = 0;i < numberOfPolys;++i ) { vtkIdType *cell(0); vtkIdType cellSize(0); vpolys->GetNextCell( cellSize, cell ); float rgba[4] = {1.0f, 1.0f, 1.0f, 0}; if (scalarVisibility && vcellscalars) { if ( useCellData ) { // color each cell according to cell data double scalar = vcellscalars->GetComponent( i+numberOfLines, 0 ); double rgb[3] = { 1.0f, 1.0f, 1.0f }; m_ScalarsToColors->GetColor(scalar, rgb); rgba[0] = (float)rgb[0]; rgba[1] = (float)rgb[1]; rgba[2] = (float)rgb[2]; rgba[3] = (float)m_ScalarsToOpacity->GetValue(scalar); } else if ( usePointData ) { double scalar = vscalars->GetComponent( i, 0 ); double rgb[3] = { 1.0f, 1.0f, 1.0f }; m_ScalarsToColors->GetColor(scalar, rgb); rgba[0] = (float)rgb[0]; rgba[1] = (float)rgb[1]; rgba[2] = (float)rgb[2]; rgba[3] = (float)m_ScalarsToOpacity->GetValue(scalar); } } glColor4fv( rgba ); glBegin( GL_POLYGON ); for (int j = 0; j < cellSize; ++j) { vpoints->GetPoint( cell[ j ], vp ); //take transformation via vtktransform into account vtktransform->TransformPoint( vp, vp ); vtk2itk( vp, p ); //convert 3D point (in mm) to 2D point on slice (also in mm) worldGeometry->Map( p, p2d ); //convert point (until now mm and in worldcoordinates) to display coordinates (units ) displayGeometry->WorldToDisplay( p2d, p2d ); //convert display coordinates ( (0,0) is top-left ) in GL coordinates ( (0,0) is bottom-left ) //p2d[1]=toGL-p2d[1]; cachedPoints[i*10+j][0] = p2d[0]; cachedPoints[i*10+j][1] = p2d[1]; //add the current vertex to the line glVertex2f( p2d[0], p2d[1] ); } glEnd(); } if (polyOutline) { vpolys->InitTraversal(); glColor4f(outlineColor[0], outlineColor[1], outlineColor[2], 1.0f); glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); for (int i = 0;i < numberOfPolys;++i) { vtkIdType *cell(0); vtkIdType cellSize(0); vpolys->GetNextCell( cellSize, cell ); glBegin( GL_POLYGON ); //glPolygonOffset(1.0, 1.0); for (int j = 0; j < cellSize; ++j) { //add the current vertex to the line glVertex2f( cachedPoints[i*10+j][0], cachedPoints[i*10+j][1] ); } glEnd(); } } } glDisable(GL_BLEND); delete cachedPoints; } vtkAbstractMapper3D* mitk::UnstructuredGridMapper2D ::GetVtkAbstractMapper3D(mitk::BaseRenderer * renderer) { //MITK_INFO << "GETVTKABSTRACTMAPPER3D\n"; mitk::DataNode::ConstPointer node = this->GetDataNode(); if ( node.IsNull() ) return 0; mitk::VtkMapper::Pointer mitkMapper = dynamic_cast< mitk::VtkMapper* > ( node->GetMapper( 2 ) ); if ( mitkMapper.IsNull() ) { return 0; } mitkMapper->Update(renderer); vtkAssembly* assembly = dynamic_cast(mitkMapper->GetVtkProp(renderer)); if (assembly) { vtkProp3DCollection* collection = assembly->GetParts(); collection->InitTraversal(); vtkProp3D* prop3d = 0; do { prop3d = collection->GetNextProp3D(); vtkActor* actor = dynamic_cast( prop3d ); if (actor) { return dynamic_cast( actor->GetMapper() ); } vtkVolume* volume = dynamic_cast( prop3d ); if (volume) { return dynamic_cast( volume->GetMapper() ); } } while (prop3d != collection->GetLastProp3D()); } else { vtkActor* actor = dynamic_cast( mitkMapper->GetVtkProp(renderer) ); if (actor) { return dynamic_cast( actor->GetMapper() ); } vtkVolume* volume = dynamic_cast( mitkMapper->GetVtkProp(renderer) ); if (volume) { return dynamic_cast( volume->GetMapper() ); } } return 0; } vtkPointSet* mitk::UnstructuredGridMapper2D ::GetVtkPointSet(mitk::BaseRenderer* renderer) { //MITK_INFO << "GETVTKPOINTSET\n"; vtkAbstractMapper3D * abstractMapper = GetVtkAbstractMapper3D(renderer); if ( abstractMapper == 0 ) { // try to get data from the node mitk::DataNode::ConstPointer node = this->GetDataNode(); if ( node.IsNull() ) return 0; mitk::BaseData::Pointer data = node->GetData(); mitk::UnstructuredGrid::Pointer grid = dynamic_cast(data.GetPointer()); if (!grid.IsNull()) return static_cast(grid->GetVtkUnstructuredGrid()); return 0; } else { vtkMapper* mapper = dynamic_cast(abstractMapper); if (mapper) { return dynamic_cast(mapper->GetInput()); } vtkAbstractVolumeMapper* volMapper = dynamic_cast(abstractMapper); if (volMapper) { return dynamic_cast(volMapper->GetDataSetInput()); } } return 0; } vtkScalarsToColors* mitk::UnstructuredGridMapper2D::GetVtkLUT(mitk::BaseRenderer* renderer) { //MITK_INFO << "GETVTKLUT\n"; vtkMapper * mapper = dynamic_cast(GetVtkAbstractMapper3D(renderer)); if (mapper) return mapper->GetLookupTable(); else { mitk::DataNode::ConstPointer node = this->GetDataNode(); if ( node.IsNull() ) return 0; mitk::VtkMapper::Pointer mitkMapper = dynamic_cast< mitk::VtkMapper* > ( node->GetMapper( 2 ) ); if ( mitkMapper.IsNull() ) { //MITK_INFO << "mitkMapper is null\n"; return 0; } mitkMapper->Update(renderer); vtkVolume* volume = dynamic_cast( mitkMapper->GetVtkProp(renderer) ); if (volume) { //MITK_INFO << "found volume prop\n"; return static_cast(volume->GetProperty()->GetRGBTransferFunction()); } vtkAssembly* assembly = dynamic_cast(mitkMapper->GetVtkProp(renderer)); if (assembly) { //MITK_INFO << "found assembly prop\n"; mitk::TransferFunctionProperty::Pointer transferFuncProp; node->GetProperty(transferFuncProp, "TransferFunction", 0); if (transferFuncProp.IsNotNull()) { MITK_INFO << "return colortransferfunction\n"; return static_cast(transferFuncProp->GetValue()->GetColorTransferFunction()); } } return 0; } } bool mitk::UnstructuredGridMapper2D::IsConvertibleToVtkPointSet(mitk::BaseRenderer * renderer) { return ( GetVtkPointSet(renderer) != 0 ); } mitk::UnstructuredGridMapper2D::UnstructuredGridMapper2D() { m_Plane = vtkPlane::New(); m_Slicer = vtkPointSetSlicer::New(); m_Slicer->SetSlicePlane( m_Plane ); m_ScalarsToColors = 0; m_ScalarsToOpacity = 0; m_VtkPointSet = 0; //m_LUT = vtkLookupTable::New(); //m_LUT->SetTableRange( 0, 255 ); //m_LUT->SetNumberOfColors( 255 ); //m_LUT->SetRampToLinear (); //m_LUT->Build(); } mitk::UnstructuredGridMapper2D::~UnstructuredGridMapper2D() { m_Slicer->Delete(); m_Plane->Delete(); if (m_ScalarsToOpacity != 0) m_ScalarsToOpacity->UnRegister(0); if (m_ScalarsToColors != 0) m_ScalarsToColors->UnRegister(0); if (m_VtkPointSet != 0) m_VtkPointSet->UnRegister(0); } diff --git a/Modules/MitkMapperExt/mitkVectorImageMapper2D.cpp b/Modules/MitkMapperExt/mitkVectorImageMapper2D.cpp index 52914b27df..272e17d85a 100644 --- a/Modules/MitkMapperExt/mitkVectorImageMapper2D.cpp +++ b/Modules/MitkMapperExt/mitkVectorImageMapper2D.cpp @@ -1,538 +1,538 @@ /*=================================================================== 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 "mitkVectorImageMapper2D.h" //vtk related includes #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include //mitk related includes #include "mitkGL.h" #include "mitkBaseRenderer.h" #include "mitkColorProperty.h" #include "mitkProperties.h" #include "mitkAbstractTransformGeometry.h" #include const mitk::Image * mitk::VectorImageMapper2D::GetInput( void ) { if ( m_Image.IsNotNull() ) return m_Image; else return dynamic_cast( GetDataNode()->GetData() ); } void mitk::VectorImageMapper2D::Paint( mitk::BaseRenderer * renderer ) { //std::cout << "2d vector mapping..." << std::endl; bool visible = true; GetDataNode()->GetVisibility(visible, renderer, "visible"); if ( !visible ) return ; mitk::Image::Pointer input = const_cast( this->GetInput() ); if ( input.IsNull() ) return ; - mitk::PlaneGeometry::Pointer worldPlaneGeometry2D = dynamic_cast< mitk::PlaneGeometry*>( const_cast( renderer->GetCurrentWorldGeometry2D() ) ); + mitk::PlaneGeometry::Pointer worldPlaneGeometry2D = dynamic_cast< mitk::PlaneGeometry*>( const_cast( renderer->GetCurrentWorldGeometry2D() ) ); assert( worldPlaneGeometry2D.IsNotNull() ); vtkImageData* vtkImage = input->GetVtkImageData( this->GetCurrentTimeStep( input, renderer ) ); // // set up the cutter orientation according to the current geometry of // the renderers plane // Point3D point; Vector3D normal; - Geometry2D::ConstPointer worldGeometry = renderer->GetCurrentWorldGeometry2D(); + PlaneGeometry::ConstPointer worldGeometry = renderer->GetCurrentWorldGeometry2D(); PlaneGeometry::ConstPointer worldPlaneGeometry = dynamic_cast( worldGeometry.GetPointer() ); if ( worldPlaneGeometry.IsNotNull() ) { // set up vtkPlane according to worldGeometry point = worldPlaneGeometry->GetOrigin(); normal = worldPlaneGeometry->GetNormal(); normal.Normalize(); m_Plane->SetTransform( (vtkAbstractTransform*)NULL ); } else { itkWarningMacro( << "worldPlaneGeometry is NULL!" ); return ; } double vp[ 3 ], vp_slice[ 3 ], vnormal[ 3 ]; vnl2vtk( point.GetVnlVector(), vp ); vnl2vtk( normal.GetVnlVector(), vnormal ); //std::cout << "Origin: " << vp[0] <<" "<< vp[1] <<" "<< vp[2] << std::endl; //std::cout << "Normal: " << vnormal[0] <<" "<< vnormal[1] <<" "<< vnormal[2] << std::endl; //normally, we would need to transform the surface and cut the transformed surface with the cutter. //This might be quite slow. Thus, the idea is, to perform an inverse transform of the plane instead. //@todo It probably does not work for scaling operations yet:scaling operations have to be //dealed with after the cut is performed by scaling the contour. vtkLinearTransform * vtktransform = GetDataNode() ->GetVtkTransform(); vtkTransform* world2vtk = vtkTransform::New(); world2vtk->Identity(); world2vtk->Concatenate(vtktransform->GetLinearInverse()); double myscale[3]; world2vtk->GetScale(myscale); world2vtk->PostMultiply(); world2vtk->Scale(1/myscale[0],1/myscale[1],1/myscale[2]); world2vtk->TransformPoint( vp, vp ); world2vtk->TransformNormalAtPoint( vp, vnormal, vnormal ); world2vtk->Delete(); // vtk works in axis align coords // thus the normal also must be axis align, since // we do not allow arbitrary cutting through volume // // vnormal should already be axis align, but in order // to get rid of precision effects, we set the two smaller // components to zero here int dims[3]; vtkImage->GetDimensions(dims); double spac[3]; vtkImage->GetSpacing(spac); vp_slice[0] = vp[0]; vp_slice[1] = vp[1]; vp_slice[2] = vp[2]; if(fabs(vnormal[0]) > fabs(vnormal[1]) && fabs(vnormal[0]) > fabs(vnormal[2]) ) { if(fabs(vp_slice[0]/spac[0]) < 0.4) vp_slice[0] = 0.4*spac[0]; if(fabs(vp_slice[0]/spac[0]) > (dims[0]-1)-0.4) vp_slice[0] = ((dims[0]-1)-0.4)*spac[0]; vnormal[1] = 0; vnormal[2] = 0; } if(fabs(vnormal[1]) > fabs(vnormal[0]) && fabs(vnormal[1]) > fabs(vnormal[2]) ) { if(fabs(vp_slice[1]/spac[1]) < 0.4) vp_slice[1] = 0.4*spac[1]; if(fabs(vp_slice[1]/spac[1]) > (dims[1]-1)-0.4) vp_slice[1] = ((dims[1]-1)-0.4)*spac[1]; vnormal[0] = 0; vnormal[2] = 0; } if(fabs(vnormal[2]) > fabs(vnormal[1]) && fabs(vnormal[2]) > fabs(vnormal[0]) ) { if(fabs(vp_slice[2]/spac[2]) < 0.4) vp_slice[2] = 0.4*spac[2]; if(fabs(vp_slice[2]/spac[2]) > (dims[2]-1)-0.4) vp_slice[2] = ((dims[2]-1)-0.4)*spac[2]; vnormal[0] = 0; vnormal[1] = 0; } m_Plane->SetOrigin( vp_slice ); m_Plane->SetNormal( vnormal ); vtkPolyData* cuttedPlane; if(!( (dims[0] == 1 && vnormal[0] != 0) || (dims[1] == 1 && vnormal[1] != 0) || (dims[2] == 1 && vnormal[2] != 0) )) { m_Cutter->SetCutFunction( m_Plane ); m_Cutter->SetInputData( vtkImage ); m_Cutter->GenerateCutScalarsOff();//! m_Cutter->Update(); cuttedPlane = m_Cutter->GetOutput(); } else { // cutting of a 2D-Volume does not work, // so we have to build up our own polydata object cuttedPlane = vtkPolyData::New(); vtkPoints* points = vtkPoints::New(); points->SetNumberOfPoints(vtkImage->GetNumberOfPoints()); for(int i=0; iGetNumberOfPoints(); i++) points->SetPoint(i, vtkImage->GetPoint(i)); cuttedPlane->SetPoints(points); vtkFloatArray* pointdata = vtkFloatArray::New(); int comps = vtkImage->GetPointData()->GetScalars()->GetNumberOfComponents(); pointdata->SetNumberOfComponents(comps); int tuples = vtkImage->GetPointData()->GetScalars()->GetNumberOfTuples(); pointdata->SetNumberOfTuples(tuples); for(int i=0; iSetTuple(i,vtkImage->GetPointData()->GetScalars()->GetTuple(i)); pointdata->SetName( "vector" ); cuttedPlane->GetPointData()->AddArray(pointdata); } if ( cuttedPlane->GetNumberOfPoints() != 0) { // // make sure, that we have point data with more than 1 component (as vectors) // vtkPointData * pointData = cuttedPlane->GetPointData(); if ( pointData == NULL ) { itkWarningMacro( << "no point data associated with cutters result!" ); return ; } if ( pointData->GetNumberOfArrays() == 0 ) { itkWarningMacro( << "point data returned by cutter doesn't have any arrays associated!" ); return ; } else if ( pointData->GetArray(0)->GetNumberOfComponents() <= 1) { itkWarningMacro( << "number of components <= 1!" ); return; } else if ( pointData->GetArrayName( 0 ) == NULL ) { pointData->GetArray( 0 ) ->SetName( "vector" ); //std::cout << "array name = vectors now" << std::endl; } //std::cout << " projecting..."<< std::endl; // // constrain the vectors to lie on the plane, which means to remove the vector component, // which is orthogonal to the plane. // vtkIdType numPoints, pointId; numPoints = cuttedPlane->GetNumberOfPoints(); vtkDataArray* inVectors = cuttedPlane->GetPointData()->GetVectors( "vector" ); assert( inVectors != NULL ); vtkFloatArray* vectorMagnitudes = vtkFloatArray::New(); vectorMagnitudes->SetName("vectorMagnitudes"); vectorMagnitudes->SetNumberOfComponents(1); vectorMagnitudes->SetNumberOfValues(numPoints); vectorMagnitudes->SetNumberOfTuples(numPoints); double inVector[ 3 ], outVector[3], wnormal[3]; //, tmpVector[ 3 ], outVector[ 3 ]; double k = 0.0; vnl2vtk( normal.GetVnlVector(), wnormal ); vtkMath::Normalize( wnormal ); bool normalizeVecs; m_DataNode->GetBoolProperty( "NormalizeVecs", normalizeVecs ); for ( pointId = 0; pointId < numPoints; ++pointId ) { inVectors->GetTuple( pointId, inVector ); if(normalizeVecs) { vnl_vector tmp(3); vtk2vnl(inVector, tmp); tmp.normalize(); vnl2vtk(tmp, inVector); } k = vtkMath::Dot( wnormal, inVector ); // Remove non orthogonal component. outVector[ 0 ] = inVector[ 0 ] - ( wnormal[ 0 ] * k ); outVector[ 1 ] = inVector[ 1 ] - ( wnormal[ 1 ] * k ); outVector[ 2 ] = inVector[ 2 ] - ( wnormal[ 2 ] * k ); inVectors->SetTuple( pointId, outVector ); // ?? this was set to norm(inVector) before, but outVector made more sense to me vectorMagnitudes->SetValue( pointId, vtkMath::Norm( outVector ) ); //std::cout << "method old: " << inVector[0] <<", " << inVector[1] << ", "<AddArray(vectorMagnitudes); pointData->CopyAllOn(); //pointData->PrintSelf(std::cout, vtkIndent(4)); //std::cout << " ...done!"<< std::endl; //std::cout << " glyphing..."<< std::endl; // call glyph2D to generate 2D glyphs for each of the // vectors vtkGlyphSource2D* glyphSource = vtkGlyphSource2D::New(); //glyphSource->SetGlyphTypeToDash(); glyphSource->DashOn(); //glyphSource->SetScale( 0.1 ); //glyphSource->SetScale2( .5 ); //glyphSource->SetCenter( 0.5, 0.5, 0.5 ); glyphSource->CrossOff(); //glyphSource->FilledOff(); //glyphSource->Update(); double spacing[3]; vtkImage->GetSpacing(spacing); double min = spacing[0]; min = min > spacing[1] ? spacing[1] : min; min = min > spacing[2] ? spacing[2] : min; float scale = 1; mitk::FloatProperty::Pointer mitkScaleProp = dynamic_cast(GetDataNode()->GetProperty("Scale")); if (mitkScaleProp.IsNotNull()) { scale = mitkScaleProp->GetValue(); } vtkMaskedGlyph3D* glyphGenerator = vtkMaskedGlyph3D::New(); glyphGenerator->SetSourceData(glyphSource->GetOutput() ); glyphGenerator->SetInput(cuttedPlane); glyphGenerator->SetInputArrayToProcess (1, 0,0, vtkDataObject::FIELD_ASSOCIATION_POINTS , "vector"); glyphGenerator->SetVectorModeToUseVector(); glyphGenerator->OrientOn(); glyphGenerator->SetScaleFactor( min*scale ); glyphGenerator->SetUseMaskPoints( true ); glyphGenerator->SetRandomMode( true ); glyphGenerator->SetMaximumNumberOfPoints( 128*128 ); glyphGenerator->Update(); /* vtkLookupTable* vtkLut = NULL; mitk::LookupTableProperty::Pointer mitkLutProp = dynamic_cast(GetDataNode()->GetProperty("LookupTable")); if (mitkLutProp.IsNotNull()) { vtkLut = mitkLutProp->GetLookupTable()->GetVtkLookupTable(); } */ mitk::Color color; mitk::ColorProperty::Pointer mitkColorProp = dynamic_cast(GetDataNode()->GetProperty("color")); if (mitkColorProp.IsNotNull()) { color = mitkColorProp->GetColor(); } else { color.SetRed(0); color.SetBlue(1); color.SetGreen(0); } float lwidth = 1; mitk::FloatProperty::Pointer mitkLWidthProp = dynamic_cast(GetDataNode()->GetProperty("LineWidth")); if (mitkLWidthProp.IsNotNull()) { lwidth = mitkLWidthProp->GetValue(); } vtkTransform* trafo = vtkTransform::New(); trafo->Identity(); trafo->Concatenate(vtktransform); trafo->PreMultiply(); double myscale[3]; trafo->GetScale(myscale); trafo->Scale(1/myscale[0],1/myscale[1],1/myscale[2]); this->PaintCells( glyphGenerator->GetOutput(), renderer->GetCurrentWorldGeometry2D(), renderer->GetDisplayGeometry(), trafo, renderer, NULL/*vtkLut*/, color, lwidth, spacing ); vectorMagnitudes->Delete(); glyphSource->Delete(); glyphGenerator->Delete(); trafo->Delete(); } else { std::cout << " no points cutted!"<< std::endl; } //std::cout << "...done!" << std::endl; } -void mitk::VectorImageMapper2D::PaintCells( vtkPolyData* glyphs, const Geometry2D* worldGeometry, const DisplayGeometry* displayGeometry, vtkLinearTransform* vtktransform, mitk::BaseRenderer* /*renderer*/, vtkScalarsToColors *lut, mitk::Color color, float lwidth, double *spacing ) +void mitk::VectorImageMapper2D::PaintCells( vtkPolyData* glyphs, const PlaneGeometry* worldGeometry, const DisplayGeometry* displayGeometry, vtkLinearTransform* vtktransform, mitk::BaseRenderer* /*renderer*/, vtkScalarsToColors *lut, mitk::Color color, float lwidth, double *spacing ) { vtkPoints * points = glyphs->GetPoints(); vtkPointData * vpointdata = glyphs->GetPointData(); vtkDataArray* vpointscalars = vpointdata->GetArray("vectorMagnitudes"); //vtkDataArray* vpointpositions = vpointdata->GetArray("pointPositions"); assert(vpointscalars != NULL); //std::cout << " Scalars range 2d:" << vpointscalars->GetRange()[0] << " " << vpointscalars->GetRange()[0] << std::endl; Point3D p; Point2D p2d; vtkIdList* idList; vtkCell* cell; double offset[3]; for (unsigned int i = 0; i < 3; ++i) { offset[i] = 0; } vtkIdType numCells = glyphs->GetNumberOfCells(); for ( vtkIdType cellId = 0; cellId < numCells; ++cellId ) { double vp[ 3 ]; cell = glyphs->GetCell( cellId ); idList = cell->GetPointIds(); int numPoints = idList->GetNumberOfIds(); if(numPoints == 1) { //take transformation via vtktransform into account double pos[ 3 ],vp_raster[3]; points->GetPoint( idList->GetId( 0 ), vp ); vp_raster[0] = vtkMath::Round(vp[0]/spacing[0])*spacing[0]; vp_raster[1] = vtkMath::Round(vp[1]/spacing[1])*spacing[1]; vp_raster[2] = vtkMath::Round(vp[2]/spacing[2])*spacing[2]; vtktransform->TransformPoint( vp_raster, pos ); offset[0] = pos[0] - vp[0]; offset[1] = pos[1] - vp[1]; offset[2] = pos[2] - vp[2]; } else { glLineWidth(lwidth); glBegin ( GL_LINE_LOOP ); for ( int pointNr = 0; pointNr < numPoints ;++pointNr ) { points->GetPoint( idList->GetId( pointNr ), vp ); vp[0] = vp[0] + offset[0]; vp[1] = vp[1] + offset[1]; vp[2] = vp[2] + offset[2]; double tmp[ 3 ]; vtktransform->TransformPoint( vp,tmp ); vtk2itk( vp, p ); //convert 3D point (in mm) to 2D point on slice (also in mm) worldGeometry->Map( p, p2d ); //convert point (until now mm and in worldcoordinates) to display coordinates (units ) displayGeometry->WorldToDisplay( p2d, p2d ); if ( lut != NULL ) { // color each point according to point data double * color; if ( vpointscalars != NULL ) { vpointscalars->GetComponent( pointNr, 0 ); color = lut->GetColor( vpointscalars->GetComponent( idList->GetId( pointNr ), 0 ) ); glColor3f( color[ 0 ], color[ 1 ], color[ 2 ] ); } } else { glColor3f( color.GetRed(), color.GetGreen(), color.GetBlue() ); } //std::cout << idList->GetId( pointNr )<< ": " << p2d[0]<< " "<< p2d[1] << std::endl; //draw the line glVertex2f( p2d[ 0 ], p2d[ 1 ] ); } glEnd (); } } } mitk::VectorImageMapper2D::VectorImageMapper2D() { m_LUT = NULL; m_Plane = vtkPlane::New(); m_Cutter = vtkCutter::New(); m_Cutter->SetCutFunction( m_Plane ); m_Cutter->GenerateValues( 1, 0, 1 ); } mitk::VectorImageMapper2D::~VectorImageMapper2D() { if ( m_LUT != NULL ) m_LUT->Delete(); if ( m_Plane != NULL ) m_Plane->Delete(); if ( m_Cutter != NULL ) m_Cutter->Delete(); } int mitk::VectorImageMapper2D::GetCurrentTimeStep( mitk::BaseData* data, mitk::BaseRenderer* renderer ) { // // get the TimeGeometry of the input object // const TimeGeometry * dataTimeGeometry = data->GetUpdatedTimeGeometry(); if ( ( dataTimeGeometry == NULL ) || ( dataTimeGeometry->CountTimeSteps() == 0 ) ) { itkWarningMacro( << "The given object is missing a mitk::TimeGeometry, or the number of time steps is 0!" ); return 0; } // // get the world time // - Geometry2D::ConstPointer worldGeometry = renderer->GetCurrentWorldGeometry2D(); + PlaneGeometry::ConstPointer worldGeometry = renderer->GetCurrentWorldGeometry2D(); assert( worldGeometry.IsNotNull() ); ScalarType time = worldGeometry->GetTimeBounds() [ 0 ]; // // convert the world time to time steps of the input object // int timestep = 0; if ( time > ScalarTypeNumericTraits::NonpositiveMin() ) timestep = dataTimeGeometry->TimePointToTimeStep( time ); if ( dataTimeGeometry->IsValidTimeStep( timestep ) == false ) { itkWarningMacro( << timestep << " is not a valid time of the given data object!" ); return 0; } return timestep; } diff --git a/Modules/MitkMapperExt/mitkVectorImageMapper2D.h b/Modules/MitkMapperExt/mitkVectorImageMapper2D.h index d532eb3dbe..8abad438a9 100644 --- a/Modules/MitkMapperExt/mitkVectorImageMapper2D.h +++ b/Modules/MitkMapperExt/mitkVectorImageMapper2D.h @@ -1,93 +1,93 @@ /*=================================================================== 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 _MITK_VECTOR_IMAGE_MAPPER_2D__H_ #define _MITK_VECTOR_IMAGE_MAPPER_2D__H_ #include "MitkMapperExtExports.h" #include "mitkCommon.h" #include "mitkGLMapper.h" #include "mitkImage.h" class vtkLookupTable; class vtkScalarsToColors; class vtkImageReslice; class vtkPolyData; class vtkGlyph2D; class vtkPlane; class vtkCutter; namespace mitk { class BaseRenderer; -class Geometry2D; +class PlaneGeometry; class DisplayGeometry; class MitkMapperExt_EXPORT VectorImageMapper2D : public GLMapper { public: mitkClassMacro( VectorImageMapper2D, GLMapper ); itkNewMacro( Self ); typedef double vtkScalarType; /** * @returns the image held by the associated with the mapper or the image * which has been explicitly set by SetImage(...) */ const mitk::Image * GetInput( void ); virtual void Paint( mitk::BaseRenderer * renderer ); /** * Explicitly set an vector image. This image will be used for * rendering instead of the image returned by GetData() */ itkSetConstObjectMacro( Image, mitk::Image ); /** * Get the explicitly set image * @returns NULL if no Image has been set instead of GetData(); */ itkGetConstObjectMacro( Image, mitk::Image ); - virtual void PaintCells( vtkPolyData* contour, const Geometry2D* worldGeometry, const DisplayGeometry* displayGeometry, vtkLinearTransform* vtktransform, BaseRenderer* renderer, vtkScalarsToColors *lut, mitk::Color color, float lwidth, double *spacing ); + virtual void PaintCells( vtkPolyData* contour, const PlaneGeometry* worldGeometry, const DisplayGeometry* displayGeometry, vtkLinearTransform* vtktransform, BaseRenderer* renderer, vtkScalarsToColors *lut, mitk::Color color, float lwidth, double *spacing ); protected: int GetCurrentTimeStep( mitk::BaseData* data, mitk::BaseRenderer* renderer ); VectorImageMapper2D(); virtual ~VectorImageMapper2D(); mitk::Image::ConstPointer m_Image; vtkLookupTable *m_LUT; vtkPlane* m_Plane; vtkCutter* m_Cutter; }; } // namespace mitk #endif diff --git a/Modules/PlanarFigure/DataManagement/mitkPlanarAngle.cpp b/Modules/PlanarFigure/DataManagement/mitkPlanarAngle.cpp index dd95f19306..98823d1ced 100644 --- a/Modules/PlanarFigure/DataManagement/mitkPlanarAngle.cpp +++ b/Modules/PlanarFigure/DataManagement/mitkPlanarAngle.cpp @@ -1,175 +1,175 @@ /*=================================================================== 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 "mitkPlanarAngle.h" -#include "mitkGeometry2D.h" +#include "mitkPlaneGeometry.h" mitk::PlanarAngle::PlanarAngle() : FEATURE_ID_ANGLE( this->AddFeature( "Angle", "deg" ) ) { // Start with two control points this->ResetNumberOfControlPoints( 2 ); this->SetNumberOfPolyLines(1); this->SetNumberOfHelperPolyLines(1); m_HelperPolyLinesToBePainted->InsertElement( 0, false ); } mitk::PlanarAngle::~PlanarAngle() { } void mitk::PlanarAngle::GeneratePolyLine() { this->ClearPolyLines(); // Generate poly-line for angle for ( unsigned int i=0; iGetNumberOfControlPoints(); i++ ) { mitk::PlanarFigure::PolyLineElement element( this->GetControlPoint( i ), i ); this->AppendPointToPolyLine( 0, element ); } } void mitk::PlanarAngle::GenerateHelperPolyLine(double mmPerDisplayUnit, unsigned int displayHeight) { // Generate helper-poly-line for angle if ( this->GetNumberOfControlPoints() < 3) { m_HelperPolyLinesToBePainted->SetElement(0, false); return; //We do not need to draw an angle as there are no two arms yet } this->ClearHelperPolyLines(); const Point2D centerPoint = this->GetControlPoint( 1 ); const Point2D boundaryPointOne = this->GetControlPoint( 0 ); const Point2D boundaryPointTwo = this->GetControlPoint( 2 ); double radius = centerPoint.EuclideanDistanceTo( boundaryPointOne ); if ( radius > centerPoint.EuclideanDistanceTo( boundaryPointTwo ) ) { radius = centerPoint.EuclideanDistanceTo( boundaryPointTwo ); } //Fixed size radius depending on screen size for the angle double nonScalingRadius = displayHeight * mmPerDisplayUnit * 0.05; if (nonScalingRadius > radius) { m_HelperPolyLinesToBePainted->SetElement(0, false); return; //if the arc has a radius that is longer than the shortest arm it should not be painted } m_HelperPolyLinesToBePainted->SetElement(0, true); radius = nonScalingRadius; double angle = this->GetQuantity( FEATURE_ID_ANGLE ); //Determine from which arm the angle should be drawn Vector2D v0 = boundaryPointOne - centerPoint; Vector2D v1 = boundaryPointTwo - centerPoint; Vector2D v2; v2[0] = 1.0; v2[1] = 0.0; v0[0] = v0[0] * cos( 0.001 ) - v0[1] * sin( 0.001 ); //rotate one arm a bit v0[1] = v0[0] * sin( 0.001 ) + v0[1] * cos( 0.001 ); v0.Normalize(); v1.Normalize(); double testAngle = acos( v0 * v1 ); //if the rotated arm is closer to the other arm than before it is the one from which we start drawing //else we start drawing from the other arm (we want to draw in the mathematically positive direction) if( angle > testAngle ) { v1[0] = v0[0] * cos( -0.001 ) - v0[1] * sin( -0.001 ); v1[1] = v0[0] * sin( -0.001 ) + v0[1] * cos( -0.001 ); //We determine if the arm is mathematically forward or backward //assuming we rotate between -pi and pi if ( acos( v0 * v2 ) > acos ( v1 * v2 )) { testAngle = acos( v1 * v2 ); } else { testAngle = -acos( v1 * v2 ); } } else { v0[0] = v1[0] * cos( -0.001 ) - v1[1] * sin( -0.001 ); v0[1] = v1[0] * sin( -0.001 ) + v1[1] * cos( -0.001 ); //We determine if the arm is mathematically forward or backward //assuming we rotate between -pi and pi if ( acos( v0 * v2 ) < acos ( v1 * v2 )) { testAngle = acos( v1 * v2 ); } else { testAngle = -acos( v1 * v2 ); } } // Generate poly-line with 16 segments for ( int t = 0; t < 16; ++t ) { double alpha = (double) t * angle / 15.0 + testAngle; Point2D polyLinePoint; polyLinePoint[0] = centerPoint[0] + radius * cos( alpha ); polyLinePoint[1] = centerPoint[1] + radius * sin( alpha ); AppendPointToHelperPolyLine( 0, PolyLineElement( polyLinePoint, t ) ); } } void mitk::PlanarAngle::EvaluateFeaturesInternal() { if ( this->GetNumberOfControlPoints() < 3 ) { // Angle not yet complete. return; } // Calculate angle between lines const Point2D &p0 = this->GetControlPoint( 0 ); const Point2D &p1 = this->GetControlPoint( 1 ); const Point2D &p2 = this->GetControlPoint( 2 ); Vector2D v0 = p1 - p0; Vector2D v1 = p1 - p2; v0.Normalize(); v1.Normalize(); double angle = acos( v0 * v1 ); this->SetQuantity( FEATURE_ID_ANGLE, angle ); } void mitk::PlanarAngle::PrintSelf( std::ostream& os, itk::Indent indent) const { Superclass::PrintSelf( os, indent ); } diff --git a/Modules/PlanarFigure/DataManagement/mitkPlanarAngle.h b/Modules/PlanarFigure/DataManagement/mitkPlanarAngle.h index d90cd00c8d..86bc288b22 100644 --- a/Modules/PlanarFigure/DataManagement/mitkPlanarAngle.h +++ b/Modules/PlanarFigure/DataManagement/mitkPlanarAngle.h @@ -1,87 +1,87 @@ /*=================================================================== 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 _MITK_PLANAR_ANGLE_H_ #define _MITK_PLANAR_ANGLE_H_ #include "mitkPlanarFigure.h" #include "PlanarFigureExports.h" namespace mitk { -class Geometry2D; +class PlaneGeometry; /** * \brief Implementation of PlanarFigure to display an angle * through three control points */ class PlanarFigure_EXPORT PlanarAngle : public PlanarFigure { public: mitkClassMacro( PlanarAngle, PlanarFigure ); itkNewMacro( Self ); public: // Feature identifiers const unsigned int FEATURE_ID_ANGLE; /** \brief Place figure in its minimal configuration (a point at least) * onto the given 2D geometry. * * Must be implemented in sub-classes. */ //virtual void Initialize(); /** \brief Angle has 3 control points per definition. */ unsigned int GetMinimumNumberOfControlPoints() const { return 3; } /** \brief Angle has 3 control points per definition. */ unsigned int GetMaximumNumberOfControlPoints() const { return 3; } protected: PlanarAngle(); virtual ~PlanarAngle(); mitkCloneMacro(Self); /** \brief Generates the poly-line representation of the planar figure. */ virtual void GeneratePolyLine(); /** \brief Generates the poly-lines that should be drawn the same size regardless of zoom.*/ virtual void GenerateHelperPolyLine(double mmPerDisplayUnit, unsigned int displayHeight); /** \brief Calculates feature quantities of the planar figure. */ virtual void EvaluateFeaturesInternal(); virtual void PrintSelf( std::ostream &os, itk::Indent indent ) const; private: }; } // namespace mitk #endif //_MITK_PLANAR_ANGLE_H_ diff --git a/Modules/PlanarFigure/DataManagement/mitkPlanarArrow.cpp b/Modules/PlanarFigure/DataManagement/mitkPlanarArrow.cpp index 8c62768eba..0b9e61ab00 100644 --- a/Modules/PlanarFigure/DataManagement/mitkPlanarArrow.cpp +++ b/Modules/PlanarFigure/DataManagement/mitkPlanarArrow.cpp @@ -1,137 +1,137 @@ /*=================================================================== 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 "mitkPlanarArrow.h" -#include "mitkGeometry2D.h" +#include "mitkPlaneGeometry.h" mitk::PlanarArrow::PlanarArrow() : FEATURE_ID_LENGTH( this->AddFeature( "Length", "mm" ) ) { // Directed arrow has two control points this->ResetNumberOfControlPoints( 2 ); m_ArrowTipScaleFactor = -1.0; this->SetNumberOfPolyLines( 1 ); this->SetNumberOfHelperPolyLines( 2 ); //m_PolyLines->InsertElement( 0, VertexContainerType::New()); // Create helper polyline object (for drawing the orthogonal orientation line) //m_HelperPolyLines->InsertElement( 0, VertexContainerType::New()); //m_HelperPolyLines->InsertElement( 1, VertexContainerType::New()); //m_HelperPolyLines->ElementAt( 0 )->Reserve( 2 ); //m_HelperPolyLines->ElementAt( 1 )->Reserve( 2 ); m_HelperPolyLinesToBePainted->InsertElement( 0, false ); m_HelperPolyLinesToBePainted->InsertElement( 1, false ); } mitk::PlanarArrow::~PlanarArrow() { } void mitk::PlanarArrow::GeneratePolyLine() { this->ClearPolyLines(); this->AppendPointToPolyLine( 0, PolyLineElement( this->GetControlPoint( 0 ), 0 )); this->AppendPointToPolyLine( 0, PolyLineElement( this->GetControlPoint( 1 ), 0 )); // TODO: start line at specified start point... // Generate poly-line //m_PolyLines->ElementAt( 0 )->Reserve( 2 ); //m_PolyLines->ElementAt( 0 )->ElementAt( 0 ) = m_ControlPoints->ElementAt( 0 ); //m_PolyLines->ElementAt( 0 )->ElementAt( 1 ) = m_ControlPoints->ElementAt( 1 ); } void mitk::PlanarArrow::GenerateHelperPolyLine(double mmPerDisplayUnit, unsigned int displayHeight) { // Generate helper polyline (orientation line orthogonal to first line) // if the third control point is currently being set if ( this->GetNumberOfControlPoints() != 2 ) { m_HelperPolyLinesToBePainted->SetElement( 0, false ); m_HelperPolyLinesToBePainted->SetElement( 1, false ); return; } this->ClearHelperPolyLines(); m_HelperPolyLinesToBePainted->SetElement( 0, true ); m_HelperPolyLinesToBePainted->SetElement( 1, true ); //Fixed size depending on screen size for the angle float scaleFactor = 0.015; if ( m_ArrowTipScaleFactor > 0.0 ) { scaleFactor = m_ArrowTipScaleFactor; } double nonScalingLength = displayHeight * mmPerDisplayUnit * scaleFactor; // Calculate arrow peak const Point2D p1 = this->GetControlPoint( 0 ); const Point2D p2 = this->GetControlPoint( 1 ); //const Point2D& p1 = m_ControlPoints->ElementAt( 0 ); //const Point2D& p2 = m_ControlPoints->ElementAt( 1 ); Vector2D n1 = p1 - p2; n1.Normalize(); double degrees = 100.0; Vector2D temp; temp[0] = n1[0] * cos(degrees) - n1[1] * sin(degrees); temp[1] = n1[0] * sin(degrees) + n1[1] * cos(degrees); Vector2D temp2; temp2[0] = n1[0] * cos(-degrees) - n1[1] * sin(-degrees); temp2[1] = n1[0] * sin(-degrees) + n1[1] * cos(-degrees); this->AppendPointToHelperPolyLine( 0, PolyLineElement( p1, 0 )); this->AppendPointToHelperPolyLine( 0, PolyLineElement( p1 - temp * nonScalingLength, 0 )); this->AppendPointToHelperPolyLine( 1, PolyLineElement( p1, 0 )); this->AppendPointToHelperPolyLine( 1, PolyLineElement( p1 - temp2 * nonScalingLength, 0 )); //m_HelperPolyLines->ElementAt( 0 )->ElementAt( 0 ) = p1; //m_HelperPolyLines->ElementAt( 0 )->ElementAt( 1 ) = p1 - temp * nonScalingLength; //m_HelperPolyLines->ElementAt( 1 )->ElementAt( 0 ) = p1; //m_HelperPolyLines->ElementAt( 1 )->ElementAt( 1 ) = p1 - temp2 * nonScalingLength; } void mitk::PlanarArrow::EvaluateFeaturesInternal() { // Calculate line length const Point3D &p0 = this->GetWorldControlPoint( 0 ); const Point3D &p1 = this->GetWorldControlPoint( 1 ); double length = p0.EuclideanDistanceTo( p1 ); this->SetQuantity( FEATURE_ID_LENGTH, length ); } void mitk::PlanarArrow::PrintSelf( std::ostream& os, itk::Indent indent) const { Superclass::PrintSelf( os, indent ); } void mitk::PlanarArrow::SetArrowTipScaleFactor( float scale ) { m_ArrowTipScaleFactor = scale; } diff --git a/Modules/PlanarFigure/DataManagement/mitkPlanarArrow.h b/Modules/PlanarFigure/DataManagement/mitkPlanarArrow.h index 686b03e165..b78ac31fd8 100644 --- a/Modules/PlanarFigure/DataManagement/mitkPlanarArrow.h +++ b/Modules/PlanarFigure/DataManagement/mitkPlanarArrow.h @@ -1,96 +1,96 @@ /*=================================================================== 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 _MITK_PLANAR_ARROW_H_ #define _MITK_PLANAR_ARROW_H_ #include "mitkPlanarFigure.h" #include "PlanarFigureExports.h" namespace mitk { -class Geometry2D; +class PlaneGeometry; /** * \brief Implementation of PlanarFigure representing an arrow * through two control points */ class PlanarFigure_EXPORT PlanarArrow : public PlanarFigure { public: mitkClassMacro( PlanarArrow, PlanarFigure ); itkNewMacro( Self ); /** \brief Place figure in its minimal configuration (a point at least) * onto the given 2D geometry. * * Must be implemented in sub-classes. */ //virtual void Initialize(); /** \brief Line has 2 control points per definition. */ unsigned int GetMinimumNumberOfControlPoints() const { return 2; } /** \brief Line has 2 control points per definition. */ unsigned int GetMaximumNumberOfControlPoints() const { return 2; } void SetArrowTipScaleFactor( float scale ); protected: PlanarArrow(); virtual ~PlanarArrow(); mitkCloneMacro(Self); /** \brief Generates the poly-line representation of the planar figure. */ virtual void GeneratePolyLine(); /** \brief Generates the poly-lines that should be drawn the same size regardless of zoom.*/ virtual void GenerateHelperPolyLine(double mmPerDisplayUnit, unsigned int displayHeight); /** \brief Calculates feature quantities of the planar figure. */ virtual void EvaluateFeaturesInternal(); virtual void PrintSelf( std::ostream &os, itk::Indent indent ) const; // Feature identifiers const unsigned int FEATURE_ID_LENGTH; // ScaleFactor defining size of helper-lines in relation to display size float m_ArrowTipScaleFactor; private: }; } // namespace mitk #endif //_MITK_PLANAR_ARROW_H_ diff --git a/Modules/PlanarFigure/DataManagement/mitkPlanarCircle.cpp b/Modules/PlanarFigure/DataManagement/mitkPlanarCircle.cpp index 19b1f9542e..2349939f66 100644 --- a/Modules/PlanarFigure/DataManagement/mitkPlanarCircle.cpp +++ b/Modules/PlanarFigure/DataManagement/mitkPlanarCircle.cpp @@ -1,165 +1,165 @@ /*=================================================================== 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 "mitkPlanarCircle.h" -#include "mitkGeometry2D.h" +#include "mitkPlaneGeometry.h" #include "mitkProperties.h" mitk::PlanarCircle::PlanarCircle() : FEATURE_ID_RADIUS( this->AddFeature( "Radius", "mm" ) ), FEATURE_ID_DIAMETER( this->AddFeature( "Diameter", "mm" ) ), FEATURE_ID_AREA( this->AddFeature( "Area", "mm2" ) ), m_MinRadius(0), m_MaxRadius(100), m_MinMaxRadiusContraintsActive(false) { // Circle has two control points this->ResetNumberOfControlPoints( 2 ); this->SetNumberOfPolyLines( 1 ); this->SetProperty( "closed", mitk::BoolProperty::New(true) ); } mitk::PlanarCircle::~PlanarCircle() { } bool mitk::PlanarCircle::SetControlPoint( unsigned int index, const Point2D &point, bool /*createIfDoesNotExist*/ ) { // moving center point if(index == 0) { const Point2D ¢erPoint = GetControlPoint( 0 ); Point2D boundaryPoint = GetControlPoint( 1 ); vnl_vector vec = (point.GetVnlVector() - centerPoint.GetVnlVector()); boundaryPoint[0] += vec[0]; boundaryPoint[1] += vec[1]; PlanarFigure::SetControlPoint( 0, point ); PlanarFigure::SetControlPoint( 1, boundaryPoint ); return true; } else if ( index == 1 ) { PlanarFigure::SetControlPoint( index, point ); return true; } return false; } mitk::Point2D mitk::PlanarCircle::ApplyControlPointConstraints(unsigned int index, const Point2D &point) { if ( this->GetGeometry2D() == NULL ) { return point; } Point2D indexPoint; this->GetGeometry2D()->WorldToIndex( point, indexPoint ); BoundingBox::BoundsArrayType bounds = this->GetGeometry2D()->GetBounds(); if ( indexPoint[0] < bounds[0] ) { indexPoint[0] = bounds[0]; } if ( indexPoint[0] > bounds[1] ) { indexPoint[0] = bounds[1]; } if ( indexPoint[1] < bounds[2] ) { indexPoint[1] = bounds[2]; } if ( indexPoint[1] > bounds[3] ) { indexPoint[1] = bounds[3]; } Point2D constrainedPoint; this->GetGeometry2D()->IndexToWorld( indexPoint, constrainedPoint ); if(m_MinMaxRadiusContraintsActive) { if( index != 0) { const Point2D ¢erPoint = this->GetControlPoint(0); double euclideanDinstanceFromCenterToPoint1 = centerPoint.EuclideanDistanceTo(point); Vector2D vectorProjectedPoint; vectorProjectedPoint = point - centerPoint; vectorProjectedPoint.Normalize(); if( euclideanDinstanceFromCenterToPoint1 > m_MaxRadius ) { vectorProjectedPoint *= m_MaxRadius; constrainedPoint = centerPoint; constrainedPoint += vectorProjectedPoint; } else if( euclideanDinstanceFromCenterToPoint1 < m_MinRadius ) { vectorProjectedPoint *= m_MinRadius; constrainedPoint = centerPoint; constrainedPoint += vectorProjectedPoint; } } } return constrainedPoint; } void mitk::PlanarCircle::GeneratePolyLine() { // TODO: start circle at specified boundary point... // clear the PolyLine-Contrainer, it will be reconstructed soon enough... this->ClearPolyLines(); const Point2D ¢erPoint = GetControlPoint( 0 ); const Point2D &boundaryPoint = GetControlPoint( 1 ); double radius = centerPoint.EuclideanDistanceTo( boundaryPoint ); // Generate poly-line with 64 segments for ( int t = 0; t < 64; ++t ) { double alpha = (double) t * vnl_math::pi / 32.0; // construct the new polyline point ... Point2D polyLinePoint; polyLinePoint[0] = centerPoint[0] + radius * cos( alpha ); polyLinePoint[1] = centerPoint[1] + radius * sin( alpha ); // ... and append it to the PolyLine. // No extending supported here, so we can set the index of the PolyLineElement to '0' AppendPointToPolyLine( 0, PolyLineElement( polyLinePoint, 0 ) ); } } void mitk::PlanarCircle::GenerateHelperPolyLine(double /*mmPerDisplayUnit*/, unsigned int /*displayHeight*/) { // A circle does not require a helper object } void mitk::PlanarCircle::EvaluateFeaturesInternal() { // Calculate circle radius and area const Point3D &p0 = this->GetWorldControlPoint( 0 ); const Point3D &p1 = this->GetWorldControlPoint( 1 ); double radius = p0.EuclideanDistanceTo( p1 ); double area = vnl_math::pi * radius * radius; this->SetQuantity( FEATURE_ID_RADIUS, radius ); this->SetQuantity( FEATURE_ID_DIAMETER, 2*radius ); this->SetQuantity( FEATURE_ID_AREA, area ); } void mitk::PlanarCircle::PrintSelf( std::ostream& os, itk::Indent indent) const { Superclass::PrintSelf( os, indent ); } diff --git a/Modules/PlanarFigure/DataManagement/mitkPlanarCircle.h b/Modules/PlanarFigure/DataManagement/mitkPlanarCircle.h index 305d06dd13..853cfb8ba8 100644 --- a/Modules/PlanarFigure/DataManagement/mitkPlanarCircle.h +++ b/Modules/PlanarFigure/DataManagement/mitkPlanarCircle.h @@ -1,134 +1,134 @@ /*=================================================================== 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 _MITK_PLANAR_CIRCLE_H_ #define _MITK_PLANAR_CIRCLE_H_ #include "mitkPlanarFigure.h" #include "PlanarFigureExports.h" namespace mitk { -class Geometry2D; +class PlaneGeometry; /** * \brief Implementation of PlanarFigure representing a circle * through two control points */ class PlanarFigure_EXPORT PlanarCircle : public PlanarFigure { public: mitkClassMacro( PlanarCircle, PlanarFigure ); itkNewMacro( Self ); /** \brief Place figure in its minimal configuration (a point at least) * onto the given 2D geometry. * * Must be implemented in sub-classes. */ //virtual void Initialize(); bool SetControlPoint( unsigned int index, const Point2D &point, bool createIfDoesNotExist = false ); /** \brief Circle has 2 control points per definition. */ unsigned int GetMinimumNumberOfControlPoints() const { return 2; } /** \brief Circle has 2 control points per definition. */ unsigned int GetMaximumNumberOfControlPoints() const { return 2; } /** \brief Sets the minimum radius */ void SetMinimumRadius( double radius ) { m_MinRadius = radius; } /** \brief Gets the minimum radius */ double GetMinimumRadius() { return m_MinRadius; } /** \brief Sets the maximum radius */ void SetMaximumRadius( double radius ) { m_MaxRadius = radius; } /** \brief Gets the minimum radius */ double GetMaximumRadius() { return m_MaxRadius; } void ActivateMinMaxRadiusContstraints( bool active ) { m_MinMaxRadiusContraintsActive = active; } protected: PlanarCircle(); virtual ~PlanarCircle(); mitkCloneMacro(Self); /** \brief Generates the poly-line representation of the planar figure. */ virtual void GeneratePolyLine(); /** \brief Generates the poly-lines that should be drawn the same size regardless of zoom.*/ virtual void GenerateHelperPolyLine(double mmPerDisplayUnit, unsigned int displayHeight); /** \brief Spatially constrain control points of second (orthogonal) line */ virtual Point2D ApplyControlPointConstraints( unsigned int index, const Point2D& point ); /** \brief Calculates feature quantities of the planar figure. */ virtual void EvaluateFeaturesInternal(); virtual void PrintSelf( std::ostream &os, itk::Indent indent ) const; // Feature identifiers const unsigned int FEATURE_ID_RADIUS; const unsigned int FEATURE_ID_DIAMETER; const unsigned int FEATURE_ID_AREA; //Member variables: double m_MinRadius; double m_MaxRadius; bool m_MinMaxRadiusContraintsActive; private: }; } // namespace mitk #endif //_MITK_PLANAR_CIRCLE_H_ diff --git a/Modules/PlanarFigure/DataManagement/mitkPlanarCross.cpp b/Modules/PlanarFigure/DataManagement/mitkPlanarCross.cpp index d08afba80e..a9cdfc08bc 100644 --- a/Modules/PlanarFigure/DataManagement/mitkPlanarCross.cpp +++ b/Modules/PlanarFigure/DataManagement/mitkPlanarCross.cpp @@ -1,350 +1,350 @@ /*=================================================================== 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 "mitkPlanarCross.h" -#include "mitkGeometry2D.h" +#include "mitkPlaneGeometry.h" #include "mitkProperties.h" mitk::PlanarCross::PlanarCross() : FEATURE_ID_LONGESTDIAMETER( this->AddFeature( "Longest Axis", "mm" ) ), FEATURE_ID_SHORTAXISDIAMETER( this->AddFeature( "Short Axis", "mm" ) ) { // Cross has two control points at the beginning this->ResetNumberOfControlPoints( 2 ); // Create property for SingleLineMode (default: false) this->SetProperty( "SingleLineMode", mitk::BoolProperty::New( false ) ); // Create helper polyline object (for drawing the orthogonal orientation line) this->SetNumberOfHelperPolyLines( 1 ); m_HelperPolyLinesToBePainted->InsertElement( 0, false ); } mitk::PlanarCross::~PlanarCross() { } void mitk::PlanarCross::SetSingleLineMode( bool singleLineMode ) { this->SetProperty( "SingleLineMode", mitk::BoolProperty::New( singleLineMode ) ); this->Modified(); } bool mitk::PlanarCross::GetSingleLineMode() const { mitk::BoolProperty* singleLineMode = dynamic_cast< mitk::BoolProperty* >( this->GetProperty( "SingleLineMode" ).GetPointer() ); if ( singleLineMode != NULL ) { return singleLineMode->GetValue(); } return false; } bool mitk::PlanarCross::ResetOnPointSelect() { if ( this->GetSingleLineMode() ) { // In single line mode --> nothing to reset return false; } switch ( m_SelectedControlPoint ) { default: // Nothing selected --> nothing to reset return false; case 0: { // Control point 0 selected: exchange points 0 and 1 Point2D tmpPoint = this->GetControlPoint( 0 ); this->SetControlPoint( 0, this->GetControlPoint( 1 ) ); this->SetControlPoint( 1, tmpPoint ); // FALLS THROUGH! } case 1: { // Control point 0 or 1 selected: reset number of control points to two this->ResetNumberOfControlPoints( 2 ); this->SelectControlPoint( 1 ); return true; } case 2: { // Control point 2 selected: replace point 0 with point 3 and point 1 with point 2 this->SetControlPoint( 0, this->GetControlPoint( 3 ) ); this->SetControlPoint( 1, this->GetControlPoint( 2 ) ); // Adjust selected control point, reset number of control points to two this->ResetNumberOfControlPoints( 2 ); this->SelectControlPoint( 1 ); return true; } case 3: { // Control point 3 selected: replace point 0 with point 2 and point 1 with point 3 this->SetControlPoint( 0, this->GetControlPoint( 2 ) ); this->SetControlPoint( 1, this->GetControlPoint( 3 ) ); // Adjust selected control point, reset number of control points to two this->ResetNumberOfControlPoints( 2 ); this->SelectControlPoint( 1 ); return true; } } } unsigned int mitk::PlanarCross::GetNumberOfFeatures() const { if ( this->GetSingleLineMode() || (this->GetNumberOfControlPoints() < 4) ) { return 1; } else { return 2; } } mitk::Point2D mitk::PlanarCross::ApplyControlPointConstraints( unsigned int index, const Point2D& point ) { // Apply spatial constraints from superclass and from this class until the resulting constrained // point converges. Although not an optimal implementation, this iterative approach // helps to respect both constraints from the superclass and from this class. Without this, // situations may occur where control points are constrained by the superclass, but again // moved out of the superclass bounds by the subclass, or vice versa. unsigned int count = 0; // ensures stop of approach if point does not converge in reasonable time Point2D confinedPoint = point; Point2D superclassConfinedPoint; do { superclassConfinedPoint = Superclass::ApplyControlPointConstraints( index, confinedPoint ); confinedPoint = this->InternalApplyControlPointConstraints( index, superclassConfinedPoint ); ++count; } while ( (confinedPoint.EuclideanDistanceTo( superclassConfinedPoint ) > mitk::eps) && (count < 32) ); return confinedPoint; } mitk::Point2D mitk::PlanarCross::InternalApplyControlPointConstraints( unsigned int index, const Point2D& point ) { // Apply constraints depending on current interaction state switch ( index ) { case 2: { // Check if 3rd control point is outside of the range (2D area) defined by the first // line (via the first two control points); if it is outside, clip it to the bounds const Point2D p1 = this->GetControlPoint( 0 ); const Point2D p2 = this->GetControlPoint( 1 ); Vector2D n1 = p2 - p1; n1.Normalize(); Vector2D v1 = point - p1; double dotProduct = n1 * v1; Point2D crossPoint = p1 + n1 * dotProduct;; Vector2D crossVector = point - crossPoint; if ( dotProduct < 0.0 ) { // Out-of-bounds on the left: clip point to left boundary return (p1 + crossVector); } else if ( dotProduct > p2.EuclideanDistanceTo( p1 ) ) { // Out-of-bounds on the right: clip point to right boundary return (p2 + crossVector); } else { // Pass back original point return point; } } case 3: { // Constrain 4th control point so that with the 3rd control point it forms // a line orthogonal to the first line (constraint 1); the 4th control point // must lie on the opposite side of the line defined by the first two control // points than the 3rd control point (constraint 2) const Point2D p1 = this->GetControlPoint( 0 ); const Point2D p2 = this->GetControlPoint( 1 ); const Point2D p3 = this->GetControlPoint( 2 ); // Calculate distance of original point from orthogonal line the corrected // point should lie on to project the point onto this line Vector2D n1 = p2 - p1; n1.Normalize(); Vector2D v1 = point - p3; double dotProduct1 = n1 * v1; Point2D pointOnLine = point - n1 * dotProduct1; // Project new point onto line [p1, p2] Vector2D v2 = pointOnLine - p1; double dotProduct2 = n1 * v2; Point2D crossingPoint = p1 + n1 * dotProduct2; // Determine whether the projected point on the line, or the crossing point should be // used (according to the second constraint in the comment above) if ( (pointOnLine.SquaredEuclideanDistanceTo( p3 ) > crossingPoint.SquaredEuclideanDistanceTo( p3 )) && (pointOnLine.SquaredEuclideanDistanceTo( p3 ) > pointOnLine.SquaredEuclideanDistanceTo( crossingPoint )) ) { return pointOnLine; } else { return crossingPoint; } } default: return point; } } void mitk::PlanarCross::GeneratePolyLine() { this->SetNumberOfPolyLines( 1 ); this->ClearPolyLines(); if ( this->GetNumberOfControlPoints() > 2) { this->SetNumberOfPolyLines( 2 ); } for ( unsigned int i = 0; i < this->GetNumberOfControlPoints(); ++i ) { if (i < 2) { this->AppendPointToPolyLine( 0, mitk::PlanarFigure::PolyLineElement( this->GetControlPoint( i ), i ) ); } if (i > 1) { this->AppendPointToPolyLine( 1, mitk::PlanarFigure::PolyLineElement( this->GetControlPoint( i ), i ) ); } } } void mitk::PlanarCross::GenerateHelperPolyLine(double /*mmPerDisplayUnit*/, unsigned int /*displayHeight*/) { // Generate helper polyline (orientation line orthogonal to first line) // if the third control point is currently being set if ( this->GetNumberOfControlPoints() != 3 ) { m_HelperPolyLinesToBePainted->SetElement( 0, false ); return; } m_HelperPolyLinesToBePainted->SetElement( 0, true ); this->ClearHelperPolyLines(); // Calculate cross point of first line (p1 to p2) and orthogonal line through // the third control point (p3) const Point2D p1 = this->GetControlPoint( 0 ); const Point2D p2 = this->GetControlPoint( 1 ); const Point2D p3 = this->GetControlPoint( 2 ); Vector2D n1 = p2 - p1; n1.Normalize(); Vector2D v1 = p3 - p1; Point2D crossPoint = p1 + n1 * (n1 * v1); Vector2D v2 = crossPoint - p3; if ( v2.GetNorm() < 1.0 ) { // If third point is on the first line, draw orthogonal "infinite" line // through cross point on line Vector2D v0; v0[0] = n1[1]; v0[1] = -n1[0]; this->AppendPointToHelperPolyLine( 0, mitk::PlanarFigure::PolyLineElement( p3 - v0 * 10000.0, 0 ) ) ; this->AppendPointToHelperPolyLine( 0, mitk::PlanarFigure::PolyLineElement( p3 + v0 * 10000.0, 0 ) ) ; } else { // Else, draw orthogonal line starting from third point and crossing the // first line, open-ended only on the other side this->AppendPointToHelperPolyLine( 0, mitk::PlanarFigure::PolyLineElement( p3, 0 ) ) ; this->AppendPointToHelperPolyLine( 0, mitk::PlanarFigure::PolyLineElement( p3 + v2 * 10000.0, 0 ) ) ; } } void mitk::PlanarCross::EvaluateFeaturesInternal() { // Calculate length of first line const Point3D &p0 = this->GetWorldControlPoint( 0 ); const Point3D &p1 = this->GetWorldControlPoint( 1 ); double l1 = p0.EuclideanDistanceTo( p1 ); // Calculate length of second line double l2 = 0.0; if ( !this->GetSingleLineMode() && (this->GetNumberOfControlPoints() > 3) ) { const Point3D &p2 = this->GetWorldControlPoint( 2 ); const Point3D &p3 = this->GetWorldControlPoint( 3 ); l2 = p2.EuclideanDistanceTo( p3 ); } double longestDiameter; double shortAxisDiameter; if ( l1 > l2 ) { longestDiameter = l1; shortAxisDiameter = l2; } else { longestDiameter = l2; shortAxisDiameter = l1; } this->SetQuantity( FEATURE_ID_LONGESTDIAMETER, longestDiameter ); this->SetQuantity( FEATURE_ID_SHORTAXISDIAMETER, shortAxisDiameter ); } void mitk::PlanarCross::PrintSelf( std::ostream& os, itk::Indent indent) const { Superclass::PrintSelf( os, indent ); } diff --git a/Modules/PlanarFigure/DataManagement/mitkPlanarCross.h b/Modules/PlanarFigure/DataManagement/mitkPlanarCross.h index 283ad199da..68ccca2d62 100644 --- a/Modules/PlanarFigure/DataManagement/mitkPlanarCross.h +++ b/Modules/PlanarFigure/DataManagement/mitkPlanarCross.h @@ -1,131 +1,131 @@ /*=================================================================== 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 _MITK_PLANAR_CROSS_H_ #define _MITK_PLANAR_CROSS_H_ #include "mitkPlanarFigure.h" #include "PlanarFigureExports.h" namespace mitk { -class Geometry2D; +class PlaneGeometry; /** * \brief Implementation of PlanarFigure modeling a cross with two orthogonal lines * on a plane. * * The cross consists of two two orthogonal lines, which are defined by four control points * lying on a plane. The two control points of the first line are freely placable within * the bounds of the underlying 2D geometry, while the two control points of the second line * are ensured to meet the following constraints: * * 1.) The lines must be orthogonal to each other * 2.) The second line must lie within the 2D area defined by the first line * 3.) The two lines must intersect (at least with their boundaries) * * When placing the second line interactively, a graphical helper polyline is provided to the * user to indicate the position and orthogonal orientation of the line if it would be placed * at the current mouse position. * * When modifying one of the lines by interactively moving its control points, the respective * other line is deleted and the user is prompted to draw it again. * * The class provide a special mode for drawing single lines (SingleLineModeOn/Off); in this * case, interaction stops after the first line has been placed. * * The class provides the lengths of both lines via the "feature" interface, ordered by size. * * \sa PlanarFigureMapper2D */ class PlanarFigure_EXPORT PlanarCross : public PlanarFigure { public: mitkClassMacro( PlanarCross, PlanarFigure ); itkNewMacro( Self ); /** \brief Indicates whether the PlanarFigure shall represent only a single line instead of an * orthogonal cross. */ void SetSingleLineMode( bool singleLineMode ); /** \brief Indicates whether the PlanarFigure shall represent only a single line instead of an * orthogonal cross. */ bool GetSingleLineMode() const; /** \brief Indicates whether the PlanarFigure shall represent only a single line instead of an * orthogonal cross. */ itkBooleanMacro( SingleLineMode ); // No need to reimplement; calls SetSingleLineMode() /** \brief PlanarCross has either two or four control points, depending on the operation mode. */ unsigned int GetMinimumNumberOfControlPoints() const { return this->GetSingleLineMode() ? 2 : 4; } /** \brief PlanarCross has either two or four control points, depending on the operation mode. */ unsigned int GetMaximumNumberOfControlPoints() const { return this->GetSingleLineMode() ? 2 : 4; } /** \brief The cross shall be reset to a single line when a control point is selected. */ virtual bool ResetOnPointSelect(); /** \brief Returns the number of features available for this PlanarCross (1 or 2). */ virtual unsigned int GetNumberOfFeatures() const; protected: PlanarCross(); virtual ~PlanarCross(); mitkCloneMacro(Self); /** \brief Spatially constrain control points of second (orthogonal) line */ virtual Point2D ApplyControlPointConstraints( unsigned int index, const Point2D& point ); /** \brief Generates the poly-line representation of the planar figure. */ virtual void GeneratePolyLine(); /** \brief Generates the poly-lines that should be drawn the same size regardless of zoom.*/ virtual void GenerateHelperPolyLine(double mmPerDisplayUnit, unsigned int displayHeight); /** \brief Calculates feature quantities of the planar figure. */ virtual void EvaluateFeaturesInternal(); virtual void PrintSelf( std::ostream &os, itk::Indent indent ) const; // Feature identifiers const unsigned int FEATURE_ID_LONGESTDIAMETER; const unsigned int FEATURE_ID_SHORTAXISDIAMETER; private: /** Internal method for applying spatial constraints. */ virtual Point2D InternalApplyControlPointConstraints( unsigned int index, const Point2D& point ); }; } // namespace mitk #endif //_MITK_PLANAR_CROSS_H_ diff --git a/Modules/PlanarFigure/DataManagement/mitkPlanarEllipse.cpp b/Modules/PlanarFigure/DataManagement/mitkPlanarEllipse.cpp index 0a766813e3..3c8744dc10 100644 --- a/Modules/PlanarFigure/DataManagement/mitkPlanarEllipse.cpp +++ b/Modules/PlanarFigure/DataManagement/mitkPlanarEllipse.cpp @@ -1,273 +1,273 @@ /*=================================================================== 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 "mitkPlanarEllipse.h" -#include "mitkGeometry2D.h" +#include "mitkPlaneGeometry.h" #include "mitkProperties.h" mitk::PlanarEllipse::PlanarEllipse() : m_MinRadius(0), m_MaxRadius(100), m_MinMaxRadiusContraintsActive(false), m_TreatAsCircle(true) { // Ellipse has three control points this->ResetNumberOfControlPoints( 4 ); this->SetNumberOfPolyLines( 2 ); this->SetProperty( "closed", mitk::BoolProperty::New(true) ); } mitk::PlanarEllipse::~PlanarEllipse() { } bool mitk::PlanarEllipse::SetControlPoint( unsigned int index, const Point2D &point, bool createIfDoesNotExist ) { if(index == 0) // moving center point and control points accordingly { const Point2D ¢erPoint = GetControlPoint( 0 ); Point2D boundaryPoint1 = GetControlPoint( 1 ); Point2D boundaryPoint2 = GetControlPoint( 2 ); Point2D boundaryPoint3 = GetControlPoint( 3 ); vnl_vector vec = (point.GetVnlVector() - centerPoint.GetVnlVector()); boundaryPoint1[0] += vec[0]; boundaryPoint1[1] += vec[1]; boundaryPoint2[0] += vec[0]; boundaryPoint2[1] += vec[1]; boundaryPoint3[0] += vec[0]; boundaryPoint3[1] += vec[1]; PlanarFigure::SetControlPoint( 0, point, createIfDoesNotExist ); PlanarFigure::SetControlPoint( 1, boundaryPoint1, createIfDoesNotExist ); PlanarFigure::SetControlPoint( 2, boundaryPoint2, createIfDoesNotExist ); PlanarFigure::SetControlPoint( 3, boundaryPoint3, createIfDoesNotExist ); return true; } else if (index < 3) { PlanarFigure::SetControlPoint( index, point, createIfDoesNotExist ); int otherIndex = index+1; if (otherIndex > 2) otherIndex = 1; const Point2D ¢erPoint = GetControlPoint( 0 ); Point2D otherPoint = GetControlPoint( otherIndex ); Point2D point3 = GetControlPoint( 3 ); Vector2D vec1 = point - centerPoint; Vector2D vec2; if (index == 1 && m_TreatAsCircle ) { float x = vec1[0]; vec2[0] = vec1[1]; vec2[1] = x; if (index==1) vec2[0] *= -1; else vec2[1] *= -1; otherPoint = centerPoint+vec2; PlanarFigure::SetControlPoint( otherIndex, otherPoint, createIfDoesNotExist ); float r = centerPoint.EuclideanDistanceTo(otherPoint); // adjust additional third control point Point2D p3 = this->GetControlPoint(3); Vector2D vec3; vec3[0] = p3[0]-centerPoint[0]; vec3[1] = p3[1]-centerPoint[1]; if (vec3[0]!=0 || vec3[1]!=0) { vec3.Normalize(); vec3 *= r; } else { vec3[0] = r; vec3[1] = 0; } point3 = centerPoint + vec3; PlanarFigure::SetControlPoint( 3, point3, createIfDoesNotExist ); } else if ( vec1.GetNorm() > 0 ) { float r = centerPoint.EuclideanDistanceTo(otherPoint); float x = vec1[0]; vec2[0] = vec1[1]; vec2[1] = x; if (index==1) vec2[0] *= -1; else vec2[1] *= -1; vec2.Normalize(); vec2 *= r; if ( vec2.GetNorm() > 0 ) { otherPoint = centerPoint+vec2; PlanarFigure::SetControlPoint( otherIndex, otherPoint, createIfDoesNotExist ); } // adjust third control point Vector2D vec3 = point3 - centerPoint; vec3.Normalize(); double r1 = centerPoint.EuclideanDistanceTo( GetControlPoint( 1 ) ); double r2 = centerPoint.EuclideanDistanceTo( GetControlPoint( 2 ) ); Point2D newPoint = centerPoint + vec3*std::max(r1, r2); PlanarFigure::SetControlPoint( 3, newPoint, createIfDoesNotExist ); m_TreatAsCircle = false; } return true; } else if (index == 3) { Point2D centerPoint = GetControlPoint( 0 ); Vector2D vec3 = point - centerPoint; vec3.Normalize(); double r1 = centerPoint.EuclideanDistanceTo( GetControlPoint( 1 ) ); double r2 = centerPoint.EuclideanDistanceTo( GetControlPoint( 2 ) ); Point2D newPoint = centerPoint + vec3*std::max(r1, r2); PlanarFigure::SetControlPoint( index, newPoint, createIfDoesNotExist ); m_TreatAsCircle = false; return true; } return false; } void mitk::PlanarEllipse::PlaceFigure( const mitk::Point2D &point ) { PlanarFigure::PlaceFigure( point ); m_SelectedControlPoint = 1; } mitk::Point2D mitk::PlanarEllipse::ApplyControlPointConstraints(unsigned int index, const Point2D &point) { return point; Point2D indexPoint; this->GetGeometry2D()->WorldToIndex( point, indexPoint ); BoundingBox::BoundsArrayType bounds = this->GetGeometry2D()->GetBounds(); if ( indexPoint[0] < bounds[0] ) { indexPoint[0] = bounds[0]; } if ( indexPoint[0] > bounds[1] ) { indexPoint[0] = bounds[1]; } if ( indexPoint[1] < bounds[2] ) { indexPoint[1] = bounds[2]; } if ( indexPoint[1] > bounds[3] ) { indexPoint[1] = bounds[3]; } Point2D constrainedPoint; this->GetGeometry2D()->IndexToWorld( indexPoint, constrainedPoint ); if(m_MinMaxRadiusContraintsActive) { if( index != 0) { const Point2D ¢erPoint = this->GetControlPoint(0); double euclideanDinstanceFromCenterToPoint1 = centerPoint.EuclideanDistanceTo(point); Vector2D vectorProjectedPoint; vectorProjectedPoint = point - centerPoint; vectorProjectedPoint.Normalize(); if( euclideanDinstanceFromCenterToPoint1 > m_MaxRadius ) { vectorProjectedPoint *= m_MaxRadius; constrainedPoint = centerPoint; constrainedPoint += vectorProjectedPoint; } else if( euclideanDinstanceFromCenterToPoint1 < m_MinRadius ) { vectorProjectedPoint *= m_MinRadius; constrainedPoint = centerPoint; constrainedPoint += vectorProjectedPoint; } } } return constrainedPoint; } void mitk::PlanarEllipse::GeneratePolyLine() { // clear the PolyLine-Contrainer, it will be reconstructed soon enough... this->ClearPolyLines(); const Point2D ¢erPoint = GetControlPoint( 0 ); const Point2D &boundaryPoint1 = GetControlPoint( 1 ); const Point2D &boundaryPoint2 = GetControlPoint( 2 ); Vector2D dir = boundaryPoint1 - centerPoint; dir.Normalize(); vnl_matrix_fixed rot; // differentiate between clockwise and counterclockwise rotation int start = 0; int end = 64; if (dir[1]<0) { dir[0] = -dir[0]; start = -32; end = 32; } // construct rotation matrix to align ellipse with control point vector rot[0][0] = dir[0]; rot[1][1] = rot[0][0]; rot[1][0] = sin(acos(rot[0][0])); rot[0][1] = -rot[1][0]; double radius1 = centerPoint.EuclideanDistanceTo( boundaryPoint1 ); double radius2 = centerPoint.EuclideanDistanceTo( boundaryPoint2 ); // Generate poly-line with 64 segments for ( int t = start; t < end; ++t ) { double alpha = (double) t * vnl_math::pi / 32.0; // construct the new polyline point ... vnl_vector_fixed< float, 2 > vec; vec[0] = radius1 * cos( alpha ); vec[1] = radius2 * sin( alpha ); vec = rot*vec; Point2D polyLinePoint; polyLinePoint[0] = centerPoint[0] + vec[0]; polyLinePoint[1] = centerPoint[1] + vec[1]; // ... and append it to the PolyLine. // No extending supported here, so we can set the index of the PolyLineElement to '0' AppendPointToPolyLine( 0, PolyLineElement( polyLinePoint, 0 ) ); } AppendPointToPolyLine( 1, PolyLineElement( centerPoint, 0 ) ); AppendPointToPolyLine( 1, PolyLineElement( GetControlPoint( 3 ), 0 ) ); } void mitk::PlanarEllipse::GenerateHelperPolyLine(double /*mmPerDisplayUnit*/, unsigned int /*displayHeight*/) { // A circle does not require a helper object } void mitk::PlanarEllipse::EvaluateFeaturesInternal() { } void mitk::PlanarEllipse::PrintSelf( std::ostream& os, itk::Indent indent) const { Superclass::PrintSelf( os, indent ); } diff --git a/Modules/PlanarFigure/DataManagement/mitkPlanarEllipse.h b/Modules/PlanarFigure/DataManagement/mitkPlanarEllipse.h index 223850f8ad..45ca7ba7f0 100644 --- a/Modules/PlanarFigure/DataManagement/mitkPlanarEllipse.h +++ b/Modules/PlanarFigure/DataManagement/mitkPlanarEllipse.h @@ -1,136 +1,136 @@ /*=================================================================== 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 _MITK_PLANAR_ELLIPSE_H_ #define _MITK_PLANAR_ELLIPSE_H_ #include "mitkPlanarFigure.h" #include "PlanarFigureExports.h" namespace mitk { -class Geometry2D; +class PlaneGeometry; /** * \brief Implementation of PlanarFigure representing a circle * through two control points */ class PlanarFigure_EXPORT PlanarEllipse : public PlanarFigure { public: mitkClassMacro( PlanarEllipse, PlanarFigure ) itkNewMacro( Self ) /** \brief Place figure in its minimal configuration (a point at least) * onto the given 2D geometry. * * Must be implemented in sub-classes. */ virtual void PlaceFigure( const Point2D &point ); bool SetControlPoint( unsigned int index, const Point2D &point, bool createIfDoesNotExist = true ); /** \brief Ellipse has 3 control points per definition. */ unsigned int GetMinimumNumberOfControlPoints() const { return 4; } /** \brief Ellipse has 3 control points per definition. */ unsigned int GetMaximumNumberOfControlPoints() const { return 4; } /** \brief Sets the minimum radius */ void SetMinimumRadius( double radius ) { m_MinRadius = radius; } /** \brief Gets the minimum radius */ double GetMinimumRadius() { return m_MinRadius; } /** \brief Sets the maximum radius */ void SetMaximumRadius( double radius ) { m_MaxRadius = radius; } /** \brief Gets the minimum radius */ double GetMaximumRadius() { return m_MaxRadius; } void ActivateMinMaxRadiusContstraints( bool active ) { m_MinMaxRadiusContraintsActive = active; } /** \brief Treat ellipse as circle (equal radii) */ void SetTreatAsCircle( bool active ) { m_TreatAsCircle = active; } protected: PlanarEllipse(); virtual ~PlanarEllipse(); mitkCloneMacro(Self); /** \brief Generates the poly-line representation of the planar figure. */ virtual void GeneratePolyLine(); /** \brief Generates the poly-lines that should be drawn the same size regardless of zoom.*/ virtual void GenerateHelperPolyLine(double mmPerDisplayUnit, unsigned int displayHeight); /** \brief Spatially constrain control points of second (orthogonal) line */ virtual Point2D ApplyControlPointConstraints( unsigned int index, const Point2D& point ); /** \brief Calculates feature quantities of the planar figure. */ virtual void EvaluateFeaturesInternal(); virtual void PrintSelf( std::ostream &os, itk::Indent indent ) const; //Member variables: double m_MinRadius; double m_MaxRadius; bool m_MinMaxRadiusContraintsActive; bool m_TreatAsCircle; private: }; } // namespace mitk #endif //_MITK_PLANAR_ELLIPSE_H_ diff --git a/Modules/PlanarFigure/DataManagement/mitkPlanarFigure.cpp b/Modules/PlanarFigure/DataManagement/mitkPlanarFigure.cpp index ca8fe70fc5..4b01db1661 100644 --- a/Modules/PlanarFigure/DataManagement/mitkPlanarFigure.cpp +++ b/Modules/PlanarFigure/DataManagement/mitkPlanarFigure.cpp @@ -1,730 +1,730 @@ /*=================================================================== 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 "mitkPlanarFigure.h" -#include "mitkGeometry2D.h" +#include "mitkPlaneGeometry.h" #include "mitkProperties.h" #include #include "algorithm" mitk::PlanarFigure::PlanarFigure() : m_SelectedControlPoint( -1 ), m_PreviewControlPointVisible( false ), m_FigurePlaced( false ), m_Geometry2D( NULL ), m_PolyLineUpToDate(false), m_HelperLinesUpToDate(false), m_FeaturesUpToDate(false), m_FeaturesMTime( 0 ) { m_HelperPolyLinesToBePainted = BoolContainerType::New(); m_DisplaySize.first = 0.0; m_DisplaySize.second = 0; this->SetProperty( "closed", mitk::BoolProperty::New( false ) ); // Currently only single-time-step geometries are supported this->InitializeTimeGeometry( 1 ); } mitk::PlanarFigure::~PlanarFigure() { } mitk::PlanarFigure::PlanarFigure(const Self& other) : BaseData(other), m_ControlPoints(other.m_ControlPoints), m_NumberOfControlPoints(other.m_NumberOfControlPoints), m_SelectedControlPoint(other.m_SelectedControlPoint), m_PolyLines(other.m_PolyLines), m_HelperPolyLines(other.m_HelperPolyLines), m_HelperPolyLinesToBePainted(other.m_HelperPolyLinesToBePainted->Clone()), m_PreviewControlPoint(other.m_PreviewControlPoint), m_PreviewControlPointVisible(other.m_PreviewControlPointVisible), m_FigurePlaced(other.m_FigurePlaced), m_Geometry2D(other.m_Geometry2D), // do not clone since SetGeometry2D() doesn't clone either m_PolyLineUpToDate(other.m_PolyLineUpToDate), m_HelperLinesUpToDate(other.m_HelperLinesUpToDate), m_FeaturesUpToDate(other.m_FeaturesUpToDate), m_Features(other.m_Features), m_FeaturesMTime(other.m_FeaturesMTime), m_DisplaySize(other.m_DisplaySize) { } -void mitk::PlanarFigure::SetGeometry2D( mitk::Geometry2D *geometry ) +void mitk::PlanarFigure::SetGeometry2D( mitk::PlaneGeometry *geometry ) { this->SetGeometry( geometry ); - m_Geometry2D = dynamic_cast(GetGeometry(0));//geometry; + m_Geometry2D = dynamic_cast(GetGeometry(0));//geometry; } -const mitk::Geometry2D *mitk::PlanarFigure::GetGeometry2D() const +const mitk::PlaneGeometry *mitk::PlanarFigure::GetGeometry2D() const { return m_Geometry2D; } bool mitk::PlanarFigure::IsClosed() const { mitk::BoolProperty* closed = dynamic_cast< mitk::BoolProperty* >( this->GetProperty( "closed" ).GetPointer() ); if ( closed != NULL ) { return closed->GetValue(); } return false; } void mitk::PlanarFigure::PlaceFigure( const mitk::Point2D& point ) { for ( unsigned int i = 0; i < this->GetNumberOfControlPoints(); ++i ) { m_ControlPoints.push_back( this->ApplyControlPointConstraints( i, point ) ); } m_FigurePlaced = true; m_SelectedControlPoint = 1; } bool mitk::PlanarFigure::AddControlPoint( const mitk::Point2D& point, int position ) { // if we already have the maximum number of control points, do nothing if ( m_NumberOfControlPoints < this->GetMaximumNumberOfControlPoints() ) { // if position has not been defined or position would be the last control point, just append the new one // we also append a new point if we click onto the line between the first two control-points if the second control-point is selected // -> special case for PlanarCross if ( position == -1 || position > (int)m_NumberOfControlPoints-1 || (position == 1 && m_SelectedControlPoint == 2) ) { if ( m_ControlPoints.size() > this->GetMaximumNumberOfControlPoints()-1 ) { // get rid of deprecated control points in the list. This is necessary // as ::ResetNumberOfControlPoints() only sets the member, does not resize the list! m_ControlPoints.resize( this->GetNumberOfControlPoints() ); } m_ControlPoints.push_back( this->ApplyControlPointConstraints( m_NumberOfControlPoints, point ) ); m_SelectedControlPoint = m_NumberOfControlPoints; } else { // insert the point at the given position and set it as selected point ControlPointListType::iterator iter = m_ControlPoints.begin() + position; m_ControlPoints.insert( iter, this->ApplyControlPointConstraints( position, point ) ); for( unsigned int i = 0; i < m_ControlPoints.size(); ++i ) { if( point == m_ControlPoints.at(i) ) { m_SelectedControlPoint = i; } } } // polylines & helperpolylines need to be repainted m_PolyLineUpToDate = false; m_HelperLinesUpToDate = false; m_FeaturesUpToDate = false; // one control point more ++m_NumberOfControlPoints; return true; } else { return false; } } bool mitk::PlanarFigure::SetControlPoint( unsigned int index, const Point2D& point, bool createIfDoesNotExist ) { bool controlPointSetCorrectly = false; if (createIfDoesNotExist) { if ( m_NumberOfControlPoints <= index ) { m_ControlPoints.push_back( this->ApplyControlPointConstraints( index, point ) ); m_NumberOfControlPoints++; } else { m_ControlPoints.at( index ) = this->ApplyControlPointConstraints( index, point ); } controlPointSetCorrectly = true; } else if ( index < m_NumberOfControlPoints ) { m_ControlPoints.at( index ) = this->ApplyControlPointConstraints( index, point ); controlPointSetCorrectly = true; } else { return false; } if ( controlPointSetCorrectly ) { m_PolyLineUpToDate = false; m_HelperLinesUpToDate = false; m_FeaturesUpToDate = false; } return controlPointSetCorrectly; } bool mitk::PlanarFigure::SetCurrentControlPoint( const Point2D& point ) { if ( (m_SelectedControlPoint < 0) || (m_SelectedControlPoint >= (int)m_NumberOfControlPoints) ) { return false; } return this->SetControlPoint(m_SelectedControlPoint, point, false); } unsigned int mitk::PlanarFigure::GetNumberOfControlPoints() const { return m_NumberOfControlPoints; } bool mitk::PlanarFigure::SelectControlPoint( unsigned int index ) { if ( index < this->GetNumberOfControlPoints() ) { m_SelectedControlPoint = index; return true; } else { return false; } } bool mitk::PlanarFigure::DeselectControlPoint() { bool wasSelected = ( m_SelectedControlPoint != -1); m_SelectedControlPoint = -1; return wasSelected; } void mitk::PlanarFigure::SetPreviewControlPoint( const Point2D& point ) { m_PreviewControlPoint = point; m_PreviewControlPointVisible = true; } void mitk::PlanarFigure::ResetPreviewContolPoint() { m_PreviewControlPointVisible = false; } mitk::Point2D mitk::PlanarFigure::GetPreviewControlPoint() { return m_PreviewControlPoint; } bool mitk::PlanarFigure::IsPreviewControlPointVisible() { return m_PreviewControlPointVisible; } mitk::Point2D mitk::PlanarFigure::GetControlPoint( unsigned int index ) const { if ( index < m_NumberOfControlPoints ) { return m_ControlPoints.at( index ); } itkExceptionMacro( << "GetControlPoint(): Invalid index!" ); } mitk::Point3D mitk::PlanarFigure::GetWorldControlPoint( unsigned int index ) const { Point3D point3D; if ( (m_Geometry2D != NULL) && (index < m_NumberOfControlPoints) ) { m_Geometry2D->Map( m_ControlPoints.at( index ), point3D ); return point3D; } itkExceptionMacro( << "GetWorldControlPoint(): Invalid index!" ); } const mitk::PlanarFigure::PolyLineType mitk::PlanarFigure::GetPolyLine(unsigned int index) { mitk::PlanarFigure::PolyLineType polyLine; if ( index > m_PolyLines.size() || !m_PolyLineUpToDate ) { this->GeneratePolyLine(); m_PolyLineUpToDate = true; } return m_PolyLines.at( index );; } const mitk::PlanarFigure::PolyLineType mitk::PlanarFigure::GetPolyLine(unsigned int index) const { return m_PolyLines.at( index ); } void mitk::PlanarFigure::ClearPolyLines() { for ( std::vector::size_type i=0; iGenerateHelperPolyLine(mmPerDisplayUnit, displayHeight); m_HelperLinesUpToDate = true; // store these parameters to be able to check next time if somebody zoomed in or out m_DisplaySize.first = mmPerDisplayUnit; m_DisplaySize.second = displayHeight; } helperPolyLine = m_HelperPolyLines.at(index); } return helperPolyLine; } void mitk::PlanarFigure::ClearHelperPolyLines() { for ( std::vector::size_type i=0; iGeneratePolyLine(); } this->EvaluateFeaturesInternal(); m_FeaturesUpToDate = true; } } void mitk::PlanarFigure::UpdateOutputInformation() { - // Bounds are NOT calculated here, since the Geometry2D defines a fixed + // Bounds are NOT calculated here, since the PlaneGeometry defines a fixed // frame (= bounds) for the planar figure. Superclass::UpdateOutputInformation(); this->GetTimeGeometry()->Update(); } void mitk::PlanarFigure::SetRequestedRegionToLargestPossibleRegion() { } bool mitk::PlanarFigure::RequestedRegionIsOutsideOfTheBufferedRegion() { return false; } bool mitk::PlanarFigure::VerifyRequestedRegion() { return true; } void mitk::PlanarFigure::SetRequestedRegion(const itk::DataObject * /*data*/ ) { } void mitk::PlanarFigure::ResetNumberOfControlPoints( int numberOfControlPoints ) { // DO NOT resize the list here, will cause crash!! m_NumberOfControlPoints = numberOfControlPoints; } mitk::Point2D mitk::PlanarFigure::ApplyControlPointConstraints( unsigned int /*index*/, const Point2D& point ) { if ( m_Geometry2D == NULL ) { return point; } Point2D indexPoint; m_Geometry2D->WorldToIndex( point, indexPoint ); BoundingBox::BoundsArrayType bounds = m_Geometry2D->GetBounds(); if ( indexPoint[0] < bounds[0] ) { indexPoint[0] = bounds[0]; } if ( indexPoint[0] > bounds[1] ) { indexPoint[0] = bounds[1]; } if ( indexPoint[1] < bounds[2] ) { indexPoint[1] = bounds[2]; } if ( indexPoint[1] > bounds[3] ) { indexPoint[1] = bounds[3]; } Point2D constrainedPoint; m_Geometry2D->IndexToWorld( indexPoint, constrainedPoint ); return constrainedPoint; } unsigned int mitk::PlanarFigure::AddFeature( const char *featureName, const char *unitName ) { unsigned int index = m_Features.size(); Feature newFeature( featureName, unitName ); m_Features.push_back( newFeature ); return index; } void mitk::PlanarFigure::SetFeatureName( unsigned int index, const char *featureName ) { if ( index < m_Features.size() ) { m_Features[index].Name = featureName; } } void mitk::PlanarFigure::SetFeatureUnit( unsigned int index, const char *unitName ) { if ( index < m_Features.size() ) { m_Features[index].Unit = unitName; } } void mitk::PlanarFigure::SetQuantity( unsigned int index, double quantity ) { if ( index < m_Features.size() ) { m_Features[index].Quantity = quantity; } } void mitk::PlanarFigure::ActivateFeature( unsigned int index ) { if ( index < m_Features.size() ) { m_Features[index].Active = true; } } void mitk::PlanarFigure::DeactivateFeature( unsigned int index ) { if ( index < m_Features.size() ) { m_Features[index].Active = false; } } void mitk::PlanarFigure::InitializeTimeGeometry( unsigned int timeSteps ) { - mitk::Geometry2D::Pointer geometry2D = mitk::Geometry2D::New(); + mitk::PlaneGeometry::Pointer geometry2D = mitk::PlaneGeometry::New(); geometry2D->Initialize(); if ( timeSteps > 1 ) { mitk::ScalarType timeBounds[] = {0.0, 1.0}; geometry2D->SetTimeBounds( timeBounds ); } // The geometry is propagated automatically to all time steps, // if EvenlyTimed is true... ProportionalTimeGeometry::Pointer timeGeometry = ProportionalTimeGeometry::New(); timeGeometry->Initialize(geometry2D, timeSteps); SetTimeGeometry(timeGeometry); } void mitk::PlanarFigure::PrintSelf( std::ostream& os, itk::Indent indent) const { Superclass::PrintSelf( os, indent ); os << indent << this->GetNameOfClass() << ":\n"; if (this->IsClosed()) os << indent << "This figure is closed\n"; else os << indent << "This figure is not closed\n"; os << indent << "Minimum number of control points: " << this->GetMinimumNumberOfControlPoints() << std::endl; os << indent << "Maximum number of control points: " << this->GetMaximumNumberOfControlPoints() << std::endl; os << indent << "Current number of control points: " << this->GetNumberOfControlPoints() << std::endl; os << indent << "Control points:" << std::endl; for ( unsigned int i = 0; i < this->GetNumberOfControlPoints(); ++i ) { //os << indent.GetNextIndent() << i << ": " << m_ControlPoints->ElementAt( i ) << std::endl; os << indent.GetNextIndent() << i << ": " << m_ControlPoints.at( i ) << std::endl; } os << indent << "Geometry:\n"; this->GetGeometry2D()->Print(os, indent.GetNextIndent()); } unsigned short mitk::PlanarFigure::GetPolyLinesSize() { if ( !m_PolyLineUpToDate ) { this->GeneratePolyLine(); m_PolyLineUpToDate = true; } return m_PolyLines.size(); } unsigned short mitk::PlanarFigure::GetHelperPolyLinesSize() { return m_HelperPolyLines.size(); } bool mitk::PlanarFigure::IsHelperToBePainted(unsigned int index) { return m_HelperPolyLinesToBePainted->GetElement( index ); } bool mitk::PlanarFigure::ResetOnPointSelect() { return false; } void mitk::PlanarFigure::RemoveControlPoint( unsigned int index ) { if ( index > m_ControlPoints.size() ) return; if ( (m_ControlPoints.size() -1) < this->GetMinimumNumberOfControlPoints() ) return; ControlPointListType::iterator iter; iter = m_ControlPoints.begin() + index; m_ControlPoints.erase( iter ); m_PolyLineUpToDate = false; m_HelperLinesUpToDate = false; m_FeaturesUpToDate = false; --m_NumberOfControlPoints; } void mitk::PlanarFigure::RemoveLastControlPoint() { RemoveControlPoint( m_ControlPoints.size()-1 ); } void mitk::PlanarFigure::DeepCopy(Self::Pointer oldFigure) { //DeepCopy only same types of planar figures //Notice to get typeid polymorph you have to use the *operator if(typeid(*oldFigure) != typeid(*this)) { itkExceptionMacro( << "DeepCopy(): Inconsistent type of source (" << typeid(*oldFigure).name() << ") and destination figure (" << typeid(*this).name() << ")!" ); return; } m_ControlPoints.clear(); this->ClearPolyLines(); this->ClearHelperPolyLines(); // clone base data members SetPropertyList(oldFigure->GetPropertyList()->Clone()); /// deep copy members m_FigurePlaced = oldFigure->m_FigurePlaced; m_SelectedControlPoint = oldFigure->m_SelectedControlPoint; m_FeaturesMTime = oldFigure->m_FeaturesMTime; m_Features = oldFigure->m_Features; m_NumberOfControlPoints = oldFigure->m_NumberOfControlPoints; //copy geometry 2D of planar figure - Geometry2D::Pointer affineGeometry = oldFigure->m_Geometry2D->Clone(); + PlaneGeometry::Pointer affineGeometry = oldFigure->m_Geometry2D->Clone(); SetGeometry2D(affineGeometry.GetPointer()); for(unsigned long index=0; index < oldFigure->GetNumberOfControlPoints(); index++) { m_ControlPoints.push_back( oldFigure->GetControlPoint( index )); } //After setting the control points we can generate the polylines this->GeneratePolyLine(); } void mitk::PlanarFigure::SetNumberOfPolyLines( unsigned int numberOfPolyLines ) { m_PolyLines.resize(numberOfPolyLines); } void mitk::PlanarFigure::SetNumberOfHelperPolyLines( unsigned int numberOfHerlperPolyLines ) { m_HelperPolyLines.resize(numberOfHerlperPolyLines); } void mitk::PlanarFigure::AppendPointToPolyLine( unsigned int index, PolyLineElement element ) { if ( index < m_PolyLines.size() ) { m_PolyLines.at( index ).push_back( element ); m_PolyLineUpToDate = false; } else { MITK_ERROR << "Tried to add point to PolyLine " << index+1 << ", although only " << m_PolyLines.size() << " exists"; } } void mitk::PlanarFigure::AppendPointToHelperPolyLine( unsigned int index, PolyLineElement element ) { if ( index < m_HelperPolyLines.size() ) { m_HelperPolyLines.at( index ).push_back( element ); m_HelperLinesUpToDate = false; } else { MITK_ERROR << "Tried to add point to HelperPolyLine " << index+1 << ", although only " << m_HelperPolyLines.size() << " exists"; } } diff --git a/Modules/PlanarFigure/DataManagement/mitkPlanarFigure.h b/Modules/PlanarFigure/DataManagement/mitkPlanarFigure.h index 060eb1b900..e299cecac7 100644 --- a/Modules/PlanarFigure/DataManagement/mitkPlanarFigure.h +++ b/Modules/PlanarFigure/DataManagement/mitkPlanarFigure.h @@ -1,418 +1,418 @@ /*=================================================================== 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 _MITK_PLANAR_FIGURE_H_ #define _MITK_PLANAR_FIGURE_H_ #include "PlanarFigureExports.h" #include "mitkBaseData.h" #include "mitkCommon.h" #include namespace mitk { -class Geometry2D; +class PlaneGeometry; /** * \brief Base-class for geometric planar (2D) figures, such as * lines, circles, rectangles, polygons, etc. * * \warning Currently does not support time-resolved data handling * * Behavior and appearance of PlanarFigures are controlled by various properties; for a detailed * list of appearance properties see mitk::PlanarFigureMapper2D * * The following properties control general PlanarFigure behavior: * *
    *
  • "selected": true if the planar figure is selected *
  • "planarfigure.ishovering": true if the mouse "hovers" over the planar figure *
  • "planarfigure.iseditable": true if the planar figure can be edited (otherwise, * it can only be picked/selected, but its control points cannot be edited); default is true *
  • "planarfigure.isextendable": true if new control points can be inserted into the list of control points; * default is false *
* * * TODO: Implement local 2D transform (including center of rotation...) * */ class PlanarFigure_EXPORT PlanarFigure : public BaseData { public: mitkClassMacro( PlanarFigure, BaseData ) itkCloneMacro( Self ) struct PolyLineElement { PolyLineElement( Point2D point, int index ) : Point( point ), Index( index ) { }; Point2D Point; int Index; }; typedef itk::VectorContainer< unsigned long, bool> BoolContainerType; typedef std::deque< Point2D > ControlPointListType; typedef std::list< PolyLineElement > PolyLineType; /** \brief Sets the 2D geometry on which this figure will be placed. * * In most cases, this is a Geometry already owned by another object, e.g. * describing the slice of the image on which measurements will be * performed. */ - virtual void SetGeometry2D( mitk::Geometry2D *geometry ); + virtual void SetGeometry2D( mitk::PlaneGeometry *geometry ); /** \brief Returns (previously set) 2D geometry of this figure. */ - virtual const Geometry2D *GetGeometry2D() const; + virtual const PlaneGeometry *GetGeometry2D() const; /** \brief True if the planar figure is closed. * * Default is false. The "closed" boolean property must be set in sub-classes. */ virtual bool IsClosed() const; /** \brief True if the planar figure has been placed (and can be * displayed/interacted with). */ virtual bool IsPlaced() const { return m_FigurePlaced; }; /** \brief Place figure at the given point (in 2D index coordinates) onto * the given 2D geometry. * * By default, the first two control points of the figure are set to the * passed point. Further points can be set via AddControlPoint(), if the * current number of control points is below the maximum number of control * points. * * Can be re-implemented in sub-classes as needed. */ virtual void PlaceFigure( const Point2D& point ); /** * \brief Adds / inserts new control-points * * This method adds a new control-point with the coordinates defined by point at the given index. * If 'index' == -1 or index is greater than the number of control-points the new point is appended * to the back of the list of control points. * If a control-point already exists for 'index', an additional point is inserted at that position. * It is not possible to add more points if the maximum number of control-points (GetMaximumNumberOfControlPoints()) * has been reached. */ virtual bool AddControlPoint( const Point2D& point, int index = -1 ); virtual bool SetControlPoint( unsigned int index, const Point2D& point, bool createIfDoesNotExist = false); virtual bool SetCurrentControlPoint( const Point2D& point ); /** \brief Returns the current number of 2D control points defining this figure. */ unsigned int GetNumberOfControlPoints() const; /** \brief Returns the minimum number of control points needed to represent * this figure. * * Must be implemented in sub-classes. */ virtual unsigned int GetMinimumNumberOfControlPoints() const = 0; /** \brief Returns the maximum number of control points allowed for * this figure (e.g. 3 for triangles). * * Must be implemented in sub-classes. */ virtual unsigned int GetMaximumNumberOfControlPoints() const = 0; /** \brief Selects currently active control points. */ virtual bool SelectControlPoint( unsigned int index ); /** \brief Deselect control point; no control point active. */ virtual bool DeselectControlPoint(); /** \brief Return currently selected control point. */ virtual int GetSelectedControlPoint() const { return m_SelectedControlPoint; } /** \brief Returns specified control point in 2D world coordinates. */ Point2D GetControlPoint( unsigned int index ) const; /** \brief Returns specified control point in world coordinates. */ Point3D GetWorldControlPoint( unsigned int index ) const; /** \brief Returns the polyline representing the planar figure * (for rendering, measurements, etc.). */ const PolyLineType GetPolyLine(unsigned int index); /** \brief Returns the polyline representing the planar figure * (for rendering, measurments, etc.). */ const PolyLineType GetPolyLine(unsigned int index) const; /** \brief Returns the polyline that should be drawn the same size at every scale * (for text, angles, etc.). */ const PolyLineType GetHelperPolyLine( unsigned int index, double mmPerDisplayUnit, unsigned int displayHeight ); /** \brief Sets the position of the PreviewControlPoint. Automatically sets it visible.*/ void SetPreviewControlPoint( const Point2D& point ); /** \brief Marks the PreviewControlPoint as invisible.*/ void ResetPreviewContolPoint(); /** \brief Returns whether or not the PreviewControlPoint is visible.*/ bool IsPreviewControlPointVisible(); /** \brief Returns the coordinates of the PreviewControlPoint. */ Point2D GetPreviewControlPoint(); /** \brief Returns the number of features available for this PlanarFigure * (such as, radius, area, ...). */ virtual unsigned int GetNumberOfFeatures() const; /** \brief Returns the name (identifier) of the specified features. */ const char *GetFeatureName( unsigned int index ) const; /** \brief Returns the physical unit of the specified features. */ const char *GetFeatureUnit( unsigned int index ) const; /** Returns quantity of the specified feature (e.g., length, radius, * area, ... ) */ double GetQuantity( unsigned int index ) const; /** \brief Returns true if the feature with the specified index exists and * is active (an inactive feature may e.g. be the area of a non-closed * polygon. */ bool IsFeatureActive( unsigned int index ) const; /** \brief Returns true if the feature with the specified index exists and is set visible */ bool IsFeatureVisible( unsigned int index ) const; /** \brief Defines if the feature with the specified index will be shown as an * overlay in the RenderWindow */ void SetFeatureVisible( unsigned int index, bool visible ); /** \brief Calculates quantities of all features of this planar figure. */ virtual void EvaluateFeatures(); /** \brief Intherited from parent */ virtual void UpdateOutputInformation(); /** \brief Intherited from parent */ virtual void SetRequestedRegionToLargestPossibleRegion(); /** \brief Intherited from parent */ virtual bool RequestedRegionIsOutsideOfTheBufferedRegion(); /** \brief Intherited from parent */ virtual bool VerifyRequestedRegion(); /** \brief Intherited from parent */ virtual void SetRequestedRegion( const itk::DataObject *data); /** \brief Returns the current number of polylines */ virtual unsigned short GetPolyLinesSize(); /** \brief Returns the current number of helperpolylines */ virtual unsigned short GetHelperPolyLinesSize(); /** \brief Returns whether a helper polyline should be painted or not */ virtual bool IsHelperToBePainted(unsigned int index); /** \brief Returns true if the planar figure is reset to "add points" mode * when a point is selected. * * Default return value is false. Subclasses can overwrite this method and * execute any reset / initialization statements required. */ virtual bool ResetOnPointSelect(); /** \brief removes the point with the given index from the list of controlpoints. */ virtual void RemoveControlPoint( unsigned int index ); /** \brief Removes last control point */ virtual void RemoveLastControlPoint(); /** \brief Copies contents and state of a figre provided as parameter to the current object. * * Requires a matching type of both figures. * * \note Deprecated, use Clone() instead. */ DEPRECATED(void DeepCopy(Self::Pointer oldFigure)); /** \brief Allow sub-classes to apply constraints on control points. * * Sub-classes can define spatial constraints to certain control points by * overwriting this method and returning a constrained point. By default, * the points are constrained by the image bounds. */ virtual Point2D ApplyControlPointConstraints( unsigned int /*index*/, const Point2D& point ); protected: PlanarFigure(); virtual ~PlanarFigure(); PlanarFigure(const Self& other); /** \brief Set the initial number of control points of the planar figure */ void ResetNumberOfControlPoints( int numberOfControlPoints ); /** Adds feature (e.g., circumference, radius, angle, ...) to feature vector * of a planar figure object and returns integer ID for the feature element. * Should be called in sub-class constructors. */ virtual unsigned int AddFeature( const char *featureName, const char *unitName ); /** Sets the name of the specified feature. INTERNAL METHOD. */ void SetFeatureName( unsigned int index, const char *featureName ); /** Sets the physical unit of the specified feature. INTERNAL METHOD. */ void SetFeatureUnit( unsigned int index, const char *unitName ); /** Sets quantity of the specified feature. INTERNAL METHOD. */ void SetQuantity( unsigned int index, double quantity ); /** Sets the specified feature as active. INTERAL METHOD. */ void ActivateFeature( unsigned int index ); /** Sets the specified feature as active. INTERAL METHOD. */ void DeactivateFeature( unsigned int index ); /** \brief Generates the poly-line representation of the planar figure. * Must be implemented in sub-classes. */ virtual void GeneratePolyLine() = 0; /** \brief Generates the poly-lines that should be drawn the same size regardless of zoom. * Must be implemented in sub-classes. */ virtual void GenerateHelperPolyLine(double mmPerDisplayUnit, unsigned int displayHeight) = 0; /** \brief Calculates quantities of all features of this planar figure. * Must be implemented in sub-classes. */ virtual void EvaluateFeaturesInternal() = 0; /** \brief Initializes the TimeGeometry describing the (time-resolved) - * geometry of this figure. Note that each time step holds one Geometry2D. + * geometry of this figure. Note that each time step holds one PlaneGeometry. */ virtual void InitializeTimeGeometry( unsigned int timeSteps = 1 ); /** \brief defines the number of PolyLines that will be available */ void SetNumberOfPolyLines( unsigned int numberOfPolyLines ); /** \brief Append a point to the PolyLine # index */ void AppendPointToPolyLine( unsigned int index, PolyLineElement element ); /** \brief clears the list of PolyLines. Call before re-calculating a new Polyline. */ void ClearPolyLines(); /** \brief defines the number of HelperPolyLines that will be available */ void SetNumberOfHelperPolyLines( unsigned int numberOfHelperPolyLines ); /** \brief Append a point to the HelperPolyLine # index */ void AppendPointToHelperPolyLine( unsigned int index, PolyLineElement element ); /** \brief clears the list of HelperPolyLines. Call before re-calculating a new HelperPolyline. */ void ClearHelperPolyLines(); virtual void PrintSelf( std::ostream& os, itk::Indent indent ) const; ControlPointListType m_ControlPoints; unsigned int m_NumberOfControlPoints; // Currently selected control point; -1 means no point selected int m_SelectedControlPoint; std::vector m_PolyLines; std::vector m_HelperPolyLines; BoolContainerType::Pointer m_HelperPolyLinesToBePainted; // this point is used to store the coordiantes an additional 'ControlPoint' that is rendered // when the mouse cursor is above the figure (and not a control-point) and when the // property 'planarfigure.isextendable' is set to true Point2D m_PreviewControlPoint; bool m_PreviewControlPointVisible; bool m_FigurePlaced; private: // not implemented to prevent PlanarFigure::New() calls which would create an itk::Object. static Pointer New(); struct Feature { Feature( const char *name, const char *unit ) : Name( name ), Unit( unit ), Quantity( 0.0 ), Active( true ), Visible( true ) { } std::string Name; std::string Unit; double Quantity; bool Active; bool Visible; }; virtual itk::LightObject::Pointer InternalClone() const = 0; - Geometry2D *m_Geometry2D; + PlaneGeometry *m_Geometry2D; bool m_PolyLineUpToDate; bool m_HelperLinesUpToDate; bool m_FeaturesUpToDate; // Vector of features available for this geometric figure typedef std::vector< Feature > FeatureVectorType; FeatureVectorType m_Features; unsigned long m_FeaturesMTime; // this pair is used to store the mmInDisplayUnits (m_DisplaySize.first) and the displayHeight (m_DisplaySize.second) // that the helperPolyLines have been calculated for. // It's used to determine whether or not GetHelperPolyLine() needs to recalculate the HelperPolyLines. std::pair m_DisplaySize; }; } // namespace mitk #endif //_MITK_PLANAR_FIGURE_H_ diff --git a/Modules/PlanarFigure/DataManagement/mitkPlanarFourPointAngle.cpp b/Modules/PlanarFigure/DataManagement/mitkPlanarFourPointAngle.cpp index fb7ec96ab7..7d9f79e2d9 100644 --- a/Modules/PlanarFigure/DataManagement/mitkPlanarFourPointAngle.cpp +++ b/Modules/PlanarFigure/DataManagement/mitkPlanarFourPointAngle.cpp @@ -1,84 +1,84 @@ /*=================================================================== 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 "mitkPlanarFourPointAngle.h" -#include "mitkGeometry2D.h" +#include "mitkPlaneGeometry.h" mitk::PlanarFourPointAngle::PlanarFourPointAngle() : FEATURE_ID_ANGLE( this->AddFeature( "Angle", "deg" ) ) { // Four point angle has two control points this->ResetNumberOfControlPoints( 2 ); this->SetNumberOfPolyLines( 2 ); } mitk::PlanarFourPointAngle::~PlanarFourPointAngle() { } void mitk::PlanarFourPointAngle::GeneratePolyLine() { this->ClearPolyLines(); // TODO: start line at specified start point... // Generate poly-line for ( unsigned int i = 0; i < this->GetNumberOfControlPoints(); ++i ) { int index = i/2; this->AppendPointToPolyLine( index, PolyLineElement( GetControlPoint( i ), i ) ); } } void mitk::PlanarFourPointAngle::GenerateHelperPolyLine(double /*mmPerDisplayUnit*/, unsigned int /*displayHeight*/) { // Generate helper-poly-line for an four point angle // Need to discuss a sensible implementation } void mitk::PlanarFourPointAngle::EvaluateFeaturesInternal() { if ( this->GetNumberOfControlPoints() < 4 ) { // Angle not yet complete. return; } // Calculate angle between lines const Point2D &p0 = this->GetControlPoint( 0 ); const Point2D &p1 = this->GetControlPoint( 1 ); const Point2D &p2 = this->GetControlPoint( 2 ); const Point2D &p3 = this->GetControlPoint( 3 ); Vector2D v0 = p1 - p0; Vector2D v1 = p3 - p2; v0.Normalize(); v1.Normalize(); double angle = acos( v0 * v1 ); this->SetQuantity( FEATURE_ID_ANGLE, angle ); } void mitk::PlanarFourPointAngle::PrintSelf( std::ostream& os, itk::Indent indent) const { Superclass::PrintSelf( os, indent ); } diff --git a/Modules/PlanarFigure/DataManagement/mitkPlanarFourPointAngle.h b/Modules/PlanarFigure/DataManagement/mitkPlanarFourPointAngle.h index 46185ee172..2391872e70 100644 --- a/Modules/PlanarFigure/DataManagement/mitkPlanarFourPointAngle.h +++ b/Modules/PlanarFigure/DataManagement/mitkPlanarFourPointAngle.h @@ -1,92 +1,92 @@ /*=================================================================== 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 _MITK_PLANAR_FOURPOINTANGLE_H_ #define _MITK_PLANAR_FOURPOINTANGLE_H_ #include "mitkPlanarFigure.h" #include "PlanarFigureExports.h" namespace mitk { -class Geometry2D; +class PlaneGeometry; /** * \brief Implementation of PlanarFigure representing a four point * angle, which is defined by two non-intersecting lines in 2D. Each of those lines * is defined by two control points. */ class PlanarFigure_EXPORT PlanarFourPointAngle : public PlanarFigure { public: mitkClassMacro( PlanarFourPointAngle, PlanarFigure ); itkNewMacro( Self ); public: // Feature identifiers const unsigned int FEATURE_ID_ANGLE; /** \brief Place figure in its minimal configuration (a point at least) * onto the given 2D geometry. * * Must be implemented in sub-classes. */ //virtual void Initialize(); /** \brief Four point angle has 4 control points per definition. */ unsigned int GetMinimumNumberOfControlPoints() const { return 4; } /** \brief Four point angle has 4 control points per definition. */ unsigned int GetMaximumNumberOfControlPoints() const { return 4; } protected: PlanarFourPointAngle(); virtual ~PlanarFourPointAngle(); mitkCloneMacro(Self); /** \brief Generates the poly-line representation of the planar figure. */ virtual void GeneratePolyLine(); /** \brief Generates the poly-lines that should be drawn the same size regardless of zoom.*/ virtual void GenerateHelperPolyLine(double mmPerDisplayUnit, unsigned int displayHeight); /** \brief Calculates feature quantities of the planar figure. */ virtual void EvaluateFeaturesInternal(); virtual void PrintSelf( std::ostream &os, itk::Indent indent ) const; private: }; } // namespace mitk #endif //_MITK_PLANAR_FOURPOINTANGLE_H_ diff --git a/Modules/PlanarFigure/DataManagement/mitkPlanarLine.cpp b/Modules/PlanarFigure/DataManagement/mitkPlanarLine.cpp index 288c98643c..ffca005ea5 100644 --- a/Modules/PlanarFigure/DataManagement/mitkPlanarLine.cpp +++ b/Modules/PlanarFigure/DataManagement/mitkPlanarLine.cpp @@ -1,66 +1,66 @@ /*=================================================================== 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 "mitkPlanarLine.h" -#include "mitkGeometry2D.h" +#include "mitkPlaneGeometry.h" mitk::PlanarLine::PlanarLine() : FEATURE_ID_LENGTH( this->AddFeature( "Length", "mm" ) ) { // Line has two control points this->ResetNumberOfControlPoints( 2 ); this->SetNumberOfPolyLines( 1 ); } mitk::PlanarLine::~PlanarLine() { } void mitk::PlanarLine::GeneratePolyLine() { this->ClearPolyLines(); // TODO: start line at specified start point... // Generate poly-line this->AppendPointToPolyLine( 0 , mitk::PlanarFigure::PolyLineElement( this->GetControlPoint(0), 0) ); this->AppendPointToPolyLine( 0 , mitk::PlanarFigure::PolyLineElement( this->GetControlPoint(1), 0) ); } void mitk::PlanarLine::GenerateHelperPolyLine(double /*mmPerDisplayUnit*/, unsigned int /*displayHeight*/) { // A line does not require a helper object } void mitk::PlanarLine::EvaluateFeaturesInternal() { // Calculate line length const Point3D &p0 = this->GetWorldControlPoint( 0 ); const Point3D &p1 = this->GetWorldControlPoint( 1 ); double length = p0.EuclideanDistanceTo( p1 ); this->SetQuantity( FEATURE_ID_LENGTH, length ); } void mitk::PlanarLine::PrintSelf( std::ostream& os, itk::Indent indent) const { Superclass::PrintSelf( os, indent ); } diff --git a/Modules/PlanarFigure/DataManagement/mitkPlanarLine.h b/Modules/PlanarFigure/DataManagement/mitkPlanarLine.h index ac1a104993..4b6ff2cb5f 100644 --- a/Modules/PlanarFigure/DataManagement/mitkPlanarLine.h +++ b/Modules/PlanarFigure/DataManagement/mitkPlanarLine.h @@ -1,92 +1,92 @@ /*=================================================================== 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 _MITK_PLANAR_LINE_H_ #define _MITK_PLANAR_LINE_H_ #include "mitkPlanarFigure.h" #include "PlanarFigureExports.h" namespace mitk { -class Geometry2D; +class PlaneGeometry; /** * \brief Implementation of PlanarFigure representing a line * through two control points */ class PlanarFigure_EXPORT PlanarLine : public PlanarFigure { public: mitkClassMacro( PlanarLine, PlanarFigure ); itkNewMacro( Self ); /** \brief Place figure in its minimal configuration (a point at least) * onto the given 2D geometry. * * Must be implemented in sub-classes. */ //virtual void Initialize(); /** \brief Line has 2 control points per definition. */ unsigned int GetMinimumNumberOfControlPoints() const { return 2; } /** \brief Line has 2 control points per definition. */ unsigned int GetMaximumNumberOfControlPoints() const { return 2; } protected: PlanarLine(); virtual ~PlanarLine(); mitkCloneMacro(Self); /** \brief Generates the poly-line representation of the planar figure. */ virtual void GeneratePolyLine(); /** \brief Generates the poly-lines that should be drawn the same size regardless of zoom.*/ virtual void GenerateHelperPolyLine(double mmPerDisplayUnit, unsigned int displayHeight); /** \brief Calculates feature quantities of the planar figure. */ virtual void EvaluateFeaturesInternal(); virtual void PrintSelf( std::ostream &os, itk::Indent indent ) const; // Feature identifiers const unsigned int FEATURE_ID_LENGTH; private: }; } // namespace mitk #endif //_MITK_PLANAR_LINE_H_ diff --git a/Modules/PlanarFigure/DataManagement/mitkPlanarPolygon.cpp b/Modules/PlanarFigure/DataManagement/mitkPlanarPolygon.cpp index 5ac118bd61..0c5719b211 100644 --- a/Modules/PlanarFigure/DataManagement/mitkPlanarPolygon.cpp +++ b/Modules/PlanarFigure/DataManagement/mitkPlanarPolygon.cpp @@ -1,284 +1,284 @@ /*=================================================================== 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 "mitkPlanarPolygon.h" -#include "mitkGeometry2D.h" +#include "mitkPlaneGeometry.h" #include "mitkProperties.h" // stl related includes #include mitk::PlanarPolygon::PlanarPolygon() : FEATURE_ID_CIRCUMFERENCE( this->AddFeature( "Circumference", "mm" ) ), FEATURE_ID_AREA( this->AddFeature( "Area", "mm2" ) ) { // Polygon has at least two control points this->ResetNumberOfControlPoints( 2 ); this->SetNumberOfPolyLines( 1 ); // Polygon is closed by default this->SetProperty( "closed", mitk::BoolProperty::New( true ) ); this->SetProperty( "subdivision", mitk::BoolProperty::New( false ) ); } mitk::PlanarPolygon::~PlanarPolygon() { } void mitk::PlanarPolygon::SetClosed( bool closed ) { this->SetProperty( "closed", mitk::BoolProperty::New( closed ) ); if ( !closed ) { // For non-closed polygons: use "Length" as feature name; disable area this->SetFeatureName( FEATURE_ID_CIRCUMFERENCE, "Length" ); this->DeactivateFeature( FEATURE_ID_AREA ); } else { // For closed polygons: use "Circumference" as feature name; enable area this->SetFeatureName( FEATURE_ID_CIRCUMFERENCE, "Circumference" ); this->ActivateFeature( FEATURE_ID_AREA ); } this->Modified(); } void mitk::PlanarPolygon::GeneratePolyLine() { this->ClearPolyLines(); for ( ControlPointListType::size_type i=0; iAppendPointToPolyLine( 0, elem ); } } void mitk::PlanarPolygon::GenerateHelperPolyLine(double /*mmPerDisplayUnit*/, unsigned int /*displayHeight*/) { // A polygon does not require helper objects } void mitk::PlanarPolygon::EvaluateFeaturesInternal() { // Calculate circumference double circumference = 0.0; unsigned int i,j; ControlPointListType polyLinePoints; polyLinePoints.clear(); PolyLineType::iterator iter; for( iter = m_PolyLines[0].begin(); iter != m_PolyLines[0].end(); ++iter ) { polyLinePoints.push_back((*iter).Point); } if(polyLinePoints.empty()) return; for ( i = 0; i <(polyLinePoints.size()-1); ++i ) { circumference += polyLinePoints[i].EuclideanDistanceTo( polyLinePoints[i + 1] ); } if ( this->IsClosed() ) { circumference += polyLinePoints[i].EuclideanDistanceTo( polyLinePoints.front() ); } this->SetQuantity( FEATURE_ID_CIRCUMFERENCE, circumference ); // Calculate polygon area (if closed) double area = 0.0; bool intersection = false; if ( this->IsClosed() && (this->GetGeometry2D() != NULL) ) { // does PlanarPolygon overlap/intersect itself? unsigned int numberOfPoints = polyLinePoints.size(); if( numberOfPoints >= 4) { for ( i = 0; i < (numberOfPoints - 1); ++i ) { // line 1 Point2D p0 = polyLinePoints[i]; Point2D p1 = polyLinePoints[i + 1]; // check for intersection with all other lines for (j = i+1; j < (numberOfPoints - 1); ++j ) { Point2D p2 = polyLinePoints[j]; Point2D p3 = polyLinePoints[j + 1]; intersection = CheckForLineIntersection(p0,p1,p2,p3); if (intersection) break; } if (intersection) break; // only because the inner loop might have changed "intersection" // last line from p_x to p_0 Point2D p2 = polyLinePoints.front(); Point2D p3 = polyLinePoints.back(); intersection = CheckForLineIntersection(p0,p1,p2,p3); if (intersection) break; } } // calculate area for ( i = 0; i < polyLinePoints.size(); ++i ) { Point2D p0 = polyLinePoints[i]; Point2D p1 = polyLinePoints[ (i + 1) % polyLinePoints.size() ]; area += p0[0] * p1[1] - p1[0] * p0[1]; } area /= 2.0; } // set area if appropiate (i.e. closed and not intersected) if(this->IsClosed() && !intersection) { SetQuantity( FEATURE_ID_AREA, fabs( area ) ); this->ActivateFeature( FEATURE_ID_AREA ); } else { SetQuantity( FEATURE_ID_AREA, 0 ); this->DeactivateFeature( FEATURE_ID_AREA ); } } void mitk::PlanarPolygon::PrintSelf( std::ostream& os, itk::Indent indent) const { Superclass::PrintSelf( os, indent ); if (this->IsClosed()) os << indent << "Polygon is closed\n"; else os << indent << "Polygon is not closed\n"; } // based on // http://flassari.is/2008/11/line-line-intersection-in-cplusplus/ bool mitk::PlanarPolygon::CheckForLineIntersection( const mitk::Point2D& p1, const mitk::Point2D& p2, const mitk::Point2D& p3, const mitk::Point2D& p4, Point2D& intersection ) const { // do not check for intersections with control points if(p1 == p2 || p1 == p3 || p1 == p4 || p2 == p3 || p2 == p4 || p3 == p4) return false; // Store the values for fast access and easy // equations-to-code conversion double x1 = p1[0], x2 = p2[0], x3 = p3[0], x4 = p4[0]; double y1 = p1[1], y2 = p2[1], y3 = p3[1], y4 = p4[1]; double d = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4); // If d is zero, there is no intersection //if (d < mitk::eps) return false; if (d == 0) return false; // Get the x and y double pre = (x1*y2 - y1*x2); double post = (x3*y4 - y3*x4); double x = ( pre * (x3 - x4) - (x1 - x2) * post ) / d; double y = ( pre * (y3 - y4) - (y1 - y2) * post ) / d; double tolerance = 0.001; // Check if the x coordinates are within both lines, including tolerance if ( x < ( std::min(x1, x2) - tolerance ) || x > ( std::max(x1, x2) + tolerance ) || x < ( std::min(x3, x4) - tolerance ) || x > ( std::max(x3, x4) + tolerance ) ) { return false; } // Check if the y coordinates are within both lines, including tolerance if ( y < ( std::min(y1, y2) - tolerance ) || y > ( std::max(y1, y2) + tolerance ) || y < ( std::min(y3, y4) - tolerance ) || y > ( std::max(y3, y4) + tolerance ) ) { return false; } // point of intersection Point2D ret; ret[0] = x; ret[1] = y; intersection = ret; return true; } bool mitk::PlanarPolygon::CheckForLineIntersection( const mitk::Point2D& p1, const mitk::Point2D& p2, const mitk::Point2D& p3, const mitk::Point2D& p4 ) const { mitk::Point2D intersection; return mitk::PlanarPolygon::CheckForLineIntersection( p1, p2, p3, p4, intersection ); } std::vector mitk::PlanarPolygon::CheckForLineIntersection( const mitk::Point2D& p1, const mitk::Point2D& p2 ) const { std::vector intersectionList; ControlPointListType polyLinePoints; PolyLineType tempList = m_PolyLines[0]; PolyLineType::iterator iter; for( iter = tempList.begin(); iter != tempList.end(); ++iter ) { polyLinePoints.push_back((*iter).Point); } for ( ControlPointListType::size_type i=0; iIsClosed() ) { mitk::Point2D intersection, lastControlPoint, firstControlPoint; lastControlPoint = polyLinePoints.back(); firstControlPoint = polyLinePoints.front(); if ( mitk::PlanarPolygon::CheckForLineIntersection( lastControlPoint, firstControlPoint, p1, p2, intersection ) ) { intersectionList.push_back( intersection ); } } return intersectionList; } diff --git a/Modules/PlanarFigure/DataManagement/mitkPlanarPolygon.h b/Modules/PlanarFigure/DataManagement/mitkPlanarPolygon.h index be4d2f7773..2d1b46b878 100644 --- a/Modules/PlanarFigure/DataManagement/mitkPlanarPolygon.h +++ b/Modules/PlanarFigure/DataManagement/mitkPlanarPolygon.h @@ -1,102 +1,102 @@ /*=================================================================== 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 _MITK_PLANAR_POLYGON_H_ #define _MITK_PLANAR_POLYGON_H_ #include "mitkPlanarFigure.h" #include "PlanarFigureExports.h" namespace mitk { -class Geometry2D; +class PlaneGeometry; /** * \brief Implementation of PlanarFigure representing a polygon * with two or more control points */ class PlanarFigure_EXPORT PlanarPolygon : public PlanarFigure { public: mitkClassMacro( PlanarPolygon, PlanarFigure ); itkNewMacro( Self ); /** \brief Set whether the polygon should be closed between first and last control point or not. */ virtual void SetClosed( bool closed ); itkBooleanMacro( Closed ); // Calls SetClosed(); no need to re-implement /** \brief Place figure in its minimal configuration (a point at least) * onto the given 2D geometry. * * Must be implemented in sub-classes. */ //virtual void Initialize(); /** \brief Polygon has 3 control points per definition. */ unsigned int GetMinimumNumberOfControlPoints() const { return 3; } /** \brief Polygon maximum number of control points is principally not limited. */ unsigned int GetMaximumNumberOfControlPoints() const { return 1000; } std::vector CheckForLineIntersection( const Point2D& p1, const Point2D& p2 ) const; protected: PlanarPolygon(); virtual ~PlanarPolygon(); mitkCloneMacro(Self); /** \brief Generates the poly-line representation of the planar figure. */ virtual void GeneratePolyLine(); /** \brief Generates the poly-lines that should be drawn the same size regardless of zoom.*/ virtual void GenerateHelperPolyLine(double mmPerDisplayUnit, unsigned int displayHeight); /** \brief Calculates feature quantities of the planar figure. */ virtual void EvaluateFeaturesInternal(); bool CheckForLineIntersection(const mitk::Point2D& p1, const mitk::Point2D& p2, const mitk::Point2D& p3, const mitk::Point2D& p4, Point2D& intersection) const ; bool CheckForLineIntersection( const mitk::Point2D& p1, const mitk::Point2D& p2, const mitk::Point2D& p3, const mitk::Point2D& p4 ) const; virtual void PrintSelf( std::ostream &os, itk::Indent indent ) const; const unsigned int FEATURE_ID_CIRCUMFERENCE; const unsigned int FEATURE_ID_AREA; private: }; } // namespace mitk #endif //_MITK_PLANAR_POLYGON_H_ diff --git a/Modules/PlanarFigure/DataManagement/mitkPlanarRectangle.cpp b/Modules/PlanarFigure/DataManagement/mitkPlanarRectangle.cpp index 6b6b7e8611..7bd8c378e2 100644 --- a/Modules/PlanarFigure/DataManagement/mitkPlanarRectangle.cpp +++ b/Modules/PlanarFigure/DataManagement/mitkPlanarRectangle.cpp @@ -1,150 +1,150 @@ /*=================================================================== 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 "mitkProperties.h" #include "mitkPlanarRectangle.h" -#include "mitkGeometry2D.h" +#include "mitkPlaneGeometry.h" mitk::PlanarRectangle::PlanarRectangle() : FEATURE_ID_CIRCUMFERENCE( this->AddFeature( "Circumference", "mm" ) ), FEATURE_ID_AREA( this->AddFeature( "Area", "mm2" ) ) { // Rectangle has four control points this->ResetNumberOfControlPoints( 4 ); this->SetProperty( "closed", mitk::BoolProperty::New(true) ); this->SetNumberOfPolyLines( 1 ); } mitk::PlanarRectangle::~PlanarRectangle() { } bool mitk::PlanarRectangle::SetControlPoint( unsigned int index, const Point2D &point, bool createIfDoesNotExist ) { // heres the deal with the rectangle: // when a point is moved all corresponding corner points are moved with him // e.g. if the lower right point (index=3) is moved the upper right point (index=1) // is moved in the same x direction // and the lower left point (index=2) is moved in the same y direction // the upper left point (index=0) is left untouched bool set = PlanarFigure::SetControlPoint( index, point, createIfDoesNotExist ); if(set) { // can be made better ... unsigned int horizontalCorrespondingPointIndex = 1; unsigned int verticalCorrespondingPointIndex = 3; if(index == 1) { horizontalCorrespondingPointIndex = 0; verticalCorrespondingPointIndex = 2; } else if(index == 2) { horizontalCorrespondingPointIndex = 3; verticalCorrespondingPointIndex = 1; } else if(index == 3) { horizontalCorrespondingPointIndex = 2; verticalCorrespondingPointIndex = 0; } Point2D verticalCorrespondingPoint = GetControlPoint( verticalCorrespondingPointIndex ); verticalCorrespondingPoint[0] = point[0]; PlanarFigure::SetControlPoint( verticalCorrespondingPointIndex, verticalCorrespondingPoint ); Point2D horizontalCorrespondingPoint = GetControlPoint( horizontalCorrespondingPointIndex ); horizontalCorrespondingPoint[1] = point[1]; PlanarFigure::SetControlPoint( horizontalCorrespondingPointIndex, horizontalCorrespondingPoint ); } return set; } void mitk::PlanarRectangle::PlaceFigure( const mitk::Point2D &point ) { PlanarFigure::PlaceFigure( point ); m_SelectedControlPoint = 3; } void mitk::PlanarRectangle::GeneratePolyLine() { // TODO: start polygon at specified initalize point... ClearPolyLines(); for ( unsigned int i = 0; i < this->GetNumberOfControlPoints(); ++i ) { AppendPointToPolyLine( 0, PolyLineElement( GetControlPoint(i), i ) ); } } void mitk::PlanarRectangle::GenerateHelperPolyLine(double /*mmPerDisplayUnit*/, unsigned int /*displayHeight*/) { // A polygon does not require helper objects } void mitk::PlanarRectangle::EvaluateFeaturesInternal() { // Calculate circumference double circumference = 0.0; unsigned int i; for ( i = 0; i < this->GetNumberOfControlPoints(); ++i ) { circumference += this->GetWorldControlPoint( i ).EuclideanDistanceTo( this->GetWorldControlPoint( (i + 1) % this->GetNumberOfControlPoints() ) ); } this->SetQuantity( FEATURE_ID_CIRCUMFERENCE, circumference ); // Calculate rectangle area (well, done a bit clumsy...) double area = 0.0; if ( this->GetGeometry2D() != NULL ) { for ( i = 0; i < this->GetNumberOfControlPoints(); ++i ) { Point2D p0 = this->GetControlPoint( i ); Point2D p1 = this->GetControlPoint( (i + 1) % this->GetNumberOfControlPoints() ); area += p0[0] * p1[1] - p1[0] * p0[1]; } area /= 2.0; } this->SetQuantity( FEATURE_ID_AREA, fabs(area) ); } void mitk::PlanarRectangle::PrintSelf( std::ostream& os, itk::Indent indent) const { Superclass::PrintSelf( os, indent ); os << indent << "Number of control points: " << this->GetNumberOfControlPoints() << std::endl; os << indent << "Control points:" << std::endl; for ( unsigned int i = 0; i < this->GetNumberOfControlPoints(); ++i ) { os << indent << indent << i << ": " < mitk::PlanarSubdivisionPolygon::PlanarSubdivisionPolygon(): m_TensionParameter(0.0625), m_SubdivisionRounds(5) { // Polygon is subdivision (in contrast to parent class PlanarPolygon this->SetProperty( "closed", mitk::BoolProperty::New( true ) ); this->SetProperty( "subdivision", mitk::BoolProperty::New( true ) ); // Other properties are inherited / already initialized by parent class PlanarPolygon } mitk::PlanarSubdivisionPolygon::~PlanarSubdivisionPolygon() { } void mitk::PlanarSubdivisionPolygon::GeneratePolyLine() { this->ClearPolyLines(); ControlPointListType subdivisionPoints; ControlPointListType newSubdivisionPoints; subdivisionPoints.clear(); subdivisionPoints = m_ControlPoints; if( m_ControlPoints.size() >= GetMinimumNumberOfControlPoints() ) { for( unsigned int i=0; i < GetSubdivisionRounds(); i++ ) { // Indices unsigned int index, indexPrev, indexNext, indexNextNext; unsigned int numberOfPoints = subdivisionPoints.size(); Point2D newPoint; // Keep cycling our array indices forward until they wrap around at the end for ( index = 0; index < numberOfPoints; ++index ) { // Create new subdivision point according to formula // p_new = (0.5 + tension) * (p_here + p_next) - tension * (p_prev + p_nextnext) indexPrev = (numberOfPoints + index - 1) % numberOfPoints; indexNext = (index + 1) % numberOfPoints; indexNextNext = (index + 2) % numberOfPoints; newPoint[0] = (0.5 + GetTensionParameter()) * (double)( subdivisionPoints[index][0] + subdivisionPoints[indexNext][0] ) - GetTensionParameter() * (double)( subdivisionPoints[indexPrev][0] + subdivisionPoints[indexNextNext][0]); newPoint[1] = (0.5 + GetTensionParameter()) * (double)( subdivisionPoints[index][1] + subdivisionPoints[indexNext][1] ) - GetTensionParameter() * (double)( subdivisionPoints[indexPrev][1] + subdivisionPoints[indexNextNext][1]); newSubdivisionPoints.push_back( newPoint ); } ControlPointListType mergedSubdivisionPoints; ControlPointListType::iterator it, itNew; for ( it = subdivisionPoints.begin() , itNew = newSubdivisionPoints.begin(); it != subdivisionPoints.end(); ++it, ++itNew ) { mergedSubdivisionPoints.push_back( *it ); mergedSubdivisionPoints.push_back( *itNew ); } subdivisionPoints = mergedSubdivisionPoints; newSubdivisionPoints.clear(); } } bool isInitiallyPlaced = this->GetProperty("initiallyplaced"); unsigned int i; ControlPointListType::iterator it; for ( it = subdivisionPoints.begin(), i = 0; it != subdivisionPoints.end(); ++it, ++i ) { // Determine the index of the control point FOLLOWING this poly-line element // (this is needed by PlanarFigureInteractor to insert new points at the correct position, // namely BEFORE the next control point) unsigned int nextIndex; if ( i == 0 ) { // For the FIRST polyline point, use the index of the LAST control point // (it will used to check if the mouse is near the very last polyline element) nextIndex = m_ControlPoints.size() - 1; } else { // For all other polyline points, use the index of the control point succeeding it // (for polyline points lying on control points, the index of the previous control point // is used) nextIndex = (((i - 1) >> this->GetSubdivisionRounds()) + 1) % m_ControlPoints.size(); if(!isInitiallyPlaced && nextIndex > m_ControlPoints.size()-2) { PolyLineElement elem( m_ControlPoints[m_ControlPoints.size()-1], nextIndex ); this->AppendPointToPolyLine( 0, elem ); break; } } PolyLineElement elem( *it, nextIndex ); this->AppendPointToPolyLine( 0, elem ); } subdivisionPoints.clear(); } diff --git a/Modules/PlanarFigure/DataManagement/mitkPlanarSubdivisionPolygon.h b/Modules/PlanarFigure/DataManagement/mitkPlanarSubdivisionPolygon.h index 9e8e756544..58daa6c7e1 100644 --- a/Modules/PlanarFigure/DataManagement/mitkPlanarSubdivisionPolygon.h +++ b/Modules/PlanarFigure/DataManagement/mitkPlanarSubdivisionPolygon.h @@ -1,106 +1,106 @@ /*=================================================================== 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 _MITK_PLANAR_SUBDIVISION_POLYGON_H_ #define _MITK_PLANAR_SUBDIVISION_POLYGON_H_ #include "mitkPlanarFigure.h" #include "PlanarFigureExports.h" #include "mitkPlanarPolygon.h" namespace mitk { -class Geometry2D; +class PlaneGeometry; /** * \brief Implementation of PlanarFigure representing a polygon * with two or more control points */ class PlanarFigure_EXPORT PlanarSubdivisionPolygon : public PlanarPolygon { public: mitkClassMacro( PlanarSubdivisionPolygon, PlanarFigure ); itkNewMacro( Self ); /** \brief Subdivision Polygon has 3 control points per definition. */ unsigned int GetMinimumNumberOfControlPoints() const { return 3; } /** \brief Polygon maximum number of control points is principally not limited. */ unsigned int GetMaximumNumberOfControlPoints() const { return 1000; } /** \brief How many times should we generate a round of subdivisions? */ unsigned int GetSubdivisionRounds() const { return m_SubdivisionRounds; } void SetSubdivisionRounds( int subdivisionRounds ) { m_SubdivisionRounds = subdivisionRounds; } /** \brief Parameter w_tension defines the tension. * the higher w_tension, the lower the "tension" on points. * Rule: 0 < w_tension < 0.1 * 0.0625 (1 / 16) seems to be a good value. */ float GetTensionParameter() const { return m_TensionParameter; } void SetTensionParameter(float tensionParameter ) { m_TensionParameter = tensionParameter; } std::vector CheckForLineIntersection( const Point2D& p1, const Point2D& p2 ) const; void IncreaseSubdivisions(); void DecreaseSubdivisions(); protected: PlanarSubdivisionPolygon(); virtual ~PlanarSubdivisionPolygon(); mitkCloneMacro(Self); /** \brief Generates the poly-line representation of the planar figure. */ virtual void GeneratePolyLine(); float m_TensionParameter; int m_SubdivisionRounds; private: }; } // namespace mitk #endif //_MITK_PLANAR_SUBDIVISION_POLYGON_H_ diff --git a/Modules/PlanarFigure/Interactions/mitkPlanarFigureInteractor.cpp b/Modules/PlanarFigure/Interactions/mitkPlanarFigureInteractor.cpp index 7d07b8aefa..197613f721 100644 --- a/Modules/PlanarFigure/Interactions/mitkPlanarFigureInteractor.cpp +++ b/Modules/PlanarFigure/Interactions/mitkPlanarFigureInteractor.cpp @@ -1,947 +1,947 @@ /*=================================================================== 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. ===================================================================*/ #define PLANARFIGUREINTERACTOR_DBG MITK_DEBUG("PlanarFigureInteractor") << __LINE__ << ": " #include "mitkPlanarFigureInteractor.h" #include "mitkPlanarFigure.h" #include "mitkPlanarPolygon.h" #include "mitkInteractionPositionEvent.h" #include "mitkInternalEvent.h" #include "mitkBaseRenderer.h" #include "mitkRenderingManager.h" //how precise must the user pick the point //default value mitk::PlanarFigureInteractor::PlanarFigureInteractor() : DataInteractor() , m_Precision( 6.5 ) , m_MinimumPointDistance( 25.0 ) , m_IsHovering( false ) , m_LastPointWasValid( false ) { } mitk::PlanarFigureInteractor::~PlanarFigureInteractor() { } void mitk::PlanarFigureInteractor::ConnectActionsAndFunctions() { CONNECT_CONDITION("figure_is_on_current_slice", CheckFigureOnRenderingGeometry); CONNECT_CONDITION("figure_is_placed", CheckFigurePlaced); CONNECT_CONDITION("minimal_figure_is_finished", CheckMinimalFigureFinished); CONNECT_CONDITION("hovering_above_figure", CheckFigureHovering); CONNECT_CONDITION("hovering_above_point", CheckControlPointHovering); CONNECT_CONDITION("figure_is_selected", CheckSelection); CONNECT_CONDITION("point_is_valid", CheckPointValidity); CONNECT_CONDITION("figure_is_finished", CheckFigureFinished); CONNECT_CONDITION("reset_on_point_select_needed", CheckResetOnPointSelect); CONNECT_CONDITION("points_can_be_added_or_removed", CheckFigureIsExtendable); CONNECT_FUNCTION( "finalize_figure", FinalizeFigure); CONNECT_FUNCTION( "hide_preview_point", HidePreviewPoint ) CONNECT_FUNCTION( "hide_control_points", HideControlPoints ) CONNECT_FUNCTION( "set_preview_point_position", SetPreviewPointPosition ) CONNECT_FUNCTION( "move_current_point", MoveCurrentPoint); CONNECT_FUNCTION( "deselect_point", DeselectPoint); CONNECT_FUNCTION( "add_new_point", AddPoint); CONNECT_FUNCTION( "add_initial_point", AddInitialPoint); CONNECT_FUNCTION( "remove_selected_point", RemoveSelectedPoint); CONNECT_FUNCTION( "request_context_menu", RequestContextMenu); CONNECT_FUNCTION( "select_figure", SelectFigure ); CONNECT_FUNCTION( "select_point", SelectPoint ); CONNECT_FUNCTION( "end_interaction", EndInteraction ); CONNECT_FUNCTION( "start_hovering", StartHovering ) CONNECT_FUNCTION( "end_hovering", EndHovering ); } bool mitk::PlanarFigureInteractor::CheckFigurePlaced( const InteractionEvent* /*interactionEvent*/ ) { mitk::PlanarFigure *planarFigure = dynamic_cast( GetDataNode()->GetData() ); bool isFigureFinished = false; planarFigure->GetPropertyList()->GetBoolProperty( "initiallyplaced", isFigureFinished ); return planarFigure->IsPlaced() && isFigureFinished; } bool mitk::PlanarFigureInteractor::MoveCurrentPoint(StateMachineAction*, InteractionEvent* interactionEvent) { mitk::InteractionPositionEvent* positionEvent = dynamic_cast( interactionEvent ); if ( positionEvent == NULL ) return false; bool isEditable = true; GetDataNode()->GetBoolProperty( "planarfigure.iseditable", isEditable ); mitk::PlanarFigure *planarFigure = dynamic_cast( GetDataNode()->GetData() ); - mitk::Geometry2D *planarFigureGeometry = - dynamic_cast< Geometry2D * >( planarFigure->GetGeometry( 0 ) ); + mitk::PlaneGeometry *planarFigureGeometry = + dynamic_cast< PlaneGeometry * >( planarFigure->GetGeometry( 0 ) ); - // Extract point in 2D world coordinates (relative to Geometry2D of + // Extract point in 2D world coordinates (relative to PlaneGeometry of // PlanarFigure) Point2D point2D; if ( !this->TransformPositionEventToPoint2D( positionEvent, planarFigureGeometry, point2D ) || !isEditable ) { return false; } // check if the control points shall be hidden during interaction bool hidecontrolpointsduringinteraction = false; GetDataNode()->GetBoolProperty( "planarfigure.hidecontrolpointsduringinteraction", hidecontrolpointsduringinteraction ); // hide the control points if necessary //interactionEvent->GetSender()->GetDataStorage()->BlockNodeModifiedEvents( true ); GetDataNode()->SetBoolProperty( "planarfigure.drawcontrolpoints", !hidecontrolpointsduringinteraction ); //interactionEvent->GetSender()->GetDataStorage()->BlockNodeModifiedEvents( false ); // Move current control point to this point planarFigure->SetCurrentControlPoint( point2D ); // Re-evaluate features planarFigure->EvaluateFeatures(); // Update rendered scene interactionEvent->GetSender()->GetRenderingManager()->RequestUpdateAll(); return true; } bool mitk::PlanarFigureInteractor::FinalizeFigure( StateMachineAction*, InteractionEvent* interactionEvent ) { mitk::PlanarFigure *planarFigure = dynamic_cast( GetDataNode()->GetData() ); planarFigure->Modified(); planarFigure->DeselectControlPoint(); planarFigure->RemoveLastControlPoint(); planarFigure->SetProperty( "initiallyplaced", mitk::BoolProperty::New( true ) ); GetDataNode()->SetBoolProperty( "planarfigure.drawcontrolpoints", true ); GetDataNode()->Modified(); planarFigure->InvokeEvent( EndPlacementPlanarFigureEvent() ); planarFigure->InvokeEvent( EndInteractionPlanarFigureEvent() ); interactionEvent->GetSender()->GetRenderingManager()->RequestUpdateAll(); return false; } bool mitk::PlanarFigureInteractor::EndInteraction( StateMachineAction*, InteractionEvent* interactionEvent ) { mitk::PlanarFigure *planarFigure = dynamic_cast( GetDataNode()->GetData() ); GetDataNode()->SetBoolProperty( "planarfigure.drawcontrolpoints", true ); planarFigure->Modified(); planarFigure->InvokeEvent( EndInteractionPlanarFigureEvent() ); interactionEvent->GetSender()->GetRenderingManager()->RequestUpdateAll(); return false; } bool mitk::PlanarFigureInteractor::EndHovering( StateMachineAction*, InteractionEvent* interactionEvent ) { mitk::PlanarFigure *planarFigure = dynamic_cast( GetDataNode()->GetData() ); planarFigure->ResetPreviewContolPoint(); // Invoke end-hover event once the mouse is exiting the figure area m_IsHovering = false; planarFigure->InvokeEvent( EndHoverPlanarFigureEvent() ); // Set bool property to indicate that planar figure is no longer in "hovering" mode GetDataNode()->SetBoolProperty( "planarfigure.ishovering", false ); interactionEvent->GetSender()->GetRenderingManager()->RequestUpdateAll(); return false; } bool mitk::PlanarFigureInteractor::CheckMinimalFigureFinished( const InteractionEvent* /*interactionEvent*/ ) { mitk::PlanarFigure *planarFigure = dynamic_cast( GetDataNode()->GetData() ); return ( planarFigure->GetNumberOfControlPoints() >= planarFigure->GetMinimumNumberOfControlPoints() ); } bool mitk::PlanarFigureInteractor::CheckFigureFinished( const InteractionEvent* /*interactionEvent*/ ) { mitk::PlanarFigure *planarFigure = dynamic_cast( GetDataNode()->GetData() ); return ( planarFigure->GetNumberOfControlPoints() >= planarFigure->GetMaximumNumberOfControlPoints() ); } bool mitk::PlanarFigureInteractor::CheckFigureIsExtendable( const InteractionEvent* /*interactionEvent*/ ) { bool isExtendable = false; GetDataNode()->GetBoolProperty("planarfigure.isextendable", isExtendable); return isExtendable; } bool mitk::PlanarFigureInteractor::DeselectPoint(StateMachineAction*, InteractionEvent* /*interactionEvent*/) { mitk::PlanarFigure *planarFigure = dynamic_cast( GetDataNode()->GetData() ); bool wasSelected = planarFigure->DeselectControlPoint(); if ( wasSelected ) { // Issue event so that listeners may update themselves planarFigure->Modified(); planarFigure->InvokeEvent( EndInteractionPlanarFigureEvent() ); GetDataNode()->SetBoolProperty( "planarfigure.drawcontrolpoints", true ); // GetDataNode()->SetBoolProperty( "planarfigure.ishovering", false ); GetDataNode()->Modified(); } return true; } bool mitk::PlanarFigureInteractor::AddPoint(StateMachineAction*, InteractionEvent* interactionEvent) { mitk::InteractionPositionEvent* positionEvent = dynamic_cast( interactionEvent ); if ( positionEvent == NULL ) return false; bool selected = false; bool isEditable = true; GetDataNode()->GetBoolProperty("selected", selected); GetDataNode()->GetBoolProperty( "planarfigure.iseditable", isEditable ); if ( !selected || !isEditable ) { return false; } mitk::PlanarFigure *planarFigure = dynamic_cast( GetDataNode()->GetData() ); - mitk::Geometry2D *planarFigureGeometry = - dynamic_cast< Geometry2D * >( planarFigure->GetGeometry( 0 ) ); + mitk::PlaneGeometry *planarFigureGeometry = + dynamic_cast< PlaneGeometry * >( planarFigure->GetGeometry( 0 ) ); // If the planarFigure already has reached the maximum number if ( planarFigure->GetNumberOfControlPoints() >= planarFigure->GetMaximumNumberOfControlPoints() ) { return false; } - // Extract point in 2D world coordinates (relative to Geometry2D of + // Extract point in 2D world coordinates (relative to PlaneGeometry of // PlanarFigure) Point2D point2D, projectedPoint; if ( !this->TransformPositionEventToPoint2D( positionEvent, planarFigureGeometry, point2D ) ) { return false; } // TODO: check segment of polyline we clicked in int nextIndex = -1; // We only need to check which position to insert the control point // when interacting with a PlanarPolygon. For all other types // new control points will always be appended /* * Added check for "initiallyplaced" due to bug 13097: * * There are two possible cases in which a point can be inserted into a PlanarPolygon: * * 1. The figure is currently drawn -> the point will be appended at the end of the figure * 2. A point is inserted at a userdefined position after the initial placement of the figure is finished * * In the second case we need to determine the proper insertion index. In the first case the index always has * to be -1 so that the point is appended to the end. * * These changes are neccessary because of a mac os x specific issue: If a users draws a PlanarPolygon then the * next point to be added moves according to the mouse position. If then the user left clicks in order to add * a point one would assume the last move position is identical to the left click position. This is actually the * case for windows and linux but somehow NOT for mac. Because of the insertion logic of a new point in the * PlanarFigure then for mac the wrong current selected point is determined. * * With this check here this problem can be avoided. However a redesign of the insertion logic should be considered */ bool isFigureFinished = false; planarFigure->GetPropertyList()->GetBoolProperty( "initiallyplaced", isFigureFinished ); mitk::BaseRenderer *renderer = interactionEvent->GetSender(); - const Geometry2D *projectionPlane = renderer->GetCurrentWorldGeometry2D(); + const PlaneGeometry *projectionPlane = renderer->GetCurrentWorldGeometry2D(); if ( dynamic_cast( planarFigure ) && isFigureFinished) { nextIndex = this->IsPositionOverFigure( positionEvent, planarFigure, planarFigureGeometry, projectionPlane, renderer->GetDisplayGeometry(), projectedPoint ); } // Add point as new control point renderer->GetDisplayGeometry()->DisplayToWorld( projectedPoint, projectedPoint ); if ( planarFigure->IsPreviewControlPointVisible() ) { point2D = planarFigure->GetPreviewControlPoint(); } planarFigure->AddControlPoint( point2D, nextIndex ); if ( planarFigure->IsPreviewControlPointVisible() ) { planarFigure->SelectControlPoint( nextIndex ); planarFigure->ResetPreviewContolPoint(); } // Re-evaluate features planarFigure->EvaluateFeatures(); //this->LogPrintPlanarFigureQuantities( planarFigure ); // Update rendered scene renderer->GetRenderingManager()->RequestUpdateAll(); return true; } bool mitk::PlanarFigureInteractor::AddInitialPoint(StateMachineAction*, InteractionEvent* interactionEvent) { mitk::InteractionPositionEvent* positionEvent = dynamic_cast( interactionEvent ); if ( positionEvent == NULL ) return false; mitk::PlanarFigure *planarFigure = dynamic_cast( GetDataNode()->GetData() ); mitk::BaseRenderer *renderer = interactionEvent->GetSender(); - mitk::Geometry2D *planarFigureGeometry = dynamic_cast< Geometry2D * >( planarFigure->GetGeometry( 0 ) ); + mitk::PlaneGeometry *planarFigureGeometry = dynamic_cast< PlaneGeometry * >( planarFigure->GetGeometry( 0 ) ); // Invoke event to notify listeners that placement of this PF starts now planarFigure->InvokeEvent( StartPlacementPlanarFigureEvent() ); - // Use Geometry2D of the renderer clicked on for this PlanarFigure + // Use PlaneGeometry of the renderer clicked on for this PlanarFigure mitk::PlaneGeometry *planeGeometry = const_cast< mitk::PlaneGeometry * >( dynamic_cast< const mitk::PlaneGeometry * >( renderer->GetSliceNavigationController()->GetCurrentPlaneGeometry() ) ); if ( planeGeometry != NULL ) { planarFigureGeometry = planeGeometry; planarFigure->SetGeometry2D( planeGeometry ); } else { return false; } - // Extract point in 2D world coordinates (relative to Geometry2D of + // Extract point in 2D world coordinates (relative to PlaneGeometry of // PlanarFigure) Point2D point2D; if ( !this->TransformPositionEventToPoint2D( positionEvent, planarFigureGeometry, point2D ) ) { return false; } // Place PlanarFigure at this point planarFigure->PlaceFigure( point2D ); // Re-evaluate features planarFigure->EvaluateFeatures(); //this->LogPrintPlanarFigureQuantities( planarFigure ); // Set a bool property indicating that the figure has been placed in // the current RenderWindow. This is required so that the same render - // window can be re-aligned to the Geometry2D of the PlanarFigure later + // window can be re-aligned to the PlaneGeometry of the PlanarFigure later // on in an application. GetDataNode()->SetBoolProperty( "PlanarFigureInitializedWindow", true, renderer ); // Update rendered scene renderer->GetRenderingManager()->RequestUpdateAll(); return true; } bool mitk::PlanarFigureInteractor::StartHovering( StateMachineAction*, InteractionEvent* interactionEvent ) { mitk::InteractionPositionEvent* positionEvent = dynamic_cast( interactionEvent ); if ( positionEvent == NULL ) return false; mitk::PlanarFigure *planarFigure = dynamic_cast( GetDataNode()->GetData() ); mitk::BaseRenderer *renderer = interactionEvent->GetSender(); if ( !m_IsHovering ) { // Invoke hover event once when the mouse is entering the figure area m_IsHovering = true; planarFigure->InvokeEvent( StartHoverPlanarFigureEvent() ); // Set bool property to indicate that planar figure is currently in "hovering" mode GetDataNode()->SetBoolProperty( "planarfigure.ishovering", true ); renderer->GetRenderingManager()->RequestUpdateAll(); } return true; } bool mitk::PlanarFigureInteractor::SetPreviewPointPosition( StateMachineAction*, InteractionEvent* interactionEvent ) { mitk::InteractionPositionEvent* positionEvent = dynamic_cast( interactionEvent ); if ( positionEvent == NULL ) return false; mitk::PlanarFigure *planarFigure = dynamic_cast( GetDataNode()->GetData() ); mitk::BaseRenderer *renderer = interactionEvent->GetSender(); planarFigure->DeselectControlPoint(); mitk::Point2D pointProjectedOntoLine; bool selected(false); bool isExtendable(false); bool isEditable(true); GetDataNode()->GetBoolProperty("selected", selected); GetDataNode()->GetBoolProperty("planarfigure.isextendable", isExtendable); GetDataNode()->GetBoolProperty("planarfigure.iseditable", isEditable ); if ( selected && isExtendable && isEditable ) { renderer->GetDisplayGeometry()->DisplayToWorld( pointProjectedOntoLine, pointProjectedOntoLine ); planarFigure->SetPreviewControlPoint( pointProjectedOntoLine ); } renderer->GetRenderingManager()->RequestUpdateAll(); return true; } bool mitk::PlanarFigureInteractor::HideControlPoints( StateMachineAction*, InteractionEvent* /*interactionEvent*/ ) { GetDataNode()->SetBoolProperty( "planarfigure.drawcontrolpoints", false ); return true; } bool mitk::PlanarFigureInteractor::HidePreviewPoint( StateMachineAction*, InteractionEvent* interactionEvent ) { mitk::PlanarFigure *planarFigure = dynamic_cast( GetDataNode()->GetData() ); planarFigure->ResetPreviewContolPoint(); mitk::BaseRenderer *renderer = interactionEvent->GetSender(); renderer->GetRenderingManager()->RequestUpdateAll(); return true; } bool mitk::PlanarFigureInteractor::CheckFigureHovering( const InteractionEvent* interactionEvent ) { const mitk::InteractionPositionEvent* positionEvent = dynamic_cast( interactionEvent ); if ( positionEvent == NULL ) return false; mitk::PlanarFigure *planarFigure = dynamic_cast( GetDataNode()->GetData() ); mitk::BaseRenderer *renderer = interactionEvent->GetSender(); - mitk::Geometry2D *planarFigureGeometry = dynamic_cast< Geometry2D * >( planarFigure->GetGeometry( 0 ) ); - const Geometry2D *projectionPlane = renderer->GetCurrentWorldGeometry2D(); + mitk::PlaneGeometry *planarFigureGeometry = dynamic_cast< PlaneGeometry * >( planarFigure->GetGeometry( 0 ) ); + const PlaneGeometry *projectionPlane = renderer->GetCurrentWorldGeometry2D(); mitk::Point2D pointProjectedOntoLine; int previousControlPoint = this->IsPositionOverFigure( positionEvent, planarFigure, planarFigureGeometry, projectionPlane, renderer->GetDisplayGeometry(), pointProjectedOntoLine ); bool isHovering = (previousControlPoint != -1); if ( isHovering ) { return true; } else { return false; } return false; } bool mitk::PlanarFigureInteractor::CheckControlPointHovering( const InteractionEvent* interactionEvent ) { const mitk::InteractionPositionEvent* positionEvent = dynamic_cast( interactionEvent ); if ( positionEvent == NULL ) return false; mitk::PlanarFigure *planarFigure = dynamic_cast( GetDataNode()->GetData() ); mitk::BaseRenderer *renderer = interactionEvent->GetSender(); - mitk::Geometry2D *planarFigureGeometry = dynamic_cast< Geometry2D * >( planarFigure->GetGeometry( 0 ) ); - const Geometry2D *projectionPlane = renderer->GetCurrentWorldGeometry2D(); + mitk::PlaneGeometry *planarFigureGeometry = dynamic_cast< PlaneGeometry * >( planarFigure->GetGeometry( 0 ) ); + const PlaneGeometry *projectionPlane = renderer->GetCurrentWorldGeometry2D(); int pointIndex = -1; pointIndex = mitk::PlanarFigureInteractor::IsPositionInsideMarker( positionEvent, planarFigure, planarFigureGeometry, projectionPlane, renderer->GetDisplayGeometry() ); if ( pointIndex >= 0 ) { return true; } else { return false; } } bool mitk::PlanarFigureInteractor::CheckSelection( const InteractionEvent* /*interactionEvent*/ ) { bool selected = false; GetDataNode()->GetBoolProperty("selected", selected); return selected; } bool mitk::PlanarFigureInteractor::SelectFigure( StateMachineAction*, InteractionEvent* /*interactionEvent*/ ) { mitk::PlanarFigure *planarFigure = dynamic_cast( GetDataNode()->GetData() ); planarFigure->InvokeEvent( SelectPlanarFigureEvent() ); return false; } bool mitk::PlanarFigureInteractor::SelectPoint( StateMachineAction*, InteractionEvent* interactionEvent ) { mitk::InteractionPositionEvent* positionEvent = dynamic_cast( interactionEvent ); if ( positionEvent == NULL ) return false; mitk::PlanarFigure *planarFigure = dynamic_cast( GetDataNode()->GetData() ); mitk::BaseRenderer *renderer = interactionEvent->GetSender(); - mitk::Geometry2D *planarFigureGeometry = dynamic_cast< Geometry2D * >( planarFigure->GetGeometry( 0 ) ); - const Geometry2D *projectionPlane = renderer->GetCurrentWorldGeometry2D(); + mitk::PlaneGeometry *planarFigureGeometry = dynamic_cast< PlaneGeometry * >( planarFigure->GetGeometry( 0 ) ); + const PlaneGeometry *projectionPlane = renderer->GetCurrentWorldGeometry2D(); int pointIndex = -1; pointIndex = mitk::PlanarFigureInteractor::IsPositionInsideMarker( positionEvent, planarFigure, planarFigureGeometry, projectionPlane, renderer->GetDisplayGeometry() ); if ( pointIndex >= 0 ) { // If mouse is above control point, mark it as selected planarFigure->SelectControlPoint( pointIndex ); } else { planarFigure->DeselectControlPoint(); } return false; } bool mitk::PlanarFigureInteractor::CheckPointValidity( const InteractionEvent* interactionEvent ) { // Check if the distance of the current point to the previously set point in display coordinates // is sufficient (if a previous point exists) // Extract display position const mitk::InteractionPositionEvent* positionEvent = dynamic_cast( interactionEvent ); if ( positionEvent == NULL ) return false; mitk::PlanarFigure *planarFigure = dynamic_cast( GetDataNode()->GetData() ); m_LastPointWasValid = IsMousePositionAcceptableAsNewControlPoint( positionEvent, planarFigure ); return m_LastPointWasValid; } bool mitk::PlanarFigureInteractor::RemoveSelectedPoint(StateMachineAction*, InteractionEvent* interactionEvent) { mitk::PlanarFigure *planarFigure = dynamic_cast( GetDataNode()->GetData() ); mitk::BaseRenderer *renderer = interactionEvent->GetSender(); int selectedControlPoint = planarFigure->GetSelectedControlPoint(); planarFigure->RemoveControlPoint( selectedControlPoint ); // Re-evaluate features planarFigure->EvaluateFeatures(); planarFigure->Modified(); GetDataNode()->SetBoolProperty( "planarfigure.drawcontrolpoints", true ); planarFigure->InvokeEvent( EndInteractionPlanarFigureEvent() ); renderer->GetRenderingManager()->RequestUpdateAll(); HandleEvent( mitk::InternalEvent::New( renderer, this, "Dummy-Event" ), GetDataNode() ); return true; } bool mitk::PlanarFigureInteractor::RequestContextMenu(StateMachineAction*, InteractionEvent* /*interactionEvent*/) { mitk::PlanarFigure *planarFigure = dynamic_cast( GetDataNode()->GetData() ); bool selected = false; GetDataNode()->GetBoolProperty("selected", selected); // no need to invoke this if the figure is already selected if ( !selected ) { planarFigure->InvokeEvent( SelectPlanarFigureEvent() ); } planarFigure->InvokeEvent( ContextMenuPlanarFigureEvent() ); return true; } bool mitk::PlanarFigureInteractor::CheckResetOnPointSelect( const InteractionEvent* /*interactionEvent*/ ) { mitk::PlanarFigure *planarFigure = dynamic_cast( GetDataNode()->GetData() ); // Invoke tmpEvent to notify listeners that interaction with this PF starts now planarFigure->InvokeEvent( StartInteractionPlanarFigureEvent() ); // Reset the PlanarFigure if required return planarFigure->ResetOnPointSelect(); } bool mitk::PlanarFigureInteractor::CheckFigureOnRenderingGeometry( const InteractionEvent* interactionEvent ) { const mitk::InteractionPositionEvent* posEvent = dynamic_cast(interactionEvent); if ( posEvent == NULL ) return false; mitk::Point3D worldPoint3D = posEvent->GetPositionInWorld(); mitk::PlanarFigure *planarFigure = dynamic_cast( GetDataNode()->GetData() ); - mitk::Geometry2D *planarFigureGeometry2D = dynamic_cast< Geometry2D * >( planarFigure->GetGeometry( 0 ) ); + mitk::PlaneGeometry *planarFigureGeometry2D = dynamic_cast< PlaneGeometry * >( planarFigure->GetGeometry( 0 ) ); double planeThickness = planarFigureGeometry2D->GetExtentInMM( 2 ); if ( planarFigureGeometry2D->Distance( worldPoint3D ) > planeThickness ) { // don't react, when interaction is too far away return false; } return true; } void mitk::PlanarFigureInteractor::SetPrecision( mitk::ScalarType precision ) { m_Precision = precision; } void mitk::PlanarFigureInteractor::SetMinimumPointDistance( ScalarType minimumDistance ) { m_MinimumPointDistance = minimumDistance; } bool mitk::PlanarFigureInteractor::TransformPositionEventToPoint2D( const InteractionPositionEvent *positionEvent, - const Geometry2D *planarFigureGeometry, + const PlaneGeometry *planarFigureGeometry, Point2D &point2D ) { mitk::Point3D worldPoint3D = positionEvent->GetPositionInWorld(); // TODO: proper handling of distance tolerance if ( planarFigureGeometry->Distance( worldPoint3D ) > 0.1 ) { return false; } // Project point onto plane of this PlanarFigure planarFigureGeometry->Map( worldPoint3D, point2D ); return true; } bool mitk::PlanarFigureInteractor::TransformObjectToDisplay( const mitk::Point2D &point2D, mitk::Point2D &displayPoint, - const mitk::Geometry2D *objectGeometry, - const mitk::Geometry2D *rendererGeometry, + const mitk::PlaneGeometry *objectGeometry, + const mitk::PlaneGeometry *rendererGeometry, const mitk::DisplayGeometry *displayGeometry ) const { mitk::Point3D point3D; // Map circle point from local 2D geometry into 3D world space objectGeometry->Map( point2D, point3D ); // TODO: proper handling of distance tolerance if ( displayGeometry->Distance( point3D ) < 0.1 ) { // Project 3D world point onto display geometry rendererGeometry->Map( point3D, displayPoint ); displayGeometry->WorldToDisplay( displayPoint, displayPoint ); return true; } return false; } bool mitk::PlanarFigureInteractor::IsPointNearLine( const mitk::Point2D& point, const mitk::Point2D& startPoint, const mitk::Point2D& endPoint, mitk::Point2D& projectedPoint ) const { mitk::Vector2D n1 = endPoint - startPoint; n1.Normalize(); // Determine dot products between line vector and startpoint-point / endpoint-point vectors double l1 = n1 * (point - startPoint); double l2 = -n1 * (point - endPoint); // Determine projection of specified point onto line defined by start / end point mitk::Point2D crossPoint = startPoint + n1 * l1; projectedPoint = crossPoint; // Point is inside encompassing rectangle IF // - its distance to its projected point is small enough // - it is not further outside of the line than the defined tolerance if (((crossPoint.SquaredEuclideanDistanceTo(point) < 20.0) && (l1 > 0.0) && (l2 > 0.0)) || endPoint.SquaredEuclideanDistanceTo(point) < 20.0 || startPoint.SquaredEuclideanDistanceTo(point) < 20.0) { return true; } return false; } int mitk::PlanarFigureInteractor::IsPositionOverFigure( const InteractionPositionEvent *positionEvent, PlanarFigure *planarFigure, - const Geometry2D *planarFigureGeometry, - const Geometry2D *rendererGeometry, + const PlaneGeometry *planarFigureGeometry, + const PlaneGeometry *rendererGeometry, const DisplayGeometry *displayGeometry, Point2D& pointProjectedOntoLine ) const { mitk::Point2D displayPosition = positionEvent->GetPointerPositionOnScreen(); // Iterate over all polylines of planar figure, and check if // any one is close to the current display position typedef mitk::PlanarFigure::PolyLineType VertexContainerType; Point2D polyLinePoint; Point2D firstPolyLinePoint; Point2D previousPolyLinePoint; for ( unsigned short loop=0; loopGetPolyLinesSize(); ++loop ) { const VertexContainerType polyLine = planarFigure->GetPolyLine( loop ); bool firstPoint( true ); for ( VertexContainerType::const_iterator it = polyLine.begin(); it != polyLine.end(); ++it ) { // Get plane coordinates of this point of polyline (if possible) if ( !this->TransformObjectToDisplay( it->Point, polyLinePoint, planarFigureGeometry, rendererGeometry, displayGeometry ) ) { break; // Poly line invalid (not on current 2D plane) --> skip it } if ( firstPoint ) { firstPolyLinePoint = polyLinePoint; firstPoint = false; } else if ( this->IsPointNearLine( displayPosition, previousPolyLinePoint, polyLinePoint, pointProjectedOntoLine ) ) { // Point is close enough to line segment --> Return index of the segment return it->Index; } previousPolyLinePoint = polyLinePoint; } // For closed figures, also check last line segment if ( planarFigure->IsClosed() && this->IsPointNearLine( displayPosition, polyLinePoint, firstPolyLinePoint, pointProjectedOntoLine ) ) { return 0; // Return index of first control point } } return -1; } int mitk::PlanarFigureInteractor::IsPositionInsideMarker( const InteractionPositionEvent* positionEvent, const PlanarFigure *planarFigure, - const Geometry2D *planarFigureGeometry, - const Geometry2D *rendererGeometry, + const PlaneGeometry *planarFigureGeometry, + const PlaneGeometry *rendererGeometry, const DisplayGeometry *displayGeometry ) const { mitk::Point2D displayPosition = positionEvent->GetPointerPositionOnScreen(); // Iterate over all control points of planar figure, and check if // any one is close to the current display position mitk::Point2D displayControlPoint; int numberOfControlPoints = planarFigure->GetNumberOfControlPoints(); for ( int i=0; iTransformObjectToDisplay( planarFigure->GetControlPoint(i), displayControlPoint, planarFigureGeometry, rendererGeometry, displayGeometry ) ) { // TODO: variable size of markers if ( displayPosition.SquaredEuclideanDistanceTo( displayControlPoint ) < 20.0 ) { return i; } } } return -1; } void mitk::PlanarFigureInteractor::LogPrintPlanarFigureQuantities( const PlanarFigure *planarFigure ) { MITK_INFO << "PlanarFigure: " << planarFigure->GetNameOfClass(); for ( unsigned int i = 0; i < planarFigure->GetNumberOfFeatures(); ++i ) { MITK_INFO << "* " << planarFigure->GetFeatureName( i ) << ": " << planarFigure->GetQuantity( i ) << " " << planarFigure->GetFeatureUnit( i ); } } bool mitk::PlanarFigureInteractor::IsMousePositionAcceptableAsNewControlPoint( const mitk::InteractionPositionEvent* positionEvent, const PlanarFigure* planarFigure ) { assert(positionEvent && planarFigure); BaseRenderer* renderer = positionEvent->GetSender(); assert(renderer); // Get the timestep to support 3D+t int timeStep( renderer->GetTimeStep( planarFigure ) ); bool tooClose(false); - const Geometry2D *renderingPlane = renderer->GetCurrentWorldGeometry2D(); + const PlaneGeometry *renderingPlane = renderer->GetCurrentWorldGeometry2D(); - mitk::Geometry2D *planarFigureGeometry = - dynamic_cast< mitk::Geometry2D * >( planarFigure->GetGeometry( timeStep ) ); + mitk::PlaneGeometry *planarFigureGeometry = + dynamic_cast< mitk::PlaneGeometry * >( planarFigure->GetGeometry( timeStep ) ); Point2D point2D, correctedPoint; // Get the point2D from the positionEvent if ( !this->TransformPositionEventToPoint2D( positionEvent, planarFigureGeometry, point2D ) ) { return false; } // apply the controlPoint constraints of the planarFigure to get the // coordinates that would actually be used. correctedPoint = const_cast( planarFigure )->ApplyControlPointConstraints( 0, point2D ); // map the 2D coordinates of the new point to world-coordinates // and transform those to display-coordinates mitk::Point3D newPoint3D; planarFigureGeometry->Map( correctedPoint, newPoint3D ); mitk::Point2D newDisplayPosition; renderingPlane->Map( newPoint3D, newDisplayPosition ); renderer->GetDisplayGeometry()->WorldToDisplay( newDisplayPosition, newDisplayPosition ); for( int i=0; i < (int)planarFigure->GetNumberOfControlPoints(); i++ ) { if ( i != planarFigure->GetSelectedControlPoint() ) { // Try to convert previous point to current display coordinates mitk::Point3D previousPoint3D; // map the 2D coordinates of the control-point to world-coordinates planarFigureGeometry->Map( planarFigure->GetControlPoint( i ), previousPoint3D ); if ( renderer->GetDisplayGeometry()->Distance( previousPoint3D ) < 0.1 ) // ugly, but assert makes this work { mitk::Point2D previousDisplayPosition; // transform the world-coordinates into display-coordinates renderingPlane->Map( previousPoint3D, previousDisplayPosition ); renderer->GetDisplayGeometry()->WorldToDisplay( previousDisplayPosition, previousDisplayPosition ); //Calculate the distance. We use display-coordinates here to make // the check independent of the zoom-level of the rendering scene. double a = newDisplayPosition[0] - previousDisplayPosition[0]; double b = newDisplayPosition[1] - previousDisplayPosition[1]; // If point is to close, do not set a new point tooClose = (a * a + b * b < m_MinimumPointDistance ); } if ( tooClose ) return false; // abort loop early } } return !tooClose; // default } void mitk::PlanarFigureInteractor::ConfigurationChanged() { mitk::PropertyList::Pointer properties = GetAttributes(); std::string precision = ""; if (properties->GetStringProperty("precision", precision)) { m_Precision = atof(precision.c_str()); } else { m_Precision = (ScalarType) 6.5; } std::string minPointDistance = ""; if (properties->GetStringProperty("minPointDistance", minPointDistance)) { m_MinimumPointDistance = atof(minPointDistance.c_str()); } else { m_MinimumPointDistance = (ScalarType) 25.0; } } diff --git a/Modules/PlanarFigure/Interactions/mitkPlanarFigureInteractor.h b/Modules/PlanarFigure/Interactions/mitkPlanarFigureInteractor.h index e8aa0b2585..4fc3800154 100644 --- a/Modules/PlanarFigure/Interactions/mitkPlanarFigureInteractor.h +++ b/Modules/PlanarFigure/Interactions/mitkPlanarFigureInteractor.h @@ -1,204 +1,204 @@ /*=================================================================== 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 MITKPLANARFIGUREINTERACTOR_H_HEADER_INCLUDED #define MITKPLANARFIGUREINTERACTOR_H_HEADER_INCLUDED #include "PlanarFigureExports.h" #include "mitkCommon.h" #include "mitkVector.h" #include "mitkDataInteractor.h" #pragma GCC visibility push(default) #include #pragma GCC visibility pop namespace mitk { class DataNode; -class Geometry2D; +class PlaneGeometry; class DisplayGeometry; class PlanarFigure; class PositionEvent; class BaseRenderer; class InteractionPositionEvent; class StateMachineAction; #pragma GCC visibility push(default) // Define events for PlanarFigure interaction notifications itkEventMacro( PlanarFigureEvent, itk::AnyEvent ); itkEventMacro( StartPlacementPlanarFigureEvent, PlanarFigureEvent ); itkEventMacro( EndPlacementPlanarFigureEvent, PlanarFigureEvent ); itkEventMacro( SelectPlanarFigureEvent, PlanarFigureEvent ); itkEventMacro( StartInteractionPlanarFigureEvent, PlanarFigureEvent ); itkEventMacro( EndInteractionPlanarFigureEvent, PlanarFigureEvent ); itkEventMacro( StartHoverPlanarFigureEvent, PlanarFigureEvent ); itkEventMacro( EndHoverPlanarFigureEvent, PlanarFigureEvent ); itkEventMacro( ContextMenuPlanarFigureEvent, PlanarFigureEvent ); #pragma GCC visibility pop /** * \brief Interaction with mitk::PlanarFigure objects via control-points * * \ingroup Interaction */ class PlanarFigure_EXPORT PlanarFigureInteractor : public DataInteractor { public: mitkClassMacro(PlanarFigureInteractor, DataInteractor); itkNewMacro(Self); /** \brief Sets the amount of precision */ void SetPrecision( ScalarType precision ); /** \brief Sets the minimal distance between two control points. */ void SetMinimumPointDistance( ScalarType minimumDistance ); protected: PlanarFigureInteractor(); virtual ~PlanarFigureInteractor(); virtual void ConnectActionsAndFunctions(); //////// Conditions //////// bool CheckFigurePlaced( const InteractionEvent* interactionEvent ); bool CheckFigureHovering( const InteractionEvent* interactionEvent ); bool CheckControlPointHovering( const InteractionEvent* interactionEvent ); bool CheckSelection( const InteractionEvent* interactionEvent ); bool CheckPointValidity( const InteractionEvent* interactionEvent ); bool CheckFigureFinished( const InteractionEvent* interactionEvent ); bool CheckResetOnPointSelect( const InteractionEvent* interactionEvent ); bool CheckFigureOnRenderingGeometry( const InteractionEvent* interactionEvent ); bool CheckMinimalFigureFinished( const InteractionEvent* interactionEvent ); bool CheckFigureIsExtendable( const InteractionEvent* interactionEvent ); //////// Actions //////// bool FinalizeFigure( StateMachineAction*, InteractionEvent* interactionEvent ); bool MoveCurrentPoint(StateMachineAction*, InteractionEvent* interactionEvent); bool DeselectPoint(StateMachineAction*, InteractionEvent* interactionEvent); bool AddPoint(StateMachineAction*, InteractionEvent* interactionEvent); bool AddInitialPoint(StateMachineAction*, InteractionEvent* interactionEvent); bool StartHovering( StateMachineAction*, InteractionEvent* interactionEvent ); bool EndHovering( StateMachineAction*, InteractionEvent* interactionEvent ); bool SetPreviewPointPosition( StateMachineAction*, InteractionEvent* interactionEvent ); bool HidePreviewPoint( StateMachineAction*, InteractionEvent* interactionEvent ); bool HideControlPoints( StateMachineAction*, InteractionEvent* interactionEvent ); bool RemoveSelectedPoint(StateMachineAction*, InteractionEvent* interactionEvent); bool RequestContextMenu(StateMachineAction*, InteractionEvent* interactionEvent); bool SelectFigure( StateMachineAction*, InteractionEvent* interactionEvent ); bool SelectPoint( StateMachineAction*, InteractionEvent* interactionEvent ); bool EndInteraction( StateMachineAction*, InteractionEvent* interactionEvent ); /** \brief Used when clicking to determine if a point is too close to the previous point. */ bool IsMousePositionAcceptableAsNewControlPoint( const mitk::InteractionPositionEvent* positionEvent, const PlanarFigure* ); bool TransformPositionEventToPoint2D( const InteractionPositionEvent* positionEvent, - const Geometry2D *planarFigureGeometry, + const PlaneGeometry *planarFigureGeometry, Point2D &point2D ); bool TransformObjectToDisplay( const mitk::Point2D &point2D, mitk::Point2D &displayPoint, - const mitk::Geometry2D *objectGeometry, - const mitk::Geometry2D *rendererGeometry, + const mitk::PlaneGeometry *objectGeometry, + const mitk::PlaneGeometry *rendererGeometry, const mitk::DisplayGeometry *displayGeometry ) const; /** \brief Returns true if the first specified point is in proximity of the line defined * the other two point; false otherwise. * * Proximity is defined as the rectangle around the line with pre-defined distance * from the line. */ bool IsPointNearLine( const mitk::Point2D& point, const mitk::Point2D& startPoint, const mitk::Point2D& endPoint, mitk::Point2D& projectedPoint ) const; /** \brief Returns true if the point contained in the passed event (in display coordinates) * is over the planar figure (with a pre-defined tolerance range); false otherwise. */ int IsPositionOverFigure( const InteractionPositionEvent* positionEvent, PlanarFigure *planarFigure, - const Geometry2D *planarFigureGeometry, - const Geometry2D *rendererGeometry, + const PlaneGeometry *planarFigureGeometry, + const PlaneGeometry *rendererGeometry, const DisplayGeometry *displayGeometry, Point2D& pointProjectedOntoLine) const; /** \brief Returns the index of the marker (control point) over which the point contained * in the passed event (in display coordinates) currently is; -1 if the point is not over * a marker. */ int IsPositionInsideMarker( const InteractionPositionEvent* positionEvent, const PlanarFigure *planarFigure, - const Geometry2D *planarFigureGeometry, - const Geometry2D *rendererGeometry, + const PlaneGeometry *planarFigureGeometry, + const PlaneGeometry *rendererGeometry, const DisplayGeometry *displayGeometry ) const; void LogPrintPlanarFigureQuantities( const PlanarFigure *planarFigure ); virtual void ConfigurationChanged(); private: /** \brief to store the value of precision to pick a point */ ScalarType m_Precision; /** \brief Store the minimal distance between two control points. */ ScalarType m_MinimumPointDistance; /** \brief True if the mouse is currently hovering over the image. */ bool m_IsHovering; bool m_LastPointWasValid; //mitk::PlanarFigure::Pointer m_PlanarFigure; }; } #endif // MITKPLANARFIGUREINTERACTOR_H_HEADER_INCLUDED diff --git a/Modules/PlanarFigure/Rendering/mitkPlanarFigureMapper2D.cpp b/Modules/PlanarFigure/Rendering/mitkPlanarFigureMapper2D.cpp index 3b0b809638..977edb2b04 100644 --- a/Modules/PlanarFigure/Rendering/mitkPlanarFigureMapper2D.cpp +++ b/Modules/PlanarFigure/Rendering/mitkPlanarFigureMapper2D.cpp @@ -1,897 +1,897 @@ /*=================================================================== 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 "mitkPlanarFigureMapper2D.h" #include "mitkBaseRenderer.h" #include "mitkPlaneGeometry.h" #include "mitkColorProperty.h" #include "mitkProperties.h" #include "mitkGL.h" #include "mitkVtkPropRenderer.h" #define _USE_MATH_DEFINES #include // offset which moves the planarfigures on top of the other content // the crosshair is rendered into the z = 1 layer. static const float PLANAR_OFFSET = 0.5f; mitk::PlanarFigureMapper2D::PlanarFigureMapper2D() : m_NodeModified(true) , m_NodeModifiedObserverTag(0) , m_NodeModifiedObserverAdded(false) { this->InitializeDefaultPlanarFigureProperties(); } mitk::PlanarFigureMapper2D::~PlanarFigureMapper2D() { if ( m_NodeModifiedObserverAdded && GetDataNode() != NULL ) { GetDataNode()->RemoveObserver( m_NodeModifiedObserverTag ); } } void mitk::PlanarFigureMapper2D::Paint( mitk::BaseRenderer *renderer ) { bool visible = true; GetDataNode()->GetVisibility(visible, renderer, "visible"); if ( !visible ) return; // Get PlanarFigure from input mitk::PlanarFigure *planarFigure = const_cast< mitk::PlanarFigure * >( static_cast< const mitk::PlanarFigure * >( GetDataNode()->GetData() ) ); // Check if PlanarFigure has already been placed; otherwise, do nothing if ( !planarFigure->IsPlaced() ) { return; } // Get 2D geometry frame of PlanarFigure - mitk::Geometry2D *planarFigureGeometry2D = - dynamic_cast< Geometry2D * >( planarFigure->GetGeometry( 0 ) ); + mitk::PlaneGeometry *planarFigureGeometry2D = + dynamic_cast< PlaneGeometry * >( planarFigure->GetGeometry( 0 ) ); if ( planarFigureGeometry2D == NULL ) { - MITK_ERROR << "PlanarFigure does not have valid Geometry2D!"; + MITK_ERROR << "PlanarFigure does not have valid PlaneGeometry!"; return; } // Get current world 2D geometry from renderer - const mitk::Geometry2D *rendererGeometry2D = renderer->GetCurrentWorldGeometry2D(); + const mitk::PlaneGeometry *rendererGeometry2D = renderer->GetCurrentWorldGeometry2D(); // If the PlanarFigure geometry is a plane geometry, check if current // world plane is parallel to and within the planar figure geometry bounds // (otherwise, display nothing) mitk::PlaneGeometry *planarFigurePlaneGeometry = dynamic_cast< PlaneGeometry * >( planarFigureGeometry2D ); const mitk::PlaneGeometry *rendererPlaneGeometry = dynamic_cast< const PlaneGeometry * >( rendererGeometry2D ); if ( (planarFigurePlaneGeometry != NULL) && (rendererPlaneGeometry != NULL) ) { double planeThickness = planarFigurePlaneGeometry->GetExtentInMM( 2 ); if ( !planarFigurePlaneGeometry->IsParallel( rendererPlaneGeometry ) || !(planarFigurePlaneGeometry->DistanceFromPlane( rendererPlaneGeometry ) < planeThickness / 3.0) ) { // Planes are not parallel or renderer plane is not within PlanarFigure // geometry bounds --> exit return; } } else { // Plane is not valid (curved reformations are not possible yet) return; } // Get display geometry mitk::DisplayGeometry *displayGeometry = renderer->GetDisplayGeometry(); assert( displayGeometry != NULL ); // Apply visual appearance properties from the PropertyList ApplyColorAndOpacityProperties( renderer ); // Enable line antialiasing glEnable( GL_LINE_SMOOTH ); glHint( GL_LINE_SMOOTH_HINT, GL_NICEST ); glEnable(GL_DEPTH_TEST); // Get properties from node (if present) const mitk::DataNode* node=this->GetDataNode(); this->InitializePlanarFigurePropertiesFromDataNode( node ); PlanarFigureDisplayMode lineDisplayMode = PF_DEFAULT; if ( m_IsSelected ) { lineDisplayMode = PF_SELECTED; } else if ( m_IsHovering ) { lineDisplayMode = PF_HOVER; } mitk::Point2D anchorPoint; anchorPoint[0] = 0; anchorPoint[1] = 1; // render the actual lines of the PlanarFigure RenderLines(lineDisplayMode, planarFigure, anchorPoint, planarFigureGeometry2D, rendererGeometry2D, displayGeometry); // position-offset of the annotations, is set in RenderAnnotations() and // used in RenderQuantities() double annotationOffset = 0.0; //Get Global Opacity float globalOpacity = 1.0; node->GetFloatProperty("opacity", globalOpacity); // draw name near the anchor point (point located on the right) std::string name = node->GetName(); if ( m_DrawName && !name.empty() ) { RenderAnnotations(renderer, name, anchorPoint, globalOpacity, lineDisplayMode, annotationOffset); } // draw feature quantities (if requested) next to the anchor point, // but under the name (that is where 'annotationOffset' is used) if ( m_DrawQuantities ) { RenderQuantities(planarFigure, renderer, anchorPoint, annotationOffset, globalOpacity, lineDisplayMode); } if ( m_DrawControlPoints ) { // draw the control-points RenderControlPoints(planarFigure, lineDisplayMode, planarFigureGeometry2D, rendererGeometry2D, displayGeometry); } glLineWidth( 1.0f ); } void mitk::PlanarFigureMapper2D::PaintPolyLine( mitk::PlanarFigure::PolyLineType vertices, bool closed, Point2D& anchorPoint, - const Geometry2D* planarFigureGeometry2D, - const Geometry2D* rendererGeometry2D, + const PlaneGeometry* planarFigureGeometry2D, + const PlaneGeometry* rendererGeometry2D, const DisplayGeometry* displayGeometry) { mitk::Point2D rightMostPoint; rightMostPoint.Fill( itk::NumericTraits::min() ); // transform all vertices into Point2Ds in display-Coordinates and store them in vector std::vector pointlist; for ( PlanarFigure::PolyLineType::iterator iter = vertices.begin(); iter!=vertices.end(); iter++ ) { // Draw this 2D point as OpenGL vertex mitk::Point2D displayPoint; this->TransformObjectToDisplay( iter->Point, displayPoint, planarFigureGeometry2D, rendererGeometry2D, displayGeometry ); pointlist.push_back(displayPoint); if ( displayPoint[0] > rightMostPoint[0] ) rightMostPoint = displayPoint; } // now paint all the points in one run std::vector::iterator pointIter; if ( closed ) { glBegin( GL_LINE_LOOP ); } else { glBegin( GL_LINE_STRIP ); } for ( pointIter = pointlist.begin(); pointIter!=pointlist.end(); pointIter++ ) { glVertex3f( (*pointIter)[0], (*pointIter)[1], PLANAR_OFFSET ); } glEnd(); anchorPoint = rightMostPoint; } void mitk::PlanarFigureMapper2D::DrawMainLines( mitk::PlanarFigure* figure, Point2D& anchorPoint, - const Geometry2D* planarFigureGeometry2D, - const Geometry2D* rendererGeometry2D, + const PlaneGeometry* planarFigureGeometry2D, + const PlaneGeometry* rendererGeometry2D, const DisplayGeometry* displayGeometry) { unsigned short numberOfPolyLines = figure->GetPolyLinesSize(); for ( unsigned short loop=0; loopGetPolyLine(loop); this->PaintPolyLine( polyline, figure->IsClosed(), anchorPoint, planarFigureGeometry2D, rendererGeometry2D, displayGeometry ); } } void mitk::PlanarFigureMapper2D::DrawHelperLines( mitk::PlanarFigure* figure, Point2D& anchorPoint, - const Geometry2D* planarFigureGeometry2D, - const Geometry2D* rendererGeometry2D, + const PlaneGeometry* planarFigureGeometry2D, + const PlaneGeometry* rendererGeometry2D, const DisplayGeometry* displayGeometry) { unsigned short numberOfHelperPolyLines = figure->GetHelperPolyLinesSize(); // Draw helper objects for ( unsigned int loop=0; loopGetHelperPolyLine(loop, displayGeometry->GetScaleFactorMMPerDisplayUnit(), displayGeometry->GetDisplayHeight() ); // Check if the current helper objects is to be painted if ( !figure->IsHelperToBePainted( loop ) ) { continue; } // ... and once normally above the shadow. this->PaintPolyLine( helperPolyLine, false, anchorPoint, planarFigureGeometry2D, rendererGeometry2D, displayGeometry ); } } void mitk::PlanarFigureMapper2D::TransformObjectToDisplay( const mitk::Point2D &point2D, mitk::Point2D &displayPoint, - const mitk::Geometry2D *objectGeometry, - const mitk::Geometry2D *rendererGeometry, + const mitk::PlaneGeometry *objectGeometry, + const mitk::PlaneGeometry *rendererGeometry, const mitk::DisplayGeometry *displayGeometry ) { mitk::Point3D point3D; // Map circle point from local 2D geometry into 3D world space objectGeometry->Map( point2D, point3D ); // Project 3D world point onto display geometry rendererGeometry->Map( point3D, displayPoint ); displayGeometry->WorldToDisplay( displayPoint, displayPoint ); } void mitk::PlanarFigureMapper2D::DrawMarker( const mitk::Point2D &point, float* lineColor, float lineOpacity, float* markerColor, float markerOpacity, float lineWidth, PlanarFigureControlPointStyleProperty::Shape shape, - const mitk::Geometry2D *objectGeometry, - const mitk::Geometry2D *rendererGeometry, + const mitk::PlaneGeometry *objectGeometry, + const mitk::PlaneGeometry *rendererGeometry, const mitk::DisplayGeometry *displayGeometry ) { mitk::Point2D displayPoint; if ( markerOpacity == 0 && lineOpacity == 0 ) return; this->TransformObjectToDisplay( point, displayPoint, objectGeometry, rendererGeometry, displayGeometry ); glColor4f( markerColor[0], markerColor[1], markerColor[2], markerOpacity ); glLineWidth( lineWidth ); switch ( shape ) { case PlanarFigureControlPointStyleProperty::Square: default: { // Paint filled square // Disable line antialiasing (does not look nice for squares) glDisable( GL_LINE_SMOOTH ); if ( markerOpacity > 0 ) { glRectf( displayPoint[0] - 4, displayPoint[1] - 4, displayPoint[0] + 4, displayPoint[1] + 4 ); } // Paint outline glColor4f( lineColor[0], lineColor[1], lineColor[2], lineOpacity ); glBegin( GL_LINE_LOOP ); glVertex3f( displayPoint[0] - 4, displayPoint[1] - 4, PLANAR_OFFSET ); glVertex3f( displayPoint[0] - 4, displayPoint[1] + 4, PLANAR_OFFSET ); glVertex3f( displayPoint[0] + 4, displayPoint[1] + 4, PLANAR_OFFSET ); glVertex3f( displayPoint[0] + 4, displayPoint[1] - 4, PLANAR_OFFSET ); glEnd(); break; } case PlanarFigureControlPointStyleProperty::Circle: { float radius = 4.0; if ( markerOpacity > 0 ) { // Paint filled circle glBegin( GL_POLYGON ); for ( int angle = 0; angle < 8; ++angle ) { float angleRad = angle * (float) 3.14159 / 4.0; float x = displayPoint[0] + radius * (float)cos( angleRad ); float y = displayPoint[1] + radius * (float)sin( angleRad ); glVertex3f(x, y, PLANAR_OFFSET); } glEnd(); } // Paint outline glColor4f( lineColor[0], lineColor[1], lineColor[2], lineOpacity ); glBegin( GL_LINE_LOOP ); for ( int angle = 0; angle < 8; ++angle ) { float angleRad = angle * (float) 3.14159 / 4.0; float x = displayPoint[0] + radius * (float)cos( angleRad ); float y = displayPoint[1] + radius * (float)sin( angleRad ); glVertex3f(x, y, PLANAR_OFFSET); } glEnd(); break; } } // end switch } void mitk::PlanarFigureMapper2D::InitializeDefaultPlanarFigureProperties() { m_IsSelected = false; m_IsHovering = false; m_DrawOutline = false; m_DrawQuantities = false; m_DrawShadow = false; m_DrawControlPoints = false; m_DrawName = true; m_DrawDashed = true; m_ShadowWidthFactor = 1.2; m_LineWidth = 1.0; m_OutlineWidth = 4.0; m_HelperlineWidth = 2.0; m_ControlPointShape = PlanarFigureControlPointStyleProperty::Square; this->SetColorProperty( m_LineColor, PF_DEFAULT, 1.0, 1.0, 1.0 ); this->SetFloatProperty( m_LineOpacity, PF_DEFAULT, 1.0 ); this->SetColorProperty( m_OutlineColor, PF_DEFAULT, 0.0, 0.0, 1.0 ); this->SetFloatProperty( m_OutlineOpacity, PF_DEFAULT, 1.0 ); this->SetColorProperty( m_HelperlineColor, PF_DEFAULT, 0.4, 0.8, 0.2 ); this->SetFloatProperty( m_HelperlineOpacity, PF_DEFAULT, 0.4 ); this->SetColorProperty( m_MarkerlineColor, PF_DEFAULT, 1.0, 1.0, 1.0 ); this->SetFloatProperty( m_MarkerlineOpacity, PF_DEFAULT, 1.0 ); this->SetColorProperty( m_MarkerColor, PF_DEFAULT, 1.0, 1.0, 1.0 ); this->SetFloatProperty( m_MarkerOpacity, PF_DEFAULT, 0.0 ); this->SetColorProperty( m_LineColor, PF_HOVER, 1.0, 0.7, 0.0 ); this->SetFloatProperty( m_LineOpacity, PF_HOVER, 1.0 ); this->SetColorProperty( m_OutlineColor, PF_HOVER, 0.0, 0.0, 1.0 ); this->SetFloatProperty( m_OutlineOpacity, PF_HOVER, 1.0 ); this->SetColorProperty( m_HelperlineColor, PF_HOVER, 0.4, 0.8, 0.2 ); this->SetFloatProperty( m_HelperlineOpacity, PF_HOVER, 0.4 ); this->SetColorProperty( m_MarkerlineColor, PF_HOVER, 1.0, 1.0, 1.0 ); this->SetFloatProperty( m_MarkerlineOpacity, PF_HOVER, 1.0 ); this->SetColorProperty( m_MarkerColor, PF_HOVER, 1.0, 0.6, 0.0 ); this->SetFloatProperty( m_MarkerOpacity, PF_HOVER, 0.2 ); this->SetColorProperty( m_LineColor, PF_SELECTED, 1.0, 0.0, 0.0 ); this->SetFloatProperty( m_LineOpacity, PF_SELECTED, 1.0 ); this->SetColorProperty( m_OutlineColor, PF_SELECTED, 0.0, 0.0, 1.0 ); this->SetFloatProperty( m_OutlineOpacity, PF_SELECTED, 1.0 ); this->SetColorProperty( m_HelperlineColor, PF_SELECTED, 0.4, 0.8, 0.2 ); this->SetFloatProperty( m_HelperlineOpacity, PF_SELECTED, 0.4 ); this->SetColorProperty( m_MarkerlineColor, PF_SELECTED, 1.0, 1.0, 1.0 ); this->SetFloatProperty( m_MarkerlineOpacity, PF_SELECTED, 1.0 ); this->SetColorProperty( m_MarkerColor, PF_SELECTED, 1.0, 0.6, 0.0 ); this->SetFloatProperty( m_MarkerOpacity, PF_SELECTED, 1.0 ); } void mitk::PlanarFigureMapper2D::InitializePlanarFigurePropertiesFromDataNode( const mitk::DataNode* node ) { if ( node == NULL ) { return; } // if we have not added an observer for ModifiedEvents on the DataNode, // we add one now. if ( !m_NodeModifiedObserverAdded ) { itk::SimpleMemberCommand::Pointer nodeModifiedCommand = itk::SimpleMemberCommand::New(); nodeModifiedCommand->SetCallbackFunction(this, &mitk::PlanarFigureMapper2D::OnNodeModified); m_NodeModifiedObserverTag = node->AddObserver(itk::ModifiedEvent(), nodeModifiedCommand); m_NodeModifiedObserverAdded = true; } // If the DataNode has not been modified since the last execution of // this method, we do not run it now. if ( !m_NodeModified ) return; // Mark the current properties as unmodified m_NodeModified = false; //Get Global Opacity float globalOpacity = 1.0; node->GetFloatProperty("opacity", globalOpacity); node->GetBoolProperty( "selected", m_IsSelected ); node->GetBoolProperty( "planarfigure.ishovering", m_IsHovering ); node->GetBoolProperty( "planarfigure.drawoutline", m_DrawOutline ); node->GetBoolProperty( "planarfigure.drawshadow", m_DrawShadow ); node->GetBoolProperty( "planarfigure.drawquantities", m_DrawQuantities ); node->GetBoolProperty( "planarfigure.drawcontrolpoints", m_DrawControlPoints ); node->GetBoolProperty( "planarfigure.drawname", m_DrawName ); node->GetBoolProperty( "planarfigure.drawdashed", m_DrawDashed ); node->GetFloatProperty( "planarfigure.line.width", m_LineWidth ); node->GetFloatProperty( "planarfigure.shadow.widthmodifier", m_ShadowWidthFactor ); node->GetFloatProperty( "planarfigure.outline.width", m_OutlineWidth ); node->GetFloatProperty( "planarfigure.helperline.width", m_HelperlineWidth ); PlanarFigureControlPointStyleProperty::Pointer styleProperty = dynamic_cast< PlanarFigureControlPointStyleProperty* >( node->GetProperty( "planarfigure.controlpointshape" ) ); if ( styleProperty.IsNotNull() ) { m_ControlPointShape = styleProperty->GetShape(); } //Set default color and opacity //If property "planarfigure.default.*.color" exists, then use that color. Otherwise global "color" property is used. if( !node->GetColor( m_LineColor[PF_DEFAULT], NULL, "planarfigure.default.line.color")) { node->GetColor( m_LineColor[PF_DEFAULT], NULL, "color" ); } node->GetFloatProperty( "planarfigure.default.line.opacity", m_LineOpacity[PF_DEFAULT] ); if( !node->GetColor( m_OutlineColor[PF_DEFAULT], NULL, "planarfigure.default.outline.color")) { node->GetColor( m_OutlineColor[PF_DEFAULT], NULL, "color" ); } node->GetFloatProperty( "planarfigure.default.outline.opacity", m_OutlineOpacity[PF_DEFAULT] ); if( !node->GetColor( m_HelperlineColor[PF_DEFAULT], NULL, "planarfigure.default.helperline.color")) { node->GetColor( m_HelperlineColor[PF_DEFAULT], NULL, "color" ); } node->GetFloatProperty( "planarfigure.default.helperline.opacity", m_HelperlineOpacity[PF_DEFAULT] ); node->GetColor( m_MarkerlineColor[PF_DEFAULT], NULL, "planarfigure.default.markerline.color" ); node->GetFloatProperty( "planarfigure.default.markerline.opacity", m_MarkerlineOpacity[PF_DEFAULT] ); node->GetColor( m_MarkerColor[PF_DEFAULT], NULL, "planarfigure.default.marker.color" ); node->GetFloatProperty( "planarfigure.default.marker.opacity", m_MarkerOpacity[PF_DEFAULT] ); //Set hover color and opacity node->GetColor( m_LineColor[PF_HOVER], NULL, "planarfigure.hover.line.color" ); node->GetFloatProperty( "planarfigure.hover.line.opacity", m_LineOpacity[PF_HOVER] ); node->GetColor( m_OutlineColor[PF_HOVER], NULL, "planarfigure.hover.outline.color" ); node->GetFloatProperty( "planarfigure.hover.outline.opacity", m_OutlineOpacity[PF_HOVER] ); node->GetColor( m_HelperlineColor[PF_HOVER], NULL, "planarfigure.hover.helperline.color" ); node->GetFloatProperty( "planarfigure.hover.helperline.opacity", m_HelperlineOpacity[PF_HOVER] ); node->GetColor( m_MarkerlineColor[PF_HOVER], NULL, "planarfigure.hover.markerline.color" ); node->GetFloatProperty( "planarfigure.hover.markerline.opacity", m_MarkerlineOpacity[PF_HOVER] ); node->GetColor( m_MarkerColor[PF_HOVER], NULL, "planarfigure.hover.marker.color" ); node->GetFloatProperty( "planarfigure.hover.marker.opacity", m_MarkerOpacity[PF_HOVER] ); //Set selected color and opacity node->GetColor( m_LineColor[PF_SELECTED], NULL, "planarfigure.selected.line.color" ); node->GetFloatProperty( "planarfigure.selected.line.opacity", m_LineOpacity[PF_SELECTED] ); node->GetColor( m_OutlineColor[PF_SELECTED], NULL, "planarfigure.selected.outline.color" ); node->GetFloatProperty( "planarfigure.selected.outline.opacity", m_OutlineOpacity[PF_SELECTED] ); node->GetColor( m_HelperlineColor[PF_SELECTED], NULL, "planarfigure.selected.helperline.color" ); node->GetFloatProperty( "planarfigure.selected.helperline.opacity", m_HelperlineOpacity[PF_SELECTED] ); node->GetColor( m_MarkerlineColor[PF_SELECTED], NULL, "planarfigure.selected.markerline.color" ); node->GetFloatProperty( "planarfigure.selected.markerline.opacity", m_MarkerlineOpacity[PF_SELECTED] ); node->GetColor( m_MarkerColor[PF_SELECTED], NULL, "planarfigure.selected.marker.color" ); node->GetFloatProperty( "planarfigure.selected.marker.opacity", m_MarkerOpacity[PF_SELECTED] ); //adapt opacity values to global "opacity" property for( unsigned int i = 0; i < PF_COUNT; ++i ) { m_LineOpacity[i] *= globalOpacity; m_OutlineOpacity[i] *= globalOpacity; m_HelperlineOpacity[i] *= globalOpacity; m_MarkerlineOpacity[i] *= globalOpacity; m_MarkerOpacity[i] *= globalOpacity; } } void mitk::PlanarFigureMapper2D::OnNodeModified() { m_NodeModified = true; } void mitk::PlanarFigureMapper2D::SetDefaultProperties( mitk::DataNode* node, mitk::BaseRenderer* renderer, bool overwrite ) { node->AddProperty( "visible", mitk::BoolProperty::New(true), renderer, overwrite ); //node->SetProperty("planarfigure.iseditable",mitk::BoolProperty::New(true)); node->AddProperty("planarfigure.isextendable",mitk::BoolProperty::New(false)); //node->AddProperty( "planarfigure.ishovering", mitk::BoolProperty::New(true) ); node->AddProperty( "planarfigure.drawoutline", mitk::BoolProperty::New(false) ); //node->AddProperty( "planarfigure.drawquantities", mitk::BoolProperty::New(true) ); node->AddProperty( "planarfigure.drawshadow", mitk::BoolProperty::New(true) ); node->AddProperty( "planarfigure.drawcontrolpoints", mitk::BoolProperty::New(true) ); node->AddProperty( "planarfigure.drawname", mitk::BoolProperty::New(true) ); node->AddProperty( "planarfigure.drawdashed", mitk::BoolProperty::New(false) ); node->AddProperty("planarfigure.line.width", mitk::FloatProperty::New(2.0) ); node->AddProperty("planarfigure.shadow.widthmodifier", mitk::FloatProperty::New(2.0) ); node->AddProperty("planarfigure.outline.width", mitk::FloatProperty::New(2.0) ); node->AddProperty("planarfigure.helperline.width", mitk::FloatProperty::New(2.0) ); node->AddProperty( "planarfigure.default.line.opacity", mitk::FloatProperty::New(1.0) ); node->AddProperty( "planarfigure.default.outline.opacity", mitk::FloatProperty::New(1.0) ); node->AddProperty( "planarfigure.default.helperline.opacity", mitk::FloatProperty::New(1.0) ); node->AddProperty( "planarfigure.default.markerline.color", mitk::ColorProperty::New(1.0,1.0,1.0) ); node->AddProperty( "planarfigure.default.markerline.opacity", mitk::FloatProperty::New(1.0) ); node->AddProperty( "planarfigure.default.marker.color", mitk::ColorProperty::New(1.0,1.0,1.0) ); node->AddProperty( "planarfigure.default.marker.opacity",mitk::FloatProperty::New(1.0) ); node->AddProperty( "planarfigure.hover.line.color", mitk::ColorProperty::New(0.0,1.0,0.0) ); node->AddProperty( "planarfigure.hover.line.opacity", mitk::FloatProperty::New(1.0) ); node->AddProperty( "planarfigure.hover.outline.color", mitk::ColorProperty::New(0.0,1.0,0.0) ); node->AddProperty( "planarfigure.hover.outline.opacity", mitk::FloatProperty::New(1.0) ); node->AddProperty( "planarfigure.hover.helperline.color", mitk::ColorProperty::New(0.0,1.0,0.0) ); node->AddProperty( "planarfigure.hover.helperline.opacity", mitk::FloatProperty::New(1.0) ); node->AddProperty( "planarfigure.hover.markerline.color", mitk::ColorProperty::New(0.0,1.0,0.0) ); node->AddProperty( "planarfigure.hover.markerline.opacity", mitk::FloatProperty::New(1.0) ); node->AddProperty( "planarfigure.hover.marker.color", mitk::ColorProperty::New(0.0,1.0,0.0) ); node->AddProperty( "planarfigure.hover.marker.opacity", mitk::FloatProperty::New(1.0) ); node->AddProperty( "planarfigure.selected.line.color", mitk::ColorProperty::New(1.0,0.0,0.0) ); node->AddProperty( "planarfigure.selected.line.opacity",mitk::FloatProperty::New(1.0) ); node->AddProperty( "planarfigure.selected.outline.color", mitk::ColorProperty::New(1.0,0.0,0.0) ); node->AddProperty( "planarfigure.selected.outline.opacity", mitk::FloatProperty::New(1.0)); node->AddProperty( "planarfigure.selected.helperline.color", mitk::ColorProperty::New(1.0,0.0,0.0) ); node->AddProperty( "planarfigure.selected.helperline.opacity",mitk::FloatProperty::New(1.0) ); node->AddProperty( "planarfigure.selected.markerline.color", mitk::ColorProperty::New(1.0,0.0,0.0) ); node->AddProperty( "planarfigure.selected.markerline.opacity", mitk::FloatProperty::New(1.0) ); node->AddProperty( "planarfigure.selected.marker.color", mitk::ColorProperty::New(1.0,0.0,0.0) ); node->AddProperty( "planarfigure.selected.marker.opacity",mitk::FloatProperty::New(1.0)); } void mitk::PlanarFigureMapper2D::RenderControlPoints( mitk::PlanarFigure * planarFigure, PlanarFigureDisplayMode lineDisplayMode, - mitk::Geometry2D * planarFigureGeometry2D, - const mitk::Geometry2D * rendererGeometry2D, + mitk::PlaneGeometry * planarFigureGeometry2D, + const mitk::PlaneGeometry * rendererGeometry2D, mitk::DisplayGeometry * displayGeometry ) { bool isEditable = true; m_DataNode->GetBoolProperty( "planarfigure.iseditable", isEditable ); PlanarFigureDisplayMode pointDisplayMode = PF_DEFAULT; unsigned int selectedControlPointsIdx = (unsigned int) planarFigure->GetSelectedControlPoint(); unsigned int numberOfControlPoints = planarFigure->GetNumberOfControlPoints(); // Draw markers at control points (selected control point will be colored) for ( unsigned int i = 0; i < numberOfControlPoints ; ++i ) { // Only if planar figure is marked as editable: display markers (control points) in a // different style if mouse is over them or they are selected if ( isEditable ) { if ( i == selectedControlPointsIdx ) { pointDisplayMode = PF_SELECTED; } else if ( m_IsHovering && isEditable ) { pointDisplayMode = PF_HOVER; } } if ( m_MarkerOpacity[pointDisplayMode] == 0 && m_MarkerlineOpacity[pointDisplayMode] == 0 ) { continue; } if ( m_DrawOutline ) { // draw outlines for markers as well // linewidth for the contour is only half, as full width looks // much too thick! this->DrawMarker( planarFigure->GetControlPoint( i ), m_OutlineColor[lineDisplayMode], m_MarkerlineOpacity[pointDisplayMode], m_OutlineColor[lineDisplayMode], m_MarkerOpacity[pointDisplayMode], m_OutlineWidth/2, m_ControlPointShape, planarFigureGeometry2D, rendererGeometry2D, displayGeometry ); } this->DrawMarker( planarFigure->GetControlPoint( i ), m_MarkerlineColor[pointDisplayMode], m_MarkerlineOpacity[pointDisplayMode], m_MarkerColor[pointDisplayMode], m_MarkerOpacity[pointDisplayMode], m_LineWidth, m_ControlPointShape, planarFigureGeometry2D, rendererGeometry2D, displayGeometry ); } if ( planarFigure->IsPreviewControlPointVisible() ) { this->DrawMarker( planarFigure->GetPreviewControlPoint(), m_MarkerlineColor[PF_HOVER], m_MarkerlineOpacity[PF_HOVER], m_MarkerColor[PF_HOVER], m_MarkerOpacity[PF_HOVER], m_LineWidth, m_ControlPointShape, planarFigureGeometry2D, rendererGeometry2D, displayGeometry ); } } void mitk::PlanarFigureMapper2D::RenderAnnotations( mitk::BaseRenderer * renderer, std::string name, mitk::Point2D anchorPoint, float globalOpacity, PlanarFigureDisplayMode lineDisplayMode, double &annotationOffset ) { mitk::VtkPropRenderer* openGLrenderer = dynamic_cast( renderer ); if ( openGLrenderer ) { openGLrenderer->WriteSimpleText( name, anchorPoint[0] + 6.0, anchorPoint[1] + 4.0, 0, 0, 0, globalOpacity ); //this is a shadow openGLrenderer->WriteSimpleText( name, anchorPoint[0] + 5.0, anchorPoint[1] + 5.0, m_LineColor[lineDisplayMode][0], m_LineColor[lineDisplayMode][1], m_LineColor[lineDisplayMode][2], globalOpacity ); // If drawing is successful, add approximate height to annotation offset annotationOffset -= 15.0; } } void mitk::PlanarFigureMapper2D::RenderQuantities( mitk::PlanarFigure * planarFigure, mitk::BaseRenderer * renderer, mitk::Point2D anchorPoint, double &annotationOffset, float globalOpacity, PlanarFigureDisplayMode lineDisplayMode ) { std::stringstream quantityString; quantityString.setf( ios::fixed, ios::floatfield ); quantityString.precision( 1 ); bool firstActiveFeature = true; for ( unsigned int i = 0; i < planarFigure->GetNumberOfFeatures(); ++i ) { if( planarFigure->IsFeatureActive(i) && planarFigure->IsFeatureVisible( i ) ) { if ( ! firstActiveFeature ) { quantityString << " x "; } quantityString << planarFigure->GetQuantity( i ) << " "; quantityString << planarFigure->GetFeatureUnit( i ); firstActiveFeature = false; } } mitk::VtkPropRenderer* openGLrenderer = dynamic_cast( renderer ); if ( openGLrenderer ) { openGLrenderer->WriteSimpleText( quantityString.str().c_str(), anchorPoint[0] + 6.0, anchorPoint[1] + 4.0 + annotationOffset, 0, 0, 0, globalOpacity ); //this is a shadow openGLrenderer->WriteSimpleText( quantityString.str().c_str(), anchorPoint[0] + 5.0, anchorPoint[1] + 5.0 + annotationOffset, m_LineColor[lineDisplayMode][0], m_LineColor[lineDisplayMode][1], m_LineColor[lineDisplayMode][2], globalOpacity ); // If drawing is successful, add approximate height to annotation offset annotationOffset -= 15.0; } } void mitk::PlanarFigureMapper2D::RenderLines( PlanarFigureDisplayMode lineDisplayMode, mitk::PlanarFigure * planarFigure, mitk::Point2D &anchorPoint, - mitk::Geometry2D * planarFigureGeometry2D, - const mitk::Geometry2D * rendererGeometry2D, + mitk::PlaneGeometry * planarFigureGeometry2D, + const mitk::PlaneGeometry * rendererGeometry2D, mitk::DisplayGeometry * displayGeometry ) { if ( m_DrawDashed ) { glLineStipple(1, 0x00FF); glEnable(GL_LINE_STIPPLE); } // If we want to draw an outline, we do it here if ( m_DrawOutline ) { float* color = m_OutlineColor[lineDisplayMode]; float opacity = m_OutlineOpacity[lineDisplayMode]; // convert to a float array that also contains opacity, faster GL float* colorVector = new float[4]; colorVector[0] = color[0]; colorVector[1] = color[1]; colorVector[2] = color[2]; colorVector[3] = opacity; // set the color and opacity here as it is common for all outlines glColor4fv( colorVector ); glLineWidth(m_OutlineWidth); // Draw the outline for all polylines if requested this->DrawMainLines( planarFigure, anchorPoint, planarFigureGeometry2D, rendererGeometry2D, displayGeometry ); // Draw the outline for all helper objects if requested this->DrawHelperLines( planarFigure, anchorPoint, planarFigureGeometry2D, rendererGeometry2D, displayGeometry ); // cleanup delete[] colorVector; } // If we want to draw a shadow, we do it here if ( m_DrawShadow ) { // determine the shadow opacity float opacity = m_OutlineOpacity[lineDisplayMode]; float shadowOpacity = 0.0f; if( opacity > 0.2f ) shadowOpacity = opacity - 0.2f; // convert to a float array that also contains opacity, faster GL float* shadow = new float[4]; shadow[0] = 0; shadow[1] = 0; shadow[2] = 0; shadow[3] = shadowOpacity; // set the color and opacity here as it is common for all shadows glColor4fv( shadow ); glLineWidth( m_OutlineWidth * m_ShadowWidthFactor ); // Draw the outline for all polylines if requested this->DrawMainLines( planarFigure, anchorPoint, planarFigureGeometry2D, rendererGeometry2D, displayGeometry ); // Draw the outline for all helper objects if requested this->DrawHelperLines( planarFigure, anchorPoint, planarFigureGeometry2D, rendererGeometry2D, displayGeometry ); // cleanup delete[] shadow; } // set this in brackets to avoid duplicate variables in the same scope { float* color = m_LineColor[lineDisplayMode]; float opacity = m_LineOpacity[lineDisplayMode]; // convert to a float array that also contains opacity, faster GL float* colorVector = new float[4]; colorVector[0] = color[0]; colorVector[1] = color[1]; colorVector[2] = color[2]; colorVector[3] = opacity; // set the color and opacity here as it is common for all mainlines glColor4fv( colorVector ); glLineWidth( m_LineWidth ); // Draw the main line for all polylines this->DrawMainLines( planarFigure, anchorPoint, planarFigureGeometry2D, rendererGeometry2D, displayGeometry ); float* helperColor = m_HelperlineColor[lineDisplayMode]; float helperOpacity = m_HelperlineOpacity[lineDisplayMode]; // convert to a float array that also contains opacity, faster GL float* helperColorVector = new float[4]; helperColorVector[0] = helperColor[0]; helperColorVector[1] = helperColor[1]; helperColorVector[2] = helperColor[2]; helperColorVector[3] = helperOpacity; // we only set the color for the helperlines as the linewidth is unchanged glColor4fv( helperColorVector ); // Draw helper objects this->DrawHelperLines( planarFigure, anchorPoint, planarFigureGeometry2D, rendererGeometry2D, displayGeometry ); // cleanup delete[] colorVector; delete[] helperColorVector; } if ( m_DrawDashed ) { glDisable(GL_LINE_STIPPLE); } } diff --git a/Modules/PlanarFigure/Rendering/mitkPlanarFigureMapper2D.h b/Modules/PlanarFigure/Rendering/mitkPlanarFigureMapper2D.h index 46fef9344b..b66c60fe5d 100644 --- a/Modules/PlanarFigure/Rendering/mitkPlanarFigureMapper2D.h +++ b/Modules/PlanarFigure/Rendering/mitkPlanarFigureMapper2D.h @@ -1,307 +1,307 @@ /*=================================================================== 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 MITK_PLANAR_FIGURE_MAPPER_2D_H_ #define MITK_PLANAR_FIGURE_MAPPER_2D_H_ #include "mitkCommon.h" #include "PlanarFigureExports.h" #include "mitkGLMapper.h" #include "mitkPlanarFigure.h" #include "mitkPlanarFigureControlPointStyleProperty.h" namespace mitk { class BaseRenderer; class Contour; /** * \brief OpenGL-based mapper to render display sub-class instances of mitk::PlanarFigure * * The appearance of planar figures can be configured through properties. If no properties are specified, * default values will be used. There are four elements a planar figure consists of: * *
    *
  1. "line": the main line segments of the planar figure (note: text is drawn in the same style) *
  2. "helperline": additional line segments of planar figures, such as arrow tips, arches of angles, etc. *
  3. "outline": background which is drawn behind the lines and helperlines of the planar figure (optional) *
  4. "marker": the markers (control points) of a planar figure *
  5. "markerline": the lines by which markers (control points) are surrounded *
* * In the following, all appearance-related planar figure properties are listed: * *
    *
  1. General properties for the planar figure *
      *
    • "planarfigure.drawoutline": if true, the "outline" lines is drawn *
    • "planarfigure.drawquantities": if true, the quantities (text) associated with the planar figure is drawn *
    • "planarfigure.drawname": if true, the name specified by the dataNode is drawn *
    • "planarfigure.drawshadow": if true, a black shadow is drawn around the planar figure *
    • "planarfigure.controlpointshape": style of the control points (enum) *
    *
  2. Line widths of planar figure elements *
      *
    • "planarfigure.line.width": width of "line" segments (float value, in mm) *
    • "planarfigure.shadow.widthmodifier": the width of the shadow is defined by width of the "line" * this modifier *
    • "planarfigure.outline.width": width of "outline" segments (float value, in mm) *
    • "planarfigure.helperline.width": width of "helperline" segments (float value, in mm) *
    *
  3. Color/opacity of planar figure elements in normal mode (unselected) *
      *
    • "planarfigure.default.line.color" *
    • "planarfigure.default.line.opacity" *
    • "planarfigure.default.outline.color" *
    • "planarfigure.default.outline.opacity" *
    • "planarfigure.default.helperline.color" *
    • "planarfigure.default.helperline.opacity" *
    • "planarfigure.default.markerline.color" *
    • "planarfigure.default.markerline.opacity" *
    • "planarfigure.default.marker.color" *
    • "planarfigure.default.marker.opacity" *
    *
  4. Color/opacity of planar figure elements in hover mode (mouse-over) *
      *
    • "planarfigure.hover.line.color" *
    • "planarfigure.hover.line.opacity" *
    • "planarfigure.hover.outline.color" *
    • "planarfigure.hover.outline.opacity" *
    • "planarfigure.hover.helperline.color" *
    • "planarfigure.hover.helperline.opacity" *
    • "planarfigure.hover.markerline.color" *
    • "planarfigure.hover.markerline.opacity" *
    • "planarfigure.hover.marker.color" *
    • "planarfigure.hover.marker.opacity" *
    *
  5. Color/opacity of planar figure elements in selected mode *
      *
    • "planarfigure.selected.line.color" *
    • "planarfigure.selected.line.opacity" *
    • "planarfigure.selected.outline.color" *
    • "planarfigure.selected.outline.opacity" *
    • "planarfigure.selected.helperline.color" *
    • "planarfigure.selected.helperline.opacity" *
    • "planarfigure.selected.markerline.color;" *
    • "planarfigure.selected.markerline.opacity" *
    • "planarfigure.selected.marker.color" *
    • "planarfigure.selected.marker.opacity" *
    *
* * \ingroup Mapper */ class PlanarFigure_EXPORT PlanarFigureMapper2D : public GLMapper { public: mitkClassMacro(PlanarFigureMapper2D, GLMapper); itkNewMacro(Self); /** * reimplemented from Baseclass */ virtual void Paint(BaseRenderer * renderer); static void SetDefaultProperties(mitk::DataNode* node, mitk::BaseRenderer* renderer = NULL, bool overwrite = false); protected: enum PlanarFigureDisplayMode { PF_DEFAULT = 0, PF_HOVER = 1, PF_SELECTED = 2, PF_COUNT = 3 //helper variable }; PlanarFigureMapper2D(); virtual ~PlanarFigureMapper2D(); /** * \brief Renders all the lines defined by the PlanarFigure. * * This method renders all the lines that are defined by the PlanarFigure. * That includes the mainlines and helperlines as well as their shadows * and the outlines. * * This method already takes responsibility for the setting of the relevant * openGL attributes to reduce unnecessary setting of these attributes. * (e.g. no need to set color twice if it's the same) */ void RenderLines( PlanarFigureDisplayMode lineDisplayMode, mitk::PlanarFigure * planarFigure, mitk::Point2D &anchorPoint, - mitk::Geometry2D * planarFigureGeometry2D, - const mitk::Geometry2D * rendererGeometry2D, + mitk::PlaneGeometry * planarFigureGeometry2D, + const mitk::PlaneGeometry * rendererGeometry2D, mitk::DisplayGeometry * displayGeometry ); /** * \brief Renders the quantities of the figure below the text annotations. */ void RenderQuantities( mitk::PlanarFigure * planarFigure, mitk::BaseRenderer * renderer, mitk::Point2D anchorPoint, double &annotationOffset, float globalOpacity, PlanarFigureDisplayMode lineDisplayMode ); /** * \brief Renders the text annotations. */ void RenderAnnotations( mitk::BaseRenderer * renderer, std::string name, mitk::Point2D anchorPoint, float globalOpacity, PlanarFigureDisplayMode lineDisplayMode, double &annotationOffset ); /** * \brief Renders the control-points. */ void RenderControlPoints( mitk::PlanarFigure * planarFigure, PlanarFigureDisplayMode lineDisplayMode, - mitk::Geometry2D * planarFigureGeometry2D, - const mitk::Geometry2D * rendererGeometry2D, + mitk::PlaneGeometry * planarFigureGeometry2D, + const mitk::PlaneGeometry * rendererGeometry2D, mitk::DisplayGeometry * displayGeometry ); void TransformObjectToDisplay( const mitk::Point2D &point2D, mitk::Point2D &displayPoint, - const mitk::Geometry2D *objectGeometry, - const mitk::Geometry2D *rendererGeometry, + const mitk::PlaneGeometry *objectGeometry, + const mitk::PlaneGeometry *rendererGeometry, const mitk::DisplayGeometry *displayGeometry ); void DrawMarker( const mitk::Point2D &point, float* lineColor, float lineOpacity, float* markerColor, float markerOpacity, float lineWidth, PlanarFigureControlPointStyleProperty::Shape shape, - const mitk::Geometry2D *objectGeometry, - const mitk::Geometry2D *rendererGeometry, + const mitk::PlaneGeometry *objectGeometry, + const mitk::PlaneGeometry *rendererGeometry, const mitk::DisplayGeometry *displayGeometry ); /** * \brief Actually paints the polyline defined by the figure. */ void PaintPolyLine( mitk::PlanarFigure::PolyLineType vertices, bool closed, Point2D& anchorPoint, - const Geometry2D* planarFigureGeometry2D, - const Geometry2D* rendererGeometry2D, + const PlaneGeometry* planarFigureGeometry2D, + const PlaneGeometry* rendererGeometry2D, const DisplayGeometry* displayGeometry); /** * \brief Internally used by RenderLines() to draw the mainlines using * PaintPolyLine(). */ void DrawMainLines( mitk::PlanarFigure* figure, Point2D& anchorPoint, - const Geometry2D* planarFigureGeometry2D, - const Geometry2D* rendererGeometry2D, + const PlaneGeometry* planarFigureGeometry2D, + const PlaneGeometry* rendererGeometry2D, const DisplayGeometry* displayGeometry) ; /** * \brief Internally used by RenderLines() to draw the helperlines using * PaintPolyLine(). */ void DrawHelperLines( mitk::PlanarFigure* figure, Point2D& anchorPoint, - const Geometry2D* planarFigureGeometry2D, - const Geometry2D* rendererGeometry2D, + const PlaneGeometry* planarFigureGeometry2D, + const PlaneGeometry* rendererGeometry2D, const DisplayGeometry* displayGeometry) ; void InitializeDefaultPlanarFigureProperties(); void InitializePlanarFigurePropertiesFromDataNode( const mitk::DataNode* node ); void SetColorProperty( float property[3][3], PlanarFigureDisplayMode mode, float red, float green, float blue ) { property[mode][0] = red; property[mode][1] = green; property[mode][2] = blue; } void SetFloatProperty( float* property, PlanarFigureDisplayMode mode, float value ) { property[mode] = value; } /** * \brief Callback that sets m_NodeModified to true. * * This method set the bool flag m_NodeModified to true. It's a callback * that is executed when a itk::ModifiedEvet is invoked on our * DataNode. */ void OnNodeModified(); private: bool m_IsSelected; bool m_IsHovering; bool m_DrawOutline; bool m_DrawQuantities; bool m_DrawShadow; bool m_DrawControlPoints; bool m_DrawName; bool m_DrawDashed; // the width of the shadow is defined as 'm_LineWidth * m_ShadowWidthFactor' float m_LineWidth; float m_ShadowWidthFactor; float m_OutlineWidth; float m_HelperlineWidth; //float m_PointWidth; PlanarFigureControlPointStyleProperty::Shape m_ControlPointShape; float m_LineColor[3][3]; float m_LineOpacity[3]; float m_OutlineColor[3][3]; float m_OutlineOpacity[3]; float m_HelperlineColor[3][3]; float m_HelperlineOpacity[3]; float m_MarkerlineColor[3][3]; float m_MarkerlineOpacity[3]; float m_MarkerColor[3][3]; float m_MarkerOpacity[3]; // Bool flag that represents whether or not the DataNode has been modified. bool m_NodeModified; // Observer-tag for listening to itk::ModifiedEvents on the DataNode unsigned long m_NodeModifiedObserverTag; // Bool flag that indicates if a node modified observer was added bool m_NodeModifiedObserverAdded; }; } // namespace mitk #endif /* MITK_PLANAR_FIGURE_MAPPER_2D_H_ */ diff --git a/Modules/PlanarFigureSegmentation/mitkPlanarFigureSegmentationController.cpp b/Modules/PlanarFigureSegmentation/mitkPlanarFigureSegmentationController.cpp index c08ba01651..182cb4d291 100644 --- a/Modules/PlanarFigureSegmentation/mitkPlanarFigureSegmentationController.cpp +++ b/Modules/PlanarFigureSegmentation/mitkPlanarFigureSegmentationController.cpp @@ -1,308 +1,308 @@ /*=================================================================== 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 "mitkPlanarFigureSegmentationController.h" #include "mitkSurfaceToImageFilter.h" #include #include #include #include #include #include #include "mitkImageWriter.h" #include "mitkSurfaceVtkWriter.h" #include "mitkImageToSurfaceFilter.h" #include "mitkImageAccessByItk.h" #include "mitkImageCast.h" mitk::PlanarFigureSegmentationController::PlanarFigureSegmentationController() : itk::Object() , m_ReduceFilter( NULL ) , m_NormalsFilter( NULL ) , m_DistanceImageCreator( NULL ) , m_ReferenceImage( NULL ) , m_SegmentationAsImage( NULL ) { InitializeFilters(); } mitk::PlanarFigureSegmentationController::~PlanarFigureSegmentationController() { } void mitk::PlanarFigureSegmentationController::SetReferenceImage( mitk::Image::Pointer referenceImage ) { m_ReferenceImage = referenceImage; } void mitk::PlanarFigureSegmentationController::AddPlanarFigure( mitk::PlanarFigure::Pointer planarFigure ) { if ( planarFigure.IsNull() ) return; bool newFigure = true; std::size_t indexOfFigure = 0; for( std::size_t i=0; iCreateSurfaceFromPlanarFigure( planarFigure ); m_SurfaceList.push_back( figureAsSurface ); if (!m_PlanarFigureList.empty()) { indexOfFigure = m_PlanarFigureList.size() -1 ; } } else { figureAsSurface = this->CreateSurfaceFromPlanarFigure( planarFigure ); m_SurfaceList.at(indexOfFigure) = figureAsSurface; } m_ReduceFilter->SetInput( indexOfFigure, figureAsSurface ); m_NormalsFilter->SetInput( indexOfFigure, m_ReduceFilter->GetOutput( indexOfFigure ) ); m_DistanceImageCreator->SetInput( indexOfFigure, m_NormalsFilter->GetOutput( indexOfFigure ) ); } void mitk::PlanarFigureSegmentationController::RemovePlanarFigure( mitk::PlanarFigure::Pointer planarFigure ) { if ( planarFigure.IsNull() ) return; bool figureFound = false; std::size_t indexOfFigure = 0; for( std::size_t i=0; iRemoveInputs( m_NormalsFilter->GetOutput( indexOfFigure ) ); // m_NormalsFilter->RemoveInput( m_ReduceFilter->GetOutput( indexOfFigure ) ); // m_ReduceFilter->RemoveInput( const_cast(m_ReduceFilter->GetInput(indexOfFigure)) ); } else { // this is not very nice! If the figure that has been removed is NOT the last // one in the list we have to create new filters and add all remaining // inputs again. // // Has to be done as the filters do not work when removing an input // other than the last one. // create new filters InitializeFilters(); // and add all existing surfaces SurfaceListType::iterator surfaceIter = m_SurfaceList.begin(); for ( surfaceIter = m_SurfaceList.begin(); surfaceIter!=m_SurfaceList.end(); surfaceIter++ ) { m_ReduceFilter->SetInput( indexOfFigure, (*surfaceIter) ); m_NormalsFilter->SetInput( indexOfFigure, m_ReduceFilter->GetOutput( indexOfFigure ) ); m_DistanceImageCreator->SetInput( indexOfFigure, m_NormalsFilter->GetOutput( indexOfFigure ) ); } } PlanarFigureListType::iterator whereIter = m_PlanarFigureList.begin(); whereIter += indexOfFigure; m_PlanarFigureList.erase( whereIter ); SurfaceListType::iterator surfaceIter = m_SurfaceList.begin(); surfaceIter += indexOfFigure; m_SurfaceList.erase( surfaceIter ); } template void mitk::PlanarFigureSegmentationController::GetImageBase(itk::Image* input, itk::ImageBase<3>::Pointer& result) { result = input; } mitk::Image::Pointer mitk::PlanarFigureSegmentationController::GetInterpolationResult() { m_SegmentationAsImage = NULL; if ( m_PlanarFigureList.size() == 0 ) { m_SegmentationAsImage = mitk::Image::New(); m_SegmentationAsImage->Initialize(mitk::MakeScalarPixelType() , *m_ReferenceImage->GetTimeGeometry()); return m_SegmentationAsImage; } itk::ImageBase<3>::Pointer itkImage; AccessFixedDimensionByItk_1( m_ReferenceImage.GetPointer(), GetImageBase, 3, itkImage ); m_DistanceImageCreator->SetReferenceImage( itkImage.GetPointer() ); m_ReduceFilter->Update(); m_NormalsFilter->Update(); m_DistanceImageCreator->Update(); mitk::Image::Pointer distanceImage = m_DistanceImageCreator->GetOutput(); // Cleanup the pipeline distanceImage->DisconnectPipeline(); m_DistanceImageCreator = NULL; m_NormalsFilter = NULL; m_ReduceFilter = NULL; itkImage = NULL; // If this bool flag is true, the distanceImage will be written to the // filesystem as nrrd-image and as surface-representation. bool debugOutput(false); if ( debugOutput ) { mitk::ImageWriter::Pointer imageWriter = mitk::ImageWriter::New(); imageWriter->SetInput( distanceImage ); imageWriter->SetExtension( ".nrrd" ); imageWriter->SetFileName( "v:/DistanceImage" ); imageWriter->Update(); } mitk::ImageToSurfaceFilter::Pointer imageToSurfaceFilter = mitk::ImageToSurfaceFilter::New(); imageToSurfaceFilter->SetInput( distanceImage ); imageToSurfaceFilter->SetThreshold( 0 ); imageToSurfaceFilter->Update(); mitk::Surface::Pointer segmentationAsSurface = imageToSurfaceFilter->GetOutput(); // Cleanup the pipeline segmentationAsSurface->DisconnectPipeline(); imageToSurfaceFilter = NULL; if ( debugOutput ) { mitk::SurfaceVtkWriter::Pointer surfaceWriter = mitk::SurfaceVtkWriter::New(); surfaceWriter->SetInput( segmentationAsSurface ); surfaceWriter->SetExtension( ".vtk" ); surfaceWriter->SetFileName( "v:/DistanceImageAsSurface.vtk" ); surfaceWriter->Update(); } mitk::SurfaceToImageFilter::Pointer surfaceToImageFilter = mitk::SurfaceToImageFilter::New(); surfaceToImageFilter->SetInput( segmentationAsSurface ); surfaceToImageFilter->SetImage( m_ReferenceImage ); surfaceToImageFilter->SetMakeOutputBinary(true); surfaceToImageFilter->Update(); m_SegmentationAsImage = surfaceToImageFilter->GetOutput(); // Cleanup the pipeline m_SegmentationAsImage->DisconnectPipeline(); return m_SegmentationAsImage; } mitk::Surface::Pointer mitk::PlanarFigureSegmentationController::CreateSurfaceFromPlanarFigure( mitk::PlanarFigure::Pointer figure ) { if ( figure.IsNull() ) { MITK_ERROR << "Given PlanarFigure is NULL. Please provide valid PlanarFigure."; return NULL; } mitk::Surface::Pointer newSurface = mitk::Surface::New(); vtkSmartPointer points = vtkSmartPointer::New(); vtkSmartPointer polygon = vtkSmartPointer::New(); vtkSmartPointer cells = vtkSmartPointer::New(); vtkSmartPointer polyData = vtkSmartPointer::New(); - const mitk::Geometry2D* figureGeometry = figure->GetGeometry2D(); + const mitk::PlaneGeometry* figureGeometry = figure->GetGeometry2D(); // Get the polyline mitk::PlanarFigure::PolyLineType planarPolyLine = figure->GetPolyLine(0); mitk::PlanarFigure::PolyLineType::iterator iter; // iterate over the polyline, ... int pointCounter = 0; for( iter = planarPolyLine.begin(); iter != planarPolyLine.end(); iter++ ) { // ... determine the world-coordinates mitk::Point2D polyLinePoint = iter->Point; mitk::Point3D pointInWorldCoordiantes; figureGeometry->Map( polyLinePoint, pointInWorldCoordiantes ); // and add them as new points to the vtkPoints points->InsertNextPoint( pointInWorldCoordiantes[0], pointInWorldCoordiantes[1], pointInWorldCoordiantes[2] ); ++pointCounter; } // create a polygon with the points of the polyline polygon->GetPointIds()->SetNumberOfIds( pointCounter ); for(int i = 0; i < pointCounter; i++) { polygon->GetPointIds()->SetId(i,i); } // initialize the vtkCellArray and vtkPolyData cells->InsertNextCell(polygon); polyData->SetPoints(points); polyData->SetPolys( cells ); // set the polydata to the surface newSurface->SetVtkPolyData( polyData ); return newSurface; } mitk::PlanarFigureSegmentationController::PlanarFigureListType mitk::PlanarFigureSegmentationController::GetAllPlanarFigures() { return m_PlanarFigureList; } void mitk::PlanarFigureSegmentationController::InitializeFilters() { m_ReduceFilter = mitk::ReduceContourSetFilter::New(); m_ReduceFilter->SetReductionType(ReduceContourSetFilter::NTH_POINT); m_ReduceFilter->SetStepSize( 10 ); m_NormalsFilter = mitk::ComputeContourSetNormalsFilter::New(); m_DistanceImageCreator = mitk::CreateDistanceImageFromSurfaceFilter::New(); } diff --git a/Modules/QmitkExt/QmitkHistogramJSWidget.cpp b/Modules/QmitkExt/QmitkHistogramJSWidget.cpp index 021e4c9889..c87b11e372 100644 --- a/Modules/QmitkExt/QmitkHistogramJSWidget.cpp +++ b/Modules/QmitkExt/QmitkHistogramJSWidget.cpp @@ -1,303 +1,303 @@ /*=================================================================== 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 "QmitkHistogramJSWidget.h" #include "mitkPixelTypeMultiplex.h" #include #include "mitkRenderingManager.h" #include "mitkBaseRenderer.h" #include "mitkImageTimeSelector.h" #include "mitkExtractSliceFilter.h" QmitkHistogramJSWidget::QmitkHistogramJSWidget(QWidget *parent) : QWebView(parent) { // set histogram type to barchart in first instance m_UseLineGraph = false; m_Page = new QmitkJSWebPage(this); setPage(m_Page); // set html from source connect(page()->mainFrame(), SIGNAL(javaScriptWindowObjectCleared()), this, SLOT(AddJSObject())); QUrl myUrl = QUrl("qrc:/qmitk/Histogram.html"); setUrl(myUrl); // set Scrollbars to be always disabled page()->mainFrame()->setScrollBarPolicy(Qt::Horizontal, Qt::ScrollBarAlwaysOff); page()->mainFrame()->setScrollBarPolicy(Qt::Vertical, Qt::ScrollBarAlwaysOff); m_ParametricPath = ParametricPathType::New(); } QmitkHistogramJSWidget::~QmitkHistogramJSWidget() { } // adds an Object of Type QmitkHistogramJSWidget to the JavaScript, using QtWebkitBridge void QmitkHistogramJSWidget::AddJSObject() { page()->mainFrame()->addToJavaScriptWindowObject(QString("histogramData"), this); } // reloads WebView, everytime its size has been changed, so the size of the Histogram fits to the size of the widget void QmitkHistogramJSWidget::resizeEvent(QResizeEvent* resizeEvent) { QWebView::resizeEvent(resizeEvent); // workaround for Qt Bug: https://bugs.webkit.org/show_bug.cgi?id=75984 page()->mainFrame()->evaluateJavaScript("disconnectSignals()"); this->reload(); } // method to expose data to JavaScript by using properties void QmitkHistogramJSWidget::ComputeHistogram(HistogramType* histogram) { m_Histogram = histogram; HistogramConstIteratorType startIt = m_Histogram->End(); HistogramConstIteratorType endIt = m_Histogram->End(); HistogramConstIteratorType it = m_Histogram->Begin(); ClearData(); unsigned int i = 0; bool firstValue = false; // removes frequencies of 0, which are outside the first and last bin for (; it != m_Histogram->End(); ++it) { if (it.GetFrequency() > 0.0) { endIt = it; if (!firstValue) { firstValue = true; startIt = it; } } } ++endIt; // generating Lists of measurement and frequencies for (it = startIt ; it != endIt; ++it, ++i) { QVariant frequency = QVariant::fromValue(it.GetFrequency()); QVariant measurement = it.GetMeasurementVector()[0]; m_Frequency.insert(i, frequency); m_Measurement.insert(i, measurement); } m_IntensityProfile = false; this->SignalDataChanged(); } void QmitkHistogramJSWidget::ClearData() { m_Frequency.clear(); m_Measurement.clear(); } void QmitkHistogramJSWidget::ClearHistogram() { this->ClearData(); this->SignalDataChanged(); } QList QmitkHistogramJSWidget::GetFrequency() { return m_Frequency; } QList QmitkHistogramJSWidget::GetMeasurement() { return m_Measurement; } bool QmitkHistogramJSWidget::GetUseLineGraph() { return m_UseLineGraph; } void QmitkHistogramJSWidget::OnBarRadioButtonSelected() { if (m_UseLineGraph) { m_UseLineGraph = false; this->SignalGraphChanged(); } } void QmitkHistogramJSWidget::OnLineRadioButtonSelected() { if (!m_UseLineGraph) { m_UseLineGraph = true; this->SignalGraphChanged(); } } void QmitkHistogramJSWidget::SetImage(mitk::Image* image) { m_Image = image; } void QmitkHistogramJSWidget::SetPlanarFigure(const mitk::PlanarFigure* planarFigure) { m_PlanarFigure = planarFigure; } template void ReadPixel(mitk::PixelType ptype, mitk::Image::Pointer image, mitk::Index3D indexPoint, double& value) { if (image->GetDimension() == 2) { mitk::ImagePixelReadAccessor readAccess(image, image->GetSliceData(0)); itk::Index<2> idx; idx[0] = indexPoint[0]; idx[1] = indexPoint[1]; value = readAccess.GetPixelByIndex(idx); } else if (image->GetDimension() == 3) { mitk::ImagePixelReadAccessor readAccess(image, image->GetVolumeData(0)); itk::Index<3> idx; idx[0] = indexPoint[0]; idx[1] = indexPoint[1]; idx[2] = indexPoint[2]; value = readAccess.GetPixelByIndex(idx); } else { //unhandled } } void QmitkHistogramJSWidget::ComputeIntensityProfile(unsigned int timeStep) { this->ClearData(); m_ParametricPath->Initialize(); if (m_PlanarFigure.IsNull()) { mitkThrow() << "PlanarFigure not set!"; } if (m_Image.IsNull()) { mitkThrow() << "Image not set!"; } // Get 2D geometry frame of PlanarFigure - mitk::Geometry2D* planarFigureGeometry2D = dynamic_cast(m_PlanarFigure->GetGeometry(0)); + mitk::PlaneGeometry* planarFigureGeometry2D = dynamic_cast(m_PlanarFigure->GetGeometry(0)); if (planarFigureGeometry2D == NULL) { mitkThrow() << "PlanarFigure has no valid geometry!"; } // Get 3D geometry from Image (needed for conversion of point to index) mitk::BaseGeometry* imageGeometry = m_Image->GetGeometry(0); if (imageGeometry == NULL) { mitkThrow() << "Image has no valid geometry!"; } // Get first poly-line of PlanarFigure (other possible poly-lines in PlanarFigure // are not supported) const VertexContainerType vertexContainer = m_PlanarFigure->GetPolyLine(0); VertexContainerType::const_iterator it; for (it = vertexContainer.begin(); it != vertexContainer.end(); ++it) { // Map PlanarFigure 2D point to 3D point mitk::Point3D point3D; planarFigureGeometry2D->Map(it->Point, point3D); // Convert world to index coordinates mitk::Point3D indexPoint3D; imageGeometry->WorldToIndex(point3D, indexPoint3D); ParametricPathType::OutputType index; index[0] = indexPoint3D[0]; index[1] = indexPoint3D[1]; index[2] = indexPoint3D[2]; // Add index to parametric path m_ParametricPath->AddVertex(index); } m_DerivedPath = m_ParametricPath; if (m_DerivedPath.IsNull()) { mitkThrow() << "No path set!"; } // Fill item model with line profile data double distance = 0.0; mitk::Point3D currentWorldPoint; double t; unsigned int i = 0; for (i = 0, t = m_DerivedPath->StartOfInput(); ;++i) { const PathType::OutputType &continousIndex = m_DerivedPath->Evaluate(t); mitk::Point3D worldPoint; imageGeometry->IndexToWorld(continousIndex, worldPoint); if (i == 0) { currentWorldPoint = worldPoint; } distance += currentWorldPoint.EuclideanDistanceTo(worldPoint); mitk::Index3D indexPoint; imageGeometry->WorldToIndex(worldPoint, indexPoint); const mitk::PixelType ptype = m_Image->GetPixelType(); double intensity = 0.0; if (m_Image->GetDimension() == 4) { mitk::ImageTimeSelector::Pointer timeSelector = mitk::ImageTimeSelector::New(); timeSelector->SetInput(m_Image); timeSelector->SetTimeNr(timeStep); timeSelector->Update(); mitk::Image::Pointer image = timeSelector->GetOutput(); mitkPixelTypeMultiplex3( ReadPixel, ptype, image, indexPoint, intensity); } else { mitkPixelTypeMultiplex3( ReadPixel, ptype, m_Image, indexPoint, intensity); } m_Measurement.insert(i, distance); m_Frequency.insert(i, intensity); // Go to next index; when iteration offset reaches zero, iteration is finished PathType::OffsetType offset = m_DerivedPath->IncrementInput(t); if (!(offset[0] || offset[1] || offset[2])) { break; } currentWorldPoint = worldPoint; } m_IntensityProfile = true; m_UseLineGraph = true; this->SignalDataChanged(); } bool QmitkHistogramJSWidget::GetIntensityProfile() { return m_IntensityProfile; } diff --git a/Modules/Segmentation/Algorithms/mitkDiffSliceOperation.cpp b/Modules/Segmentation/Algorithms/mitkDiffSliceOperation.cpp index b50b971cc5..a3868317d6 100644 --- a/Modules/Segmentation/Algorithms/mitkDiffSliceOperation.cpp +++ b/Modules/Segmentation/Algorithms/mitkDiffSliceOperation.cpp @@ -1,104 +1,104 @@ /*=================================================================== 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 "mitkDiffSliceOperation.h" #include mitk::DiffSliceOperation::DiffSliceOperation():Operation(1) { m_TimeStep = 0; m_Slice = NULL; m_Image = NULL; m_WorldGeometry = NULL; m_SliceGeometry = NULL; m_ImageIsValid = false; } mitk::DiffSliceOperation::DiffSliceOperation(mitk::Image* imageVolume, vtkImageData* slice, BaseGeometry* sliceGeometry, unsigned int timestep, BaseGeometry* currentWorldGeometry):Operation(1) { m_WorldGeometry = currentWorldGeometry->Clone(); /* Quick fix for bug 12338. Guard object - fix this when clone method of PlaneGeometry is cloning the reference geometry (see bug 13392)*/ m_GuardReferenceGeometry = mitk::BaseGeometry::New(); - m_GuardReferenceGeometry = dynamic_cast(m_WorldGeometry.GetPointer())->GetReferenceGeometry(); + m_GuardReferenceGeometry = dynamic_cast(m_WorldGeometry.GetPointer())->GetReferenceGeometry(); /*---------------------------------------------------------------------------------------------------*/ m_SliceGeometry = sliceGeometry->Clone(); m_TimeStep = timestep; /*m_zlibSliceContainer = CompressedImageContainer::New(); m_zlibSliceContainer->SetImage( slice );*/ m_Slice = vtkSmartPointer::New(); m_Slice->DeepCopy(slice); m_Image = imageVolume; if ( m_Image) { /*add an observer to listen to the delete event of the image, this is necessary because the operation is then invalid*/ itk::SimpleMemberCommand< DiffSliceOperation >::Pointer command = itk::SimpleMemberCommand< DiffSliceOperation >::New(); command->SetCallbackFunction( this, &DiffSliceOperation::OnImageDeleted ); //get the id of the observer, used to remove it later on m_DeleteObserverTag = imageVolume->AddObserver( itk::DeleteEvent(), command ); m_ImageIsValid = true; } else m_ImageIsValid = false; } mitk::DiffSliceOperation::~DiffSliceOperation() { m_Slice = NULL; m_WorldGeometry = NULL; //m_zlibSliceContainer = NULL; if (m_ImageIsValid) { //if the image is still there, we have to remove the observer from it m_Image->RemoveObserver( m_DeleteObserverTag ); } m_Image = NULL; } vtkImageData* mitk::DiffSliceOperation::GetSlice() { //Image::ConstPointer image = m_zlibSliceContainer->GetImage().GetPointer(); return m_Slice; } bool mitk::DiffSliceOperation::IsValid() { return m_ImageIsValid && (m_Slice.GetPointer() != NULL) && (m_WorldGeometry.IsNotNull());//TODO improve } void mitk::DiffSliceOperation::OnImageDeleted() { //if our imageVolume is removed e.g. from the datastorage the operation is no lnger valid m_ImageIsValid = false; } \ No newline at end of file diff --git a/Modules/Segmentation/Algorithms/mitkDiffSliceOperation.h b/Modules/Segmentation/Algorithms/mitkDiffSliceOperation.h index 9125382f59..c3f01ef5c7 100644 --- a/Modules/Segmentation/Algorithms/mitkDiffSliceOperation.h +++ b/Modules/Segmentation/Algorithms/mitkDiffSliceOperation.h @@ -1,139 +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 mitkDiffSliceOperation_h_Included #define mitkDiffSliceOperation_h_Included #include "SegmentationExports.h" #include "mitkCommon.h" #include //#include "mitkCompressedImageContainer.h" #include #include #include //DEPRECATED #include namespace mitk { /** \brief An Operation for applying an edited slice to the volume. \sa DiffSliceOperationApplier The information for the operation is specified by properties: imageVolume the volume where the slice was extracted from. slice the slice to be applied. timestep the timestep in an 4D image. currentWorldGeometry specifies the axis where the slice has to be applied in the volume. This Operation can be used to realize undo-redo functionality for e.g. segmentation purposes. */ class Segmentation_EXPORT DiffSliceOperation : public Operation { public: mitkClassMacro(DiffSliceOperation, OperationActor); //itkNewMacro(DiffSliceOperation); - //mitkNewMacro4Param(DiffSliceOperation,mitk::Image,mitk::Image,unsigned int, mitk::Geometry2D); + //mitkNewMacro4Param(DiffSliceOperation,mitk::Image,mitk::Image,unsigned int, mitk::PlaneGeometry); /** \brief Creates an empty instance. Note that it is not valid yet. The properties of the object have to be set. */ DiffSliceOperation(); /** \brief */ DiffSliceOperation( mitk::Image* imageVolume, vtkImageData* slice, BaseGeometry* sliceGeometry, unsigned int timestep, BaseGeometry* currentWorldGeometry); /** \brief * * \deprecatedSince{2013_09} Please use TimeGeometry instead of TimeSlicedGeometry. For more information see http://www.mitk.org/Development/Refactoring%20of%20the%20Geometry%20Classes%20-%20Part%201 */ DEPRECATED(DiffSliceOperation( mitk::Image* imageVolume, vtkImageData* slice, TimeSlicedGeometry* sliceGeometry, unsigned int timestep, BaseGeometry* currentWorldGeometry)); /** \brief Check if it is a valid operation.*/ bool IsValid(); /** \brief Set the image volume.*/ void SetImage(mitk::Image* image){ this->m_Image = image;} /** \brief Get th image volume.*/ mitk::Image* GetImage(){return this->m_Image;} /** \brief Set thee slice to be applied.*/ void SetImage(vtkImageData* slice){ this->m_Slice = slice;} /** \brief Get the slice that is applied in the operation.*/ vtkImageData* GetSlice(); /** \brief Get timeStep.*/ void SetTimeStep(unsigned int timestep){this->m_TimeStep = timestep;} /** \brief Set timeStep*/ unsigned int GetTimeStep(){return this->m_TimeStep;} /** \brief Set the axis where the slice has to be applied in the volume.*/ void SetSliceGeometry(BaseGeometry* sliceGeometry){this->m_SliceGeometry = sliceGeometry;} /** \brief Get the axis where the slice has to be applied in the volume.*/ BaseGeometry* GetSliceGeometry(){return this->m_SliceGeometry;} /** \brief Set the axis where the slice has to be applied in the volume. * \deprecatedSince{2013_09} Please use TimeGeometry instead of TimeSlicedGeometry. For more information see http://www.mitk.org/Development/Refactoring%20of%20the%20Geometry%20Classes%20-%20Part%201 */ void SetSliceGeometry(TimeSlicedGeometry* sliceGeometry); /** \brief Set the axis where the slice has to be applied in the volume.*/ void SetCurrentWorldGeometry(BaseGeometry* worldGeometry){this->m_WorldGeometry = worldGeometry;} /** \brief Get the axis where the slice has to be applied in the volume.*/ BaseGeometry* GetWorldGeometry(){return this->m_WorldGeometry;} /** \brief Set the axis where the slice has to be applied in the volume. * \deprecatedSince{2013_09} Please use TimeGeometry instead of TimeSlicedGeometry. For more information see http://www.mitk.org/Development/Refactoring%20of%20the%20Geometry%20Classes%20-%20Part%201 */ void SetCurrentWorldGeometry(TimeSlicedGeometry* worldGeometry); protected: virtual ~DiffSliceOperation(); /** \brief Callback for image observer.*/ void OnImageDeleted(); //CompressedImageContainer::Pointer m_zlibSliceContainer; mitk::Image* m_Image; vtkSmartPointer m_Slice; BaseGeometry::Pointer m_SliceGeometry; unsigned int m_TimeStep; BaseGeometry::Pointer m_WorldGeometry; bool m_ImageIsValid; unsigned long m_DeleteObserverTag; mitk::BaseGeometry::Pointer m_GuardReferenceGeometry; }; } #endif diff --git a/Modules/Segmentation/Algorithms/mitkDiffSliceOperationApplier.cpp b/Modules/Segmentation/Algorithms/mitkDiffSliceOperationApplier.cpp index 4c5610dc1f..ecc79ea58b 100644 --- a/Modules/Segmentation/Algorithms/mitkDiffSliceOperationApplier.cpp +++ b/Modules/Segmentation/Algorithms/mitkDiffSliceOperationApplier.cpp @@ -1,76 +1,76 @@ /*=================================================================== 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 "mitkDiffSliceOperationApplier.h" #include "mitkRenderingManager.h" #include mitk::DiffSliceOperationApplier::DiffSliceOperationApplier() { } mitk::DiffSliceOperationApplier::~DiffSliceOperationApplier() { } void mitk::DiffSliceOperationApplier::ExecuteOperation( Operation* operation ) { DiffSliceOperation* imageOperation = dynamic_cast( operation ); //as we only support DiffSliceOperation return if operation is not type of DiffSliceOperation if(!imageOperation) return; //chak if the operation is valid if(imageOperation->IsValid()) { //the actual overwrite filter (vtk) vtkSmartPointer reslice = vtkSmartPointer::New(); //Set the slice as 'input' reslice->SetInputSlice(imageOperation->GetSlice()); //set overwrite mode to true to write back to the image volume reslice->SetOverwriteMode(true); reslice->Modified(); //a wrapper for vtkImageOverwrite mitk::ExtractSliceFilter::Pointer extractor = mitk::ExtractSliceFilter::New(reslice); extractor->SetInput( imageOperation->GetImage() ); extractor->SetTimeStep( imageOperation->GetTimeStep() ); - extractor->SetWorldGeometry( dynamic_cast(imageOperation->GetWorldGeometry()) ); + extractor->SetWorldGeometry( dynamic_cast(imageOperation->GetWorldGeometry()) ); extractor->SetVtkOutputRequest(true); extractor->SetResliceTransformByGeometry( imageOperation->GetImage()->GetGeometry( imageOperation->GetTimeStep() ) ); extractor->Modified(); extractor->Update(); //make sure the modification is rendered RenderingManager::GetInstance()->RequestUpdateAll(); imageOperation->GetImage()->Modified(); } } //mitk::DiffSliceOperationApplier* mitk::DiffSliceOperationApplier::s_Instance = NULL; mitk::DiffSliceOperationApplier* mitk::DiffSliceOperationApplier::GetInstance() { //if(!s_Instance) static DiffSliceOperationApplier* s_Instance = new DiffSliceOperationApplier(); return s_Instance; } \ No newline at end of file diff --git a/Modules/Segmentation/Interactions/mitkSegTool2D.cpp b/Modules/Segmentation/Interactions/mitkSegTool2D.cpp index 2ae3764b42..4e8dbed00e 100644 --- a/Modules/Segmentation/Interactions/mitkSegTool2D.cpp +++ b/Modules/Segmentation/Interactions/mitkSegTool2D.cpp @@ -1,402 +1,402 @@ /*=================================================================== 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 "mitkSegTool2D.h" #include "mitkToolManager.h" #include "mitkDataStorage.h" #include "mitkBaseRenderer.h" #include "mitkPlaneGeometry.h" #include "mitkExtractImageFilter.h" #include "mitkExtractDirectedPlaneImageFilter.h" //Include of the new ImageExtractor #include "mitkExtractDirectedPlaneImageFilterNew.h" #include "mitkPlanarCircle.h" #include "mitkOverwriteSliceImageFilter.h" #include "mitkOverwriteDirectedPlaneImageFilter.h" #include "usGetModuleContext.h" //Includes for 3DSurfaceInterpolation #include "mitkImageToContourFilter.h" #include "mitkSurfaceInterpolationController.h" //includes for resling and overwriting #include #include #include #include #include #include "mitkOperationEvent.h" #include "mitkUndoController.h" #define ROUND(a) ((a)>0 ? (int)((a)+0.5) : -(int)(0.5-(a))) mitk::SegTool2D::SegTool2D(const char* type) :Tool(type), m_LastEventSender(NULL), m_LastEventSlice(0), m_Contourmarkername ("Position"), m_ShowMarkerNodes (false), m_3DInterpolationEnabled(true) { } mitk::SegTool2D::~SegTool2D() { } float mitk::SegTool2D::CanHandleEvent( StateEvent const *stateEvent) const { const PositionEvent* positionEvent = dynamic_cast(stateEvent->GetEvent()); if (!positionEvent) return 0.0; if ( positionEvent->GetSender()->GetMapperID() != BaseRenderer::Standard2D ) return 0.0; // we don't want anything but 2D //This are the mouse event that are used by the statemachine patterns for zooming and panning. This must be possible although a tool is activ if (stateEvent->GetId() == EIDRIGHTMOUSEBTN || stateEvent->GetId() == EIDMIDDLEMOUSEBTN || stateEvent->GetId() == EIDRIGHTMOUSEBTNANDCTRL || stateEvent->GetId() == EIDMIDDLEMOUSERELEASE || stateEvent->GetId() == EIDRIGHTMOUSERELEASE || stateEvent->GetId() == EIDRIGHTMOUSEBTNANDMOUSEMOVE || stateEvent->GetId() == EIDMIDDLEMOUSEBTNANDMOUSEMOVE || stateEvent->GetId() == EIDCTRLANDRIGHTMOUSEBTNANDMOUSEMOVE || stateEvent->GetId() == EIDCTRLANDRIGHTMOUSEBTNRELEASE ) { //Since the usual segmentation tools currently do not need right click interaction but the mitkDisplayVectorInteractor return 0.0; } else { return 1.0; } } bool mitk::SegTool2D::DetermineAffectedImageSlice( const Image* image, const PlaneGeometry* plane, int& affectedDimension, int& affectedSlice ) { assert(image); assert(plane); // compare normal of plane to the three axis vectors of the image Vector3D normal = plane->GetNormal(); Vector3D imageNormal0 = image->GetSlicedGeometry()->GetAxisVector(0); Vector3D imageNormal1 = image->GetSlicedGeometry()->GetAxisVector(1); Vector3D imageNormal2 = image->GetSlicedGeometry()->GetAxisVector(2); normal.Normalize(); imageNormal0.Normalize(); imageNormal1.Normalize(); imageNormal2.Normalize(); imageNormal0.SetVnlVector( vnl_cross_3d(normal.GetVnlVector(),imageNormal0.GetVnlVector()) ); imageNormal1.SetVnlVector( vnl_cross_3d(normal.GetVnlVector(),imageNormal1.GetVnlVector()) ); imageNormal2.SetVnlVector( vnl_cross_3d(normal.GetVnlVector(),imageNormal2.GetVnlVector()) ); double eps( 0.00001 ); // axial if ( imageNormal2.GetNorm() <= eps ) { affectedDimension = 2; } // sagittal else if ( imageNormal1.GetNorm() <= eps ) { affectedDimension = 1; } // frontal else if ( imageNormal0.GetNorm() <= eps ) { affectedDimension = 0; } else { affectedDimension = -1; // no idea return false; } // determine slice number in image BaseGeometry* imageGeometry = image->GetGeometry(0); Point3D testPoint = imageGeometry->GetCenter(); Point3D projectedPoint; plane->Project( testPoint, projectedPoint ); Point3D indexPoint; imageGeometry->WorldToIndex( projectedPoint, indexPoint ); affectedSlice = ROUND( indexPoint[affectedDimension] ); MITK_DEBUG << "indexPoint " << indexPoint << " affectedDimension " << affectedDimension << " affectedSlice " << affectedSlice; // check if this index is still within the image if ( affectedSlice < 0 || affectedSlice >= static_cast(image->GetDimension(affectedDimension)) ) return false; return true; } mitk::Image::Pointer mitk::SegTool2D::GetAffectedImageSliceAs2DImage(const PositionEvent* positionEvent, const Image* image) { if (!positionEvent) return NULL; assert( positionEvent->GetSender() ); // sure, right? unsigned int timeStep = positionEvent->GetSender()->GetTimeStep( image ); // get the timestep of the visible part (time-wise) of the image // first, we determine, which slice is affected const PlaneGeometry* planeGeometry( dynamic_cast (positionEvent->GetSender()->GetCurrentWorldGeometry2D() ) ); return this->GetAffectedImageSliceAs2DImage(planeGeometry, image, timeStep); } mitk::Image::Pointer mitk::SegTool2D::GetAffectedImageSliceAs2DImage(const PlaneGeometry* planeGeometry, const Image* image, unsigned int timeStep) { if ( !image || !planeGeometry ) return NULL; //Make sure that for reslicing and overwriting the same alogrithm is used. We can specify the mode of the vtk reslicer vtkSmartPointer reslice = vtkSmartPointer::New(); //set to false to extract a slice reslice->SetOverwriteMode(false); reslice->Modified(); //use ExtractSliceFilter with our specific vtkImageReslice for overwriting and extracting mitk::ExtractSliceFilter::Pointer extractor = mitk::ExtractSliceFilter::New(reslice); extractor->SetInput( image ); extractor->SetTimeStep( timeStep ); extractor->SetWorldGeometry( planeGeometry ); extractor->SetVtkOutputRequest(false); extractor->SetResliceTransformByGeometry( image->GetTimeGeometry()->GetGeometryForTimeStep( timeStep ) ); extractor->Modified(); extractor->Update(); Image::Pointer slice = extractor->GetOutput(); /*============= BEGIN undo feature block ========================*/ //specify the undo operation with the non edited slice m_undoOperation = new DiffSliceOperation(const_cast(image), extractor->GetVtkOutput(), slice->GetGeometry(), timeStep, const_cast(planeGeometry)); /*============= END undo feature block ========================*/ return slice; } mitk::Image::Pointer mitk::SegTool2D::GetAffectedWorkingSlice(const PositionEvent* positionEvent) { DataNode* workingNode( m_ToolManager->GetWorkingData(0) ); if ( !workingNode ) return NULL; Image* workingImage = dynamic_cast(workingNode->GetData()); if ( !workingImage ) return NULL; return GetAffectedImageSliceAs2DImage( positionEvent, workingImage ); } mitk::Image::Pointer mitk::SegTool2D::GetAffectedReferenceSlice(const PositionEvent* positionEvent) { DataNode* referenceNode( m_ToolManager->GetReferenceData(0) ); if ( !referenceNode ) return NULL; Image* referenceImage = dynamic_cast(referenceNode->GetData()); if ( !referenceImage ) return NULL; return GetAffectedImageSliceAs2DImage( positionEvent, referenceImage ); } void mitk::SegTool2D::WriteBackSegmentationResult (const PositionEvent* positionEvent, Image* slice) { if(!positionEvent) return; const PlaneGeometry* planeGeometry( dynamic_cast (positionEvent->GetSender()->GetCurrentWorldGeometry2D() ) ); if( planeGeometry && slice) { DataNode* workingNode( m_ToolManager->GetWorkingData(0) ); Image* image = dynamic_cast(workingNode->GetData()); unsigned int timeStep = positionEvent->GetSender()->GetTimeStep( image ); this->WriteBackSegmentationResult(planeGeometry, slice, timeStep); slice->DisconnectPipeline(); ImageToContourFilter::Pointer contourExtractor = ImageToContourFilter::New(); contourExtractor->SetInput(slice); contourExtractor->Update(); mitk::Surface::Pointer contour = contourExtractor->GetOutput(); if (m_3DInterpolationEnabled && contour->GetVtkPolyData()->GetNumberOfPoints() > 0 && image->GetDimension() == 3) { unsigned int pos = this->AddContourmarker(positionEvent); us::ServiceReference serviceRef = us::GetModuleContext()->GetServiceReference(); PlanePositionManagerService* service = us::GetModuleContext()->GetService(serviceRef); mitk::SurfaceInterpolationController::GetInstance()->AddNewContour( contour, service->GetPlanePosition(pos)); contour->DisconnectPipeline(); } } } void mitk::SegTool2D::WriteBackSegmentationResult (const PlaneGeometry* planeGeometry, Image* slice, unsigned int timeStep) { if(!planeGeometry || !slice) return; DataNode* workingNode( m_ToolManager->GetWorkingData(0) ); Image* image = dynamic_cast(workingNode->GetData()); //Make sure that for reslicing and overwriting the same alogrithm is used. We can specify the mode of the vtk reslicer vtkSmartPointer reslice = vtkSmartPointer::New(); //Set the slice as 'input' reslice->SetInputSlice(slice->GetVtkImageData()); //set overwrite mode to true to write back to the image volume reslice->SetOverwriteMode(true); reslice->Modified(); mitk::ExtractSliceFilter::Pointer extractor = mitk::ExtractSliceFilter::New(reslice); extractor->SetInput( image ); extractor->SetTimeStep( timeStep ); extractor->SetWorldGeometry( planeGeometry ); extractor->SetVtkOutputRequest(true); extractor->SetResliceTransformByGeometry( image->GetGeometry( timeStep ) ); extractor->Modified(); extractor->Update(); //the image was modified within the pipeline, but not marked so image->Modified(); image->GetVtkImageData()->Modified(); /*============= BEGIN undo feature block ========================*/ //specify the undo operation with the edited slice m_doOperation = new DiffSliceOperation(image, extractor->GetVtkOutput(),slice->GetGeometry(), timeStep, const_cast(planeGeometry)); //create an operation event for the undo stack OperationEvent* undoStackItem = new OperationEvent( DiffSliceOperationApplier::GetInstance(), m_doOperation, m_undoOperation, "Segmentation" ); //add it to the undo controller UndoController::GetCurrentUndoModel()->SetOperationEvent( undoStackItem ); //clear the pointers as the operation are stored in the undocontroller and also deleted from there m_undoOperation = NULL; m_doOperation = NULL; /*============= END undo feature block ========================*/ mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void mitk::SegTool2D::SetShowMarkerNodes(bool status) { m_ShowMarkerNodes = status; } void mitk::SegTool2D::SetEnable3DInterpolation(bool enabled) { m_3DInterpolationEnabled = enabled; } unsigned int mitk::SegTool2D::AddContourmarker ( const PositionEvent* positionEvent ) { - const mitk::Geometry2D* plane = dynamic_cast (dynamic_cast< const mitk::SlicedGeometry3D*>( + const mitk::PlaneGeometry* plane = dynamic_cast (dynamic_cast< const mitk::SlicedGeometry3D*>( positionEvent->GetSender()->GetSliceNavigationController()->GetCurrentGeometry3D())->GetGeometry2D(0)); us::ServiceReference serviceRef = us::GetModuleContext()->GetServiceReference(); PlanePositionManagerService* service = us::GetModuleContext()->GetService(serviceRef); unsigned int size = service->GetNumberOfPlanePositions(); unsigned int id = service->AddNewPlanePosition(plane, positionEvent->GetSender()->GetSliceNavigationController()->GetSlice()->GetPos()); mitk::PlanarCircle::Pointer contourMarker = mitk::PlanarCircle::New(); mitk::Point2D p1; plane->Map(plane->GetCenter(), p1); mitk::Point2D p2 = p1; p2[0] -= plane->GetSpacing()[0]; p2[1] -= plane->GetSpacing()[1]; contourMarker->PlaceFigure( p1 ); contourMarker->SetCurrentControlPoint( p1 ); - contourMarker->SetGeometry2D( const_cast(plane)); + contourMarker->SetGeometry2D( const_cast(plane)); std::stringstream markerStream; mitk::DataNode* workingNode (m_ToolManager->GetWorkingData(0)); markerStream << m_Contourmarkername ; markerStream << " "; markerStream << id+1; DataNode::Pointer rotatedContourNode = DataNode::New(); rotatedContourNode->SetData(contourMarker); rotatedContourNode->SetProperty( "name", StringProperty::New(markerStream.str()) ); rotatedContourNode->SetProperty( "isContourMarker", BoolProperty::New(true)); rotatedContourNode->SetBoolProperty( "PlanarFigureInitializedWindow", true, positionEvent->GetSender() ); rotatedContourNode->SetProperty( "includeInBoundingBox", BoolProperty::New(false)); rotatedContourNode->SetProperty( "helper object", mitk::BoolProperty::New(!m_ShowMarkerNodes)); rotatedContourNode->SetProperty( "planarfigure.drawcontrolpoints", BoolProperty::New(false)); rotatedContourNode->SetProperty( "planarfigure.drawname", BoolProperty::New(false)); rotatedContourNode->SetProperty( "planarfigure.drawoutline", BoolProperty::New(false)); rotatedContourNode->SetProperty( "planarfigure.drawshadow", BoolProperty::New(false)); if (plane) { if ( id == size ) { m_ToolManager->GetDataStorage()->Add(rotatedContourNode, workingNode); } else { mitk::NodePredicateProperty::Pointer isMarker = mitk::NodePredicateProperty::New("isContourMarker", mitk::BoolProperty::New(true)); mitk::DataStorage::SetOfObjects::ConstPointer markers = m_ToolManager->GetDataStorage()->GetDerivations(workingNode,isMarker); for ( mitk::DataStorage::SetOfObjects::const_iterator iter = markers->begin(); iter != markers->end(); ++iter) { std::string nodeName = (*iter)->GetName(); unsigned int t = nodeName.find_last_of(" "); unsigned int markerId = atof(nodeName.substr(t+1).c_str())-1; if(id == markerId) { return id; } } m_ToolManager->GetDataStorage()->Add(rotatedContourNode, workingNode); } } return id; } void mitk::SegTool2D::InteractiveSegmentationBugMessage( const std::string& message ) { MITK_ERROR << "********************************************************************************" << std::endl << " " << message << std::endl << "********************************************************************************" << std::endl << " " << std::endl << " If your image is rotated or the 2D views don't really contain the patient image, try to press the button next to the image selection. " << std::endl << " " << std::endl << " Please file a BUG REPORT: " << std::endl << " http://bugs.mitk.org" << std::endl << " Contain the following information:" << std::endl << " - What image were you working on?" << std::endl << " - Which region of the image?" << std::endl << " - Which tool did you use?" << std::endl << " - What did you do?" << std::endl << " - What happened (not)? What did you expect?" << std::endl; }