diff --git a/Core/Code/Algorithms/mitkClippedSurfaceBoundsCalculator.cpp b/Core/Code/Algorithms/mitkClippedSurfaceBoundsCalculator.cpp index 8cf5763fd9..ade2ae5f4b 100644 --- a/Core/Code/Algorithms/mitkClippedSurfaceBoundsCalculator.cpp +++ b/Core/Code/Algorithms/mitkClippedSurfaceBoundsCalculator.cpp @@ -1,354 +1,354 @@ /*=================================================================== 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 "mitkClippedSurfaceBoundsCalculator.h" #include "mitkLine.h" #define ROUND_P(x) ((x)>=0?(int)((x)+0.5):(int)((x)-0.5)) mitk::ClippedSurfaceBoundsCalculator::ClippedSurfaceBoundsCalculator( const mitk::PlaneGeometry* geometry, mitk::Image::Pointer image) : m_PlaneGeometry(NULL) , m_Geometry3D(NULL) , m_Image(NULL) { this->InitializeOutput(); this->SetInput(geometry, image); } mitk::ClippedSurfaceBoundsCalculator::ClippedSurfaceBoundsCalculator( - const mitk::Geometry3D* geometry, + const mitk::BaseGeometry* geometry, mitk::Image::Pointer image) : m_PlaneGeometry(NULL) , m_Geometry3D(NULL) , m_Image(NULL) { this->InitializeOutput(); this->SetInput(geometry, image); } mitk::ClippedSurfaceBoundsCalculator::ClippedSurfaceBoundsCalculator( const PointListType pointlist, mitk::Image::Pointer image ) : m_PlaneGeometry(NULL) , m_Geometry3D(NULL) , m_Image(image) { this->InitializeOutput(); m_ObjectPointsInWorldCoordinates = pointlist; } void mitk::ClippedSurfaceBoundsCalculator::InitializeOutput() { // initialize with meaningless slice indices m_MinMaxOutput.clear(); for(int i = 0; i < 3; i++) { m_MinMaxOutput.push_back( OutputType( std::numeric_limits::max() , std::numeric_limits::min() )); } } mitk::ClippedSurfaceBoundsCalculator::~ClippedSurfaceBoundsCalculator() { } void mitk::ClippedSurfaceBoundsCalculator::SetInput( const mitk::PlaneGeometry* geometry, mitk::Image* image) { if(geometry && image) { this->m_PlaneGeometry = geometry; this->m_Image = image; this->m_Geometry3D = NULL; //Not possible to set both m_ObjectPointsInWorldCoordinates.clear(); } } void mitk::ClippedSurfaceBoundsCalculator::SetInput( - const mitk::Geometry3D* geometry, + const mitk::BaseGeometry* geometry, mitk::Image* image) { if(geometry && image) { this->m_Geometry3D = geometry; this->m_Image = image; this->m_PlaneGeometry = NULL; //Not possible to set both m_ObjectPointsInWorldCoordinates.clear(); } } void mitk::ClippedSurfaceBoundsCalculator::SetInput( const std::vector pointlist, mitk::Image *image ) { if ( !pointlist.empty() && image ) { m_Geometry3D = NULL; m_PlaneGeometry = NULL; m_Image = image; m_ObjectPointsInWorldCoordinates = pointlist; } } mitk::ClippedSurfaceBoundsCalculator::OutputType mitk::ClippedSurfaceBoundsCalculator::GetMinMaxSpatialDirectionX() { return this->m_MinMaxOutput[0]; } mitk::ClippedSurfaceBoundsCalculator::OutputType mitk::ClippedSurfaceBoundsCalculator::GetMinMaxSpatialDirectionY() { return this->m_MinMaxOutput[1]; } mitk::ClippedSurfaceBoundsCalculator::OutputType mitk::ClippedSurfaceBoundsCalculator::GetMinMaxSpatialDirectionZ() { return this->m_MinMaxOutput[2]; } void mitk::ClippedSurfaceBoundsCalculator::Update() { this->m_MinMaxOutput.clear(); for(int i = 0; i < 3; i++) { this->m_MinMaxOutput.push_back(OutputType( std::numeric_limits::max() , std::numeric_limits::min() )); } if(m_PlaneGeometry.IsNotNull()) { this->CalculateIntersectionPoints(m_PlaneGeometry); } else if(m_Geometry3D.IsNotNull()) { // go through all slices of the image, ... const mitk::SlicedGeometry3D* slicedGeometry3D = dynamic_cast( m_Geometry3D.GetPointer() ); int allSlices = slicedGeometry3D->GetSlices(); this->CalculateIntersectionPoints(dynamic_cast(slicedGeometry3D->GetGeometry2D(0))); this->CalculateIntersectionPoints(dynamic_cast(slicedGeometry3D->GetGeometry2D(allSlices-1))); } else if( !m_ObjectPointsInWorldCoordinates.empty() ) { this->CalculateIntersectionPoints( m_ObjectPointsInWorldCoordinates ); } } void mitk::ClippedSurfaceBoundsCalculator::CalculateIntersectionPoints(const mitk::PlaneGeometry* geometry) { // SEE HEADER DOCUMENTATION for explanation typedef std::vector< std::pair > EdgesVector; Point3D origin; Vector3D xDirection, yDirection, zDirection; const Vector3D spacing = m_Image->GetGeometry()->GetSpacing(); origin = m_Image->GetGeometry()->GetOrigin(); //Left, bottom, front //Get axis vector for the spatial directions xDirection = m_Image->GetGeometry()->GetAxisVector(1); yDirection = m_Image->GetGeometry()->GetAxisVector(0); zDirection = m_Image->GetGeometry()->GetAxisVector(2); /* * For the calculation of the intersection points we need as corner points the center-based image coordinates. * With the method GetCornerPoint() of the class Geometry3D we only get the corner-based coordinates. * Therefore we need to calculate the center-based corner points here. For that we add/substract the corner- * based coordinates with the spacing of the geometry3D. */ for( int i = 0; i < 3; i++ ) { if(xDirection[i] < 0) { xDirection[i] += spacing[i]; } else if( xDirection[i] > 0 ) { xDirection[i] -= spacing[i]; } if(yDirection[i] < 0) { yDirection[i] += spacing[i]; } else if( yDirection[i] > 0 ) { yDirection[i] -= spacing[i]; } if(zDirection[i] < 0) { zDirection[i] += spacing[i]; } else if( zDirection[i] > 0 ) { zDirection[i] -= spacing[i]; } } Point3D leftBottomFront, leftTopFront, leftBottomBack, leftTopBack; Point3D rightBottomFront, rightTopFront, rightBottomBack, rightTopBack; leftBottomFront = origin; leftTopFront = origin + yDirection; leftBottomBack = origin + zDirection; leftTopBack = origin + yDirection + zDirection; rightBottomFront = origin + xDirection; rightTopFront = origin + xDirection + yDirection; rightBottomBack = origin + xDirection + zDirection; rightTopBack = origin + xDirection + yDirection + zDirection; EdgesVector edgesOf3DBox; edgesOf3DBox.push_back(std::make_pair(leftBottomBack, // x = left=xfront, y=bottom=yfront, z=front=zfront leftTopFront)); // left, top, front edgesOf3DBox.push_back(std::make_pair(leftBottomFront, // left, bottom, front leftBottomBack)); // left, bottom, back edgesOf3DBox.push_back(std::make_pair(leftBottomFront, // left, bottom, front rightBottomFront)); // right, bottom, front edgesOf3DBox.push_back(std::make_pair(leftTopFront, // left, top, front rightTopFront)); // right, top, front edgesOf3DBox.push_back(std::make_pair(leftTopFront, // left, top, front leftTopBack)); // left, top, back edgesOf3DBox.push_back(std::make_pair(rightTopFront, // right, top, front rightTopBack)); // right, top, back edgesOf3DBox.push_back(std::make_pair(rightTopFront, // right, top, front rightBottomFront)); // right, bottom, front edgesOf3DBox.push_back(std::make_pair(rightBottomFront, // right, bottom, front rightBottomBack)); // right, bottom, back edgesOf3DBox.push_back(std::make_pair(rightBottomBack, // right, bottom, back leftBottomBack)); // left, bottom, back edgesOf3DBox.push_back(std::make_pair(rightBottomBack, // right, bottom, back rightTopBack)); // right, top, back edgesOf3DBox.push_back(std::make_pair(rightTopBack, // right, top, back leftTopBack)); // left, top, back edgesOf3DBox.push_back(std::make_pair(leftTopBack, // left, top, back leftBottomBack)); // left, bottom, back for (EdgesVector::iterator iterator = edgesOf3DBox.begin(); iterator != edgesOf3DBox.end();iterator++) { Point3D startPoint = (*iterator).first; // start point of the line Point3D endPoint = (*iterator).second; // end point of the line Vector3D lineDirection = endPoint - startPoint; mitk::Line3D line(startPoint, lineDirection); Point3D intersectionWorldPoint; intersectionWorldPoint.Fill(std::numeric_limits::min()); // Get intersection point of line and plane geometry geometry->IntersectionPoint(line, intersectionWorldPoint); double t = -1.0; bool doesLineIntersectWithPlane(false); if(line.GetDirection().GetNorm() < mitk::eps && geometry->Distance(line.GetPoint1()) < mitk::sqrteps) { t = 1.0; doesLineIntersectWithPlane = true; intersectionWorldPoint = line.GetPoint1(); } else { geometry->IntersectionPoint(line, intersectionWorldPoint); doesLineIntersectWithPlane = geometry->IntersectionPointParam(line, t); } mitk::Point3D intersectionIndexPoint; //Get index point m_Image->GetGeometry()->WorldToIndex(intersectionWorldPoint, intersectionIndexPoint); if ( doesLineIntersectWithPlane && -mitk::sqrteps <= t && t <= 1.0 + mitk::sqrteps ) { for(int dim = 0; dim < 3; dim++) { // minimum //If new point value is lower than old if( this->m_MinMaxOutput[dim].first > ROUND_P(intersectionIndexPoint[dim]) ) { this->m_MinMaxOutput[dim].first = ROUND_P(intersectionIndexPoint[dim]); //set new value } // maximum //If new point value is higher than old if( this->m_MinMaxOutput[dim].second < ROUND_P(intersectionIndexPoint[dim]) ) { this->m_MinMaxOutput[dim].second = ROUND_P(intersectionIndexPoint[dim]); //set new value } } this->EnforceImageBounds(); } } } void mitk::ClippedSurfaceBoundsCalculator::CalculateIntersectionPoints( PointListType pointList ) { PointListType::iterator pointIterator; mitk::SlicedGeometry3D::Pointer imageGeometry = m_Image->GetSlicedGeometry(); for ( pointIterator = pointList.begin(); pointIterator != pointList.end(); pointIterator++ ) { mitk::Point3D pntInIndexCoordinates; imageGeometry->WorldToIndex( (*pointIterator), pntInIndexCoordinates ); m_MinMaxOutput[0].first = pntInIndexCoordinates[0] < m_MinMaxOutput[0].first ? ROUND_P(pntInIndexCoordinates[0]) : m_MinMaxOutput[0].first; m_MinMaxOutput[0].second = pntInIndexCoordinates[0] > m_MinMaxOutput[0].second ? ROUND_P(pntInIndexCoordinates[0]) : m_MinMaxOutput[0].second; m_MinMaxOutput[1].first = pntInIndexCoordinates[1] < m_MinMaxOutput[1].first ? ROUND_P(pntInIndexCoordinates[1]) : m_MinMaxOutput[1].first; m_MinMaxOutput[1].second = pntInIndexCoordinates[1] > m_MinMaxOutput[1].second ? ROUND_P(pntInIndexCoordinates[1]) : m_MinMaxOutput[1].second; m_MinMaxOutput[2].first = pntInIndexCoordinates[2] < m_MinMaxOutput[2].first ? ROUND_P(pntInIndexCoordinates[2]) : m_MinMaxOutput[2].first; m_MinMaxOutput[2].second = pntInIndexCoordinates[2] > m_MinMaxOutput[2].second ? ROUND_P(pntInIndexCoordinates[2]) : m_MinMaxOutput[2].second; } this->EnforceImageBounds(); } void mitk::ClippedSurfaceBoundsCalculator::EnforceImageBounds() { m_MinMaxOutput[0].first = std::max( m_MinMaxOutput[0].first, 0 ); m_MinMaxOutput[1].first = std::max( m_MinMaxOutput[1].first, 0 ); m_MinMaxOutput[2].first = std::max( m_MinMaxOutput[2].first, 0 ); m_MinMaxOutput[0].second = std::min( m_MinMaxOutput[0].second, (int) m_Image->GetDimension(0)-1 ); m_MinMaxOutput[1].second = std::min( m_MinMaxOutput[1].second, (int) m_Image->GetDimension(1)-1 ); m_MinMaxOutput[2].second = std::min( m_MinMaxOutput[2].second, (int) m_Image->GetDimension(2)-1 ); } diff --git a/Core/Code/Algorithms/mitkClippedSurfaceBoundsCalculator.h b/Core/Code/Algorithms/mitkClippedSurfaceBoundsCalculator.h index 690c10e554..8d9fe18283 100644 --- a/Core/Code/Algorithms/mitkClippedSurfaceBoundsCalculator.h +++ b/Core/Code/Algorithms/mitkClippedSurfaceBoundsCalculator.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 ClippedSurfaceBoundsCalculator_h_included #define ClippedSurfaceBoundsCalculator_h_included #include "mitkImage.h" #include "mitkPlaneGeometry.h" #include /** * \brief Find image slices visible on a given plane. * * The class name is not helpful in finding this class. Good suggestions welcome. * * Given a PlaneGeometry (e.g. the 2D plane of a render window), this class * calculates which slices of an mitk::Image are visible on this plane. * Calculation is done for X, Y, and Z direction, the result is available in * form of a pair (minimum,maximum) slice index. * * Such calculations are useful if you want to display information about the * currently visible slice (overlays, statistics, ...) and you don't want to * depend on any prior information about hat the renderwindow is currently showing. * * \warning The interface attempts to look like an ITK filter but it is far from being one. */ namespace mitk { class MITK_CORE_EXPORT ClippedSurfaceBoundsCalculator { public: typedef std::vector PointListType; ClippedSurfaceBoundsCalculator(const mitk::PlaneGeometry* geometry = NULL, mitk::Image::Pointer image = NULL); - ClippedSurfaceBoundsCalculator(const mitk::Geometry3D* geometry, + ClippedSurfaceBoundsCalculator(const mitk::BaseGeometry* geometry, mitk::Image::Pointer image); ClippedSurfaceBoundsCalculator(const PointListType pointlist, mitk::Image::Pointer image); void InitializeOutput(); virtual ~ClippedSurfaceBoundsCalculator(); void SetInput(const mitk::PlaneGeometry* geometry, mitk::Image* image); - void SetInput(const mitk::Geometry3D *geometry, mitk::Image *image); + void SetInput(const mitk::BaseGeometry *geometry, mitk::Image *image); void SetInput(const PointListType pointlist, mitk::Image *image); /** \brief Request calculation. How cut/visible slice indices are determined: 1. construct a bounding box of the image. This is the box that connect the outer voxel centers(!). 2. check the edges of this box. 3. intersect each edge with the plane geometry - if the intersection point is within the image box, we update the visible/cut slice indices for all dimensions. - else we ignore the cut */ void Update(); /** \brief Minimum (first) and maximum (second) slice index. */ typedef std::pair OutputType; /** \brief What X coordinates (slice indices) are cut/visible in given plane. */ OutputType GetMinMaxSpatialDirectionX(); /** \brief What Y coordinates (slice indices) are cut/visible in given plane. */ OutputType GetMinMaxSpatialDirectionY(); /** \brief What Z coordinates (slice indices) are cut/visible in given plane. */ OutputType GetMinMaxSpatialDirectionZ(); protected: void CalculateIntersectionPoints(const mitk::PlaneGeometry* geometry); void CalculateIntersectionPoints( PointListType pointList ); /** * \brief Clips the resulting index-coordinates to make sure they do * not exceed the imagebounds. */ void EnforceImageBounds(); mitk::PlaneGeometry::ConstPointer m_PlaneGeometry; - mitk::Geometry3D::ConstPointer m_Geometry3D; + mitk::BaseGeometry::ConstPointer m_Geometry3D; mitk::Image::Pointer m_Image; std::vector m_ObjectPointsInWorldCoordinates; std::vector< OutputType > m_MinMaxOutput; }; } //namespace mitk #endif diff --git a/Core/Code/Algorithms/mitkExtractSliceFilter.cpp b/Core/Code/Algorithms/mitkExtractSliceFilter.cpp index f0f3a58634..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 Geometry3D *boundingGeometry, +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 9431a89d1b..978e70416a 100644 --- a/Core/Code/Algorithms/mitkExtractSliceFilter.h +++ b/Core/Code/Algorithms/mitkExtractSliceFilter.h @@ -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. ===================================================================*/ #ifndef mitkExtractSliceFilter_h_Included #define mitkExtractSliceFilter_h_Included #include "MitkCoreExports.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); itkFactorylessNewMacro(Self) itkCloneMacro(Self) 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 Geometry3D* transform){ this->m_ResliceTransform = transform; } + 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 Geometry3D *boundingGeometry, + 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; - Geometry3D::ConstPointer m_ResliceTransform; + 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.cpp b/Core/Code/Algorithms/mitkGeometry2DDataToSurfaceFilter.cpp index fb1251d18d..cbac51447a 100644 --- a/Core/Code/Algorithms/mitkGeometry2DDataToSurfaceFilter.cpp +++ b/Core/Code/Algorithms/mitkGeometry2DDataToSurfaceFilter.cpp @@ -1,447 +1,447 @@ /*=================================================================== 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 "mitkGeometry3D.h" #include "mitkGeometry2DData.h" #include "mitkPlaneGeometry.h" #include "mitkAbstractTransformGeometry.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include mitk::Geometry2DDataToSurfaceFilter::Geometry2DDataToSurfaceFilter() : m_UseGeometryParametricBounds( true ), m_XResolution( 10 ), m_YResolution( 10 ), m_PlaceByGeometry( false ), m_UseBoundingBox( false ) { m_PlaneSource = vtkPlaneSource::New(); m_Transform = vtkTransform::New(); m_CubeSource = vtkCubeSource::New(); m_PolyDataTransformer = vtkTransformPolyDataFilter::New(); m_Plane = vtkPlane::New(); m_PlaneCutter = vtkCutter::New(); m_PlaneStripper = vtkStripper::New(); m_PlanePolyData = vtkPolyData::New(); m_NormalsUpdater = vtkPPolyDataNormals::New(); m_PlaneTriangler = vtkTriangleFilter::New(); m_TextureMapToPlane = vtkTextureMapToPlane::New(); m_Box = vtkBox::New(); m_PlaneClipper = vtkClipPolyData::New(); m_VtkTransformPlaneFilter = vtkTransformPolyDataFilter::New(); m_VtkTransformPlaneFilter->SetInputConnection(m_PlaneSource->GetOutputPort() ); } mitk::Geometry2DDataToSurfaceFilter::~Geometry2DDataToSurfaceFilter() { m_PlaneSource->Delete(); m_Transform->Delete(); m_CubeSource->Delete(); m_PolyDataTransformer->Delete(); m_Plane->Delete(); m_PlaneCutter->Delete(); m_PlaneStripper->Delete(); m_PlanePolyData->Delete(); m_NormalsUpdater->Delete(); m_PlaneTriangler->Delete(); m_TextureMapToPlane->Delete(); m_Box->Delete(); m_PlaneClipper->Delete(); m_VtkTransformPlaneFilter->Delete(); } void mitk::Geometry2DDataToSurfaceFilter::GenerateOutputInformation() { mitk::Geometry2DData::ConstPointer input = this->GetInput(); mitk::Surface::Pointer output = this->GetOutput(); if ( input.IsNull() || (input->GetGeometry2D() == NULL) || (input->GetGeometry2D()->IsValid() == false) || (m_UseBoundingBox && (m_BoundingBox.IsNull() || (m_BoundingBox->GetDiagonalLength2() < mitk::eps))) ) { return; } Point3D origin; Point3D right, bottom; vtkPolyData *planeSurface = NULL; // Does the Geometry2DData contain a PlaneGeometry? if ( dynamic_cast< PlaneGeometry * >( input->GetGeometry2D() ) != NULL ) { mitk::PlaneGeometry *planeGeometry = dynamic_cast< PlaneGeometry * >( input->GetGeometry2D() ); if ( m_PlaceByGeometry ) { // Let the output use the input geometry to appropriately transform the // coordinate system. mitk::Geometry3D::TransformType *affineTransform = planeGeometry->GetIndexToWorldTransform(); TimeGeometry *timeGeometry = output->GetTimeGeometry(); - Geometry3D *geometrie3d = timeGeometry->GetGeometryForTimeStep( 0 ); + BaseGeometry *geometrie3d = timeGeometry->GetGeometryForTimeStep( 0 ); geometrie3d->SetIndexToWorldTransform( affineTransform ); } if ( !m_UseBoundingBox) { // We do not have a bounding box, so no clipping is required. if ( m_PlaceByGeometry ) { // Derive coordinate axes and origin from input geometry extent origin.Fill( 0.0 ); FillVector3D( right, planeGeometry->GetExtent(0), 0.0, 0.0 ); FillVector3D( bottom, 0.0, planeGeometry->GetExtent(1), 0.0 ); } else { // Take the coordinate axes and origin directly from the input geometry. origin = planeGeometry->GetOrigin(); right = planeGeometry->GetCornerPoint( false, true ); bottom = planeGeometry->GetCornerPoint( true, false ); } // Since the plane is planar, there is no need to subdivide the grid // (cf. AbstractTransformGeometry case) m_PlaneSource->SetXResolution( 1 ); m_PlaneSource->SetYResolution( 1 ); m_PlaneSource->SetOrigin( origin[0], origin[1], origin[2] ); m_PlaneSource->SetPoint1( right[0], right[1], right[2] ); m_PlaneSource->SetPoint2( bottom[0], bottom[1], bottom[2] ); m_PlaneSource->Update(); planeSurface = m_PlaneSource->GetOutput(); } else { // Set up a cube with the extent and origin of the bounding box. This // cube will be clipped by a plane later on. The intersection of the // cube and the plane will be the surface we are interested in. Note // that the bounding box needs to be explicitly specified by the user // of this class, since it is not necessarily clear from the data // available herein which bounding box to use. In most cases, this // would be the bounding box of the input geometry's reference // geometry, but this is not an inevitable requirement. mitk::BoundingBox::PointType boundingBoxMin = m_BoundingBox->GetMinimum(); mitk::BoundingBox::PointType boundingBoxMax = m_BoundingBox->GetMaximum(); mitk::BoundingBox::PointType boundingBoxCenter = m_BoundingBox->GetCenter(); m_CubeSource->SetXLength( boundingBoxMax[0] - boundingBoxMin[0] ); m_CubeSource->SetYLength( boundingBoxMax[1] - boundingBoxMin[1] ); m_CubeSource->SetZLength( boundingBoxMax[2] - boundingBoxMin[2] ); m_CubeSource->SetCenter( boundingBoxCenter[0], boundingBoxCenter[1], boundingBoxCenter[2] ); // Now we have to transform the cube, so that it will cut our plane // appropriately. (As can be seen below, the plane corresponds to the // z-plane in the coordinate system and is *not* transformed.) Therefore, // we get the inverse of the plane geometry's transform and concatenate // it with the transform of the reference geometry, if available. m_Transform->Identity(); m_Transform->Concatenate( planeGeometry->GetVtkTransform()->GetLinearInverse() ); - Geometry3D *referenceGeometry = planeGeometry->GetReferenceGeometry(); + BaseGeometry *referenceGeometry = planeGeometry->GetReferenceGeometry(); if ( referenceGeometry ) { m_Transform->Concatenate( referenceGeometry->GetVtkTransform() ); } // Transform the cube accordingly (s.a.) m_PolyDataTransformer->SetInputConnection( m_CubeSource->GetOutputPort() ); m_PolyDataTransformer->SetTransform( m_Transform ); // Initialize the plane to clip the cube with, as lying on the z-plane m_Plane->SetOrigin( 0.0, 0.0, 0.0 ); m_Plane->SetNormal( 0.0, 0.0, 1.0 ); // Cut the plane with the cube. m_PlaneCutter->SetInputConnection( m_PolyDataTransformer->GetOutputPort() ); m_PlaneCutter->SetCutFunction( m_Plane ); // The output of the cutter must be converted into appropriate poly data. m_PlaneStripper->SetInputConnection( m_PlaneCutter->GetOutputPort() ); m_PlaneStripper->Update(); if ( m_PlaneStripper->GetOutput()->GetNumberOfPoints() < 3 ) { return; } m_PlanePolyData->SetPoints( m_PlaneStripper->GetOutput()->GetPoints() ); m_PlanePolyData->SetPolys( m_PlaneStripper->GetOutput()->GetLines() ); m_PlaneTriangler->SetInputData( m_PlanePolyData ); // Get bounds of the resulting surface and use it to generate the texture // mapping information m_PlaneTriangler->Update(); m_PlaneTriangler->GetOutput()->ComputeBounds(); double *surfaceBounds = m_PlaneTriangler->GetOutput()->GetBounds(); origin[0] = surfaceBounds[0]; origin[1] = surfaceBounds[2]; origin[2] = surfaceBounds[4]; right[0] = surfaceBounds[1]; right[1] = surfaceBounds[2]; right[2] = surfaceBounds[4]; bottom[0] = surfaceBounds[0]; bottom[1] = surfaceBounds[3]; bottom[2] = surfaceBounds[4]; // Now we tell the data how it shall be textured afterwards; // description see above. m_TextureMapToPlane->SetInputConnection( m_PlaneTriangler->GetOutputPort() ); m_TextureMapToPlane->AutomaticPlaneGenerationOn(); m_TextureMapToPlane->SetOrigin( origin[0], origin[1], origin[2] ); m_TextureMapToPlane->SetPoint1( right[0], right[1], right[2] ); m_TextureMapToPlane->SetPoint2( bottom[0], bottom[1], bottom[2] ); // Need to call update so that output data and bounds are immediately // available m_TextureMapToPlane->Update(); // Return the output of this generation process planeSurface = dynamic_cast< vtkPolyData * >( m_TextureMapToPlane->GetOutput() ); } } // Does the Geometry2DData contain an AbstractTransformGeometry? else if ( mitk::AbstractTransformGeometry *abstractGeometry = dynamic_cast< AbstractTransformGeometry * >( input->GetGeometry2D() ) ) { // In the case of an AbstractTransformGeometry (which holds a possibly // non-rigid transform), we proceed slightly differently: since the // plane can be arbitrarily deformed, we need to transform it by the // abstract transform before clipping it. The setup for this is partially // done in the constructor. origin = abstractGeometry->GetPlane()->GetOrigin(); right = origin + abstractGeometry->GetPlane()->GetAxisVector( 0 ); bottom = origin + abstractGeometry->GetPlane()->GetAxisVector( 1 ); // Define the plane m_PlaneSource->SetOrigin( origin[0], origin[1], origin[2] ); m_PlaneSource->SetPoint1( right[0], right[1], right[2] ); m_PlaneSource->SetPoint2( bottom[0], bottom[1], bottom[2] ); // Set the plane's resolution (unlike for non-deformable planes, the plane // grid needs to have a certain resolution so that the deformation has the // desired effect). if ( m_UseGeometryParametricBounds ) { m_PlaneSource->SetXResolution( (int)abstractGeometry->GetParametricExtent(0) ); m_PlaneSource->SetYResolution( (int)abstractGeometry->GetParametricExtent(1) ); } else { m_PlaneSource->SetXResolution( m_XResolution ); m_PlaneSource->SetYResolution( m_YResolution ); } if ( m_PlaceByGeometry ) { // Let the output use the input geometry to appropriately transform the // coordinate system. mitk::Geometry3D::TransformType *affineTransform = abstractGeometry->GetIndexToWorldTransform(); TimeGeometry *timeGeometry = output->GetTimeGeometry(); - Geometry3D *g3d = timeGeometry->GetGeometryForTimeStep( 0 ); + BaseGeometry *g3d = timeGeometry->GetGeometryForTimeStep( 0 ); g3d->SetIndexToWorldTransform( affineTransform ); vtkGeneralTransform *composedResliceTransform = vtkGeneralTransform::New(); composedResliceTransform->Identity(); composedResliceTransform->Concatenate( abstractGeometry->GetVtkTransform()->GetLinearInverse() ); composedResliceTransform->Concatenate( abstractGeometry->GetVtkAbstractTransform() ); // Use the non-rigid transform for transforming the plane. m_VtkTransformPlaneFilter->SetTransform( composedResliceTransform ); } else { // Use the non-rigid transform for transforming the plane. m_VtkTransformPlaneFilter->SetTransform( abstractGeometry->GetVtkAbstractTransform() ); } if ( m_UseBoundingBox ) { mitk::BoundingBox::PointType boundingBoxMin = m_BoundingBox->GetMinimum(); mitk::BoundingBox::PointType boundingBoxMax = m_BoundingBox->GetMaximum(); //mitk::BoundingBox::PointType boundingBoxCenter = m_BoundingBox->GetCenter(); m_Box->SetXMin( boundingBoxMin[0], boundingBoxMin[1], boundingBoxMin[2] ); m_Box->SetXMax( boundingBoxMax[0], boundingBoxMax[1], boundingBoxMax[2] ); } else { // Plane will not be clipped m_Box->SetXMin( -10000.0, -10000.0, -10000.0 ); m_Box->SetXMax( 10000.0, 10000.0, 10000.0 ); } m_Transform->Identity(); m_Transform->Concatenate( input->GetGeometry2D()->GetVtkTransform() ); m_Transform->PreMultiply(); m_Box->SetTransform( m_Transform ); m_PlaneClipper->SetInputConnection(m_VtkTransformPlaneFilter->GetOutputPort() ); m_PlaneClipper->SetClipFunction( m_Box ); m_PlaneClipper->GenerateClippedOutputOff(); // important to NOT generate normals data for clipped part m_PlaneClipper->InsideOutOn(); m_PlaneClipper->SetValue( 0.0 ); m_PlaneClipper->Update(); planeSurface = m_PlaneClipper->GetOutput(); } m_NormalsUpdater->SetInputData( planeSurface ); m_NormalsUpdater->AutoOrientNormalsOn(); // that's the trick! Brings consistency between // normals direction and front/back faces direction (see bug 1440) m_NormalsUpdater->ComputePointNormalsOn(); m_NormalsUpdater->Update(); output->SetVtkPolyData( m_NormalsUpdater->GetOutput() ); output->CalculateBoundingBox(); } void mitk::Geometry2DDataToSurfaceFilter::GenerateData() { mitk::Surface::Pointer output = this->GetOutput(); if (output.IsNull()) return; if (output->GetVtkPolyData()==NULL) return; // output->GetVtkPolyData()->Update(); //VTK6_TODO vtk pipeline } const mitk::Geometry2DData *mitk::Geometry2DDataToSurfaceFilter::GetInput() { if (this->GetNumberOfInputs() < 1) { return 0; } return static_cast ( this->ProcessObject::GetInput(0) ); } const mitk::Geometry2DData * mitk::Geometry2DDataToSurfaceFilter ::GetInput(unsigned int idx) { return static_cast< const mitk::Geometry2DData * > ( this->ProcessObject::GetInput(idx) ); } void mitk::Geometry2DDataToSurfaceFilter ::SetInput(const mitk::Geometry2DData *input) { // Process object is not const-correct so the const_cast is required here this->ProcessObject::SetNthInput( 0, const_cast< mitk::Geometry2DData * >( input ) ); } void mitk::Geometry2DDataToSurfaceFilter ::SetInput(unsigned int index, const mitk::Geometry2DData *input) { if( index+1 > this->GetNumberOfInputs() ) { this->SetNumberOfRequiredInputs( index + 1 ); } // Process object is not const-correct so the const_cast is required here this->ProcessObject::SetNthInput(index, const_cast< mitk::Geometry2DData *>( input ) ); } void mitk::Geometry2DDataToSurfaceFilter ::SetBoundingBox( const mitk::BoundingBox *boundingBox ) { m_BoundingBox = boundingBox; this->UseBoundingBoxOn(); } const mitk::BoundingBox * mitk::Geometry2DDataToSurfaceFilter ::GetBoundingBox() const { return m_BoundingBox.GetPointer(); } diff --git a/Core/Code/Algorithms/mitkGeometry2DDataToSurfaceFilter.h b/Core/Code/Algorithms/mitkGeometry2DDataToSurfaceFilter.h index 3a44adb4fd..0e2f95f72e 100644 --- a/Core/Code/Algorithms/mitkGeometry2DDataToSurfaceFilter.h +++ b/Core/Code/Algorithms/mitkGeometry2DDataToSurfaceFilter.h @@ -1,228 +1,228 @@ /*=================================================================== 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); itkFactorylessNewMacro(Self) itkCloneMacro(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/Algorithms/mitkImageSliceSelector.cpp b/Core/Code/Algorithms/mitkImageSliceSelector.cpp index d8d62f30d2..5fdf29436f 100644 --- a/Core/Code/Algorithms/mitkImageSliceSelector.cpp +++ b/Core/Code/Algorithms/mitkImageSliceSelector.cpp @@ -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. ===================================================================*/ #include "mitkImageSliceSelector.h" void mitk::ImageSliceSelector::GenerateOutputInformation() { mitk::Image::ConstPointer input = this->GetInput(); mitk::Image::Pointer output = this->GetOutput(); itkDebugMacro(<<"GenerateOutputInformation()"); output->Initialize(input->GetPixelType(), 2, input->GetDimensions()); if( (unsigned int)m_SliceNr >= input->GetDimension(2) ) { m_SliceNr = input->GetDimension(2)-1; } if( (unsigned int)m_TimeNr >= input->GetDimension(3) ) { m_TimeNr = input->GetDimension(3)-1; } // initialize geometry - output->SetGeometry(dynamic_cast(input->GetSlicedGeometry(m_TimeNr)->GetGeometry2D(m_SliceNr)->Clone().GetPointer())); + output->SetGeometry(dynamic_cast(input->GetSlicedGeometry(m_TimeNr)->GetGeometry2D(m_SliceNr)->Clone().GetPointer())); output->SetPropertyList(input->GetPropertyList()->Clone()); } void mitk::ImageSliceSelector::GenerateData() { SetSliceItem(GetSliceData(m_SliceNr, m_TimeNr, m_ChannelNr), 0); } mitk::ImageSliceSelector::ImageSliceSelector() : m_SliceNr(0), m_TimeNr(0), m_ChannelNr(0) { } mitk::ImageSliceSelector::~ImageSliceSelector() { } void mitk::ImageSliceSelector::GenerateInputRequestedRegion() { Superclass::GenerateInputRequestedRegion(); mitk::ImageToImageFilter::InputImagePointer input = const_cast< mitk::ImageToImageFilter::InputImageType * > ( this->GetInput() ); mitk::Image::Pointer output = this->GetOutput(); Image::RegionType requestedRegion; requestedRegion = output->GetRequestedRegion(); requestedRegion.SetIndex(2, m_SliceNr); requestedRegion.SetIndex(3, m_TimeNr); requestedRegion.SetIndex(4, m_ChannelNr); requestedRegion.SetSize(2, 1); requestedRegion.SetSize(3, 1); requestedRegion.SetSize(4, 1); input->SetRequestedRegion( & requestedRegion ); } diff --git a/Core/Code/Algorithms/mitkImageTimeSelector.cpp b/Core/Code/Algorithms/mitkImageTimeSelector.cpp index 57a8952a17..8cbd73a08e 100644 --- a/Core/Code/Algorithms/mitkImageTimeSelector.cpp +++ b/Core/Code/Algorithms/mitkImageTimeSelector.cpp @@ -1,99 +1,99 @@ /*=================================================================== 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 "mitkImageTimeSelector.h" mitk::ImageTimeSelector::ImageTimeSelector() : m_TimeNr(0), m_ChannelNr(0) { } mitk::ImageTimeSelector::~ImageTimeSelector() { } void mitk::ImageTimeSelector::GenerateOutputInformation() { Image::ConstPointer input = this->GetInput(); Image::Pointer output = this->GetOutput(); itkDebugMacro(<<"GenerateOutputInformation()"); int dim=(input->GetDimension()<3?input->GetDimension():3); output->Initialize(input->GetPixelType(), dim, input->GetDimensions()); if( (unsigned int) m_TimeNr >= input->GetDimension(3) ) { m_TimeNr = input->GetDimension(3)-1; } // initialize geometry mitk::SlicedGeometry3D::Pointer sliced_geo = input->GetSlicedGeometry(m_TimeNr); if( sliced_geo.IsNull() ) { mitkThrow() << "Failed to retrieve SlicedGeometry from input at timestep " << m_TimeNr; } mitk::SlicedGeometry3D::Pointer sliced_geo_clone = sliced_geo->Clone(); if( sliced_geo_clone.IsNull() ) { mitkThrow() << "Failed to clone the retrieved sliced geometry."; } - mitk::Geometry3D::Pointer geom_3d = dynamic_cast(sliced_geo_clone.GetPointer()); + mitk::BaseGeometry::Pointer geom_3d = dynamic_cast(sliced_geo_clone.GetPointer()); if( geom_3d.IsNotNull() ) { output->SetGeometry(geom_3d.GetPointer() ); } else { mitkThrow() << "Failed to cast the retrieved SlicedGeometry to a Geometry3D object."; } output->SetPropertyList(input->GetPropertyList()->Clone()); } void mitk::ImageTimeSelector::GenerateData() { const Image::RegionType& requestedRegion = this->GetOutput()->GetRequestedRegion(); //do we really need a complete volume at a time? if(requestedRegion.GetSize(2)>1) this->SetVolumeItem( this->GetVolumeData(m_TimeNr, m_ChannelNr), 0 ); else //no, so take just a slice! this->SetSliceItem( this->GetSliceData(requestedRegion.GetIndex(2), m_TimeNr, m_ChannelNr), requestedRegion.GetIndex(2), 0 ); } void mitk::ImageTimeSelector::GenerateInputRequestedRegion() { Superclass::GenerateInputRequestedRegion(); ImageToImageFilter::InputImagePointer input = const_cast< mitk::ImageToImageFilter::InputImageType * > ( this->GetInput() ); Image::Pointer output = this->GetOutput(); Image::RegionType requestedRegion; requestedRegion = output->GetRequestedRegion(); requestedRegion.SetIndex(3, m_TimeNr); requestedRegion.SetIndex(4, m_ChannelNr); requestedRegion.SetSize(3, 1); requestedRegion.SetSize(4, 1); input->SetRequestedRegion( & requestedRegion ); } diff --git a/Core/Code/Algorithms/mitkPlaneClipping.h b/Core/Code/Algorithms/mitkPlaneClipping.h index 256fbcd8c1..047976bafb 100644 --- a/Core/Code/Algorithms/mitkPlaneClipping.h +++ b/Core/Code/Algorithms/mitkPlaneClipping.h @@ -1,141 +1,141 @@ /*=================================================================== 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 MITKPLANECLIPPING_H_HEADER_INCLUDED #define MITKPLANECLIPPING_H_HEADER_INCLUDED #include #include #include #include namespace mitk { namespace PlaneClipping { /** \brief Internal helper method for intersection testing used only in CalculateClippedPlaneBounds() */ static bool LineIntersectZero( vtkPoints *points, int p1, int p2, double *bounds ) { double point1[3]; double point2[3]; points->GetPoint( p1, point1 ); points->GetPoint( p2, point2 ); if ( (point1[2] * point2[2] <= 0.0) && (point1[2] != point2[2]) ) { double x, y; x = ( point1[0] * point2[2] - point1[2] * point2[0] ) / ( point2[2] - point1[2] ); y = ( point1[1] * point2[2] - point1[2] * point2[1] ) / ( point2[2] - point1[2] ); if ( x < bounds[0] ) { bounds[0] = x; } if ( x > bounds[1] ) { bounds[1] = x; } if ( y < bounds[2] ) { bounds[2] = y; } if ( y > bounds[3] ) { bounds[3] = y; } bounds[4] = bounds[5] = 0.0; return true; } return false; } /** \brief Calculate the bounding box of the resliced image. This is necessary for arbitrarily rotated planes in an image volume. A rotated plane (e.g. in swivel mode) will have a new bounding box, which needs to be calculated. */ -static bool CalculateClippedPlaneBounds( const Geometry3D *boundingGeometry, const PlaneGeometry *planeGeometry, double *bounds ) +static bool CalculateClippedPlaneBounds( const BaseGeometry *boundingGeometry, const PlaneGeometry *planeGeometry, double *bounds ) { // Clip the plane with the bounding geometry. To do so, the corner points // of the bounding box are transformed by the inverse transformation // matrix, and the transformed bounding box edges derived therefrom are // clipped with the plane z=0. The resulting min/max values are taken as // bounds for the image reslicer. const mitk::BoundingBox *boundingBox = boundingGeometry->GetBoundingBox(); mitk::BoundingBox::PointType bbMin = boundingBox->GetMinimum(); mitk::BoundingBox::PointType bbMax = boundingBox->GetMaximum(); vtkSmartPointer points = vtkSmartPointer::New(); if(boundingGeometry->GetImageGeometry()) { points->InsertPoint( 0, bbMin[0]-0.5, bbMin[1]-0.5, bbMin[2]-0.5 ); points->InsertPoint( 1, bbMin[0]-0.5, bbMin[1]-0.5, bbMax[2]-0.5 ); points->InsertPoint( 2, bbMin[0]-0.5, bbMax[1]-0.5, bbMax[2]-0.5 ); points->InsertPoint( 3, bbMin[0]-0.5, bbMax[1]-0.5, bbMin[2]-0.5 ); points->InsertPoint( 4, bbMax[0]-0.5, bbMin[1]-0.5, bbMin[2]-0.5 ); points->InsertPoint( 5, bbMax[0]-0.5, bbMin[1]-0.5, bbMax[2]-0.5 ); points->InsertPoint( 6, bbMax[0]-0.5, bbMax[1]-0.5, bbMax[2]-0.5 ); points->InsertPoint( 7, bbMax[0]-0.5, bbMax[1]-0.5, bbMin[2]-0.5 ); } else { points->InsertPoint( 0, bbMin[0], bbMin[1], bbMin[2] ); points->InsertPoint( 1, bbMin[0], bbMin[1], bbMax[2] ); points->InsertPoint( 2, bbMin[0], bbMax[1], bbMax[2] ); points->InsertPoint( 3, bbMin[0], bbMax[1], bbMin[2] ); points->InsertPoint( 4, bbMax[0], bbMin[1], bbMin[2] ); points->InsertPoint( 5, bbMax[0], bbMin[1], bbMax[2] ); points->InsertPoint( 6, bbMax[0], bbMax[1], bbMax[2] ); points->InsertPoint( 7, bbMax[0], bbMax[1], bbMin[2] ); } vtkSmartPointer newPoints = vtkSmartPointer::New(); vtkSmartPointer transform = vtkSmartPointer::New(); transform->Identity(); transform->Concatenate( planeGeometry->GetVtkTransform()->GetLinearInverse() ); transform->Concatenate( boundingGeometry->GetVtkTransform() ); transform->TransformPoints( points, newPoints ); bounds[0] = bounds[2] = 10000000.0; bounds[1] = bounds[3] = -10000000.0; bounds[4] = bounds[5] = 0.0; LineIntersectZero( newPoints, 0, 1, bounds ); LineIntersectZero( newPoints, 1, 2, bounds ); LineIntersectZero( newPoints, 2, 3, bounds ); LineIntersectZero( newPoints, 3, 0, bounds ); LineIntersectZero( newPoints, 0, 4, bounds ); LineIntersectZero( newPoints, 1, 5, bounds ); LineIntersectZero( newPoints, 2, 6, bounds ); LineIntersectZero( newPoints, 3, 7, bounds ); LineIntersectZero( newPoints, 4, 5, bounds ); LineIntersectZero( newPoints, 5, 6, bounds ); LineIntersectZero( newPoints, 6, 7, bounds ); LineIntersectZero( newPoints, 7, 4, bounds ); if ( (bounds[0] > 9999999.0) || (bounds[2] > 9999999.0) || (bounds[1] < -9999999.0) || (bounds[3] < -9999999.0) ) { return false; } else { // The resulting bounds must be adjusted by the plane spacing, since we // we have so far dealt with index coordinates - const float *planeSpacing = planeGeometry->GetFloatSpacing(); + const mitk::Vector3D planeSpacing = planeGeometry->GetSpacing(); bounds[0] *= planeSpacing[0]; bounds[1] *= planeSpacing[0]; bounds[2] *= planeSpacing[1]; bounds[3] *= planeSpacing[1]; bounds[4] *= planeSpacing[2]; bounds[5] *= planeSpacing[2]; return true; } } } } #endif diff --git a/Core/Code/Algorithms/mitkSurfaceToImageFilter.cpp b/Core/Code/Algorithms/mitkSurfaceToImageFilter.cpp index d68c61baff..7cbb76e444 100644 --- a/Core/Code/Algorithms/mitkSurfaceToImageFilter.cpp +++ b/Core/Code/Algorithms/mitkSurfaceToImageFilter.cpp @@ -1,223 +1,221 @@ /*=================================================================== 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 "mitkSurfaceToImageFilter.h" #include "mitkTimeHelper.h" #include "mitkImageWriteAccessor.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include mitk::SurfaceToImageFilter::SurfaceToImageFilter() : m_MakeOutputBinary( false ), m_BackgroundValue( -10000 ) { } mitk::SurfaceToImageFilter::~SurfaceToImageFilter() { } void mitk::SurfaceToImageFilter::GenerateInputRequestedRegion() { mitk::Image* output = this->GetOutput(); if((output->IsInitialized()==false) ) return; GenerateTimeInInputRegion(output, const_cast< mitk::Image * > ( this->GetImage() )); } void mitk::SurfaceToImageFilter::GenerateOutputInformation() { mitk::Image *inputImage = (mitk::Image*)this->GetImage(); mitk::Image::Pointer output = this->GetOutput(); itkDebugMacro(<<"GenerateOutputInformation()"); if((inputImage == NULL) || (inputImage->IsInitialized() == false) || (inputImage->GetTimeGeometry() == NULL)) return; if (m_MakeOutputBinary) output->Initialize(mitk::MakeScalarPixelType() , *inputImage->GetTimeGeometry()); else output->Initialize(inputImage->GetPixelType(), *inputImage->GetTimeGeometry()); output->SetPropertyList(inputImage->GetPropertyList()->Clone()); } void mitk::SurfaceToImageFilter::GenerateData() { mitk::Image::ConstPointer inputImage = this->GetImage(); mitk::Image::Pointer output = this->GetOutput(); if(inputImage.IsNull()) return; if(output->IsInitialized()==false ) return; mitk::Image::RegionType outputRegion = output->GetRequestedRegion(); int tstart=outputRegion.GetIndex(3); int tmax=tstart+outputRegion.GetSize(3); if ( tmax > 0) { int t; for(t=tstart;tGetOutput(); mitk::Image::Pointer binaryImage = mitk::Image::New(); unsigned int size = sizeof(unsigned char); if (m_MakeOutputBinary) binaryImage->Initialize(mitk::MakeScalarPixelType(), *this->GetImage()->GetTimeGeometry(),1,1); else { binaryImage->Initialize(this->GetImage()->GetPixelType(), *this->GetImage()->GetTimeGeometry(),1,1); size = this->GetImage()->GetPixelType().GetSize(); } for (unsigned int i = 0; i < binaryImage->GetDimension(); ++i) size *= binaryImage->GetDimension(i); mitk::ImageWriteAccessor accessor( binaryImage ); memset( accessor.GetData(), 1, size ); const mitk::TimeGeometry *surfaceTimeGeometry = GetInput()->GetTimeGeometry(); const mitk::TimeGeometry *imageTimeGeometry = GetImage()->GetTimeGeometry(); // Convert time step from image time-frame to surface time-frame mitk::TimePointType matchingTimePoint = imageTimeGeometry->TimeStepToTimePoint(time); mitk::TimeStepType surfaceTimeStep = surfaceTimeGeometry->TimePointToTimeStep(matchingTimePoint); vtkPolyData * polydata = ( (mitk::Surface*)GetInput() )->GetVtkPolyData( surfaceTimeStep ); if(polydata) { vtkSmartPointer move=vtkTransformPolyDataFilter::New(); move->SetInputData(polydata); move->ReleaseDataFlagOn(); vtkSmartPointer transform=vtkTransform::New(); - Geometry3D* geometry = surfaceTimeGeometry->GetGeometryForTimeStep( surfaceTimeStep ); + BaseGeometry* geometry = surfaceTimeGeometry->GetGeometryForTimeStep( surfaceTimeStep ); if(!geometry) { geometry = GetInput()->GetGeometry(); } - geometry->TransferItkToVtkTransform(); transform->PostMultiply(); transform->Concatenate(geometry->GetVtkTransform()->GetMatrix()); // take image geometry into account. vtk-Image information will be changed to unit spacing and zero origin below. - Geometry3D* imageGeometry = imageTimeGeometry->GetGeometryForTimeStep(time); - imageGeometry->TransferItkToVtkTransform(); + BaseGeometry* imageGeometry = imageTimeGeometry->GetGeometryForTimeStep(time); transform->Concatenate(imageGeometry->GetVtkTransform()->GetLinearInverse()); move->SetTransform(transform); vtkSmartPointer normalsFilter = vtkPolyDataNormals::New(); normalsFilter->SetFeatureAngle(50); normalsFilter->SetConsistency(1); normalsFilter->SetSplitting(1); normalsFilter->SetFlipNormals(0); normalsFilter->ReleaseDataFlagOn(); normalsFilter->SetInputConnection(move->GetOutputPort()); vtkSmartPointer surfaceConverter = vtkPolyDataToImageStencil::New(); surfaceConverter->SetTolerance( 0.0 ); surfaceConverter->ReleaseDataFlagOn(); surfaceConverter->SetInputConnection( normalsFilter->GetOutputPort() ); vtkImageData *image = m_MakeOutputBinary ? binaryImage->GetVtkImageData() : const_cast(this->GetImage())->GetVtkImageData(time); // Create stencil and use numerical minimum of pixel type as background value vtkSmartPointer stencil = vtkImageStencil::New(); stencil->SetInputData(image); stencil->ReverseStencilOff(); stencil->ReleaseDataFlagOn(); stencil->SetStencilConnection(surfaceConverter->GetOutputPort()); stencil->SetBackgroundValue(m_MakeOutputBinary ? 0 : m_BackgroundValue); stencil->Update(); output->SetVolume( stencil->GetOutput()->GetScalarPointer(), time ); MITK_INFO << "stencil ref count: " << stencil->GetReferenceCount() << std::endl; } else { memset( accessor.GetData(), 0, size ); output->SetVolume(accessor.GetData(),time); } } const mitk::Surface *mitk::SurfaceToImageFilter::GetInput(void) { if (this->GetNumberOfInputs() < 1) { return 0; } return static_cast ( this->ProcessObject::GetInput(0) ); } void mitk::SurfaceToImageFilter::SetInput(const mitk::Surface *input) { // Process object is not const-correct so the const_cast is required here this->ProcessObject::SetNthInput(0, const_cast< mitk::Surface * >( input ) ); } void mitk::SurfaceToImageFilter::SetImage(const mitk::Image *source) { this->ProcessObject::SetNthInput( 1, const_cast< mitk::Image * >( source ) ); } const mitk::Image *mitk::SurfaceToImageFilter::GetImage(void) { return static_cast< const mitk::Image * >(this->ProcessObject::GetInput(1)); } 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/mitkRenderingManager.cpp b/Core/Code/Controllers/mitkRenderingManager.cpp index 4269af05db..7407c8a738 100644 --- a/Core/Code/Controllers/mitkRenderingManager.cpp +++ b/Core/Code/Controllers/mitkRenderingManager.cpp @@ -1,925 +1,925 @@ /*=================================================================== 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 "mitkRenderingManager.h" #include "mitkRenderingManagerFactory.h" #include "mitkBaseRenderer.h" #include "mitkGlobalInteraction.h" #include "mitkNodePredicateNot.h" #include "mitkNodePredicateProperty.h" #include #include #include "mitkVector.h" #include #include #include #include namespace mitk { RenderingManager::Pointer RenderingManager::s_Instance = 0; RenderingManagerFactory *RenderingManager::s_RenderingManagerFactory = 0; RenderingManager ::RenderingManager() : m_UpdatePending( false ), m_MaxLOD( 1 ), m_LODIncreaseBlocked( false ), m_LODAbortMechanismEnabled( false ), m_ClippingPlaneEnabled( false ), m_TimeNavigationController( SliceNavigationController::New("dummy") ), m_DataStorage( NULL ), m_ConstrainedPaddingZooming ( true ) { m_ShadingEnabled.assign( 3, false ); m_ShadingValues.assign( 4, 0.0 ); m_GlobalInteraction = mitk::GlobalInteraction::GetInstance(); InitializePropertyList(); } RenderingManager ::~RenderingManager() { // Decrease reference counts of all registered vtkRenderWindows for // proper destruction RenderWindowVector::iterator it; for ( it = m_AllRenderWindows.begin(); it != m_AllRenderWindows.end(); ++it ) { (*it)->UnRegister( NULL ); RenderWindowCallbacksList::iterator callbacks_it = this->m_RenderWindowCallbacksList.find(*it); if (callbacks_it != this->m_RenderWindowCallbacksList.end()) { (*it)->RemoveObserver(callbacks_it->second.commands[0u]); (*it)->RemoveObserver(callbacks_it->second.commands[1u]); (*it)->RemoveObserver(callbacks_it->second.commands[2u]); } } } void RenderingManager ::SetFactory( RenderingManagerFactory *factory ) { s_RenderingManagerFactory = factory; } const RenderingManagerFactory * RenderingManager ::GetFactory() { return s_RenderingManagerFactory; } bool RenderingManager ::HasFactory() { if ( RenderingManager::s_RenderingManagerFactory ) { return true; } else { return false; } } RenderingManager::Pointer RenderingManager ::New() { const RenderingManagerFactory* factory = GetFactory(); if(factory == NULL) return NULL; return factory->CreateRenderingManager(); } RenderingManager * RenderingManager ::GetInstance() { if ( !RenderingManager::s_Instance ) { if ( s_RenderingManagerFactory ) { s_Instance = s_RenderingManagerFactory->CreateRenderingManager(); } } return s_Instance; } bool RenderingManager ::IsInstantiated() { if ( RenderingManager::s_Instance ) return true; else return false; } void RenderingManager ::AddRenderWindow( vtkRenderWindow *renderWindow ) { if ( renderWindow && (m_RenderWindowList.find( renderWindow ) == m_RenderWindowList.end()) ) { m_RenderWindowList[renderWindow] = RENDERING_INACTIVE; m_AllRenderWindows.push_back( renderWindow ); if ( m_DataStorage.IsNotNull() ) mitk::BaseRenderer::GetInstance( renderWindow )->SetDataStorage( m_DataStorage.GetPointer() ); // Register vtkRenderWindow instance renderWindow->Register( NULL ); // Add callbacks for rendering abort mechanism //BaseRenderer *renderer = BaseRenderer::GetInstance( renderWindow ); vtkCallbackCommand *startCallbackCommand = vtkCallbackCommand::New(); startCallbackCommand->SetCallback( RenderingManager::RenderingStartCallback ); renderWindow->AddObserver( vtkCommand::StartEvent, startCallbackCommand ); vtkCallbackCommand *progressCallbackCommand = vtkCallbackCommand::New(); progressCallbackCommand->SetCallback( RenderingManager::RenderingProgressCallback ); renderWindow->AddObserver( vtkCommand::AbortCheckEvent, progressCallbackCommand ); vtkCallbackCommand *endCallbackCommand = vtkCallbackCommand::New(); endCallbackCommand->SetCallback( RenderingManager::RenderingEndCallback ); renderWindow->AddObserver( vtkCommand::EndEvent, endCallbackCommand ); RenderWindowCallbacks callbacks; callbacks.commands[0u] = startCallbackCommand; callbacks.commands[1u] = progressCallbackCommand; callbacks.commands[2u] = endCallbackCommand; this->m_RenderWindowCallbacksList[renderWindow] = callbacks; //Delete vtk variables correctly startCallbackCommand->Delete(); progressCallbackCommand->Delete(); endCallbackCommand->Delete(); } } void RenderingManager ::RemoveRenderWindow( vtkRenderWindow *renderWindow ) { if (m_RenderWindowList.erase( renderWindow )) { RenderWindowCallbacksList::iterator callbacks_it = this->m_RenderWindowCallbacksList.find(renderWindow); if(callbacks_it != this->m_RenderWindowCallbacksList.end()) { renderWindow->RemoveObserver(callbacks_it->second.commands[0u]); renderWindow->RemoveObserver(callbacks_it->second.commands[1u]); renderWindow->RemoveObserver(callbacks_it->second.commands[2u]); this->m_RenderWindowCallbacksList.erase(callbacks_it); } RenderWindowVector::iterator rw_it = std::find( m_AllRenderWindows.begin(), m_AllRenderWindows.end(), renderWindow ); if(rw_it != m_AllRenderWindows.end()) { // Decrease reference count for proper destruction (*rw_it)->UnRegister(NULL); m_AllRenderWindows.erase( rw_it ); } } } const RenderingManager::RenderWindowVector& RenderingManager ::GetAllRegisteredRenderWindows() { return m_AllRenderWindows; } void RenderingManager ::RequestUpdate( vtkRenderWindow *renderWindow ) { // If the renderWindow is not valid, we do not want to inadvertantly create // an entry in the m_RenderWindowList map. It is possible if the user is // regularly calling AddRenderer and RemoveRenderer for a rendering update // to come into this method with a renderWindow pointer that is valid in the // sense that the window does exist within the application, but that // renderWindow has been temporarily removed from this RenderingManager for // performance reasons. if (m_RenderWindowList.find( renderWindow ) == m_RenderWindowList.end()) { return; } m_RenderWindowList[renderWindow] = RENDERING_REQUESTED; if ( !m_UpdatePending ) { m_UpdatePending = true; this->GenerateRenderingRequestEvent(); } } void RenderingManager ::ForceImmediateUpdate( vtkRenderWindow *renderWindow ) { // If the renderWindow is not valid, we do not want to inadvertantly create // an entry in the m_RenderWindowList map. It is possible if the user is // regularly calling AddRenderer and RemoveRenderer for a rendering update // to come into this method with a renderWindow pointer that is valid in the // sense that the window does exist within the application, but that // renderWindow has been temporarily removed from this RenderingManager for // performance reasons. if (m_RenderWindowList.find( renderWindow ) == m_RenderWindowList.end()) { return; } // Erase potentially pending requests for this window m_RenderWindowList[renderWindow] = RENDERING_INACTIVE; m_UpdatePending = false; // Immediately repaint this window (implementation platform specific) // If the size is 0 it crahses int *size = renderWindow->GetSize(); if ( 0 != size[0] && 0 != size[1] ) { //prepare the camera etc. before rendering //Note: this is a very important step which should be called before the VTK render! //If you modify the camera anywhere else or after the render call, the scene cannot be seen. mitk::VtkPropRenderer *vPR = dynamic_cast(mitk::BaseRenderer::GetInstance( renderWindow )); if(vPR) vPR->PrepareRender(); // Execute rendering renderWindow->Render(); } } void RenderingManager ::RequestUpdateAll( RequestType type ) { RenderWindowList::iterator it; for ( it = m_RenderWindowList.begin(); it != m_RenderWindowList.end(); ++it ) { int id = BaseRenderer::GetInstance(it->first)->GetMapperID(); if ( (type == REQUEST_UPDATE_ALL) || ((type == REQUEST_UPDATE_2DWINDOWS) && (id == 1)) || ((type == REQUEST_UPDATE_3DWINDOWS) && (id == 2)) ) { this->RequestUpdate( it->first ); } } } void RenderingManager ::ForceImmediateUpdateAll( RequestType type ) { RenderWindowList::iterator it; for ( it = m_RenderWindowList.begin(); it != m_RenderWindowList.end(); ++it ) { int id = BaseRenderer::GetInstance(it->first)->GetMapperID(); if ( (type == REQUEST_UPDATE_ALL) || ((type == REQUEST_UPDATE_2DWINDOWS) && (id == 1)) || ((type == REQUEST_UPDATE_3DWINDOWS) && (id == 2)) ) { // Immediately repaint this window (implementation platform specific) // If the size is 0, it crashes this->ForceImmediateUpdate(it->first); } } } void RenderingManager::InitializeViewsByBoundingObjects( const DataStorage *ds) { if (!ds) return; // get all nodes that have not set "includeInBoundingBox" to false mitk::NodePredicateNot::Pointer pred = mitk::NodePredicateNot::New(mitk::NodePredicateProperty::New("includeInBoundingBox" , mitk::BoolProperty::New(false))); mitk::DataStorage::SetOfObjects::ConstPointer rs = ds->GetSubset(pred); // calculate bounding geometry of these nodes mitk::TimeGeometry::Pointer bounds = ds->ComputeBoundingGeometry3D(rs, "visible"); // initialize the views to the bounding geometry this->InitializeViews(bounds); } //TODO_GOETZ // Remove old function, so only this one is working. bool RenderingManager -::InitializeViews( const Geometry3D * dataGeometry, RequestType type, bool preserveRoughOrientationInWorldSpace ) +::InitializeViews( const BaseGeometry * dataGeometry, RequestType type, bool preserveRoughOrientationInWorldSpace ) { ProportionalTimeGeometry::Pointer propTimeGeometry = ProportionalTimeGeometry::New(); - propTimeGeometry->Initialize(dynamic_cast(dataGeometry->Clone().GetPointer()), 1); + propTimeGeometry->Initialize(dynamic_cast(dataGeometry->Clone().GetPointer()), 1); return InitializeViews(propTimeGeometry,type, preserveRoughOrientationInWorldSpace); } bool RenderingManager ::InitializeViews( const TimeGeometry * dataGeometry, RequestType type, bool /*preserveRoughOrientationInWorldSpace*/ ) { MITK_DEBUG << "initializing views"; bool boundingBoxInitialized = false; TimeGeometry::ConstPointer timeGeometry = dataGeometry; TimeGeometry::Pointer modifiedGeometry = NULL; if (dataGeometry!=NULL) { modifiedGeometry = dataGeometry->Clone(); } // //TODO_GOETZ previously this code section has been disabled by // a later asignment to geometry (e.g. timeGeometry) // This has been fixed during Geometry-1-Plattform Project // Propably this code is not working anymore, test!! /* if (dataGeometry && preserveRoughOrientationInWorldSpace) { // clone the input geometry assert(modifiedGeometry.IsNotNull()); // construct an affine transform from it Geometry3D::TransformType::Pointer transform = Geometry3D::TransformType::New(); assert( modifiedGeometry->GetGeometryForTimeStep(0)->GetIndexToWorldTransform() ); transform->SetMatrix( modifiedGeometry->GetGeometryForTimeStep(0)->GetIndexToWorldTransform()->GetMatrix() ); transform->SetOffset( modifiedGeometry->GetGeometryForTimeStep(0)->GetIndexToWorldTransform()->GetOffset() ); // get transform matrix Geometry3D::TransformType::MatrixType::InternalMatrixType& oldMatrix = const_cast< Geometry3D::TransformType::MatrixType::InternalMatrixType& > ( transform->GetMatrix().GetVnlMatrix() ); Geometry3D::TransformType::MatrixType::InternalMatrixType newMatrix(oldMatrix); // get offset and bound Vector3D offset = modifiedGeometry->GetIndexToWorldTransform()->GetOffset(); Geometry3D::BoundsArrayType oldBounds = modifiedGeometry->GetBounds(); Geometry3D::BoundsArrayType newBounds = modifiedGeometry->GetBounds(); // get rid of rotation other than pi/2 degree for ( unsigned int i = 0; i < 3; ++i ) { // i-th column of the direction matrix Vector3D currentVector; currentVector[0] = oldMatrix(0,i); currentVector[1] = oldMatrix(1,i); currentVector[2] = oldMatrix(2,i); // matchingRow will store the row that holds the biggest // value in the column unsigned int matchingRow = 0; // maximum value in the column ScalarType max = std::numeric_limits::min(); // sign of the maximum value (-1 or 1) int sign = 1; // iterate through the column vector for (unsigned int dim = 0; dim < 3; ++dim) { if ( fabs(currentVector[dim]) > max ) { matchingRow = dim; max = fabs(currentVector[dim]); if(currentVector[dim]<0) sign = -1; else sign = 1; } } // in case we found a negative maximum, // we negate the column and adjust the offset // (in order to run through the dimension in the opposite direction) if(sign == -1) { currentVector *= sign; offset += modifiedGeometry->GetAxisVector(i); } // matchingRow is now used as column index to place currentVector // correctly in the new matrix vnl_vector newMatrixColumn(3); newMatrixColumn[0] = currentVector[0]; newMatrixColumn[1] = currentVector[1]; newMatrixColumn[2] = currentVector[2]; newMatrix.set_column( matchingRow, newMatrixColumn ); // if a column is moved, we also have to adjust the bounding // box accordingly, this is done here newBounds[2*matchingRow ] = oldBounds[2*i ]; newBounds[2*matchingRow+1] = oldBounds[2*i+1]; } // set the newly calculated bounds array modifiedGeometry->SetBounds(newBounds); // set new offset and direction matrix Geometry3D::TransformType::MatrixType newMatrixITK( newMatrix ); transform->SetMatrix( newMatrixITK ); transform->SetOffset( offset ); modifiedGeometry->SetIndexToWorldTransform( transform ); geometry = modifiedGeometry; }*/ int warningLevel = vtkObject::GetGlobalWarningDisplay(); vtkObject::GlobalWarningDisplayOff(); if ( (timeGeometry.IsNotNull() ) && (const_cast< mitk::BoundingBox * >( timeGeometry->GetBoundingBoxInWorld())->GetDiagonalLength2() > mitk::eps) ) { boundingBoxInitialized = true; } if (timeGeometry.IsNotNull() ) {// make sure bounding box has an extent bigger than zero in any direction // clone the input geometry //Old Geometry3D::Pointer modifiedGeometry = dynamic_cast( dataGeometry->Clone().GetPointer() ); assert(modifiedGeometry.IsNotNull()); for (TimeStepType step = 0; step < modifiedGeometry->CountTimeSteps(); ++step) { - Geometry3D::BoundsArrayType newBounds = modifiedGeometry->GetGeometryForTimeStep(step)->GetBounds(); + BaseGeometry::BoundsArrayType newBounds = modifiedGeometry->GetGeometryForTimeStep(step)->GetBounds(); for( unsigned int dimension = 0; ( 2 * dimension ) < newBounds.Size() ; dimension++ ) { //check for equality but for an epsilon if( Equal( newBounds[ 2 * dimension ], newBounds[ 2 * dimension + 1 ] ) ) { newBounds[ 2 * dimension + 1 ] += 1; } } modifiedGeometry->GetGeometryForTimeStep(step)->SetBounds(newBounds); } } timeGeometry = modifiedGeometry; RenderWindowList::iterator it; for ( it = m_RenderWindowList.begin(); it != m_RenderWindowList.end(); ++it ) { mitk::BaseRenderer *baseRenderer = mitk::BaseRenderer::GetInstance( it->first ); baseRenderer->GetDisplayGeometry()->SetConstrainZoomingAndPanning(m_ConstrainedPaddingZooming); int id = baseRenderer->GetMapperID(); if ( ((type == REQUEST_UPDATE_ALL) || ((type == REQUEST_UPDATE_2DWINDOWS) && (id == 1)) || ((type == REQUEST_UPDATE_3DWINDOWS) && (id == 2))) ) { this->InternalViewInitialization( baseRenderer, timeGeometry, boundingBoxInitialized, id ); } } if ( boundingBoxInitialized ) { m_TimeNavigationController->SetInputWorldTimeGeometry( timeGeometry ); } m_TimeNavigationController->Update(); this->RequestUpdateAll( type ); vtkObject::SetGlobalWarningDisplay( warningLevel ); // Inform listeners that views have been initialized this->InvokeEvent( mitk::RenderingManagerViewsInitializedEvent() ); return boundingBoxInitialized; } bool RenderingManager ::InitializeViews( RequestType type ) { RenderWindowList::iterator it; for ( it = m_RenderWindowList.begin(); it != m_RenderWindowList.end(); ++it ) { mitk::BaseRenderer *baseRenderer = mitk::BaseRenderer::GetInstance( it->first ); int id = baseRenderer->GetMapperID(); if ( (type == REQUEST_UPDATE_ALL) || ((type == REQUEST_UPDATE_2DWINDOWS) && (id == 1)) || ((type == REQUEST_UPDATE_3DWINDOWS) && (id == 2)) ) { mitk::SliceNavigationController *nc = baseRenderer->GetSliceNavigationController(); // Re-initialize view direction nc->SetViewDirectionToDefault(); // Update the SNC nc->Update(); } } this->RequestUpdateAll( type ); return true; } -bool RenderingManager::InitializeView( vtkRenderWindow * renderWindow, const Geometry3D * geometry, bool initializeGlobalTimeSNC ) +bool RenderingManager::InitializeView( vtkRenderWindow * renderWindow, const BaseGeometry * geometry, bool initializeGlobalTimeSNC ) { ProportionalTimeGeometry::Pointer propTimeGeometry = ProportionalTimeGeometry::New(); - propTimeGeometry->Initialize(dynamic_cast(geometry->Clone().GetPointer()), 1); + propTimeGeometry->Initialize(dynamic_cast(geometry->Clone().GetPointer()), 1); return InitializeView(renderWindow, propTimeGeometry, initializeGlobalTimeSNC ); } bool RenderingManager::InitializeView( vtkRenderWindow * renderWindow, const TimeGeometry * geometry, bool initializeGlobalTimeSNC ) { bool boundingBoxInitialized = false; int warningLevel = vtkObject::GetGlobalWarningDisplay(); vtkObject::GlobalWarningDisplayOff(); if ( (geometry != NULL ) && (const_cast< mitk::BoundingBox * >( geometry->GetBoundingBoxInWorld())->GetDiagonalLength2() > mitk::eps) ) { boundingBoxInitialized = true; } mitk::BaseRenderer *baseRenderer = mitk::BaseRenderer::GetInstance( renderWindow ); int id = baseRenderer->GetMapperID(); this->InternalViewInitialization( baseRenderer, geometry, boundingBoxInitialized, id ); if ( boundingBoxInitialized && initializeGlobalTimeSNC ) { m_TimeNavigationController->SetInputWorldTimeGeometry( geometry ); } m_TimeNavigationController->Update(); this->RequestUpdate( renderWindow ); vtkObject::SetGlobalWarningDisplay( warningLevel ); return boundingBoxInitialized; } bool RenderingManager::InitializeView( vtkRenderWindow * renderWindow ) { mitk::BaseRenderer *baseRenderer = mitk::BaseRenderer::GetInstance( renderWindow ); mitk::SliceNavigationController *nc = baseRenderer->GetSliceNavigationController(); // Re-initialize view direction nc->SetViewDirectionToDefault(); // Update the SNC nc->Update(); this->RequestUpdate( renderWindow ); return true; } void RenderingManager::InternalViewInitialization(mitk::BaseRenderer *baseRenderer, const mitk::TimeGeometry *geometry, bool boundingBoxInitialized, int mapperID ) { mitk::SliceNavigationController *nc = baseRenderer->GetSliceNavigationController(); // Re-initialize view direction nc->SetViewDirectionToDefault(); if ( boundingBoxInitialized ) { // Set geometry for NC nc->SetInputWorldTimeGeometry( geometry ); nc->Update(); if ( mapperID == 1 ) { // For 2D SNCs, steppers are set so that the cross is centered // in the image nc->GetSlice()->SetPos( nc->GetSlice()->GetSteps() / 2 ); } // Fit the render window DisplayGeometry baseRenderer->GetDisplayGeometry()->Fit(); baseRenderer->GetCameraController()->SetViewToAnterior(); } else { nc->Update(); } } const SliceNavigationController* RenderingManager::GetTimeNavigationController() const { return m_TimeNavigationController.GetPointer(); } SliceNavigationController* RenderingManager::GetTimeNavigationController() { return m_TimeNavigationController.GetPointer(); } void RenderingManager::ExecutePendingRequests() { m_UpdatePending = false; // Satisfy all pending update requests RenderWindowList::iterator it; int i = 0; for ( it = m_RenderWindowList.begin(); it != m_RenderWindowList.end(); ++it, ++i ) { if ( it->second == RENDERING_REQUESTED ) { this->ForceImmediateUpdate( it->first ); } } } void RenderingManager::RenderingStartCallback( vtkObject *caller, unsigned long , void *, void * ) { vtkRenderWindow *renderWindow = dynamic_cast< vtkRenderWindow * >( caller ); mitk::RenderingManager* renman = mitk::BaseRenderer::GetInstance(renderWindow)->GetRenderingManager(); RenderWindowList &renderWindowList = renman->m_RenderWindowList; if ( renderWindow ) { renderWindowList[renderWindow] = RENDERING_INPROGRESS; } renman->m_UpdatePending = false; } void RenderingManager ::RenderingProgressCallback( vtkObject *caller, unsigned long , void *, void * ) { vtkRenderWindow *renderWindow = dynamic_cast< vtkRenderWindow * >( caller ); mitk::RenderingManager* renman = mitk::BaseRenderer::GetInstance(renderWindow)->GetRenderingManager(); if ( renman->m_LODAbortMechanismEnabled ) { vtkRenderWindow *renderWindow = dynamic_cast< vtkRenderWindow * >( caller ); if ( renderWindow ) { BaseRenderer *renderer = BaseRenderer::GetInstance( renderWindow ); if ( renderer && (renderer->GetNumberOfVisibleLODEnabledMappers() > 0) ) { renman->DoMonitorRendering(); } } } } void RenderingManager ::RenderingEndCallback( vtkObject *caller, unsigned long , void *, void * ) { vtkRenderWindow *renderWindow = dynamic_cast< vtkRenderWindow * >( caller ); mitk::RenderingManager* renman = mitk::BaseRenderer::GetInstance(renderWindow)->GetRenderingManager(); RenderWindowList &renderWindowList = renman->m_RenderWindowList; RendererIntMap &nextLODMap = renman->m_NextLODMap; if ( renderWindow ) { BaseRenderer *renderer = BaseRenderer::GetInstance( renderWindow ); if ( renderer ) { renderWindowList[renderer->GetRenderWindow()] = RENDERING_INACTIVE; // Level-of-Detail handling if ( renderer->GetNumberOfVisibleLODEnabledMappers() > 0 ) { if(nextLODMap[renderer]==0) renman->StartOrResetTimer(); else nextLODMap[renderer] = 0; } } } } bool RenderingManager ::IsRendering() const { RenderWindowList::const_iterator it; for ( it = m_RenderWindowList.begin(); it != m_RenderWindowList.end(); ++it ) { if ( it->second == RENDERING_INPROGRESS ) { return true; } } return false; } void RenderingManager ::AbortRendering() { RenderWindowList::iterator it; for ( it = m_RenderWindowList.begin(); it != m_RenderWindowList.end(); ++it ) { if ( it->second == RENDERING_INPROGRESS ) { it->first->SetAbortRender( true ); m_RenderingAbortedMap[BaseRenderer::GetInstance(it->first)] = true; } } } int RenderingManager ::GetNextLOD( BaseRenderer *renderer ) { if ( renderer != NULL ) { return m_NextLODMap[renderer]; } else { return 0; } } void RenderingManager ::ExecutePendingHighResRenderingRequest() { RenderWindowList::iterator it; for ( it = m_RenderWindowList.begin(); it != m_RenderWindowList.end(); ++it ) { BaseRenderer *renderer = BaseRenderer::GetInstance( it->first ); if(renderer->GetNumberOfVisibleLODEnabledMappers()>0) { if(m_NextLODMap[renderer]==0) { m_NextLODMap[renderer]=1; RequestUpdate( it->first ); } } } } void RenderingManager ::SetMaximumLOD( unsigned int max ) { m_MaxLOD = max; } //enable/disable shading void RenderingManager ::SetShading(bool state, unsigned int lod) { if(lod>m_MaxLOD) { itkWarningMacro(<<"LOD out of range requested: " << lod << " maxLOD: " << m_MaxLOD); return; } m_ShadingEnabled[lod] = state; } bool RenderingManager ::GetShading(unsigned int lod) { if(lod>m_MaxLOD) { itkWarningMacro(<<"LOD out of range requested: " << lod << " maxLOD: " << m_MaxLOD); return false; } return m_ShadingEnabled[lod]; } //enable/disable the clipping plane void RenderingManager ::SetClippingPlaneStatus(bool status) { m_ClippingPlaneEnabled = status; } bool RenderingManager ::GetClippingPlaneStatus() { return m_ClippingPlaneEnabled; } void RenderingManager ::SetShadingValues(float ambient, float diffuse, float specular, float specpower) { m_ShadingValues[0] = ambient; m_ShadingValues[1] = diffuse; m_ShadingValues[2] = specular; m_ShadingValues[3] = specpower; } RenderingManager::FloatVector & RenderingManager ::GetShadingValues() { return m_ShadingValues; } void RenderingManager::InitializePropertyList() { if (m_PropertyList.IsNull()) { m_PropertyList = PropertyList::New(); } this->SetProperty("coupled-zoom", BoolProperty::New(false)); this->SetProperty("coupled-plane-rotation", BoolProperty::New(false)); this->SetProperty("MIP-slice-rendering", BoolProperty::New(false)); } PropertyList::Pointer RenderingManager::GetPropertyList() const { return m_PropertyList; } BaseProperty* RenderingManager::GetProperty(const char *propertyKey) const { return m_PropertyList->GetProperty(propertyKey); } void RenderingManager::SetProperty(const char *propertyKey, BaseProperty* propertyValue) { m_PropertyList->SetProperty(propertyKey, propertyValue); } void RenderingManager::SetDataStorage( DataStorage* storage ) { if ( storage != NULL ) { m_DataStorage = storage; RenderingManager::RenderWindowVector::iterator iter; for ( iter = m_AllRenderWindows.begin(); iterSetDataStorage( m_DataStorage.GetPointer() ); } } } mitk::DataStorage* RenderingManager::GetDataStorage() { return m_DataStorage; } void RenderingManager::SetGlobalInteraction( mitk::GlobalInteraction* globalInteraction ) { if ( globalInteraction != NULL ) { m_GlobalInteraction = globalInteraction; } } mitk::GlobalInteraction* RenderingManager::GetGlobalInteraction() { return m_GlobalInteraction; } // Create and register generic RenderingManagerFactory. TestingRenderingManagerFactory renderingManagerFactory; } // namespace \ No newline at end of file diff --git a/Core/Code/Controllers/mitkRenderingManager.h b/Core/Code/Controllers/mitkRenderingManager.h index 10e531ab86..2652835e2c 100644 --- a/Core/Code/Controllers/mitkRenderingManager.h +++ b/Core/Code/Controllers/mitkRenderingManager.h @@ -1,407 +1,407 @@ /*=================================================================== 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 MITKRENDERINGMANAGER_H_HEADER_INCLUDED_C135A197 #define MITKRENDERINGMANAGER_H_HEADER_INCLUDED_C135A197 #include #include #include #include #include #include "mitkPropertyList.h" #include "mitkProperties.h" #include "mitkTimeGeometry.h" class vtkRenderWindow; class vtkObject; namespace mitk { class RenderingManager; class RenderingManagerFactory; -class Geometry3D; +class BaseGeometry; class SliceNavigationController; class BaseRenderer; class DataStorage; class GlobalInteraction; /** * \brief Manager for coordinating the rendering process. * * RenderingManager is a central instance retrieving and executing * RenderWindow update requests. Its main purpose is to coordinate * distributed requests which cannot be aware of each other - lacking the * knowledge of whether they are really necessary or not. For example, two * objects might determine that a specific RenderWindow needs to be updated. * This would result in one unnecessary update, if both executed the update * on their own. * * The RenderingManager addresses this by letting each such object * request an update, and waiting for other objects to possibly * issue the same request. The actual update will then only be executed at a * well-defined point in the main event loop (this may be each time after * event processing is done). * * Convinience methods for updating all RenderWindows which have been * registered with the RenderingManager exist. If theses methods are not * used, it is not required to register (add) RenderWindows prior to using * the RenderingManager. * * The methods #ForceImmediateUpdate() and #ForceImmediateUpdateAll() can * be used to force the RenderWindow update execution without any delay, * bypassing the request functionality. * * The interface of RenderingManager is platform independent. Platform * specific subclasses have to be implemented, though, to supply an * appropriate event issueing for controlling the update execution process. * See method documentation for a description of how this can be done. * * \sa TestingRenderingManager An "empty" RenderingManager implementation which * can be used in tests etc. * */ class MITK_CORE_EXPORT RenderingManager : public itk::Object { public: mitkClassMacro(RenderingManager,itk::Object); typedef std::vector< vtkRenderWindow* > RenderWindowVector; typedef std::vector< float > FloatVector; typedef std::vector< bool > BoolVector; typedef itk::SmartPointer< DataStorage > DataStoragePointer; typedef itk::SmartPointer< GlobalInteraction > GlobalInteractionPointer; enum RequestType { REQUEST_UPDATE_ALL = 0, REQUEST_UPDATE_2DWINDOWS, REQUEST_UPDATE_3DWINDOWS }; static Pointer New(); /** Set the object factory which produces the desired platform specific * RenderingManager singleton instance. */ static void SetFactory( RenderingManagerFactory *factory ); /** Get the object factory which produces the platform specific * RenderingManager instances. */ static const RenderingManagerFactory *GetFactory(); /** Returns true if a factory has already been set. */ static bool HasFactory(); /** Get the RenderingManager singleton instance. */ static RenderingManager *GetInstance(); /** Returns true if the singleton instance does already exist. */ static bool IsInstantiated(); /** Adds a RenderWindow. This is required if the methods #RequestUpdateAll * or #ForceImmediateUpdate are to be used. */ void AddRenderWindow( vtkRenderWindow *renderWindow ); /** Removes a RenderWindow. */ void RemoveRenderWindow( vtkRenderWindow *renderWindow ); /** Get a list of all registered RenderWindows */ const RenderWindowVector &GetAllRegisteredRenderWindows(); /** Requests an update for the specified RenderWindow, to be executed as * soon as the main loop is ready for rendering. */ void RequestUpdate( vtkRenderWindow *renderWindow ); /** Immediately executes an update of the specified RenderWindow. */ void ForceImmediateUpdate( vtkRenderWindow *renderWindow ); /** Requests all currently registered RenderWindows to be updated. * If only 2D or 3D windows should be updated, this can be specified * via the parameter requestType. */ void RequestUpdateAll( RequestType type = REQUEST_UPDATE_ALL ); /** Immediately executes an update of all registered RenderWindows. * If only 2D or 3D windows should be updated, this can be specified * via the parameter requestType. */ void ForceImmediateUpdateAll( RequestType type = REQUEST_UPDATE_ALL ); /** Initializes the windows specified by requestType to the geometry of the * given DataStorage. */ //virtual bool InitializeViews( const DataStorage *storage, const DataNode* node = NULL, // RequestType type = REQUEST_UPDATE_ALL, bool preserveRoughOrientationInWorldSpace = false ); /** Initializes the windows specified by requestType to the given * geometry. PLATFORM SPECIFIC. TODO: HOW IS THIS PLATFORM SPECIFIC? */ - virtual bool InitializeViews( const Geometry3D *geometry, + virtual bool InitializeViews( const BaseGeometry *geometry, RequestType type = REQUEST_UPDATE_ALL, bool preserveRoughOrientationInWorldSpace = false ); virtual bool InitializeViews( const TimeGeometry *geometry, RequestType type = REQUEST_UPDATE_ALL, bool preserveRoughOrientationInWorldSpace = false ); /** Initializes the windows to the default viewing direction * (geomtry information is NOT changed). PLATFORM SPECIFIC. */ virtual bool InitializeViews( RequestType type = REQUEST_UPDATE_ALL ); /** Initializes the specified window to the geometry of the given * DataNode. Set "initializeGlobalTimeSNC" to true in order to use this * geometry as global TimeGeometry. PLATFORM SPECIFIC. */ //virtual bool InitializeView( vtkRenderWindow *renderWindow, const DataStorage* ds, const DataNode* node = NULL, bool initializeGlobalTimeSNC = false ); /** Initializes the specified window to the given geometry. Set * "initializeGlobalTimeSNC" to true in order to use this geometry as * global TimeGeometry. PLATFORM SPECIFIC. */ - virtual bool InitializeView( vtkRenderWindow *renderWindow, const Geometry3D *geometry, bool initializeGlobalTimeSNC = false); + virtual bool InitializeView( vtkRenderWindow *renderWindow, const BaseGeometry *geometry, bool initializeGlobalTimeSNC = false); virtual bool InitializeView( vtkRenderWindow *renderWindow, const TimeGeometry *geometry, bool initializeGlobalTimeSNC = false); /** Initializes the specified window to the default viewing direction * (geomtry information is NOT changed). PLATFORM SPECIFIC. */ virtual bool InitializeView( vtkRenderWindow *renderWindow ); /** * @brief Initializes the renderwindows by the aggregated geometry of * all objects that are held in the data storage. * This is basically a global reinit * @param The data storage from which the bounding object can be retrieved */ virtual void InitializeViewsByBoundingObjects(const DataStorage * ); /** Gets the (global) SliceNavigationController responsible for * time-slicing. */ const SliceNavigationController *GetTimeNavigationController() const; /** Gets the (global) SliceNavigationController responsible for * time-slicing. */ SliceNavigationController *GetTimeNavigationController(); virtual ~RenderingManager(); /** Executes all pending requests. This method has to be called by the * system whenever a RenderingManager induced request event occurs in * the system pipeline (see concrete RenderingManager implementations). */ virtual void ExecutePendingRequests(); bool IsRendering() const; void AbortRendering(); /** En-/Disable LOD increase globally. */ itkSetMacro( LODIncreaseBlocked, bool ); /** En-/Disable LOD increase globally. */ itkGetMacro( LODIncreaseBlocked, bool ); /** En-/Disable LOD increase globally. */ itkBooleanMacro( LODIncreaseBlocked ); /** En-/Disable LOD abort mechanism. */ itkSetMacro( LODAbortMechanismEnabled, bool ); /** En-/Disable LOD abort mechanism. */ itkGetMacro( LODAbortMechanismEnabled, bool ); /** En-/Disable LOD abort mechanism. */ itkBooleanMacro( LODAbortMechanismEnabled ); /** Force a sub-class to start a timer for a pending hires-rendering request */ virtual void StartOrResetTimer() {}; /** To be called by a sub-class from a timer callback */ void ExecutePendingHighResRenderingRequest(); virtual void DoStartRendering() {}; virtual void DoMonitorRendering() {}; virtual void DoFinishAbortRendering() {}; int GetNextLOD( BaseRenderer* renderer ); /** Set current LOD (NULL means all renderers)*/ void SetMaximumLOD( unsigned int max ); void SetShading( bool state, unsigned int lod ); bool GetShading( unsigned int lod ); void SetClippingPlaneStatus( bool status ); bool GetClippingPlaneStatus(); void SetShadingValues( float ambient, float diffuse, float specular, float specpower ); FloatVector &GetShadingValues(); /** Returns a property list */ PropertyList::Pointer GetPropertyList() const; /** Returns a property from m_PropertyList */ BaseProperty* GetProperty(const char *propertyKey) const; /** Sets or adds (if not present) a property in m_PropertyList */ void SetProperty(const char *propertyKey, BaseProperty* propertyValue); /** * \brief Setter / Getter for internal DataStorage * * Sets / returns the mitk::DataStorage that is used internally. This instance holds all mitk::DataNodes that are * rendered by the registered BaseRenderers. * * If this DataStorage is changed at runtime by calling SetDataStorage(), * all currently registered BaseRenderers are automatically given the correct instance. * When a new BaseRenderer is added, it is automatically initialized with the currently active DataStorage. */ void SetDataStorage( mitk::DataStorage* storage ); /** * \brief Setter / Getter for internal DataStorage * * Sets / returns the mitk::DataStorage that is used internally. This instance holds all mitk::DataNodes that are * rendered by the registered BaseRenderers. * * If this DataStorage is changed at runtime by calling SetDataStorage(), * all currently registered BaseRenderers are automatically given the correct instance. * When a new BaseRenderer is added, it is automatically initialized with the currently active DataStorage. */ mitk::DataStorage* GetDataStorage(); /** * \brief Setter / Getter for internal GloabInteraction * * Sets / returns the instance of mitk::GlobalInteraction that is internally held. * It'S not actually used by this class but offers it to all registered BaseRenderers. * These need it for their own internal initialization of the FocusManager and the corresponding EventMappers. */ void SetGlobalInteraction( mitk::GlobalInteraction* globalInteraction ); /** * \brief Setter / Getter for internal GloabInteraction * * Sets / returns the instance of mitk::GlobalInteraction that is internally held. * It'S not actually used by this class but offers it to all registered BaseRenderers. * These need it for their own internal initialization of the FocusManager and the corresponding EventMappers. */ mitk::GlobalInteraction* GetGlobalInteraction(); itkSetMacro(ConstrainedPaddingZooming, bool); protected: enum { RENDERING_INACTIVE = 0, RENDERING_REQUESTED, RENDERING_INPROGRESS }; RenderingManager(); /** Abstract method for generating a system specific event for rendering * request. This method is called whenever an update is requested */ virtual void GenerateRenderingRequestEvent() = 0; virtual void InitializePropertyList(); bool m_UpdatePending; typedef std::map< BaseRenderer *, unsigned int > RendererIntMap; typedef std::map< BaseRenderer *, bool > RendererBoolMap; RendererBoolMap m_RenderingAbortedMap; RendererIntMap m_NextLODMap; unsigned int m_MaxLOD; bool m_LODIncreaseBlocked; bool m_LODAbortMechanismEnabled; BoolVector m_ShadingEnabled; bool m_ClippingPlaneEnabled; FloatVector m_ShadingValues; static void RenderingStartCallback( vtkObject *caller, unsigned long eid, void *clientdata, void *calldata ); static void RenderingProgressCallback( vtkObject *caller, unsigned long eid, void *clientdata, void *calldata ); static void RenderingEndCallback( vtkObject *caller, unsigned long eid, void *clientdata, void *calldata ); typedef std::map< vtkRenderWindow *, int > RenderWindowList; RenderWindowList m_RenderWindowList; RenderWindowVector m_AllRenderWindows; struct RenderWindowCallbacks { vtkCallbackCommand* commands[3u]; }; typedef std::map RenderWindowCallbacksList; RenderWindowCallbacksList m_RenderWindowCallbacksList; itk::SmartPointer m_TimeNavigationController; static RenderingManager::Pointer s_Instance; static RenderingManagerFactory *s_RenderingManagerFactory; PropertyList::Pointer m_PropertyList; DataStoragePointer m_DataStorage; GlobalInteractionPointer m_GlobalInteraction; bool m_ConstrainedPaddingZooming; private: void InternalViewInitialization( mitk::BaseRenderer *baseRenderer, const mitk::TimeGeometry *geometry, bool boundingBoxInitialized, int mapperID ); }; #pragma GCC visibility push(default) itkEventMacro( RenderingManagerEvent, itk::AnyEvent ); itkEventMacro( RenderingManagerViewsInitializedEvent, RenderingManagerEvent ); #pragma GCC visibility pop /** * Generic RenderingManager implementation for "non-rendering-plattform", * e.g. for tests. Its factory (TestingRenderingManagerFactory) is * automatically on start-up and is used by default if not other * RenderingManagerFactory is instantiated explicitly thereafter. * (see mitkRenderingManager.cpp) */ class MITK_CORE_EXPORT TestingRenderingManager : public RenderingManager { public: mitkClassMacro(TestingRenderingManager,RenderingManager); itkFactorylessNewMacro(Self) itkCloneMacro(Self) protected: virtual void GenerateRenderingRequestEvent() { // ForceImmediateUpdateAll(); }; }; } // namespace mitk #endif /* MITKRenderingManager_H_HEADER_INCLUDED_C135A197 */ diff --git a/Core/Code/Controllers/mitkSliceNavigationController.cpp b/Core/Code/Controllers/mitkSliceNavigationController.cpp index def6a28050..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 Geometry3D *geometry ) +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; - Geometry3D::ConstPointer currentGeometry = 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::Geometry3D * +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 74efc11c4c..2e24d0dcb6 100644 --- a/Core/Code/Controllers/mitkSliceNavigationController.h +++ b/Core/Code/Controllers/mitkSliceNavigationController.h @@ -1,599 +1,599 @@ /*=================================================================== 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 Geometry3D; +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); itkFactorylessNewMacro(Self) itkCloneMacro(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 { Axial, Sagittal, Frontal, Original }; /** * \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::Geometry3D* geometry); - itkGetConstObjectMacro(InputWorldGeometry3D, mitk::Geometry3D); + 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::Geometry3D *GetCurrentGeometry3D(); + 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::Geometry3D::ConstPointer m_InputWorldGeometry3D; + 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 35b415ca1d..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->Geometry3D::IndexToWorld( + clickedGeometry->BaseGeometry::IndexToWorld( Vector3D( xVector), m_RotationPlaneXVector ); - clickedGeometry->Geometry3D::IndexToWorld( + 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.cpp b/Core/Code/DataManagement/mitkAbstractTransformGeometry.cpp index c0743239ac..7e42b82396 100644 --- a/Core/Code/DataManagement/mitkAbstractTransformGeometry.cpp +++ b/Core/Code/DataManagement/mitkAbstractTransformGeometry.cpp @@ -1,283 +1,318 @@ /*=================================================================== 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 "mitkAbstractTransformGeometry.h" #include mitk::AbstractTransformGeometry::AbstractTransformGeometry() : m_Plane(NULL), m_FrameGeometry(NULL) { Initialize(); } -mitk::AbstractTransformGeometry::AbstractTransformGeometry(const AbstractTransformGeometry& other) : Superclass(other) +mitk::AbstractTransformGeometry::AbstractTransformGeometry(const AbstractTransformGeometry& other) : Superclass(other), m_ParametricBoundingBox(other.m_ParametricBoundingBox) { if(other.m_ParametricBoundingBox.IsNotNull()) { + m_ParametricBoundingBox = other.m_ParametricBoundingBox->DeepCopy(); this->SetParametricBounds(m_ParametricBoundingBox->GetBounds()); } this->SetPlane(other.m_Plane); this->SetFrameGeometry(other.m_FrameGeometry); } - mitk::AbstractTransformGeometry::~AbstractTransformGeometry() { } -void mitk::AbstractTransformGeometry::Initialize() +void mitk::AbstractTransformGeometry::PostInitialize() { - Superclass::Initialize(); - m_ItkVtkAbstractTransform = itk::VtkAbstractTransform::New(); } vtkAbstractTransform* mitk::AbstractTransformGeometry::GetVtkAbstractTransform() const { return m_ItkVtkAbstractTransform->GetVtkAbstractTransform(); } mitk::ScalarType mitk::AbstractTransformGeometry::GetParametricExtentInMM(int direction) const { if(m_Plane.IsNull()) { itkExceptionMacro(<<"m_Plane is NULL."); } return m_Plane->GetExtentInMM(direction); } const mitk::Transform3D* mitk::AbstractTransformGeometry::GetParametricTransform() const { return m_ItkVtkAbstractTransform; } bool mitk::AbstractTransformGeometry::Project(const mitk::Point3D &pt3d_mm, mitk::Point3D &projectedPt3d_mm) const { - assert(m_BoundingBox.IsNotNull()); + assert(this->IsBoundingBoxNull()==false); mitk::Point2D pt2d_mm; bool isInside; isInside = Map(pt3d_mm, pt2d_mm); Map(pt2d_mm, projectedPt3d_mm); return isInside; //Point3D pt3d_units; //pt3d_units = m_ItkVtkAbstractTransform->BackTransform(pt3d_mm); //pt3d_units[2] = 0; //projectedPt3d_mm = m_ItkVtkAbstractTransform->TransformPoint(pt3d_units); //return const_cast(m_BoundingBox.GetPointer())->IsInside(pt3d_units); } bool mitk::AbstractTransformGeometry::Map(const mitk::Point3D &pt3d_mm, mitk::Point2D &pt2d_mm) const { assert((m_ItkVtkAbstractTransform.IsNotNull()) && (m_Plane.IsNotNull())); Point3D pt3d_units; pt3d_units = m_ItkVtkAbstractTransform->BackTransform(pt3d_mm); return m_Plane->Map(pt3d_units, pt2d_mm); } void mitk::AbstractTransformGeometry::Map(const mitk::Point2D &pt2d_mm, mitk::Point3D &pt3d_mm) const { assert((m_ItkVtkAbstractTransform.IsNotNull()) && (m_Plane.IsNotNull())); m_Plane->Map(pt2d_mm, pt3d_mm); pt3d_mm = m_ItkVtkAbstractTransform->TransformPoint(pt3d_mm); } bool mitk::AbstractTransformGeometry::Project(const mitk::Point3D & atPt3d_mm, const mitk::Vector3D &vec3d_mm, mitk::Vector3D &projectedVec3d_mm) const { itkExceptionMacro("not implemented yet - replace GetIndexToWorldTransform by m_ItkVtkAbstractTransform->GetInverseVtkAbstractTransform()"); - assert(m_BoundingBox.IsNotNull()); + assert(this->IsBoundingBoxNull()==false); Vector3D vec3d_units; vec3d_units = GetIndexToWorldTransform()->GetInverseMatrix() * vec3d_mm; vec3d_units[2] = 0; projectedVec3d_mm = GetIndexToWorldTransform()->TransformVector(vec3d_units); Point3D pt3d_units; mitk::ScalarType temp[3]; unsigned int i, j; for (j = 0; j < 3; ++j) temp[j] = atPt3d_mm[j] - GetIndexToWorldTransform()->GetOffset()[j]; for (i = 0; i < 3; ++i) { pt3d_units[i] = 0.0; for (j = 0; j < 3; ++j) pt3d_units[i] += GetIndexToWorldTransform()->GetInverseMatrix()[i][j] * temp[j]; } - return const_cast(m_BoundingBox.GetPointer())->IsInside(pt3d_units); + return const_cast(this->GetBoundingBox())->IsInside(pt3d_units); } bool mitk::AbstractTransformGeometry::Project(const mitk::Vector3D &/*vec3d_mm*/, mitk::Vector3D &/*projectedVec3d_mm*/) const { - MITK_WARN << "Need additional point! No standard value defined. Please use Project(const mitk::Point3D & atPt3d_mm, const mitk::Vector3D &vec3d_mm, mitk::Vector3D &projectedVec3d_mm). Unfortunatley this one is not implemented at the moment. Sorry :("; - itkExceptionMacro("not implemented yet - replace GetIndexToWorldTransform by m_ItkVtkAbstractTransform->GetInverseVtkAbstractTransform()"); - return false; + MITK_WARN << "Need additional point! No standard value defined. Please use Project(const mitk::Point3D & atPt3d_mm, const mitk::Vector3D &vec3d_mm, mitk::Vector3D &projectedVec3d_mm). Unfortunatley this one is not implemented at the moment. Sorry :("; + itkExceptionMacro("not implemented yet - replace GetIndexToWorldTransform by m_ItkVtkAbstractTransform->GetInverseVtkAbstractTransform()"); + return false; } - bool mitk::AbstractTransformGeometry::Map(const mitk::Point3D & atPt3d_mm, const mitk::Vector3D &vec3d_mm, mitk::Vector2D &vec2d_mm) const { assert((m_ItkVtkAbstractTransform.IsNotNull()) && (m_Plane.IsNotNull())); ScalarType vtkpt[3], vtkvec[3]; itk2vtk(atPt3d_mm, vtkpt); itk2vtk(vec3d_mm, vtkvec); m_ItkVtkAbstractTransform->GetInverseVtkAbstractTransform()->TransformVectorAtPoint(vtkpt, vtkvec, vtkvec); mitk::Vector3D vec3d_units; vtk2itk(vtkvec, vec3d_units); return m_Plane->Map(atPt3d_mm, vec3d_units, vec2d_mm); } void mitk::AbstractTransformGeometry::Map(const mitk::Point2D & atPt2d_mm, const mitk::Vector2D &vec2d_mm, mitk::Vector3D &vec3d_mm) const { m_Plane->Map(atPt2d_mm, vec2d_mm, vec3d_mm); Point3D atPt3d_mm; Map(atPt2d_mm, atPt3d_mm); float vtkpt[3], vtkvec[3]; itk2vtk(atPt3d_mm, vtkpt); itk2vtk(vec3d_mm, vtkvec); m_ItkVtkAbstractTransform->GetVtkAbstractTransform()->TransformVectorAtPoint(vtkpt, vtkvec, vtkvec); vtk2itk(vtkvec, vec3d_mm); } void mitk::AbstractTransformGeometry::IndexToWorld(const mitk::Point2D &pt_units, mitk::Point2D &pt_mm) const { m_Plane->IndexToWorld(pt_units, pt_mm); } void mitk::AbstractTransformGeometry::WorldToIndex(const mitk::Point2D &pt_mm, mitk::Point2D &pt_units) const { m_Plane->WorldToIndex(pt_mm, pt_units); } void mitk::AbstractTransformGeometry::IndexToWorld(const mitk::Point2D & /*atPt2d_units*/, const mitk::Vector2D &vec_units, mitk::Vector2D &vec_mm) const { MITK_WARN<<"Warning! Call of the deprecated function AbstractTransformGeometry::IndexToWorld(point, vec, vec). Use AbstractTransformGeometry::IndexToWorld(vec, vec) instead!"; this->IndexToWorld(vec_units, vec_mm); } void mitk::AbstractTransformGeometry::IndexToWorld(const mitk::Vector2D &vec_units, mitk::Vector2D &vec_mm) const { m_Plane->IndexToWorld(vec_units, vec_mm); } void mitk::AbstractTransformGeometry::WorldToIndex(const mitk::Point2D & /*atPt2d_mm*/, const mitk::Vector2D &vec_mm, mitk::Vector2D &vec_units) const { MITK_WARN<<"Warning! Call of the deprecated function AbstractTransformGeometry::WorldToIndex(point, vec, vec). Use AbstractTransformGeometry::WorldToIndex(vec, vec) instead!"; this->WorldToIndex(vec_mm, vec_units); } void mitk::AbstractTransformGeometry::WorldToIndex(const mitk::Vector2D &vec_mm, mitk::Vector2D &vec_units) const { m_Plane->WorldToIndex(vec_mm, vec_units); } - bool mitk::AbstractTransformGeometry::IsAbove(const mitk::Point3D& pt3d_mm) const { assert((m_ItkVtkAbstractTransform.IsNotNull()) && (m_Plane.IsNotNull())); Point3D pt3d_ParametricWorld; pt3d_ParametricWorld = m_ItkVtkAbstractTransform->BackTransform(pt3d_mm); Point3D pt3d_ParametricUnits; - ((Geometry3D*)m_Plane)->WorldToIndex(pt3d_ParametricWorld, pt3d_ParametricUnits); + ((BaseGeometry*)m_Plane)->WorldToIndex(pt3d_ParametricWorld, pt3d_ParametricUnits); return (pt3d_ParametricUnits[2] > m_ParametricBoundingBox->GetBounds()[4]); } void mitk::AbstractTransformGeometry::SetVtkAbstractTransform(vtkAbstractTransform* aVtkAbstractTransform) { m_ItkVtkAbstractTransform->SetVtkAbstractTransform(aVtkAbstractTransform); } void mitk::AbstractTransformGeometry::SetPlane(const mitk::PlaneGeometry* aPlane) { if(aPlane!=NULL) { m_Plane = static_cast(aPlane->Clone().GetPointer()); BoundingBox::BoundsArrayType b=m_Plane->GetBoundingBox()->GetBounds(); SetParametricBounds(b); CalculateFrameGeometry(); } else { if(m_Plane.IsNull()) return; m_Plane=NULL; } Modified(); } void mitk::AbstractTransformGeometry::CalculateFrameGeometry() { if((m_Plane.IsNull()) || (m_FrameGeometry.IsNotNull())) return; //@warning affine-transforms and bounding-box should be set by specific sub-classes! SetBounds(m_Plane->GetBoundingBox()->GetBounds()); } -void mitk::AbstractTransformGeometry::SetFrameGeometry(const mitk::Geometry3D* frameGeometry) +void mitk::AbstractTransformGeometry::SetFrameGeometry(const mitk::BaseGeometry* frameGeometry) { if((frameGeometry != NULL) && (frameGeometry->IsValid())) { - m_FrameGeometry = static_cast(frameGeometry->Clone().GetPointer()); + m_FrameGeometry = static_cast(frameGeometry->Clone().GetPointer()); SetIndexToWorldTransform(m_FrameGeometry->GetIndexToWorldTransform()); SetBounds(m_FrameGeometry->GetBounds()); } else { m_FrameGeometry = NULL; } } unsigned long mitk::AbstractTransformGeometry::GetMTime() const { if(Superclass::GetMTime()GetMTime()) return m_ItkVtkAbstractTransform->GetMTime(); return Superclass::GetMTime(); } void mitk::AbstractTransformGeometry::SetOversampling(mitk::ScalarType oversampling) { if(m_Plane.IsNull()) { itkExceptionMacro(<< "m_Plane is not set."); } mitk::BoundingBox::BoundsArrayType bounds = m_Plane->GetBounds(); bounds[1]*=oversampling; bounds[3]*=oversampling; bounds[5]*=oversampling; SetParametricBounds(bounds); } itk::LightObject::Pointer mitk::AbstractTransformGeometry::InternalClone() const { Self::Pointer newGeometry = new AbstractTransformGeometry(*this); newGeometry->UnRegister(); return newGeometry.GetPointer(); } + +void mitk::AbstractTransformGeometry::SetParametricBounds(const BoundingBox::BoundsArrayType& bounds) +{ + m_ParametricBoundingBox = BoundingBoxType::New(); + + BoundingBoxType::PointsContainer::Pointer pointscontainer = + BoundingBoxType::PointsContainer::New(); + BoundingBoxType::PointType p; + BoundingBoxType::PointIdentifier pointid; + + for(pointid=0; pointid<2;++pointid) + { + unsigned int i; + for(i=0; iInsertElement(pointid, p); + } + + m_ParametricBoundingBox->SetPoints(pointscontainer); + m_ParametricBoundingBox->ComputeBoundingBox(); + this->Modified(); +} + +const mitk::BoundingBox::BoundsArrayType& mitk::AbstractTransformGeometry::GetParametricBounds() const +{ + assert(m_ParametricBoundingBox.IsNotNull()); + return m_ParametricBoundingBox->GetBounds(); +} + +mitk::ScalarType mitk::AbstractTransformGeometry::GetParametricExtent(int direction) const +{ + if (direction < 0 || direction>=3) + mitkThrow() << "Invalid direction. Must be between either 0, 1 or 2. "; + assert(m_ParametricBoundingBox.IsNotNull()); + + BoundingBoxType::BoundsArrayType bounds = m_ParametricBoundingBox->GetBounds(); + return bounds[direction*2+1]-bounds[direction*2]; +} diff --git a/Core/Code/DataManagement/mitkAbstractTransformGeometry.h b/Core/Code/DataManagement/mitkAbstractTransformGeometry.h index faf8a23874..e872adaa27 100644 --- a/Core/Code/DataManagement/mitkAbstractTransformGeometry.h +++ b/Core/Code/DataManagement/mitkAbstractTransformGeometry.h @@ -1,193 +1,220 @@ /*=================================================================== 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 "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 -//## 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 -{ -public: - mitkClassMacro(AbstractTransformGeometry, Geometry2D); - - itkFactorylessNewMacro(Self) - itkCloneMacro(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. + //## @brief Describes a geometry defined by an vtkAbstractTransform and a 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); - - virtual void Initialize(); - - //##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::Geometry3D* frameGeometry); - - virtual itk::LightObject::Pointer InternalClone() 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. + //## 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 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. - //## @note The PlaneGeometry is cloned, @em not linked/referenced. - virtual void SetPlane(const mitk::PlaneGeometry* aPlane); + //## @sa ExternAbstractTransformGeometry + //## @ingroup Geometry + class MITK_CORE_EXPORT AbstractTransformGeometry : public PlaneGeometry + { + public: + mitkClassMacro(AbstractTransformGeometry, PlaneGeometry); - //##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; + itkFactorylessNewMacro(Self) + itkCloneMacro(Self) - itk::VtkAbstractTransform::Pointer m_ItkVtkAbstractTransform; + //##Documentation + //## @brief Get the vtkAbstractTransform (stored in m_VtkAbstractTransform) + virtual vtkAbstractTransform* GetVtkAbstractTransform() const; - mitk::Geometry3D::Pointer m_FrameGeometry; -}; + 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 PostInitialize(); + + //##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/mitkBaseData.cpp b/Core/Code/DataManagement/mitkBaseData.cpp index a48a9dd05b..e2a7530b25 100644 --- a/Core/Code/DataManagement/mitkBaseData.cpp +++ b/Core/Code/DataManagement/mitkBaseData.cpp @@ -1,296 +1,298 @@ /*=================================================================== 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 "mitkBaseData.h" #include #include #include +#include mitk::BaseData::BaseData() : m_RequestedRegionInitialized(false), m_SourceOutputIndexDuplicate(0), m_Initialized(true) { m_TimeGeometry = mitk::ProportionalTimeGeometry::New(); m_PropertyList = PropertyList::New(); } mitk::BaseData::BaseData( const BaseData &other ): itk::DataObject(), mitk::OperationActor(), m_RequestedRegionInitialized(other.m_RequestedRegionInitialized), m_SourceOutputIndexDuplicate(other.m_SourceOutputIndexDuplicate), m_Initialized(other.m_Initialized) { m_TimeGeometry = dynamic_cast(other.m_TimeGeometry->Clone().GetPointer()); m_PropertyList = other.m_PropertyList->Clone(); } mitk::BaseData::~BaseData() { } void mitk::BaseData::InitializeTimeGeometry(unsigned int timeSteps) { - mitk::Geometry3D::Pointer g3d = mitk::Geometry3D::New(); - g3d->Initialize(); + mitk::Geometry3D::Pointer geo3D = mitk::Geometry3D::New(); + mitk::BaseGeometry::Pointer baseGeo = dynamic_cast(geo3D.GetPointer()); + baseGeo->Initialize(); if ( timeSteps > 1 ) { mitk::ScalarType timeBounds[] = {0.0, 1.0}; - g3d->SetTimeBounds( timeBounds ); + baseGeo->SetTimeBounds( timeBounds ); } // The geometry is propagated automatically to the other items, // if EvenlyTimed is true... //Old timeGeometry->InitializeEvenlyTimed( g3d.GetPointer(), timeSteps ); TimeGeometry::Pointer timeGeometry = this->GetTimeGeometry(); timeGeometry->Initialize(); timeGeometry->Expand(timeSteps); for (TimeStepType step = 0; step < timeSteps; ++step) { - timeGeometry->SetTimeStepGeometry(g3d.GetPointer(),step); + timeGeometry->SetTimeStepGeometry(baseGeo.GetPointer(),step); } } void mitk::BaseData::UpdateOutputInformation() { if ( this->GetSource() ) { this->GetSource()->UpdateOutputInformation(); } if (m_TimeGeometry.IsNotNull()) { m_TimeGeometry->UpdateBoundingBox(); } } const mitk::TimeGeometry* mitk::BaseData::GetUpdatedTimeGeometry() { SetRequestedRegionToLargestPossibleRegion(); UpdateOutputInformation(); return GetTimeGeometry(); } void mitk::BaseData::Expand( unsigned int timeSteps ) { if (m_TimeGeometry.IsNotNull() ) { ProportionalTimeGeometry * propTimeGeometry = dynamic_cast (m_TimeGeometry.GetPointer()); if (propTimeGeometry) { propTimeGeometry->Expand(timeSteps); return; } mitkThrow() << "TimeGeometry is of an unkown Type. Could not expand it. "; } else { this->InitializeTimeGeometry(timeSteps); } } -const mitk::Geometry3D* mitk::BaseData::GetUpdatedGeometry(int t) +const mitk::BaseGeometry* mitk::BaseData::GetUpdatedGeometry(int t) { SetRequestedRegionToLargestPossibleRegion(); UpdateOutputInformation(); return GetGeometry(t); } -void mitk::BaseData::SetGeometry(Geometry3D* geometry) +void mitk::BaseData::SetGeometry(BaseGeometry* geometry) { ProportionalTimeGeometry::Pointer timeGeometry = ProportionalTimeGeometry::New(); if(geometry!=NULL) { timeGeometry->Initialize(geometry, 1); } SetTimeGeometry(timeGeometry); return; } void mitk::BaseData::SetTimeGeometry(TimeGeometry* geometry) { m_TimeGeometry = geometry; this->Modified(); } -void mitk::BaseData::SetClonedGeometry(const Geometry3D* aGeometry3D) +void mitk::BaseData::SetClonedGeometry(const BaseGeometry* aGeometry3D) { - SetGeometry(static_cast(aGeometry3D->Clone().GetPointer())); + SetGeometry(static_cast(aGeometry3D->Clone().GetPointer())); } void mitk::BaseData::SetClonedTimeGeometry(const TimeGeometry* geometry) { TimeGeometry::Pointer clonedGeometry = geometry->Clone(); SetTimeGeometry(clonedGeometry.GetPointer()); } -void mitk::BaseData::SetClonedGeometry(const Geometry3D* aGeometry3D, unsigned int time) +void mitk::BaseData::SetClonedGeometry(const BaseGeometry* aGeometry3D, unsigned int time) { if (m_TimeGeometry) { - m_TimeGeometry->SetTimeStepGeometry(static_cast(aGeometry3D->Clone().GetPointer()),time); + m_TimeGeometry->SetTimeStepGeometry(static_cast(aGeometry3D->Clone().GetPointer()),time); } } bool mitk::BaseData::IsEmptyTimeStep(unsigned int) const { return IsInitialized() == false; } bool mitk::BaseData::IsEmpty() const { if(IsInitialized() == false) return true; const TimeGeometry* timeGeometry = const_cast(this)->GetUpdatedTimeGeometry(); if(timeGeometry == NULL) return true; unsigned int timeSteps = timeGeometry->CountTimeSteps(); for ( unsigned int t = 0 ; t < timeSteps ; ++t ) { if(IsEmptyTimeStep(t) == false) return false; } return true; } itk::SmartPointer mitk::BaseData::GetSource() const { return static_cast(Superclass::GetSource().GetPointer()); } mitk::PropertyList::Pointer mitk::BaseData::GetPropertyList() const { return m_PropertyList; } mitk::BaseProperty::Pointer mitk::BaseData::GetProperty(const char *propertyKey) const { return m_PropertyList->GetProperty(propertyKey); } void mitk::BaseData::SetProperty(const char *propertyKey, BaseProperty* propertyValue) { m_PropertyList->SetProperty(propertyKey, propertyValue); } void mitk::BaseData::SetPropertyList(PropertyList *pList) { m_PropertyList = pList; } void mitk::BaseData::SetOrigin(const mitk::Point3D& origin) { TimeGeometry* timeGeom = GetTimeGeometry(); assert (timeGeom != NULL); - Geometry3D* geometry; + BaseGeometry* geometry; TimeStepType steps = timeGeom->CountTimeSteps(); for (TimeStepType timestep = 0; timestep < steps; ++timestep) { geometry = GetGeometry(timestep); if (geometry != NULL) { geometry->SetOrigin(origin); } } } unsigned long mitk::BaseData::GetMTime() const { unsigned long time = Superclass::GetMTime(); if(m_TimeGeometry.IsNotNull()) { if((time < m_TimeGeometry->GetMTime())) { Modified(); return Superclass::GetMTime(); } } return time; } void mitk::BaseData::Graft(const itk::DataObject*) { itkExceptionMacro(<< "Graft not implemented for mitk::BaseData subclass " << this->GetNameOfClass()) } void mitk::BaseData::CopyInformation( const itk::DataObject* data ) { const Self* bd = dynamic_cast(data); if (bd != NULL) { m_PropertyList = bd->GetPropertyList()->Clone(); if (bd->GetTimeGeometry()!=NULL) { m_TimeGeometry = bd->GetTimeGeometry()->Clone(); } } else { // pointer could not be cast back down; this can be the case if your filters input // and output objects differ in type; then you have to write your own GenerateOutputInformation method itkExceptionMacro(<< "mitk::BaseData::CopyInformation() cannot cast " << typeid(data).name() << " to " << typeid(Self*).name() ); } } bool mitk::BaseData::IsInitialized() const { return m_Initialized; } void mitk::BaseData::Clear() { this->ClearData(); this->InitializeEmpty(); } void mitk::BaseData::ClearData() { if(m_Initialized) { ReleaseData(); m_Initialized = false; } } void mitk::BaseData::ExecuteOperation(mitk::Operation* /*operation*/) { //empty by default. override if needed! } void mitk::BaseData::PrintSelf(std::ostream& os, itk::Indent indent) const { os << std::endl; os << indent << " TimeGeometry: "; if(GetTimeGeometry() == NULL) os << "NULL" << std::endl; else GetTimeGeometry()->Print(os, indent); } diff --git a/Core/Code/DataManagement/mitkBaseData.h b/Core/Code/DataManagement/mitkBaseData.h index 0331b4f57e..52abb68cb2 100644 --- a/Core/Code/DataManagement/mitkBaseData.h +++ b/Core/Code/DataManagement/mitkBaseData.h @@ -1,429 +1,429 @@ /*=================================================================== 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 BASEDATA_H_HEADER_INCLUDED_C1EBB6FA #define BASEDATA_H_HEADER_INCLUDED_C1EBB6FA #include #include "mitkBaseProcess.h" #include "mitkTimeGeometry.h" #include #include "mitkOperationActor.h" #include "mitkPropertyList.h" namespace mitk { //class BaseProcess; //##Documentation //## @brief Base of all data objects //## //## Base of all data objects, e.g., images, contours, surfaces etc. Inherits //## from itk::DataObject and thus can be included in a pipeline. //## Inherits also from OperationActor and can be used as a destination for Undo //## @ingroup Data class MITK_CORE_EXPORT BaseData : public itk::DataObject, public OperationActor { public: mitkClassMacro(BaseData,itk::DataObject) /** * \brief Return the TimeGeometry of the data as const pointer. * * \warning No update will be called. Use GetUpdatedGeometry() if you cannot * be sure that the geometry is up-to-date. * * Normally used in GenerateOutputInformation of subclasses of BaseProcess. */ const mitk::TimeGeometry* GetTimeGeometry() const { return m_TimeGeometry.GetPointer(); } /** * \brief Return the TimeGeometry of the data as const pointer. * * \warning No update will be called. Use GetUpdatedGeometry() if you cannot * be sure that the geometry is up-to-date. * * Normally used in GenerateOutputInformation of subclasses of BaseProcess. * \deprecatedSince{2013_09} Please use GetTimeGeometry instead: For additional information see http://www.mitk.org/Development/Refactoring%20of%20the%20Geometry%20Classes%20-%20Part%201 */ DEPRECATED(const mitk::TimeGeometry* GetTimeSlicedGeometry() const) { return GetTimeGeometry(); } /** * @brief Return the TimeGeometry of the data as pointer. * * \warning No update will be called. Use GetUpdatedGeometry() if you cannot * be sure that the geometry is up-to-date. * * Normally used in GenerateOutputInformation of subclasses of BaseProcess. */ mitk::TimeGeometry* GetTimeGeometry() { return m_TimeGeometry.GetPointer(); } /** * @brief Return the Geometry3D of the data. * * The method does not simply return the value of the m_TimeGeometry * member. Before doing this, it makes sure that the TimeGeometry * is up-to-date (by setting the update extent to largest possible and * calling UpdateOutputInformation). */ const mitk::TimeGeometry* GetUpdatedTimeGeometry(); /** * @brief Return the Geometry3D of the data. * * The method does not simply return the value of the m_TimeGeometry * member. Before doing this, it makes sure that the TimeGeometry * is up-to-date (by setting the update extent to largest possible and * calling UpdateOutputInformation). * \deprecatedSince{2013_09} Please use GetUpdatedTimeGeometry instead: For additional information see http://www.mitk.org/Development/Refactoring%20of%20the%20Geometry%20Classes%20-%20Part%201 */ DEPRECATED(const mitk::TimeGeometry* GetUpdatedTimeSliceGeometry()) { return GetUpdatedTimeGeometry(); } /** * \brief Expands the TimeGeometry to a number of TimeSteps. * * The method expands the TimeGeometry to the given number of TimeSteps, * filling newly created elements with empty geometries. Sub-classes should override * this method to handle the elongation of their data vectors, too. * Note that a shrinking is neither possible nor intended. */ virtual void Expand( unsigned int timeSteps ); /** * \brief Return the Geometry3D of the data at time \a t. * * The method does not simply return * m_TimeGeometry->GetGeometry(t). * Before doing this, it makes sure that the Geometry3D is up-to-date * (by setting the update extent appropriately and calling * UpdateOutputInformation). * * @todo Appropriate setting of the update extent is missing. */ - const mitk::Geometry3D* GetUpdatedGeometry(int t=0); + const mitk::BaseGeometry* GetUpdatedGeometry(int t=0); //##Documentation //## @brief Return the geometry, which is a TimeGeometry, of the data //## as non-const pointer. //## //## \warning No update will be called. Use GetUpdatedGeometry() if you cannot //## be sure that the geometry is up-to-date. //## //## Normally used in GenerateOutputInformation of subclasses of BaseProcess. - mitk::Geometry3D* GetGeometry(int t=0) const + mitk::BaseGeometry* GetGeometry(int t=0) const { if(m_TimeGeometry.IsNull()) return NULL; return m_TimeGeometry->GetGeometryForTimeStep(t); } //##Documentation //## @brief Update the information for this BaseData (the geometry in particular) //## so that it can be used as an output of a BaseProcess. //## //## This method is used in the pipeline mechanism to propagate information and //## initialize the meta data associated with a BaseData. Any implementation //## of this method in a derived class is assumed to call its source's //## BaseProcess::UpdateOutputInformation() which determines modified //## times, LargestPossibleRegions, and any extra meta data like spacing, //## origin, etc. Default implementation simply call's it's source's //## UpdateOutputInformation(). //## \note Implementations of this methods in derived classes must take care //## that the geometry is updated by calling //## GetTimeGeometry()->UpdateInformation() //## \em after calling its source's BaseProcess::UpdateOutputInformation(). void UpdateOutputInformation(); //##Documentation //## @brief 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()=0; //##Documentation //## @brief 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()=0; //##Documentation //## @brief 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() = 0; //##Documentation //## @brief Copy information from the specified data set. //## //## This method is part of the pipeline execution model. By default, a //## BaseProcess will copy meta-data from the first input to all of its //## outputs. See ProcessObject::GenerateOutputInformation(). Each //## subclass of DataObject is responsible for being able to copy //## whatever meta-data it needs from another DataObject. //## The default implementation of this method copies the time sliced geometry //## and the property list of an object. If a subclass overrides this //## method, it should always call its superclass' version. void CopyInformation(const itk::DataObject* data); //##Documentation //## @brief Check whether the data has been initialized, i.e., //## at least the Geometry and other header data has been set //## //## \warning Set to \a true by default for compatibility reasons. //## Set m_Initialized=false in constructors of sub-classes that //## support distinction between initialized and uninitialized state. virtual bool IsInitialized() const; //##Documentation //## @brief Calls ClearData() and InitializeEmpty(); //## \warning Only use in subclasses that reimplemented these methods. //## Just calling Clear from BaseData will reset an object to a not initialized, //## invalid state. virtual void Clear(); //##Documentation //## @brief Check whether object contains data (at //## a specified time), e.g., a set of points may be empty //## //## \warning Returns IsInitialized()==false by default for //## compatibility reasons. Override in sub-classes that //## support distinction between empty/non-empty state. virtual bool IsEmptyTimeStep(unsigned int t) const; //##Documentation //## @brief Check whether object contains data (at //## least at one point in time), e.g., a set of points //## may be empty //## //## \warning Returns IsInitialized()==false by default for //## compatibility reasons. Override in sub-classes that //## support distinction between empty/non-empty state. virtual bool IsEmpty() const; //##Documentation //## @brief 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 BaseData. virtual void SetRequestedRegion(const itk::DataObject *data)=0; //##Documentation //##@brief overwrite if the Data can be called by an Interactor (StateMachine). //## //## Empty by default. Overwrite and implement all the necessary operations here //## and get the necessary information from the parameter operation. void ExecuteOperation(Operation* operation); /** * \brief Set the Geometry3D of the data, which will be referenced (not copied!). * Assumes the data object has only 1 time step ( is a 3D object ) and creates a * new TimeGeometry which saves the given Geometry3D. If an TimeGeometry has already * been set for the object, it will be replaced after calling this function. * * @warning This method will normally be called internally by the sub-class of BaseData * during initialization. * \sa SetClonedGeometry */ - virtual void SetGeometry(Geometry3D* aGeometry3D); + virtual void SetGeometry(BaseGeometry* aGeometry3D); /** * \brief Set the TimeGeometry of the data, which will be referenced (not copied!). * * @warning This method will normally be called internally by the sub-class of BaseData * during initialization. * \sa SetClonedTimeGeometry */ virtual void SetTimeGeometry (TimeGeometry* geometry); /** * \brief Set a clone of the provided TimeGeometry as TimeGeometry of the data. * Assumes the data object has only 1 time step ( is a 3D object ) and * creates a new TimeGeometry. If an TimeGeometry has already * been set for the object, it will be replaced after calling this function. * * \sa SetGeometry */ - virtual void SetClonedGeometry(const Geometry3D* aGeometry3D); + virtual void SetClonedGeometry(const BaseGeometry* aGeometry3D); /** * \brief Set a clone of the provided TimeGeometry as TimeGeometry of the data. * * \sa SetGeometry */ virtual void SetClonedTimeGeometry (const TimeGeometry* geometry); //##Documentation //## @brief Set a clone of the provided geometry as Geometry3D of a given time step. //## //## \sa SetGeometry - virtual void SetClonedGeometry(const Geometry3D* aGeometry3D, unsigned int time); + virtual void SetClonedGeometry(const BaseGeometry* aGeometry3D, unsigned int time); //##Documentation //## @brief Get the data's property list //## @sa GetProperty //## @sa m_PropertyList mitk::PropertyList::Pointer GetPropertyList() const; //##Documentation //## @brief Set the data's property list //## @sa SetProperty //## @sa m_PropertyList void SetPropertyList(PropertyList* propertyList); //##Documentation //## @brief Get the property (instance of BaseProperty) with key @a propertyKey from the PropertyList, //## and set it to this, respectively; //## @sa GetPropertyList //## @sa m_PropertyList //## @sa m_MapOfPropertyLists mitk::BaseProperty::Pointer GetProperty(const char *propertyKey) const; void SetProperty(const char *propertyKey, BaseProperty* property); //##Documentation //## @brief Convenience method for setting the origin of //## the Geometry3D instances of all time steps //## //## \warning Geometries contained in the Geometry3D will //## \em not be changed, e.g. in case the Geometry3D is a //## SlicedGeometry3D the origin will \em not be propagated //## to the contained slices. The sub-class SlicedData //## does this for the case that the SlicedGeometry3D is //## evenly spaced. virtual void SetOrigin(const Point3D& origin); /** \brief Get the process object that generated this data object. * * If there is no process object, then the data object has * been disconnected from the pipeline, or the data object * was created manually. (Note: we cannot use the GetObjectMacro() * defined in itkMacro because the mutual dependency of * DataObject and ProcessObject causes compile problems. Also, * a forward reference smart pointer is returned, not a smart pointer, * because of the circular dependency between the process and data object.) * * GetSource() returns a SmartPointer and not a WeakPointer * because it is assumed the code calling GetSource() wants to hold a * long term reference to the source. */ itk::SmartPointer GetSource() const; //##Documentation //## @brief Get the number of time steps from the TimeGeometry //## As the base data has not a data vector given by itself, the number //## of time steps is defined over the time sliced geometry. In sub classes, //## a better implementation could be over the length of the data vector. unsigned int GetTimeSteps() const { return m_TimeGeometry->CountTimeSteps(); } //##Documentation //## @brief Get the modified time of the last change of the contents //## this data object or its geometry. virtual unsigned long GetMTime() const; /** * \sa itk::ProcessObject::Graft */ virtual void Graft(const DataObject*); protected: BaseData(); BaseData(const BaseData &other); ~BaseData(); //##Documentation //## \brief Initialize the TimeGeometry for a number of time steps. //## The TimeGeometry is initialized empty and evenly timed. //## In many cases it will be necessary to overwrite this in sub-classes. virtual void InitializeTimeGeometry( unsigned int timeSteps = 1 ); /** * \brief Initialize the TimeGeometry for a number of time steps. * The TimeGeometry is initialized empty and evenly timed. * In many cases it will be necessary to overwrite this in sub-classes. * \deprecatedSince{2013_09} Please use GetUpdatedTimeGeometry instead: For additional information see http://www.mitk.org/Development/Refactoring%20of%20the%20Geometry%20Classes%20-%20Part%201 */ DEPRECATED(virtual void InitializeTimeSlicedGeometry( unsigned int timeSteps = 1 )) { InitializeTimeGeometry(timeSteps); } //##Documentation //## @brief reset to non-initialized state, release memory virtual void ClearData(); //##Documentation //## @brief Pure virtual; Must be used in subclasses to get a data object to a //## valid state. Should at least create one empty object and call //## Superclass::InitializeTimeGeometry() to ensure an existing valid geometry virtual void InitializeEmpty(){} virtual void PrintSelf(std::ostream& os, itk::Indent indent) const; bool m_RequestedRegionInitialized; bool m_LastRequestedRegionWasOutsideOfTheBufferedRegion; mutable unsigned int m_SourceOutputIndexDuplicate; bool m_Initialized; private: //##Documentation //## @brief PropertyList, f.e. to hold pic-tags, tracking-data,.. //## PropertyList::Pointer m_PropertyList; TimeGeometry::Pointer m_TimeGeometry; }; } // namespace mitk #endif /* BASEDATA_H_HEADER_INCLUDED_C1EBB6FA */ diff --git a/Core/Code/DataManagement/mitkGeometry3D.cpp b/Core/Code/DataManagement/mitkBaseGeometry.cpp similarity index 64% copy from Core/Code/DataManagement/mitkGeometry3D.cpp copy to Core/Code/DataManagement/mitkBaseGeometry.cpp index 70789582b1..4200af572c 100644 --- a/Core/Code/DataManagement/mitkGeometry3D.cpp +++ b/Core/Code/DataManagement/mitkBaseGeometry.cpp @@ -1,978 +1,1070 @@ /*=================================================================== 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 "mitkGeometry3D.h" +#include "mitkBaseGeometry.h" +#include "mitkvector.h" #include "mitkMatrixConvert.h" +#include +#include #include "mitkRotationOperation.h" #include "mitkRestorePlanePositionOperation.h" #include "mitkApplyTransformMatrixOperation.h" #include "mitkPointOperation.h" #include "mitkInteractionConst.h" -#include -#include - -// Standard constructor for the New() macro. Sets the geometry to 3 dimensions -mitk::Geometry3D::Geometry3D() - : m_ParametricBoundingBox(NULL), - m_ImageGeometry(false), m_Valid(true), m_FrameOfReferenceID(0), m_IndexToWorldTransformLastModified(0) +mitk::BaseGeometry::BaseGeometry(): Superclass(), mitk::OperationActor(), + m_FrameOfReferenceID(0), m_IndexToWorldTransformLastModified(0), m_ImageGeometry(false) { - FillVector3D(m_FloatSpacing, 1,1,1); m_VtkMatrix = vtkMatrix4x4::New(); m_VtkIndexToWorldTransform = vtkMatrixToLinearTransform::New(); m_VtkIndexToWorldTransform->SetInput(m_VtkMatrix); Initialize(); } -mitk::Geometry3D::Geometry3D(const Geometry3D& other) : Superclass(), mitk::OperationActor(), m_ParametricBoundingBox(other.m_ParametricBoundingBox),m_TimeBounds(other.m_TimeBounds), - m_ImageGeometry(other.m_ImageGeometry), m_Valid(other.m_Valid), m_FrameOfReferenceID(other.m_FrameOfReferenceID), m_IndexToWorldTransformLastModified(other.m_IndexToWorldTransformLastModified), m_RotationQuaternion( other.m_RotationQuaternion ) , m_Origin(other.m_Origin) + +mitk::BaseGeometry::BaseGeometry(const BaseGeometry& other): Superclass(), m_TimeBounds(other.m_TimeBounds), + m_FrameOfReferenceID(other.m_FrameOfReferenceID), m_IndexToWorldTransformLastModified(other.m_IndexToWorldTransformLastModified), m_Origin(other.m_Origin), + m_ImageGeometry(other.m_ImageGeometry) { + // DEPRECATED(m_RotationQuaternion = other.m_RotationQuaternion); // AffineGeometryFrame SetBounds(other.GetBounds()); - //SetIndexToObjectTransform(other.GetIndexToObjectTransform()); - //SetObjectToNodeTransform(other.GetObjectToNodeTransform()); - //SetIndexToWorldTransform(other.GetIndexToWorldTransform()); - // this is not used in AffineGeometryFrame of ITK, thus there are not Get and Set methods - // m_IndexToNodeTransform = other.m_IndexToNodeTransform; - // m_InvertedTransform = TransformType::New(); - // m_InvertedTransform = TransformType::New(); - // m_InvertedTransform->DeepCopy(other.m_InvertedTransform); m_VtkMatrix = vtkMatrix4x4::New(); m_VtkMatrix->DeepCopy(other.m_VtkMatrix); - if (other.m_ParametricBoundingBox.IsNotNull()) - { - m_ParametricBoundingBox = other.m_ParametricBoundingBox->DeepCopy(); - } - FillVector3D(m_FloatSpacing,other.m_FloatSpacing[0],other.m_FloatSpacing[1],other.m_FloatSpacing[2]); + m_VtkIndexToWorldTransform = vtkMatrixToLinearTransform::New(); m_VtkIndexToWorldTransform->DeepCopy(other.m_VtkIndexToWorldTransform); m_VtkIndexToWorldTransform->SetInput(m_VtkMatrix); other.InitializeGeometry(this); } -mitk::Geometry3D::~Geometry3D() +mitk::BaseGeometry::~BaseGeometry() { m_VtkMatrix->Delete(); m_VtkIndexToWorldTransform->Delete(); } +const mitk::Point3D& mitk::BaseGeometry::GetOrigin() const +{ + return m_Origin; +} + +void mitk::BaseGeometry::SetOrigin(const Point3D & origin) +{ + if(origin!=GetOrigin()) + { + m_Origin = origin; + m_IndexToWorldTransform->SetOffset(m_Origin.GetVectorFromOrigin()); + Modified(); + TransferItkToVtkTransform(); + } +} -static void CopySpacingFromTransform(mitk::AffineTransform3D* transform, mitk::Vector3D& spacing, float floatSpacing[3]) +void mitk::BaseGeometry::TransferItkToVtkTransform() +{ + // copy m_IndexToWorldTransform into m_VtkIndexToWorldTransform + + TransferItkTransformToVtkMatrix(m_IndexToWorldTransform.GetPointer(), m_VtkMatrix); + m_VtkIndexToWorldTransform->Modified(); +} + +static void CopySpacingFromTransform(mitk::AffineTransform3D* transform, mitk::Vector3D& spacing) { mitk::AffineTransform3D::MatrixType::InternalMatrixType vnlmatrix; vnlmatrix = transform->GetMatrix().GetVnlMatrix(); spacing[0]=vnlmatrix.get_column(0).magnitude(); spacing[1]=vnlmatrix.get_column(1).magnitude(); spacing[2]=vnlmatrix.get_column(2).magnitude(); - floatSpacing[0]=spacing[0]; - floatSpacing[1]=spacing[1]; - floatSpacing[2]=spacing[2]; } - -void mitk::Geometry3D::Initialize() +void mitk::BaseGeometry::Initialize() { float b[6] = {0,1,0,1,0,1}; SetFloatBounds(b); if(m_IndexToWorldTransform.IsNull()) m_IndexToWorldTransform = TransformType::New(); else m_IndexToWorldTransform->SetIdentity(); - CopySpacingFromTransform(m_IndexToWorldTransform, m_Spacing, m_FloatSpacing); + CopySpacingFromTransform(m_IndexToWorldTransform, m_Spacing); vtk2itk(m_IndexToWorldTransform->GetOffset(), m_Origin); m_VtkMatrix->Identity(); m_TimeBounds[0]=ScalarTypeNumericTraits::NonpositiveMin(); m_TimeBounds[1]=ScalarTypeNumericTraits::max(); m_FrameOfReferenceID = 0; m_ImageGeometry = false; -} - -void mitk::Geometry3D::TransferItkToVtkTransform() -{ - // copy m_IndexToWorldTransform into m_VtkIndexToWorldTransform - TransferItkTransformToVtkMatrix(m_IndexToWorldTransform.GetPointer(), m_VtkMatrix); - m_VtkIndexToWorldTransform->Modified(); -} -void mitk::Geometry3D::TransferVtkToItkTransform() -{ - TransferVtkMatrixToItkTransform(m_VtkMatrix, m_IndexToWorldTransform.GetPointer()); - CopySpacingFromTransform(m_IndexToWorldTransform, m_Spacing, m_FloatSpacing); - vtk2itk(m_IndexToWorldTransform->GetOffset(), m_Origin); + this->PostInitialize(); } -void mitk::Geometry3D::SetIndexToWorldTransformByVtkMatrix(vtkMatrix4x4* vtkmatrix) +void mitk::BaseGeometry::PostInitializeGeometry(BaseGeometry * newGeometry) const { - m_VtkMatrix->DeepCopy(vtkmatrix); - TransferVtkToItkTransform(); -} - -void mitk::Geometry3D::SetTimeBounds(const TimeBounds& timebounds) -{ - if(m_TimeBounds != timebounds) - { - m_TimeBounds = timebounds; - Modified(); - } + newGeometry->m_ImageGeometry = m_ImageGeometry; } -void mitk::Geometry3D::SetFloatBounds(const float bounds[6]) +void mitk::BaseGeometry::SetFloatBounds(const float bounds[6]) { mitk::BoundingBox::BoundsArrayType b; const float *input = bounds; int i=0; for(mitk::BoundingBox::BoundsArrayType::Iterator it = b.Begin(); i < 6 ;++i) *it++ = (mitk::ScalarType)*input++; SetBounds(b); } -void mitk::Geometry3D::SetFloatBounds(const double bounds[6]) +void mitk::BaseGeometry::SetFloatBounds(const double bounds[6]) { mitk::BoundingBox::BoundsArrayType b; const double *input = bounds; int i=0; for(mitk::BoundingBox::BoundsArrayType::Iterator it = b.Begin(); i < 6 ;++i) *it++ = (mitk::ScalarType)*input++; SetBounds(b); } -void mitk::Geometry3D::SetParametricBounds(const BoundingBox::BoundsArrayType& bounds) +/** Initialize the geometry */ +void + mitk::BaseGeometry::InitializeGeometry(BaseGeometry* newGeometry) const +{ + newGeometry->SetBounds(m_BoundingBox->GetBounds()); + // we have to create a new transform!! + newGeometry->SetTimeBounds(m_TimeBounds); + newGeometry->SetFrameOfReferenceID(GetFrameOfReferenceID()); + + if(m_IndexToWorldTransform) + { + TransformType::Pointer indexToWorldTransform = TransformType::New(); + indexToWorldTransform->SetCenter( m_IndexToWorldTransform->GetCenter() ); + indexToWorldTransform->SetMatrix( m_IndexToWorldTransform->GetMatrix() ); + indexToWorldTransform->SetOffset( m_IndexToWorldTransform->GetOffset() ); + newGeometry->SetIndexToWorldTransform(indexToWorldTransform); + } + + this->PostInitializeGeometry(newGeometry); +} +void mitk::BaseGeometry::PostInitialize() +{ +} + +/** Set the bounds */ +void mitk::BaseGeometry::SetBounds(const BoundsArrayType& bounds) { - m_ParametricBoundingBox = BoundingBoxType::New(); + PreSetBounds(bounds); + + m_BoundingBox = BoundingBoxType::New(); BoundingBoxType::PointsContainer::Pointer pointscontainer = - BoundingBoxType::PointsContainer::New(); + BoundingBoxType::PointsContainer::New(); BoundingBoxType::PointType p; BoundingBoxType::PointIdentifier pointid; for(pointid=0; pointid<2;++pointid) - { + { unsigned int i; - for(i=0; iInsertElement(pointid, p); } + pointscontainer->InsertElement(pointid, p); + } - m_ParametricBoundingBox->SetPoints(pointscontainer); - m_ParametricBoundingBox->ComputeBoundingBox(); + m_BoundingBox->SetPoints(pointscontainer); + m_BoundingBox->ComputeBoundingBox(); this->Modified(); } -void mitk::Geometry3D::WorldToIndex(const mitk::Point3D &pt_mm, mitk::Point3D &pt_units) const +void mitk::BaseGeometry::PreSetBounds(const BoundsArrayType& bounds){}; + +void mitk::BaseGeometry::SetIndexToWorldTransform(mitk::AffineTransform3D* transform) { - BackTransform(pt_mm, pt_units); + PreSetIndexToWorldTransform(transform); + if(m_IndexToWorldTransform.GetPointer() != transform) + { + m_IndexToWorldTransform = transform; + CopySpacingFromTransform(m_IndexToWorldTransform, m_Spacing); + vtk2itk(m_IndexToWorldTransform->GetOffset(), m_Origin); + TransferItkToVtkTransform(); + Modified(); + } + PostSetIndexToWorldTransform(transform); } -void mitk::Geometry3D::IndexToWorld(const mitk::Point3D &pt_units, mitk::Point3D &pt_mm) const +void mitk::BaseGeometry::PreSetIndexToWorldTransform(mitk::AffineTransform3D* transform) +{} +void mitk::BaseGeometry::PostSetIndexToWorldTransform(mitk::AffineTransform3D* transform) +{} + +const mitk::BaseGeometry::BoundsArrayType mitk::BaseGeometry::GetBounds() const { - pt_mm = m_IndexToWorldTransform->TransformPoint(pt_units); + assert(m_BoundingBox.IsNotNull()); + return m_BoundingBox->GetBounds(); } -void mitk::Geometry3D::WorldToIndex(const mitk::Point3D & /*atPt3d_mm*/, const mitk::Vector3D &vec_mm, mitk::Vector3D &vec_units) const +bool mitk::BaseGeometry::IsValid() const { - MITK_WARN<<"Warning! Call of the deprecated function Geometry3D::WorldToIndex(point, vec, vec). Use Geometry3D::WorldToIndex(vec, vec) instead!"; - //BackTransform(atPt3d_mm, vec_mm, vec_units); - this->WorldToIndex(vec_mm, vec_units); + return true; } -void mitk::Geometry3D::WorldToIndex( const mitk::Vector3D &vec_mm, mitk::Vector3D &vec_units) const +void mitk::BaseGeometry::SetSpacing(const mitk::Vector3D& aSpacing, bool enforceSetSpacing ) { - BackTransform( vec_mm, vec_units); + PreSetSpacing(aSpacing); + _SetSpacing(aSpacing, enforceSetSpacing); +} + +void mitk::BaseGeometry::PreSetSpacing(const mitk::Vector3D& aSpacing) +{} +void mitk::BaseGeometry::_SetSpacing(const mitk::Vector3D& aSpacing, bool enforceSetSpacing){ + if(mitk::Equal(m_Spacing, aSpacing) == false || enforceSetSpacing) + { + assert(aSpacing[0]>0 && aSpacing[1]>0 && aSpacing[2]>0); + + m_Spacing = aSpacing; + + AffineTransform3D::MatrixType::InternalMatrixType vnlmatrix; + + vnlmatrix = m_IndexToWorldTransform->GetMatrix().GetVnlMatrix(); + + mitk::VnlVector col; + col = vnlmatrix.get_column(0); col.normalize(); col*=aSpacing[0]; vnlmatrix.set_column(0, col); + col = vnlmatrix.get_column(1); col.normalize(); col*=aSpacing[1]; vnlmatrix.set_column(1, col); + col = vnlmatrix.get_column(2); col.normalize(); col*=aSpacing[2]; vnlmatrix.set_column(2, col); + + Matrix3D matrix; + matrix = vnlmatrix; + + AffineTransform3D::Pointer transform = AffineTransform3D::New(); + transform->SetMatrix(matrix); + transform->SetOffset(m_IndexToWorldTransform->GetOffset()); + + SetIndexToWorldTransform(transform.GetPointer()); + } } -void mitk::Geometry3D::IndexToWorld(const mitk::Point3D &/*atPt3d_units*/, const mitk::Vector3D &vec_units, mitk::Vector3D &vec_mm) const +mitk::Vector3D mitk::BaseGeometry::GetAxisVector(unsigned int direction) const { - MITK_WARN<<"Warning! Call of the deprecated function Geometry3D::IndexToWorld(point, vec, vec). Use Geometry3D::IndexToWorld(vec, vec) instead!"; - //vec_mm = m_IndexToWorldTransform->TransformVector(vec_units); - this->IndexToWorld(vec_units, vec_mm); + Vector3D frontToBack; + frontToBack.SetVnlVector(m_IndexToWorldTransform->GetMatrix().GetVnlMatrix().get_column(direction)); + frontToBack *= GetExtent(direction); + return frontToBack; } -void mitk::Geometry3D::IndexToWorld(const mitk::Vector3D &vec_units, mitk::Vector3D &vec_mm) const +mitk::ScalarType mitk::BaseGeometry::GetExtent(unsigned int direction) const { - vec_mm = m_IndexToWorldTransform->TransformVector(vec_units); + assert(m_BoundingBox.IsNotNull()); + if (direction>=m_NDimensions) + mitkThrow() << "Direction is too big. This geometry is for 3D Data"; + BoundsArrayType bounds = m_BoundingBox->GetBounds(); + return bounds[direction*2+1]-bounds[direction*2]; } -void mitk::Geometry3D::SetIndexToWorldTransform(mitk::AffineTransform3D* transform) +bool mitk::BaseGeometry::Is2DConvertable() { - if(m_IndexToWorldTransform.GetPointer() != transform) + bool isConvertableWithoutLoss = true; + do { - m_IndexToWorldTransform = transform; - CopySpacingFromTransform(m_IndexToWorldTransform, m_Spacing, m_FloatSpacing); - vtk2itk(m_IndexToWorldTransform->GetOffset(), m_Origin); - TransferItkToVtkTransform(); - Modified(); - } + if (this->GetSpacing()[2] != 1) + { + isConvertableWithoutLoss = false; + break; + } + if (this->GetOrigin()[2] != 0) + { + isConvertableWithoutLoss = false; + break; + } + mitk::Vector3D col0, col1, col2; + col0.SetVnlVector(this->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(0)); + col1.SetVnlVector(this->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(1)); + col2.SetVnlVector(this->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(2)); + + if ((col0[2] != 0) || (col1[2] != 0) || (col2[0] != 0) || (col2[1] != 0) || (col2[2] != 1)) + { + isConvertableWithoutLoss = false; + break; + } + } while (0); + + return isConvertableWithoutLoss; } -itk::LightObject::Pointer mitk::Geometry3D::InternalClone() const +mitk::Point3D mitk::BaseGeometry::GetCenter() const { - Self::Pointer newGeometry = new Self(*this); - newGeometry->UnRegister(); - return newGeometry.GetPointer(); + assert(m_BoundingBox.IsNotNull()); + return m_IndexToWorldTransform->TransformPoint(m_BoundingBox->GetCenter()); } -/* -void mitk::Geometry3D::InitializeGeometry(Geometry3D * newGeometry) const + +double mitk::BaseGeometry::GetDiagonalLength2() const { - Superclass::InitializeGeometry(newGeometry); + Vector3D diagonalvector = GetCornerPoint()-GetCornerPoint(false, false, false); + return diagonalvector.GetSquaredNorm(); +} - newGeometry->SetTimeBounds(m_TimeBounds); +//##Documentation +//## @brief Get the length of the diagonal of the bounding-box in mm +//## +double mitk::BaseGeometry::GetDiagonalLength() const +{ + return sqrt(GetDiagonalLength2()); +} - //newGeometry->GetVtkTransform()->SetMatrix(m_VtkIndexToWorldTransform->GetMatrix()); IW - //newGeometry->TransferVtkToItkTransform(); //MH +mitk::Point3D mitk::BaseGeometry::GetCornerPoint(int id) const +{ + assert(id >= 0); + assert(this->IsBoundingBoxNull()==false); - newGeometry->SetFrameOfReferenceID(GetFrameOfReferenceID()); - newGeometry->m_ImageGeometry = m_ImageGeometry; + BoundingBox::BoundsArrayType bounds = this->GetBoundingBox()->GetBounds(); + + Point3D cornerpoint; + switch(id) + { + case 0: FillVector3D(cornerpoint, bounds[0],bounds[2],bounds[4]); break; + case 1: FillVector3D(cornerpoint, bounds[0],bounds[2],bounds[5]); break; + case 2: FillVector3D(cornerpoint, bounds[0],bounds[3],bounds[4]); break; + case 3: FillVector3D(cornerpoint, bounds[0],bounds[3],bounds[5]); break; + case 4: FillVector3D(cornerpoint, bounds[1],bounds[2],bounds[4]); break; + case 5: FillVector3D(cornerpoint, bounds[1],bounds[2],bounds[5]); break; + case 6: FillVector3D(cornerpoint, bounds[1],bounds[3],bounds[4]); break; + case 7: FillVector3D(cornerpoint, bounds[1],bounds[3],bounds[5]); break; + default: + { + itkExceptionMacro(<<"A cube only has 8 corners. These are labeled 0-7."); + } + } + if(m_ImageGeometry) + { + // Here i have to adjust the 0.5 offset manually, because the cornerpoint is the corner of the + // bounding box. The bounding box itself is no image, so it is corner-based + FillVector3D(cornerpoint, cornerpoint[0]-0.5, cornerpoint[1]-0.5, cornerpoint[2]-0.5); + } + return this->GetIndexToWorldTransform()->TransformPoint(cornerpoint); } -*/ -void mitk::Geometry3D::SetExtentInMM(int direction, ScalarType extentInMM) + +mitk::Point3D mitk::BaseGeometry::GetCornerPoint(bool xFront, bool yFront, bool zFront) const +{ + assert(this->IsBoundingBoxNull()==false); + BoundingBox::BoundsArrayType bounds = this->GetBoundingBox()->GetBounds(); + + Point3D cornerpoint; + cornerpoint[0] = (xFront ? bounds[0] : bounds[1]); + cornerpoint[1] = (yFront ? bounds[2] : bounds[3]); + cornerpoint[2] = (zFront ? bounds[4] : bounds[5]); + if(m_ImageGeometry) + { + // Here i have to adjust the 0.5 offset manually, because the cornerpoint is the corner of the + // bounding box. The bounding box itself is no image, so it is corner-based + FillVector3D(cornerpoint, cornerpoint[0]-0.5, cornerpoint[1]-0.5, cornerpoint[2]-0.5); + } + + return this->GetIndexToWorldTransform()->TransformPoint(cornerpoint); +} + +mitk::ScalarType mitk::BaseGeometry::GetExtentInMM(int direction) const +{ + return m_IndexToWorldTransform->GetMatrix().GetVnlMatrix().get_column(direction).magnitude()*GetExtent(direction); +} + +void mitk::BaseGeometry::SetExtentInMM(int direction, ScalarType extentInMM) { ScalarType len = GetExtentInMM(direction); if(fabs(len - extentInMM)>=mitk::eps) { AffineTransform3D::MatrixType::InternalMatrixType vnlmatrix; vnlmatrix = m_IndexToWorldTransform->GetMatrix().GetVnlMatrix(); if(len>extentInMM) vnlmatrix.set_column(direction, vnlmatrix.get_column(direction)/len*extentInMM); else vnlmatrix.set_column(direction, vnlmatrix.get_column(direction)*extentInMM/len); Matrix3D matrix; matrix = vnlmatrix; m_IndexToWorldTransform->SetMatrix(matrix); Modified(); } + PostSetExtentInMM(direction,extentInMM); } -mitk::BoundingBox::Pointer mitk::Geometry3D::CalculateBoundingBoxRelativeToTransform(const mitk::AffineTransform3D* transform) const -{ - mitk::BoundingBox::PointsContainer::Pointer pointscontainer=mitk::BoundingBox::PointsContainer::New(); +void mitk::BaseGeometry::PostSetExtentInMM(int direction, ScalarType extentInMM){}; - mitk::BoundingBox::PointIdentifier pointid=0; +bool mitk::BaseGeometry::IsInside(const mitk::Point3D& p) const +{ + mitk::Point3D index; + WorldToIndex(p, index); + return IsIndexInside(index); +} - unsigned char i; - if(transform!=NULL) +bool mitk::BaseGeometry::IsIndexInside(const mitk::Point3D& index) const +{ + bool inside = false; + //if it is an image geometry, we need to convert the index to discrete values + //this is done by applying the rounding function also used in WorldToIndex (see line 323) + if (m_ImageGeometry) { - mitk::AffineTransform3D::Pointer inverse = mitk::AffineTransform3D::New(); - transform->GetInverse(inverse); - for(i=0; i<8; ++i) - pointscontainer->InsertElement( pointid++, inverse->TransformPoint( GetCornerPoint(i) )); + mitk::Point3D discretIndex; + discretIndex[0]=itk::Math::RoundHalfIntegerUp( index[0] ); + discretIndex[1]=itk::Math::RoundHalfIntegerUp( index[1] ); + discretIndex[2]=itk::Math::RoundHalfIntegerUp( index[2] ); + + inside = this->GetBoundingBox()->IsInside(discretIndex); + //we have to check if the index is at the upper border of each dimension, + // because the boundingbox is not centerbased + if (inside) + { + const BoundingBox::BoundsArrayType& bounds = this->GetBoundingBox()->GetBounds(); + if((discretIndex[0] == bounds[1]) || + (discretIndex[1] == bounds[3]) || + (discretIndex[2] == bounds[5])) + inside = false; + } } else + inside = this->GetBoundingBox()->IsInside(index); + + return inside; +} + +void mitk::BaseGeometry::WorldToIndex(const mitk::Point3D &pt_mm, mitk::Point3D &pt_units) const +{ + BackTransform(pt_mm, pt_units); +} + +void mitk::BaseGeometry::WorldToIndex( const mitk::Vector3D &vec_mm, mitk::Vector3D &vec_units) const +{ + BackTransform( vec_mm, vec_units); +} + +void mitk::BaseGeometry::BackTransform(const mitk::Vector3D& in, mitk::Vector3D& out) const +{ + // Get WorldToIndex transform + if (m_IndexToWorldTransformLastModified != m_IndexToWorldTransform->GetMTime()) { - for(i=0; i<8; ++i) - pointscontainer->InsertElement( pointid++, GetCornerPoint(i) ); + m_InvertedTransform = TransformType::New(); + if (!m_IndexToWorldTransform->GetInverse( m_InvertedTransform.GetPointer() )) + { + itkExceptionMacro( "Internal ITK matrix inversion error, cannot proceed." ); + } + m_IndexToWorldTransformLastModified = m_IndexToWorldTransform->GetMTime(); } - mitk::BoundingBox::Pointer result = mitk::BoundingBox::New(); - result->SetPoints(pointscontainer); - result->ComputeBoundingBox(); + // Check for valid matrix inversion + const TransformType::MatrixType& inverse = m_InvertedTransform->GetMatrix(); + if(inverse.GetVnlMatrix().has_nans()) + { + itkExceptionMacro( "Internal ITK matrix inversion error, cannot proceed. Matrix was: " << std::endl + << m_IndexToWorldTransform->GetMatrix() << "Suggested inverted matrix is:" << std::endl + << inverse ); + } - return result; + // Transform vector + for (unsigned int i = 0; i < 3; i++) + { + out[i] = 0.0; + for (unsigned int j = 0; j < 3; j++) + { + out[i] += inverse[i][j]*in[j]; + } + } +} + +void mitk::BaseGeometry::BackTransform(const mitk::Point3D &in, mitk::Point3D& out) const +{ + ScalarType temp[3]; + unsigned int i, j; + const TransformType::OffsetType& offset = m_IndexToWorldTransform->GetOffset(); + + // Remove offset + for (j = 0; j < 3; j++) + { + temp[j] = in[j] - offset[j]; + } + + // Get WorldToIndex transform + if (m_IndexToWorldTransformLastModified != m_IndexToWorldTransform->GetMTime()) + { + m_InvertedTransform = TransformType::New(); + if (!m_IndexToWorldTransform->GetInverse( m_InvertedTransform.GetPointer() )) + { + itkExceptionMacro( "Internal ITK matrix inversion error, cannot proceed." ); + } + m_IndexToWorldTransformLastModified = m_IndexToWorldTransform->GetMTime(); + } + + // Check for valid matrix inversion + const TransformType::MatrixType& inverse = m_InvertedTransform->GetMatrix(); + if(inverse.GetVnlMatrix().has_nans()) + { + itkExceptionMacro( "Internal ITK matrix inversion error, cannot proceed. Matrix was: " << std::endl + << m_IndexToWorldTransform->GetMatrix() << "Suggested inverted matrix is:" << std::endl + << inverse ); + } + + // Transform point + for (i = 0; i < 3; i++) + { + out[i] = 0.0; + for (j = 0; j < 3; j++) + { + out[i] += inverse[i][j]*temp[j]; + } + } +} + +mitk::VnlVector mitk::BaseGeometry::GetOriginVnl() const +{ + return const_cast(this)->m_Origin.GetVnlVector(); +} + +vtkLinearTransform* mitk::BaseGeometry::GetVtkTransform() const +{ + return (vtkLinearTransform*)m_VtkIndexToWorldTransform; +} + +void mitk::BaseGeometry::SetIdentity() +{ + m_IndexToWorldTransform->SetIdentity(); + m_Origin.Fill(0); + CopySpacingFromTransform(m_IndexToWorldTransform, m_Spacing); + Modified(); + TransferItkToVtkTransform(); +} + +void mitk::BaseGeometry::TransferVtkToItkTransform() +{ + TransferVtkMatrixToItkTransform(m_VtkMatrix, m_IndexToWorldTransform.GetPointer()); + CopySpacingFromTransform(m_IndexToWorldTransform, m_Spacing); + vtk2itk(m_IndexToWorldTransform->GetOffset(), m_Origin); +} + +void mitk::BaseGeometry::Compose( const mitk::BaseGeometry::TransformType * other, bool pre ) +{ + m_IndexToWorldTransform->Compose(other, pre); + CopySpacingFromTransform(m_IndexToWorldTransform, m_Spacing); + vtk2itk(m_IndexToWorldTransform->GetOffset(), m_Origin); + Modified(); + TransferItkToVtkTransform(); +} + +void mitk::BaseGeometry::Compose( const vtkMatrix4x4 * vtkmatrix, bool pre ) +{ + mitk::BaseGeometry::TransformType::Pointer itkTransform = mitk::BaseGeometry::TransformType::New(); + TransferVtkMatrixToItkTransform(vtkmatrix, itkTransform.GetPointer()); + Compose(itkTransform, pre); +} + +void mitk::BaseGeometry::Translate(const Vector3D & vector) +{ + if((vector[0] != 0) || (vector[1] != 0) || (vector[2] != 0)) + { + this->SetOrigin(m_Origin + vector); + } +} + +void mitk::BaseGeometry::IndexToWorld(const mitk::Point3D &pt_units, mitk::Point3D &pt_mm) const +{ + pt_mm = m_IndexToWorldTransform->TransformPoint(pt_units); +} + +void mitk::BaseGeometry::IndexToWorld(const mitk::Vector3D &vec_units, mitk::Vector3D &vec_mm) const +{ + vec_mm = m_IndexToWorldTransform->TransformVector(vec_units); } #include -void mitk::Geometry3D::ExecuteOperation(Operation* operation) +void mitk::BaseGeometry::ExecuteOperation(Operation* operation) { vtkTransform *vtktransform = vtkTransform::New(); vtktransform->SetMatrix(m_VtkMatrix); switch (operation->GetOperationType()) { case OpNOTHING: break; case OpMOVE: { mitk::PointOperation *pointOp = dynamic_cast(operation); if (pointOp == NULL) { //mitk::StatusBar::GetInstance()->DisplayText("received wrong type of operation!See mitkAffineInteractor.cpp", 10000); return; } mitk::Point3D newPos = pointOp->GetPoint(); ScalarType data[3]; vtktransform->GetPosition(data); vtktransform->PostMultiply(); vtktransform->Translate(newPos[0], newPos[1], newPos[2]); vtktransform->PreMultiply(); break; } case OpSCALE: { mitk::PointOperation *pointOp = dynamic_cast(operation); if (pointOp == NULL) { //mitk::StatusBar::GetInstance()->DisplayText("received wrong type of operation!See mitkAffineInteractor.cpp", 10000); return; } mitk::Point3D newScale = pointOp->GetPoint(); ScalarType data[3]; /* calculate new scale: newscale = oldscale * (oldscale + scaletoadd)/oldscale */ data[0] = 1 + (newScale[0] / GetMatrixColumn(0).magnitude()); data[1] = 1 + (newScale[1] / GetMatrixColumn(1).magnitude()); data[2] = 1 + (newScale[2] / GetMatrixColumn(2).magnitude()); mitk::Point3D center = const_cast(m_BoundingBox.GetPointer())->GetCenter(); ScalarType pos[3]; vtktransform->GetPosition(pos); vtktransform->PostMultiply(); vtktransform->Translate(-pos[0], -pos[1], -pos[2]); vtktransform->Translate(-center[0], -center[1], -center[2]); vtktransform->PreMultiply(); vtktransform->Scale(data[0], data[1], data[2]); vtktransform->PostMultiply(); vtktransform->Translate(+center[0], +center[1], +center[2]); vtktransform->Translate(pos[0], pos[1], pos[2]); vtktransform->PreMultiply(); break; } case OpROTATE: { mitk::RotationOperation *rotateOp = dynamic_cast(operation); if (rotateOp == NULL) { //mitk::StatusBar::GetInstance()->DisplayText("received wrong type of operation!See mitkAffineInteractor.cpp", 10000); return; } Vector3D rotationVector = rotateOp->GetVectorOfRotation(); Point3D center = rotateOp->GetCenterOfRotation(); ScalarType angle = rotateOp->GetAngleOfRotation(); vtktransform->PostMultiply(); vtktransform->Translate(-center[0], -center[1], -center[2]); vtktransform->RotateWXYZ(angle, rotationVector[0], rotationVector[1], rotationVector[2]); vtktransform->Translate(center[0], center[1], center[2]); vtktransform->PreMultiply(); break; } case OpRESTOREPLANEPOSITION: - { + { //Copy necessary to avoid vtk warning vtkMatrix4x4* matrix = vtkMatrix4x4::New(); TransferItkTransformToVtkMatrix(dynamic_cast(operation)->GetTransform().GetPointer(), matrix); vtktransform->SetMatrix(matrix); break; - } + } case OpAPPLYTRANSFORMMATRIX: - { - ApplyTransformMatrixOperation *applyMatrixOp = dynamic_cast< ApplyTransformMatrixOperation* >( operation ); - vtktransform->SetMatrix(applyMatrixOp->GetMatrix()); - break; - } + { + ApplyTransformMatrixOperation *applyMatrixOp = dynamic_cast< ApplyTransformMatrixOperation* >( operation ); + vtktransform->SetMatrix(applyMatrixOp->GetMatrix()); + break; + } default: vtktransform->Delete(); return; } m_VtkMatrix->DeepCopy(vtktransform->GetMatrix()); TransferVtkToItkTransform(); Modified(); vtktransform->Delete(); } -void mitk::Geometry3D::BackTransform(const mitk::Point3D &in, mitk::Point3D& out) const +mitk::VnlVector mitk::BaseGeometry::GetMatrixColumn(unsigned int direction) const { - ScalarType temp[3]; - unsigned int i, j; - const TransformType::OffsetType& offset = m_IndexToWorldTransform->GetOffset(); + return m_IndexToWorldTransform->GetMatrix().GetVnlMatrix().get_column(direction); +} - // Remove offset - for (j = 0; j < 3; j++) +mitk::BoundingBox::Pointer mitk::BaseGeometry::CalculateBoundingBoxRelativeToTransform(const mitk::AffineTransform3D* transform) const +{ + mitk::BoundingBox::PointsContainer::Pointer pointscontainer=mitk::BoundingBox::PointsContainer::New(); + + mitk::BoundingBox::PointIdentifier pointid=0; + + unsigned char i; + if(transform!=NULL) { - temp[j] = in[j] - offset[j]; + mitk::AffineTransform3D::Pointer inverse = mitk::AffineTransform3D::New(); + transform->GetInverse(inverse); + for(i=0; i<8; ++i) + pointscontainer->InsertElement( pointid++, inverse->TransformPoint( GetCornerPoint(i) )); } - - // Get WorldToIndex transform - if (m_IndexToWorldTransformLastModified != m_IndexToWorldTransform->GetMTime()) + else { - m_InvertedTransform = TransformType::New(); - if (!m_IndexToWorldTransform->GetInverse( m_InvertedTransform.GetPointer() )) - { - itkExceptionMacro( "Internal ITK matrix inversion error, cannot proceed." ); - } - m_IndexToWorldTransformLastModified = m_IndexToWorldTransform->GetMTime(); + for(i=0; i<8; ++i) + pointscontainer->InsertElement( pointid++, GetCornerPoint(i) ); } - // Check for valid matrix inversion - const TransformType::MatrixType& inverse = m_InvertedTransform->GetMatrix(); - if(inverse.GetVnlMatrix().has_nans()) + mitk::BoundingBox::Pointer result = mitk::BoundingBox::New(); + result->SetPoints(pointscontainer); + result->ComputeBoundingBox(); + + return result; +} + +void mitk::BaseGeometry::SetTimeBounds(const TimeBounds& timebounds) +{ + if(m_TimeBounds != timebounds) { - itkExceptionMacro( "Internal ITK matrix inversion error, cannot proceed. Matrix was: " << std::endl - << m_IndexToWorldTransform->GetMatrix() << "Suggested inverted matrix is:" << std::endl - << inverse ); + m_TimeBounds = timebounds; + Modified(); } + PostSetTimeBounds(timebounds); +} - // Transform point - for (i = 0; i < 3; i++) +void mitk::BaseGeometry::PostSetTimeBounds(const TimeBounds& timebounds) +{} + +const std::string mitk::BaseGeometry::GetTransformAsString( TransformType* transformType ) +{ + std::ostringstream out; + + out << '['; + + for( int i=0; i<3; ++i ) { - out[i] = 0.0; - for (j = 0; j < 3; j++) - { - out[i] += inverse[i][j]*temp[j]; - } + out << '['; + for( int j=0; j<3; ++j ) + out << transformType->GetMatrix().GetVnlMatrix().get(i, j) << ' '; + out << ']'; } + + out << "]["; + + for( int i=0; i<3; ++i ) + out << transformType->GetOffset()[i] << ' '; + + out << "]\0"; + + return out.str(); +} + +void mitk::BaseGeometry::SetIndexToWorldTransformByVtkMatrix(vtkMatrix4x4* vtkmatrix) +{ + m_VtkMatrix->DeepCopy(vtkmatrix); + TransferVtkToItkTransform(); +} + +void mitk::BaseGeometry::WorldToIndex(const mitk::Point3D & /*atPt3d_mm*/, const mitk::Vector3D &vec_mm, mitk::Vector3D &vec_units) const +{ + MITK_WARN<<"Warning! Call of the deprecated function BaseGeometry::WorldToIndex(point, vec, vec). Use BaseGeometry::WorldToIndex(vec, vec) instead!"; + //BackTransform(atPt3d_mm, vec_mm, vec_units); + this->WorldToIndex(vec_mm, vec_units); +} + +void mitk::BaseGeometry::IndexToWorld(const mitk::Point3D &/*atPt3d_units*/, const mitk::Vector3D &vec_units, mitk::Vector3D &vec_mm) const +{ + MITK_WARN<<"Warning! Call of the deprecated function BaseGeometry::IndexToWorld(point, vec, vec). Use BaseGeometry::IndexToWorld(vec, vec) instead!"; + //vec_mm = m_IndexToWorldTransform->TransformVector(vec_units); + this->IndexToWorld(vec_units, vec_mm); } -void mitk::Geometry3D::BackTransform(const mitk::Point3D &/*at*/, const mitk::Vector3D &in, mitk::Vector3D& out) const +void mitk::BaseGeometry::BackTransform(const mitk::Point3D &/*at*/, const mitk::Vector3D &in, mitk::Vector3D& out) const { - MITK_INFO<<"Warning! Call of the deprecated function Geometry3D::BackTransform(point, vec, vec). Use Geometry3D::BackTransform(vec, vec) instead!"; + MITK_INFO<<"Warning! Call of the deprecated function BaseGeometry::BackTransform(point, vec, vec). Use BaseGeometry::BackTransform(vec, vec) instead!"; //// Get WorldToIndex transform //if (m_IndexToWorldTransformLastModified != m_IndexToWorldTransform->GetMTime()) //{ // m_InvertedTransform = TransformType::New(); // if (!m_IndexToWorldTransform->GetInverse( m_InvertedTransform.GetPointer() )) // { // itkExceptionMacro( "Internal ITK matrix inversion error, cannot proceed." ); // } // m_IndexToWorldTransformLastModified = m_IndexToWorldTransform->GetMTime(); //} //// Check for valid matrix inversion //const TransformType::MatrixType& inverse = m_InvertedTransform->GetMatrix(); //if(inverse.GetVnlMatrix().has_nans()) //{ // itkExceptionMacro( "Internal ITK matrix inversion error, cannot proceed. Matrix was: " << std::endl // << m_IndexToWorldTransform->GetMatrix() << "Suggested inverted matrix is:" << std::endl // << inverse ); //} //// Transform vector //for (unsigned int i = 0; i < 3; i++) //{ // out[i] = 0.0; // for (unsigned int j = 0; j < 3; j++) // { // out[i] += inverse[i][j]*in[j]; // } //} this->BackTransform(in, out); } -void mitk::Geometry3D::BackTransform(const mitk::Vector3D& in, mitk::Vector3D& out) const -{ - // Get WorldToIndex transform - if (m_IndexToWorldTransformLastModified != m_IndexToWorldTransform->GetMTime()) - { - m_InvertedTransform = TransformType::New(); - if (!m_IndexToWorldTransform->GetInverse( m_InvertedTransform.GetPointer() )) - { - itkExceptionMacro( "Internal ITK matrix inversion error, cannot proceed." ); - } - m_IndexToWorldTransformLastModified = m_IndexToWorldTransform->GetMTime(); - } - - // Check for valid matrix inversion - const TransformType::MatrixType& inverse = m_InvertedTransform->GetMatrix(); - if(inverse.GetVnlMatrix().has_nans()) - { - itkExceptionMacro( "Internal ITK matrix inversion error, cannot proceed. Matrix was: " << std::endl - << m_IndexToWorldTransform->GetMatrix() << "Suggested inverted matrix is:" << std::endl - << inverse ); - } - - // Transform vector - for (unsigned int i = 0; i < 3; i++) - { - out[i] = 0.0; - for (unsigned int j = 0; j < 3; j++) - { - out[i] += inverse[i][j]*in[j]; - } - } -} - -const float* mitk::Geometry3D::GetFloatSpacing() const -{ - return m_FloatSpacing; -} - -void mitk::Geometry3D::SetSpacing(const mitk::Vector3D& aSpacing) -{ - if(mitk::Equal(m_Spacing, aSpacing) == false) - { - assert(aSpacing[0]>0 && aSpacing[1]>0 && aSpacing[2]>0); - - m_Spacing = aSpacing; - - AffineTransform3D::MatrixType::InternalMatrixType vnlmatrix; - - vnlmatrix = m_IndexToWorldTransform->GetMatrix().GetVnlMatrix(); - - mitk::VnlVector col; - col = vnlmatrix.get_column(0); col.normalize(); col*=aSpacing[0]; vnlmatrix.set_column(0, col); - col = vnlmatrix.get_column(1); col.normalize(); col*=aSpacing[1]; vnlmatrix.set_column(1, col); - col = vnlmatrix.get_column(2); col.normalize(); col*=aSpacing[2]; vnlmatrix.set_column(2, col); - - Matrix3D matrix; - matrix = vnlmatrix; - - AffineTransform3D::Pointer transform = AffineTransform3D::New(); - transform->SetMatrix(matrix); - transform->SetOffset(m_IndexToWorldTransform->GetOffset()); - - SetIndexToWorldTransform(transform.GetPointer()); - - itk2vtk(m_Spacing, m_FloatSpacing); - } -} - -void mitk::Geometry3D::SetOrigin(const Point3D & origin) -{ - if(origin!=GetOrigin()) - { - m_Origin = origin; - m_IndexToWorldTransform->SetOffset(m_Origin.GetVectorFromOrigin()); - Modified(); - TransferItkToVtkTransform(); - } +vtkMatrix4x4* mitk::BaseGeometry::GetVtkMatrix(){ + return m_VtkMatrix; } -void mitk::Geometry3D::Translate(const Vector3D & vector) -{ - if((vector[0] != 0) || (vector[1] != 0) || (vector[2] != 0)) - { - this->SetOrigin(m_Origin + vector); -// m_IndexToWorldTransform->SetOffset(m_IndexToWorldTransform->GetOffset()+vector); -// TransferItkToVtkTransform(); -// Modified(); - } +bool mitk::BaseGeometry::IsBoundingBoxNull() const{ + return m_BoundingBox.IsNull(); } -void mitk::Geometry3D::SetIdentity() -{ - m_IndexToWorldTransform->SetIdentity(); - m_Origin.Fill(0); - Modified(); - TransferItkToVtkTransform(); +bool mitk::BaseGeometry::IsIndexToWorldTransformNull() const{ + return m_IndexToWorldTransform.IsNull(); } -void mitk::Geometry3D::Compose( const mitk::Geometry3D::TransformType * other, bool pre ) +void + mitk::BaseGeometry::ChangeImageGeometryConsideringOriginOffset( const bool isAnImageGeometry ) { - m_IndexToWorldTransform->Compose(other, pre); - CopySpacingFromTransform(m_IndexToWorldTransform, m_Spacing, m_FloatSpacing); - vtk2itk(m_IndexToWorldTransform->GetOffset(), m_Origin); - Modified(); - TransferItkToVtkTransform(); -} + // If Geometry is switched to ImageGeometry, you have to put an offset to the origin, because + // imageGeometries origins are pixel-center-based + // ... and remove the offset, if you switch an imageGeometry back to a normal geometry + // For more information please see the Geometry documentation page -void mitk::Geometry3D::Compose( const vtkMatrix4x4 * vtkmatrix, bool pre ) -{ - mitk::Geometry3D::TransformType::Pointer itkTransform = mitk::Geometry3D::TransformType::New(); - TransferVtkMatrixToItkTransform(vtkmatrix, itkTransform.GetPointer()); - Compose(itkTransform, pre); -} + if(m_ImageGeometry == isAnImageGeometry) + return; -const std::string mitk::Geometry3D::GetTransformAsString( TransformType* transformType ) -{ - std::ostringstream out; + const BoundingBox::BoundsArrayType& boundsarray = + this->GetBoundingBox()->GetBounds(); - out << '['; + Point3D originIndex; + FillVector3D(originIndex, boundsarray[0], boundsarray[2], boundsarray[4]); - for( int i=0; i<3; ++i ) - { - out << '['; - for( int j=0; j<3; ++j ) - out << transformType->GetMatrix().GetVnlMatrix().get(i, j) << ' '; - out << ']'; - } + if(isAnImageGeometry == true) + FillVector3D( originIndex, + originIndex[0] + 0.5, + originIndex[1] + 0.5, + originIndex[2] + 0.5 ); + else + FillVector3D( originIndex, + originIndex[0] - 0.5, + originIndex[1] - 0.5, + originIndex[2] - 0.5 ); - out << "]["; + Point3D originWorld; - for( int i=0; i<3; ++i ) - out << transformType->GetOffset()[i] << ' '; + originWorld = GetIndexToWorldTransform() + ->TransformPoint( originIndex ); + // instead could as well call IndexToWorld(originIndex,originWorld); - out << "]\0"; + SetOrigin(originWorld); - return out.str(); + this->SetImageGeometry(isAnImageGeometry); } - -void mitk::Geometry3D::PrintSelf(std::ostream& os, itk::Indent indent) const +//itk::LightObject::Pointer mitk::BaseGeometry::InternalClone() const +//{ +// Self::Pointer newGeometry = new Self(*this); +// newGeometry->UnRegister(); +// return newGeometry.GetPointer(); +//} + +void mitk::BaseGeometry::PrintSelf(std::ostream& os, itk::Indent indent) const { os << indent << " IndexToWorldTransform: "; - if(m_IndexToWorldTransform.IsNull()) + if(this->IsIndexToWorldTransformNull()) os << "NULL" << std::endl; else { // from itk::MatrixOffsetTransformBase unsigned int i, j; os << std::endl; os << indent << "Matrix: " << std::endl; for (i = 0; i < 3; i++) { os << indent.GetNextIndent(); for (j = 0; j < 3; j++) { - os << m_IndexToWorldTransform->GetMatrix()[i][j] << " "; + os << this->GetIndexToWorldTransform()->GetMatrix()[i][j] << " "; } os << std::endl; } - os << indent << "Offset: " << m_IndexToWorldTransform->GetOffset() << std::endl; - os << indent << "Center: " << m_IndexToWorldTransform->GetCenter() << std::endl; - os << indent << "Translation: " << m_IndexToWorldTransform->GetTranslation() << std::endl; + os << indent << "Offset: " << this->GetIndexToWorldTransform()->GetOffset() << std::endl; + os << indent << "Center: " << this->GetIndexToWorldTransform()->GetCenter() << std::endl; + os << indent << "Translation: " << this->GetIndexToWorldTransform()->GetTranslation() << std::endl; os << indent << "Inverse: " << std::endl; for (i = 0; i < 3; i++) { os << indent.GetNextIndent(); for (j = 0; j < 3; j++) { - os << m_IndexToWorldTransform->GetInverseMatrix()[i][j] << " "; + os << this->GetIndexToWorldTransform()->GetInverseMatrix()[i][j] << " "; } os << std::endl; } // from itk::ScalableAffineTransform os << indent << "Scale : "; for (i = 0; i < 3; i++) { - os << m_IndexToWorldTransform->GetScale()[i] << " "; + os << this->GetIndexToWorldTransform()->GetScale()[i] << " "; } os << std::endl; } os << indent << " BoundingBox: "; - if(m_BoundingBox.IsNull()) + if(this->IsBoundingBoxNull()) os << "NULL" << std::endl; else { os << indent << "( "; for (unsigned int i=0; i<3; i++) { - os << m_BoundingBox->GetBounds()[2*i] << "," << m_BoundingBox->GetBounds()[2*i+1] << " "; + os << this->GetBoundingBox()->GetBounds()[2*i] << "," << this->GetBoundingBox()->GetBounds()[2*i+1] << " "; } os << " )" << std::endl; } - os << indent << " Origin: " << m_Origin << std::endl; - os << indent << " ImageGeometry: " << m_ImageGeometry << std::endl; - os << indent << " Spacing: " << m_Spacing << std::endl; - os << indent << " TimeBounds: " << m_TimeBounds << std::endl; -} - -mitk::Point3D mitk::Geometry3D::GetCornerPoint(int id) const -{ - assert(id >= 0); - assert(m_BoundingBox.IsNotNull()); - - BoundingBox::BoundsArrayType bounds = m_BoundingBox->GetBounds(); - - Point3D cornerpoint; - switch(id) - { - case 0: FillVector3D(cornerpoint, bounds[0],bounds[2],bounds[4]); break; - case 1: FillVector3D(cornerpoint, bounds[0],bounds[2],bounds[5]); break; - case 2: FillVector3D(cornerpoint, bounds[0],bounds[3],bounds[4]); break; - case 3: FillVector3D(cornerpoint, bounds[0],bounds[3],bounds[5]); break; - case 4: FillVector3D(cornerpoint, bounds[1],bounds[2],bounds[4]); break; - case 5: FillVector3D(cornerpoint, bounds[1],bounds[2],bounds[5]); break; - case 6: FillVector3D(cornerpoint, bounds[1],bounds[3],bounds[4]); break; - case 7: FillVector3D(cornerpoint, bounds[1],bounds[3],bounds[5]); break; - default: - { - itkExceptionMacro(<<"A cube only has 8 corners. These are labeled 0-7."); - } - } - if(m_ImageGeometry) - { - // Here i have to adjust the 0.5 offset manually, because the cornerpoint is the corner of the - // bounding box. The bounding box itself is no image, so it is corner-based - FillVector3D(cornerpoint, cornerpoint[0]-0.5, cornerpoint[1]-0.5, cornerpoint[2]-0.5); - } - return m_IndexToWorldTransform->TransformPoint(cornerpoint); -} - -mitk::Point3D mitk::Geometry3D::GetCornerPoint(bool xFront, bool yFront, bool zFront) const -{ - assert(m_BoundingBox.IsNotNull()); - BoundingBox::BoundsArrayType bounds = m_BoundingBox->GetBounds(); - - Point3D cornerpoint; - cornerpoint[0] = (xFront ? bounds[0] : bounds[1]); - cornerpoint[1] = (yFront ? bounds[2] : bounds[3]); - cornerpoint[2] = (zFront ? bounds[4] : bounds[5]); - if(m_ImageGeometry) - { - // Here i have to adjust the 0.5 offset manually, because the cornerpoint is the corner of the - // bounding box. The bounding box itself is no image, so it is corner-based - FillVector3D(cornerpoint, cornerpoint[0]-0.5, cornerpoint[1]-0.5, cornerpoint[2]-0.5); - } - - return m_IndexToWorldTransform->TransformPoint(cornerpoint); -} - -void -mitk::Geometry3D::ResetSubTransforms() -{ -} - -void -mitk::Geometry3D::ChangeImageGeometryConsideringOriginOffset( const bool isAnImageGeometry ) -{ - // If Geometry is switched to ImageGeometry, you have to put an offset to the origin, because - // imageGeometries origins are pixel-center-based - // ... and remove the offset, if you switch an imageGeometry back to a normal geometry - // For more information please see the Geometry documentation page - - if(m_ImageGeometry == isAnImageGeometry) - return; - - const BoundingBox::BoundsArrayType& boundsarray = - this->GetBoundingBox()->GetBounds(); - - Point3D originIndex; - FillVector3D(originIndex, boundsarray[0], boundsarray[2], boundsarray[4]); - - if(isAnImageGeometry == true) - FillVector3D( originIndex, - originIndex[0] + 0.5, - originIndex[1] + 0.5, - originIndex[2] + 0.5 ); - else - FillVector3D( originIndex, - originIndex[0] - 0.5, - originIndex[1] - 0.5, - originIndex[2] - 0.5 ); - - Point3D originWorld; - - originWorld = GetIndexToWorldTransform() - ->TransformPoint( originIndex ); - // instead could as well call IndexToWorld(originIndex,originWorld); - - SetOrigin(originWorld); - - this->SetImageGeometry(isAnImageGeometry); -} - -bool mitk::Geometry3D::Is2DConvertable() -{ - bool isConvertableWithoutLoss = true; - do - { - if (this->GetSpacing()[2] != 1) - { - isConvertableWithoutLoss = false; - break; - } - if (this->GetOrigin()[2] != 0) - { - isConvertableWithoutLoss = false; - break; - } - mitk::Vector3D col0, col1, col2; - col0.SetVnlVector(this->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(0)); - col1.SetVnlVector(this->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(1)); - col2.SetVnlVector(this->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(2)); - - if ((col0[2] != 0) || (col1[2] != 0) || (col2[0] != 0) || (col2[1] != 0) || (col2[2] != 1)) - { - isConvertableWithoutLoss = false; - break; - } - } while (0); - - return isConvertableWithoutLoss; + os << indent << " Origin: " << this->GetOrigin() << std::endl; + os << indent << " ImageGeometry: " << this->GetImageGeometry() << std::endl; + os << indent << " Spacing: " << this->GetSpacing() << std::endl; + os << indent << " TimeBounds: " << this->GetTimeBounds() << std::endl; } -bool mitk::Equal( const mitk::Geometry3D::BoundingBoxType *leftHandSide, const mitk::Geometry3D::BoundingBoxType *rightHandSide, ScalarType eps, bool verbose ) +bool mitk::Equal( const mitk::BaseGeometry::BoundingBoxType *leftHandSide, const mitk::BaseGeometry::BoundingBoxType *rightHandSide, ScalarType eps, bool verbose ) { if(( leftHandSide == NULL) || ( rightHandSide == NULL )) { MITK_ERROR << "mitk::Equal( const mitk::Geometry3D::BoundingBoxType *leftHandSide, const mitk::Geometry3D::BoundingBoxType *rightHandSide, ScalarType eps, bool verbose ) does not with NULL pointer input."; return false; } return Equal( *leftHandSide, *rightHandSide, eps, verbose); } -bool mitk::Equal( const mitk::Geometry3D::BoundingBoxType& leftHandSide, const mitk::Geometry3D::BoundingBoxType& rightHandSide, ScalarType eps, bool verbose ) +bool mitk::Equal( const mitk::BaseGeometry::BoundingBoxType& leftHandSide, const mitk::BaseGeometry::BoundingBoxType& rightHandSide, ScalarType eps, bool verbose ) { bool result = true; Geometry3D::BoundsArrayType rightBounds = rightHandSide.GetBounds(); Geometry3D::BoundsArrayType leftBounds = leftHandSide.GetBounds(); Geometry3D::BoundsArrayType::Iterator itLeft = leftBounds.Begin(); for( Geometry3D::BoundsArrayType::Iterator itRight = rightBounds.Begin(); itRight != rightBounds.End(); ++itRight) { if(( !mitk::Equal( *itLeft, *itRight, eps )) ) { if(verbose) { MITK_INFO << "[( Geometry3D::BoundingBoxType )] bounds are not equal."; MITK_INFO << "rightHandSide is " << setprecision(12) << *itRight << " : leftHandSide is " << *itLeft << " and tolerance is " << eps; } result = false; } itLeft++; } return result; } -bool mitk::Equal(const mitk::Geometry3D *leftHandSide, const mitk::Geometry3D *rightHandSide, ScalarType eps, bool verbose) +bool mitk::Equal(const mitk::BaseGeometry *leftHandSide, const mitk::BaseGeometry *rightHandSide, ScalarType eps, bool verbose) { if(( leftHandSide == NULL) || ( rightHandSide == NULL )) { MITK_ERROR << "mitk::Equal(const mitk::Geometry3D *leftHandSide, const mitk::Geometry3D *rightHandSide, ScalarType eps, bool verbose) does not with NULL pointer input."; return false; } return Equal( *leftHandSide, *rightHandSide, eps, verbose); } -bool mitk::Equal(const mitk::Geometry3D& leftHandSide, const mitk::Geometry3D& rightHandSide, ScalarType eps, bool verbose) +bool mitk::Equal(const mitk::BaseGeometry& leftHandSide, const mitk::BaseGeometry& rightHandSide, ScalarType eps, bool verbose) { bool result = true; //Compare spacings if( !mitk::Equal( leftHandSide.GetSpacing(), rightHandSide.GetSpacing(), eps ) ) { if(verbose) { MITK_INFO << "[( Geometry3D )] Spacing differs."; MITK_INFO << "rightHandSide is " << setprecision(12) << rightHandSide.GetSpacing() << " : leftHandSide is " << leftHandSide.GetSpacing() << " and tolerance is " << eps; } result = false; } //Compare Origins if( !mitk::Equal( leftHandSide.GetOrigin(), rightHandSide.GetOrigin(), eps ) ) { if(verbose) { MITK_INFO << "[( Geometry3D )] Origin differs."; MITK_INFO << "rightHandSide is " << setprecision(12) << rightHandSide.GetOrigin() << " : leftHandSide is " << leftHandSide.GetOrigin() << " and tolerance is " << eps; } result = false; } //Compare Axis and Extents for( unsigned int i=0; i<3; ++i) { if( !mitk::Equal( leftHandSide.GetAxisVector(i), rightHandSide.GetAxisVector(i), eps)) { if(verbose) { MITK_INFO << "[( Geometry3D )] AxisVector #" << i << " differ"; MITK_INFO << "rightHandSide is " << setprecision(12) << rightHandSide.GetAxisVector(i) << " : leftHandSide is " << leftHandSide.GetAxisVector(i) << " and tolerance is " << eps; } result = false; } if( !mitk::Equal( leftHandSide.GetExtent(i), rightHandSide.GetExtent(i), eps) ) { if(verbose) { MITK_INFO << "[( Geometry3D )] Extent #" << i << " differ"; MITK_INFO << "rightHandSide is " << setprecision(12) << rightHandSide.GetExtent(i) << " : leftHandSide is " << leftHandSide.GetExtent(i) << " and tolerance is " << eps; } result = false; } } //Compare ImageGeometry Flag if( rightHandSide.GetImageGeometry() != leftHandSide.GetImageGeometry() ) { if(verbose) { MITK_INFO << "[( Geometry3D )] GetImageGeometry is different."; MITK_INFO << "rightHandSide is " << rightHandSide.GetImageGeometry() << " : leftHandSide is " << leftHandSide.GetImageGeometry(); } result = false; } //Compare BoundingBoxes if( !mitk::Equal( *leftHandSide.GetBoundingBox(), *rightHandSide.GetBoundingBox(), eps, verbose) ) { result = false; } //Compare IndexToWorldTransform Matrix if( !mitk::Equal( *leftHandSide.GetIndexToWorldTransform(), *rightHandSide.GetIndexToWorldTransform(), eps, verbose) ) { result = false; } return result; } -bool mitk::Equal(const Geometry3D::TransformType *leftHandSide, const Geometry3D::TransformType *rightHandSide, ScalarType eps, bool verbose ) +bool mitk::Equal(const BaseGeometry::TransformType *leftHandSide, const BaseGeometry::TransformType *rightHandSide, ScalarType eps, bool verbose ) { if(( leftHandSide == NULL) || ( rightHandSide == NULL )) { MITK_ERROR << "mitk::Equal(const Geometry3D::TransformType *leftHandSide, const Geometry3D::TransformType *rightHandSide, ScalarType eps, bool verbose ) does not with NULL pointer input."; return false; } return Equal( *leftHandSide, *rightHandSide, eps, verbose); } -bool mitk::Equal(const Geometry3D::TransformType& leftHandSide, const Geometry3D::TransformType& rightHandSide, ScalarType eps, bool verbose ) +bool mitk::Equal(const BaseGeometry::TransformType& leftHandSide, const BaseGeometry::TransformType& rightHandSide, ScalarType eps, bool verbose ) { //Compare IndexToWorldTransform Matrix if( !mitk::MatrixEqualElementWise( leftHandSide.GetMatrix(), - rightHandSide.GetMatrix() ) ) + rightHandSide.GetMatrix() ) ) { if(verbose) { MITK_INFO << "[( Geometry3D::TransformType )] Index to World Transformation matrix differs."; MITK_INFO << "rightHandSide is " << setprecision(12) << rightHandSide.GetMatrix() << " : leftHandSide is " << leftHandSide.GetMatrix() << " and tolerance is " << eps; } return false; } return true; } - -/** Initialize the geometry */ -void -mitk::Geometry3D::InitializeGeometry(Geometry3D* newGeometry) const -{ - newGeometry->SetBounds(m_BoundingBox->GetBounds()); - // we have to create a new transform!! - - if(m_IndexToWorldTransform) - { - TransformType::Pointer indexToWorldTransform = TransformType::New(); - indexToWorldTransform->SetCenter( m_IndexToWorldTransform->GetCenter() ); - indexToWorldTransform->SetMatrix( m_IndexToWorldTransform->GetMatrix() ); - indexToWorldTransform->SetOffset( m_IndexToWorldTransform->GetOffset() ); - newGeometry->SetIndexToWorldTransform(indexToWorldTransform); - } -} - -/** Set the bounds */ -void mitk::Geometry3D::SetBounds(const BoundsArrayType& bounds) -{ - m_BoundingBox = BoundingBoxType::New(); - - BoundingBoxType::PointsContainer::Pointer pointscontainer = - BoundingBoxType::PointsContainer::New(); - BoundingBoxType::PointType p; - BoundingBoxType::PointIdentifier pointid; - - for(pointid=0; pointid<2;++pointid) - { - unsigned int i; - for(i=0; iInsertElement(pointid, p); - } - - m_BoundingBox->SetPoints(pointscontainer); - m_BoundingBox->ComputeBoundingBox(); - this->Modified(); -} diff --git a/Core/Code/DataManagement/mitkBaseGeometry.h b/Core/Code/DataManagement/mitkBaseGeometry.h new file mode 100644 index 0000000000..ae4b78a6fd --- /dev/null +++ b/Core/Code/DataManagement/mitkBaseGeometry.h @@ -0,0 +1,735 @@ +/*=================================================================== + +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 BaseGeometry_H_HEADER_INCLUDED +#define BaseGeometry_H_HEADER_INCLUDED + +#include +#include +#include "mitkoperationactor.h" + +#include +#include "mitkvector.h" +#include +#include +#include "itkScalableAffineTransform.h" +#include + +class vtkMatrix4x4; +class vtkMatrixToLinearTransform; +class vtkLinearTransform; + +namespace mitk { + //##Documentation + //## @brief Standard 3D-BoundingBox typedef + //## + //## Standard 3D-BoundingBox typedef to get rid of template arguments (3D, type). + typedef itk::BoundingBox BoundingBox; + + //##Documentation + //## @brief Standard typedef for time-bounds + typedef itk::FixedArray TimeBounds; + typedef itk::FixedArray FixedArrayType; + + //##Documentation + //## @brief BaseGeometry Describes the geometry of a data object + //## + //## The class holds + //## \li a bounding box which is axes-parallel in intrinsic coordinates + //## (often integer indices of pixels), to be accessed by + //## GetBoundingBox() + //## \li a transform to convert intrinsic coordinates into a + //## world-coordinate system with coordinates in millimeters + //## and milliseconds (all are floating point values), to + //## be accessed by GetIndexToWorldTransform() + //## \li a life span, i.e. a bounding box in time in ms (with + //## start and end time), to be accessed by GetTimeBounds(). + //## The default is minus infinity to plus infinity. + //## \li an origin and spacing to define the geometry + //## + //## BaseGeometry and its sub-classes allow converting between + //## intrinsic coordinates (called index or unit coordinates) + //## and world-coordinates (called world or mm coordinates), + //## e.g. WorldToIndex. + //## In case you need integer index coordinates, provide an + //## mitk::Index3D (or itk::Index) as target variable to + //## WorldToIndex, otherwise you will get a continuous index + //## (floating point values). + //## + //## An important sub-class is SlicedGeometry3D, which descibes + //## data objects consisting of slices, e.g., objects of type Image. + //## Conversions between world coordinates (in mm) and unit coordinates + //## (e.g., pixels in the case of an Image) can be performed. + //## + //## For more information on related classes, see \ref Geometry. + //## + //## BaseGeometry instances referring to an Image need a slightly + //## different definition of corners, see SetImageGeometry. This + //## is usualy automatically called by Image. + //## + //## BaseGeometry 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 + //## itk::ProcessObject::GenerateOutputInformation(), + //## itk::DataObject::CopyInformation() and + //## itk::DataObject::UpdateOutputInformation(). + //## + //## At least, it can return the bounding box of the data object. + //## + //## The BaseGeometry class is an abstract class. The most simple implementation + //## is the sublass Geometry3D. + //## + //## Rule: everything is in mm (ms) if not stated otherwise. + //## @ingroup Geometry + class MITK_CORE_EXPORT BaseGeometry : public itk::Object, public OperationActor + { + public: + mitkClassMacro(BaseGeometry, itk::Object); + + //##Documentation + //## @brief clones the geometry + //## + //## Overwrite in all sub-classes. + //## Normally looks like: + //## \code + //## Self::Pointer newGeometry = new Self(*this); + //## newGeometry->UnRegister(); + //## return newGeometry.GetPointer(); + //## \endcode + virtual itk::LightObject::Pointer InternalClone() const =0; + + // ********************************** TypeDef ********************************** + + typedef itk::ScalableAffineTransform TransformType; + typedef itk::BoundingBox BoundingBoxType; + typedef BoundingBoxType::BoundsArrayType BoundsArrayType; + typedef BoundingBoxType::Pointer BoundingBoxPointer; + + // ********************************** Origin, Spacing ********************************** + + //##Documentation + //## @brief Get the origin, e.g. the upper-left corner of the plane + const Point3D& GetOrigin() const; + + //##Documentation + //## @brief Set the origin, i.e. the upper-left corner of the plane + //## + void SetOrigin(const Point3D& origin); + + //##Documentation + //## @brief Get the spacing (size of a pixel). + //## + itkGetConstReferenceMacro(Spacing, mitk::Vector3D); + + //##Documentation + //## @brief Set the spacing (m_Spacing). + //## + //##The spacing is also changed in the IndexToWorldTransform. + void SetSpacing(const mitk::Vector3D& aSpacing, bool enforceSetSpacing = false); + + //##Documentation + //## @brief Get the origin as VnlVector + //## + //## \sa GetOrigin + VnlVector GetOriginVnl() const; + + // ********************************** other functions ********************************** + + //##Documentation + //## @brief Get the DICOM FrameOfReferenceID referring to the + //## used world coordinate system + itkGetConstMacro(FrameOfReferenceID, unsigned int); + //##Documentation + //## @brief Set the DICOM FrameOfReferenceID referring to the + //## used world coordinate system + itkSetMacro(FrameOfReferenceID, unsigned int); + + itkGetConstMacro(IndexToWorldTransformLastModified, unsigned long); + + //##Documentation + //## @brief Is this BaseGeometry in a state that is valid? + //## + //## This function returns always true in the BaseGeometry class. Other implementations are possible in subclasses. + virtual bool IsValid() const; + + // ********************************** Initialize ********************************** + + //##Documentation + //## @brief Initialize the BaseGeometry + void Initialize(); + + void InitializeGeometry(Self * newGeometry) const; + + // ********************************** Transformations Set/Get ********************************** + + // a bit of a misuse, but we want only doxygen to see the following: +#ifdef DOXYGEN_SKIP + //##Documentation + //## @brief Get the transformation used to convert from index + //## to world coordinates + itkGetObjectMacro(IndexToWorldTransform, AffineTransform3D); +#endif + //## @brief Set the transformation used to convert from index + //## to world coordinates. The spacing of the new transform is + //## copied to m_spacing. + void SetIndexToWorldTransform(mitk::AffineTransform3D* transform); + + //##Documentation + //## @brief Convenience method for setting the ITK transform + //## (m_IndexToWorldTransform) via an vtkMatrix4x4.The spacing of + //## the new transform is copied to m_spacing. + //## \sa SetIndexToWorldTransform + virtual void SetIndexToWorldTransformByVtkMatrix(vtkMatrix4x4* vtkmatrix); + + //## Get the IndexToWorldTransform + itkGetConstObjectMacro(IndexToWorldTransform, AffineTransform3D); + itkGetObjectMacro(IndexToWorldTransform, AffineTransform3D); + + //## Get the Vtk Matrix which describes the transform. + vtkMatrix4x4* GetVtkMatrix(); + + //##Documentation + //## @brief Get the m_IndexToWorldTransform as a vtkLinearTransform + vtkLinearTransform* GetVtkTransform() const; + + //##Documentation + //## @brief Set the transform to identity, the spacing to 1 and origin to 0 + //## + virtual void SetIdentity(); + + // ********************************** Transformations ********************************** + + //##Documentation + //## @brief Compose new IndexToWorldTransform with a given transform. + //## + //## This method composes m_IndexToWorldTransform with another transform, + //## modifying self to be the composition of self and other. + //## If the argument pre is true, then other is precomposed with self; + //## that is, the resulting transformation consists of first applying + //## other to the source, followed by self. If pre is false or omitted, + //## then other is post-composed with self; that is the resulting + //## transformation consists of first applying self to the source, + //## followed by other. + //## This method also changes m_spacing. + void Compose( const BaseGeometry::TransformType * other, bool pre = 0 ); + + //##Documentation + //## @brief Compose new IndexToWorldTransform with a given vtkMatrix4x4. + //## + //## Converts the vtkMatrix4x4 into a itk-transform and calls the previous method. + void Compose( const vtkMatrix4x4 * vtkmatrix, bool pre = 0 ); + + //##Documentation + //## @brief Translate the origin by a vector + //## + void Translate(const Vector3D& vector); + + //##Documentation + //##@brief executes affine operations (translate, rotate, scale) + virtual void ExecuteOperation(Operation* operation); + + //##Documentation + //## @brief Convert world coordinates (in mm) of a \em point to (continuous!) index coordinates + //## \warning If you need (discrete) integer index coordinates (e.g., for iterating easily over an image), + //## use WorldToIndex(const mitk::Point3D& pt_mm, itk::Index &index). + //## For further information about coordinates types, please see the Geometry documentation + void WorldToIndex(const mitk::Point3D& pt_mm, mitk::Point3D& pt_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 + void WorldToIndex(const mitk::Vector3D& vec_mm, mitk::Vector3D& vec_units) const; + + //##Documentation + //## @brief Convert world coordinates (in mm) of a \em point to (discrete!) index coordinates. + //## This method rounds to integer indices! + //## For further information about coordinates types, please see the Geometry documentation + template + void WorldToIndex(const mitk::Point3D& pt_mm, itk::Index &index) const + { + typedef itk::Index IndexType; + mitk::Point3D pt_units; + this->WorldToIndex(pt_mm, pt_units); + int i, dim=index.GetIndexDimension(); + if(dim>3) + { + index.Fill(0); + dim=3; + } + for(i=0;i( pt_units[i] ); + } + } + + //##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 + void IndexToWorld(const mitk::Vector3D& vec_units, mitk::Vector3D& vec_mm) const; + + //##Documentation + //## @brief Convert (continuous or discrete) index coordinates of a \em point to world coordinates (in mm) + //## For further information about coordinates types, please see the Geometry documentation + void IndexToWorld(const mitk::Point3D& pt_units, mitk::Point3D& pt_mm) const; + + //##Documentation + //## @brief Convert (discrete) index coordinates of a \em point to world coordinates (in mm) + //## For further information about coordinates types, please see the Geometry documentation + template + void IndexToWorld(const itk::Index &index, mitk::Point3D& pt_mm ) const + { + mitk::Point3D pt_units; + pt_units.Fill(0); + int i, dim=index.GetIndexDimension(); + if(dim>3) + { + dim=3; + } + for(i=0;i + void ItkPhysicalPointToWorld(const itk::Point& itkPhysicalPoint, + mitk::Point3D& pt_mm) const + { + mitk::vtk2itk(itkPhysicalPoint, pt_mm); + } + + //##Documentation + //## @brief Deprecated for use with ITK version 3.10 or newer. + //## Convert world coordinates (in mm) of a \em point to + //## ITK physical coordinates (in mm, but without a possible rotation) + //## + //## This method is useful if you have want to access an mitk::Image + //## via an itk::Image. ITK v3.8 and older did not support rotated (tilted) + //## images, i.e., ITK images are always parallel to the coordinate axes. + //## When accessing a (possibly rotated) mitk::Image via an itk::Image + //## the rotational part of the transformation in the BaseGeometry is + //## simply discarded; in other word: only the origin and spacing is + //## used by ITK, not the complete matrix available in MITK. + //## With WorldToItkPhysicalPoint you can convert an MITK world + //## coordinate (including the rotation) into a coordinate that + //## can be used with the ITK image as a ITK physical coordinate + //## (excluding the rotation). + template + void WorldToItkPhysicalPoint(const mitk::Point3D& pt_mm, + itk::Point& itkPhysicalPoint) const + { + mitk::vtk2itk(pt_mm, itkPhysicalPoint); + } + + // ********************************** BoundingBox ********************************** + + /** Get the bounding box */ + itkGetConstObjectMacro(BoundingBox, BoundingBoxType); + + //##Documentation + //## @brief Get the time bounds (in ms) + itkGetConstReferenceMacro(TimeBounds, TimeBounds); + + // a bit of a misuse, but we want only doxygen to see the following: +#ifdef DOXYGEN_SKIP + //##Documentation + //## @brief Get bounding box (in index/unit coordinates) + itkGetConstObjectMacro(BoundingBox, BoundingBoxType); + //##Documentation + //## @brief Get bounding box (in index/unit coordinates) as a BoundsArrayType + const BoundsArrayType GetBounds() const; +#endif + const BoundsArrayType GetBounds() const; + + //##Documentation + //## \brief Set the bounding box (in index/unit coordinates) + //## + //## Only possible via the BoundsArray to make clear that a + //## copy of the bounding-box is stored, not a reference to it. + void SetBounds(const BoundsArrayType& bounds); + + //##Documentation + //## @brief Set the bounding box (in index/unit coordinates) via a float array + void SetFloatBounds(const float bounds[6]); + //##Documentation + //## @brief Set the bounding box (in index/unit coordinates) via a double array + void SetFloatBounds(const double bounds[6]); + + //##Documentation + //## @brief Get a VnlVector along bounding-box in the specified + //## @a direction, length is spacing + //## + //## \sa GetAxisVector + VnlVector GetMatrixColumn(unsigned int direction) const; + + //##Documentation + //## @brief Calculates a bounding-box around the geometry relative + //## to a coordinate system defined by a transform + //## + mitk::BoundingBox::Pointer CalculateBoundingBoxRelativeToTransform(const mitk::AffineTransform3D* transform) const; + + //##Documentation + //## @brief Set the time bounds (in ms) + void SetTimeBounds(const TimeBounds& timebounds); + + // ********************************** Geometry ********************************** + +#ifdef DOXYGEN_SKIP + //##Documentation + //## @brief Get the extent of the bounding box (in index/unit coordinates) + //## + //## To access the extent in mm use GetExtentInMM + ScalarType GetExtent(unsigned int direction) const; +#endif + + /** Get the extent of the bounding box */ + ScalarType GetExtent(unsigned int direction) const; + + //##Documentation + //## @brief Get the extent of the bounding-box in the specified @a direction in mm + //## + //## Equals length of GetAxisVector(direction). + ScalarType GetExtentInMM(int direction) const; + + //##Documentation + //## @brief Get vector along bounding-box in the specified @a direction in mm + //## + //## The length of the vector is the size of the bounding-box in the + //## specified @a direction in mm + //## \sa GetMatrixColumn + Vector3D GetAxisVector(unsigned int direction) const; + + //##Documentation + //## @brief Checks, if the given geometry can be converted to 2D without information loss + //## e.g. when a 2D image is saved, the matrix is usually cropped to 2x2, and when you load it back to MITK + //## it will be filled with standard values. This function checks, if information would be lost during this + //## procedure + virtual bool Is2DConvertable(); + + //##Documentation + //## @brief Get the center of the bounding-box in mm + //## + Point3D GetCenter() const; + + //##Documentation + //## @brief Get the squared length of the diagonal of the bounding-box in mm + //## + double GetDiagonalLength2() const; + + //##Documentation + //## @brief Get the length of the diagonal of the bounding-box in mm + //## + double GetDiagonalLength() const; + + //##Documentation + //## @brief Get the position of the corner number \a id (in world coordinates) + //## + //## See SetImageGeometry for how a corner is defined on images. + Point3D GetCornerPoint(int id) const; + + //##Documentation + //## @brief Get the position of a corner (in world coordinates) + //## + //## See SetImageGeometry for how a corner is defined on images. + Point3D GetCornerPoint(bool xFront=true, bool yFront=true, bool zFront=true) const; + + //##Documentation + //## @brief Set the extent of the bounding-box in the specified @a direction in mm + //## + //## @note This changes the matrix in the transform, @a not the bounds, which are given in units! + void SetExtentInMM(int direction, ScalarType extentInMM); + + //##Documentation + //## @brief Test whether the point \a p (world coordinates in mm) is + //## inside the bounding box + bool IsInside(const mitk::Point3D& p) const; + + //##Documentation + //## @brief Test whether the point \a p ((continous!)index coordinates in units) is + //## inside the bounding box + bool IsIndexInside(const mitk::Point3D& index) const; + + //##Documentation + //## @brief Convenience method for working with ITK indices + template + bool IsIndexInside(const itk::Index &index) const + { + int i, dim=index.GetIndexDimension(); + Point3D pt_index; + pt_index.Fill(0); + for ( i = 0; i < dim; ++i ) + { + pt_index[i] = index[i]; + } + return IsIndexInside(pt_index); + } + + // ********************************* Image Geometry ******************************** + //##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 ); + + //##Documentation + //## @brief Is this an ImageGeometry? + //## + //## For more information, see SetImageGeometry + itkGetConstMacro(ImageGeometry, bool); + //##Documentation + //## @brief Define that this BaseGeometry is refering to an Image + //## + //## A geometry referring to an Image needs a slightly different + //## definition of the position of the corners (see GetCornerPoint). + //## The position of a voxel is defined by the position of its center. + //## If we would use the origin (position of the (center of) the first + //## voxel) as a corner and display this point, it would seem to be + //## \em not at the corner but a bit within the image. Even worse for + //## the opposite corner of the image: here the corner would appear + //## outside the image (by half of the voxel diameter). Thus, we have + //## to correct for this and to be able to do that, we need to know + //## that the Geometry3D is referring to an Image. + itkSetMacro(ImageGeometry, bool); + itkBooleanMacro(ImageGeometry); + + protected: + + // ********************************** Constructor ********************************** + BaseGeometry(); + BaseGeometry(const BaseGeometry& other); + virtual ~BaseGeometry(); + + virtual void PrintSelf(std::ostream& os, itk::Indent indent) const; + + void BackTransform(const mitk::Point3D& in, mitk::Point3D& out) const; + + //Without redundant parameter Point3D + void BackTransform(const mitk::Vector3D& in, mitk::Vector3D& out) const; + + //##Documentation + //## @brief Deprecated + void BackTransform(const mitk::Point3D& at, const mitk::Vector3D& in, mitk::Vector3D& out) const; + + //##Documentation + //## @brief Copy the ITK transform + //## (m_IndexToWorldTransform) to the VTK transform + //## \sa SetIndexToWorldTransform + void TransferItkToVtkTransform(); + + //##Documentation + //## @brief Copy the VTK transform + //## to the ITK transform (m_IndexToWorldTransform) + //## \sa SetIndexToWorldTransform + void TransferVtkToItkTransform(); + + static const std::string GetTransformAsString( TransformType* transformType ); + + //##Documentation + //## @brief Pre- and Post-functions are empty in BaseGeometry + //## + //## These virtual functions allow for a different beahiour in subclasses. + virtual void PreSetBounds(const BoundsArrayType& bounds); + + virtual void PostInitialize(); + virtual void PostInitializeGeometry(Self * newGeometry) const; + + virtual void PostSetExtentInMM(int direction, ScalarType extentInMM); + + virtual void PostSetTimeBounds(const TimeBounds& timebounds); + + virtual void PreSetIndexToWorldTransform(mitk::AffineTransform3D* transform); + virtual void PostSetIndexToWorldTransform(mitk::AffineTransform3D* transform); + + virtual void PreSetSpacing(const mitk::Vector3D& aSpacing); + void _SetSpacing(const mitk::Vector3D& aSpacing, bool enforceSetSpacing = false); + + itkGetConstMacro(NDimensions, unsigned int); + + bool IsBoundingBoxNull() const; + + bool IsIndexToWorldTransformNull() const; + + private: + // ********************************** Variables ********************************** + //##Documentation + //## @brief Spacing, measurement of the resolution + //## + mitk::Vector3D m_Spacing; + + //##Documentation + //## @brief Index to World Transform, contains a transformation matrix to convert + //## points from indes coordinates to world coordinates (mm). The Spacing is included in this variable. + AffineTransform3D::Pointer m_IndexToWorldTransform; + + //##Documentation + //## @brief Bounding Box, which is axes-parallel in intrinsic coordinates + //## (often integer indices of pixels) + BoundingBoxPointer m_BoundingBox; + + vtkMatrixToLinearTransform* m_VtkIndexToWorldTransform; + + vtkMatrix4x4* m_VtkMatrix; + + unsigned int m_FrameOfReferenceID; + + mitk::TimeBounds m_TimeBounds; + + //##Documentation + //## @brief Origin, i.e. upper-left corner of the plane + //## + Point3D m_Origin; + + static const unsigned int m_NDimensions = 3; + + mutable TransformType::Pointer m_InvertedTransform; + + mutable unsigned long m_IndexToWorldTransformLastModified; + + bool m_ImageGeometry; + }; + + // ********************************** Equal Functions ********************************** + // + // Static compare functions mainly for testing + // + /** + * @brief Equal A function comparing two geometries for beeing identical. + * @warning This method is deprecated and will not be available in the future. Use the \a bool mitk::Equal(const mitk::mitk::Geometry3D& g1, const mitk::Geometry3D& g2) instead. + * + * @ingroup MITKTestingAPI + * + * The function compares the spacing, origin, axisvectors, extents, the matrix of the + * IndexToWorldTransform (elementwise), the bounding (elementwise) and the ImageGeometry flag. + * + * The parameter eps is a tolarence value for all methods which are internally used for comparion. + * If you want to use different tolarance values for different parts of the geometry, feel free to use + * the other comparison methods and write your own implementation of Equal. + * @param rightHandSide Compare this against leftHandSide. + * @param leftHandSide Compare this against rightHandSide. + * @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 comparison are true. False in any other case. + */ + DEPRECATED( MITK_CORE_EXPORT bool Equal(const mitk::BaseGeometry* leftHandSide, const mitk::BaseGeometry* rightHandSide, ScalarType eps, bool verbose)); + + /** + * @brief Equal A function comparing two geometries for beeing identical. + * + * @ingroup MITKTestingAPI + * + * The function compares the spacing, origin, axisvectors, extents, the matrix of the + * IndexToWorldTransform (elementwise), the bounding (elementwise) and the ImageGeometry flag. + * + * The parameter eps is a tolarence value for all methods which are internally used for comparion. + * If you want to use different tolarance values for different parts of the geometry, feel free to use + * the other comparison methods and write your own implementation of Equal. + * @param rightHandSide Compare this against leftHandSide. + * @param leftHandSide Compare this against rightHandSide. + * @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 comparison are true. False in any other case. + */ + MITK_CORE_EXPORT bool Equal(const mitk::BaseGeometry& leftHandSide, const mitk::BaseGeometry& rightHandSide, ScalarType eps, bool verbose); + + /** + * @brief Equal A function comparing two transforms (TransformType) for beeing identical. + * @warning This method is deprecated and will not be available in the future. Use the \a bool mitk::Equal(const mitk::mitk::Geometry3D::TransformType& t1, const mitk::Geometry3D::TransformType& t2) instead. + * + * @ingroup MITKTestingAPI + * + * The function compares the IndexToWorldTransform (elementwise). + * + * The parameter eps is a tolarence value for all methods which are internally used for comparion. + * @param rightHandSide Compare this against leftHandSide. + * @param leftHandSide Compare this against rightHandSide. + * @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 comparison are true. False in any other case. + */ + DEPRECATED( MITK_CORE_EXPORT bool Equal(const mitk::BaseGeometry::TransformType *leftHandSide, const mitk::BaseGeometry::TransformType *rightHandSide, ScalarType eps, bool verbose)); + + /** + * @brief Equal A function comparing two transforms (TransformType) for beeing identical. + * + * @ingroup MITKTestingAPI + * + * The function compares the IndexToWorldTransform (elementwise). + * + * The parameter eps is a tolarence value for all methods which are internally used for comparion. + * @param rightHandSide Compare this against leftHandSide. + * @param leftHandSide Compare this against rightHandSide. + * @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 comparison are true. False in any other case. + */ + MITK_CORE_EXPORT bool Equal(const mitk::BaseGeometry::TransformType& leftHandSide, const mitk::BaseGeometry::TransformType& rightHandSide, ScalarType eps, bool verbose); + + /** + * @brief Equal A function comparing two bounding boxes (BoundingBoxType) for beeing identical. + * @warning This method is deprecated and will not be available in the future. Use the \a bool mitk::Equal(const mitk::mitk::Geometry3D::BoundingBoxType& b1, const mitk::Geometry3D::BoundingBoxType& b2) instead. + * + * @ingroup MITKTestingAPI + * + * The function compares the bounds (elementwise). + * + * The parameter eps is a tolarence value for all methods which are internally used for comparion. + * @param rightHandSide Compare this against leftHandSide. + * @param leftHandSide Compare this against rightHandSide. + * @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 comparison are true. False in any other case. + */ + DEPRECATED( MITK_CORE_EXPORT bool Equal( const mitk::BaseGeometry::BoundingBoxType *leftHandSide, const mitk::BaseGeometry::BoundingBoxType *rightHandSide, ScalarType eps, bool verbose)); + + /** + * @brief Equal A function comparing two bounding boxes (BoundingBoxType) for beeing identical. + * + * @ingroup MITKTestingAPI + * + * The function compares the bounds (elementwise). + * + * The parameter eps is a tolarence value for all methods which are internally used for comparion. + * @param rightHandSide Compare this against leftHandSide. + * @param leftHandSide Compare this against rightHandSide. + * @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 comparison are true. False in any other case. + */ + MITK_CORE_EXPORT bool Equal( const mitk::BaseGeometry::BoundingBoxType& leftHandSide, const mitk::BaseGeometry::BoundingBoxType& rightHandSide, ScalarType eps, bool verbose); +} // namespace mitk + +#endif /* BaseGeometry_H_HEADER_INCLUDED */ diff --git a/Core/Code/DataManagement/mitkDataNode.cpp b/Core/Code/DataManagement/mitkDataNode.cpp index 22fdada0d5..3692d5c362 100644 --- a/Core/Code/DataManagement/mitkDataNode.cpp +++ b/Core/Code/DataManagement/mitkDataNode.cpp @@ -1,579 +1,579 @@ /*=================================================================== 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 "mitkDataNode.h" #include "mitkCoreObjectFactory.h" #include #include "mitkProperties.h" #include "mitkStringProperty.h" #include "mitkGroupTagProperty.h" #include "mitkSmartPointerProperty.h" //#include "mitkMaterialProperty.h" #include "mitkColorProperty.h" #include "mitkLevelWindowProperty.h" #include "mitkGeometry3D.h" #include "mitkRenderingManager.h" #include "mitkGlobalInteraction.h" #include "mitkEventMapper.h" #include "mitkGenericProperty.h" #include "mitkCoreObjectFactory.h" mitk::Mapper* mitk::DataNode::GetMapper(MapperSlotId id) const { if( (id >= m_Mappers.size()) || (m_Mappers[id].IsNull()) ) { if(id >= m_Mappers.capacity()) { // int i, size=id-m_Mappers.capacity()+10; m_Mappers.resize(id+10); } m_Mappers[id] = CoreObjectFactory::GetInstance()->CreateMapper(const_cast(this),id); } return m_Mappers[id]; } mitk::BaseData* mitk::DataNode::GetData() const { return m_Data; } mitk::Interactor* mitk::DataNode::GetInteractor() const { return m_Interactor; } void mitk::DataNode::SetData(mitk::BaseData* baseData) { if(m_Data!=baseData) { m_Data=baseData; m_Mappers.clear(); m_Mappers.resize(10); mitk::CoreObjectFactory::GetInstance()->SetDefaultProperties(this); m_DataReferenceChangedTime.Modified(); Modified(); //inform the interactor about the change if (m_Interactor.IsNotNull()) m_Interactor->DataChanged(); } } void mitk::DataNode::SetInteractor(mitk::Interactor* interactor) { m_Interactor = interactor; if(m_Interactor.IsNotNull()) m_Interactor->SetDataNode(this); } mitk::DataNode::DataNode() : m_Data(NULL), m_PropertyListModifiedObserverTag(0) { m_Mappers.resize(10); m_PropertyList = PropertyList::New(); // subscribe for modified event itk::MemberCommand::Pointer _PropertyListModifiedCommand = itk::MemberCommand::New(); _PropertyListModifiedCommand->SetCallbackFunction(this, &mitk::DataNode::PropertyListModified); m_PropertyListModifiedObserverTag = m_PropertyList->AddObserver(itk::ModifiedEvent(), _PropertyListModifiedCommand); } mitk::DataNode::~DataNode() { if(m_PropertyList.IsNotNull()) // remove modified event listener m_PropertyList->RemoveObserver(m_PropertyListModifiedObserverTag); Interactor* interactor = this->GetInteractor(); if ( interactor ) { mitk::GlobalInteraction::GetInstance()->RemoveInteractor( interactor ); } m_Mappers.clear(); m_Data = NULL; } mitk::DataNode& mitk::DataNode::operator=(const DataNode& right) { mitk::DataNode* node=mitk::DataNode::New(); node->SetData(right.GetData()); return *node; } mitk::DataNode& mitk::DataNode::operator=(mitk::BaseData* right) { mitk::DataNode* node=mitk::DataNode::New(); node->SetData(right); return *node; } #if (_MSC_VER > 1200) || !defined(_MSC_VER) MBI_STD::istream& mitk::operator>>( MBI_STD::istream& i, mitk::DataNode::Pointer& dtn ) #endif #if ((defined(_MSC_VER)) && (_MSC_VER <= 1200)) MBI_STD::istream& operator>>( MBI_STD::istream& i, mitk::DataNode::Pointer& dtn ) #endif { dtn = mitk::DataNode::New(); //i >> av.get(); return i; } #if (_MSC_VER > 1200) || !defined(_MSC_VER) MBI_STD::ostream& mitk::operator<<( MBI_STD::ostream& o, mitk::DataNode::Pointer& dtn) #endif #if ((defined(_MSC_VER)) && (_MSC_VER <= 1200)) MBI_STD::ostream& operator<<( MBI_STD::ostream& o, mitk::DataNode::Pointer& dtn) #endif { if(dtn->GetData()!=NULL) o<GetData()->GetNameOfClass(); else o<<"empty data"; return o; } void mitk::DataNode::SetMapper(MapperSlotId id, mitk::Mapper* mapper) { m_Mappers[id] = mapper; if (mapper!=NULL) mapper->SetDataNode(this); } void mitk::DataNode::UpdateOutputInformation() { if (this->GetSource()) { this->GetSource()->UpdateOutputInformation(); } } void mitk::DataNode::SetRequestedRegionToLargestPossibleRegion() { } bool mitk::DataNode::RequestedRegionIsOutsideOfTheBufferedRegion() { return false; } bool mitk::DataNode::VerifyRequestedRegion() { return true; } void mitk::DataNode::SetRequestedRegion( const itk::DataObject * /*data*/) { } void mitk::DataNode::CopyInformation(const itk::DataObject * /*data*/) { } mitk::PropertyList* mitk::DataNode::GetPropertyList(const mitk::BaseRenderer* renderer) const { if(renderer==NULL) return m_PropertyList; return this->GetPropertyList(renderer->GetName()); } mitk::PropertyList* mitk::DataNode::GetPropertyList(const std::string& rendererName) const { if (rendererName.empty()) return m_PropertyList; mitk::PropertyList::Pointer & propertyList = m_MapOfPropertyLists[rendererName]; if(propertyList.IsNull()) propertyList = mitk::PropertyList::New(); assert(m_MapOfPropertyLists[rendererName].IsNotNull()); return propertyList; } void mitk::DataNode::ConcatenatePropertyList(PropertyList *pList, bool replace) { m_PropertyList->ConcatenatePropertyList(pList, replace); } mitk::BaseProperty* mitk::DataNode::GetProperty(const char *propertyKey, const mitk::BaseRenderer* renderer) const { if(propertyKey==NULL) return NULL; //renderer specified? if (renderer) { std::string rendererName = renderer->GetName(); MapOfPropertyLists::const_iterator it; //check for the renderer specific property it=m_MapOfPropertyLists.find(rendererName); if(it!=m_MapOfPropertyLists.end()) //found { mitk::BaseProperty::Pointer property; property=it->second->GetProperty(propertyKey); if(property.IsNotNull())//found an enabled property in the render specific list return property; else //found a renderer specific list, but not the desired property return m_PropertyList->GetProperty(propertyKey); //return renderer unspecific property } else //didn't find the property list of the given renderer { //return the renderer unspecific property if there is one return m_PropertyList->GetProperty(propertyKey); } } else //no specific renderer given; use the renderer independent one { mitk::BaseProperty::Pointer property; property=m_PropertyList->GetProperty(propertyKey); if(property.IsNotNull()) return property; } //only to satisfy compiler! return NULL; } mitk::DataNode::GroupTagList mitk::DataNode::GetGroupTags() const { GroupTagList groups; const PropertyList::PropertyMap* propertyMap = m_PropertyList->GetMap(); for ( PropertyList::PropertyMap::const_iterator groupIter = propertyMap->begin(); // m_PropertyList is created in the constructor, so we don't check it here groupIter != propertyMap->end(); ++groupIter ) { const BaseProperty* bp = groupIter->second; if ( dynamic_cast(bp) ) { groups.insert( groupIter->first ); } } return groups; } bool mitk::DataNode::GetBoolProperty(const char* propertyKey, bool& boolValue, mitk::BaseRenderer* renderer) const { mitk::BoolProperty::Pointer boolprop = dynamic_cast(GetProperty(propertyKey, renderer)); if(boolprop.IsNull()) return false; boolValue = boolprop->GetValue(); return true; } bool mitk::DataNode::GetIntProperty(const char* propertyKey, int &intValue, mitk::BaseRenderer* renderer) const { mitk::IntProperty::Pointer intprop = dynamic_cast(GetProperty(propertyKey, renderer)); if(intprop.IsNull()) return false; intValue = intprop->GetValue(); return true; } bool mitk::DataNode::GetFloatProperty(const char* propertyKey, float &floatValue, mitk::BaseRenderer* renderer) const { mitk::FloatProperty::Pointer floatprop = dynamic_cast(GetProperty(propertyKey, renderer)); if(floatprop.IsNull()) return false; floatValue = floatprop->GetValue(); return true; } bool mitk::DataNode::GetStringProperty(const char* propertyKey, std::string& string, mitk::BaseRenderer* renderer) const { mitk::StringProperty::Pointer stringProp = dynamic_cast(GetProperty(propertyKey, renderer)); if(stringProp.IsNull()) { return false; } else { //memcpy((void*)string, stringProp->GetValue(), strlen(stringProp->GetValue()) + 1 ); // looks dangerous string = stringProp->GetValue(); return true; } } bool mitk::DataNode::GetColor(float rgb[3], mitk::BaseRenderer* renderer, const char* propertyKey) const { mitk::ColorProperty::Pointer colorprop = dynamic_cast(GetProperty(propertyKey, renderer)); if(colorprop.IsNull()) return false; memcpy(rgb, colorprop->GetColor().GetDataPointer(), 3*sizeof(float)); return true; } bool mitk::DataNode::GetOpacity(float &opacity, mitk::BaseRenderer* renderer, const char* propertyKey) const { mitk::FloatProperty::Pointer opacityprop = dynamic_cast(GetProperty(propertyKey, renderer)); if(opacityprop.IsNull()) return false; opacity=opacityprop->GetValue(); return true; } bool mitk::DataNode::GetLevelWindow(mitk::LevelWindow &levelWindow, mitk::BaseRenderer* renderer, const char* propertyKey) const { mitk::LevelWindowProperty::Pointer levWinProp = dynamic_cast(GetProperty(propertyKey, renderer)); if(levWinProp.IsNull()) return false; levelWindow=levWinProp->GetLevelWindow(); return true; } void mitk::DataNode::SetColor(const mitk::Color &color, mitk::BaseRenderer* renderer, const char* propertyKey) { mitk::ColorProperty::Pointer prop; prop = mitk::ColorProperty::New(color); GetPropertyList(renderer)->SetProperty(propertyKey, prop); } void mitk::DataNode::SetColor(float red, float green, float blue, mitk::BaseRenderer* renderer, const char* propertyKey) { float color[3]; color[0]=red; color[1]=green; color[2]=blue; SetColor(color, renderer, propertyKey); } void mitk::DataNode::SetColor(const float rgb[3], mitk::BaseRenderer* renderer, const char* propertyKey) { mitk::ColorProperty::Pointer prop; prop = mitk::ColorProperty::New(rgb); GetPropertyList(renderer)->SetProperty(propertyKey, prop); } void mitk::DataNode::SetVisibility(bool visible, mitk::BaseRenderer* renderer, const char* propertyKey) { mitk::BoolProperty::Pointer prop; prop = mitk::BoolProperty::New(visible); GetPropertyList(renderer)->SetProperty(propertyKey, prop); } void mitk::DataNode::SetOpacity(float opacity, mitk::BaseRenderer* renderer, const char* propertyKey) { mitk::FloatProperty::Pointer prop; prop = mitk::FloatProperty::New(opacity); GetPropertyList(renderer)->SetProperty(propertyKey, prop); } void mitk::DataNode::SetLevelWindow(mitk::LevelWindow levelWindow, mitk::BaseRenderer* renderer, const char* propertyKey) { mitk::LevelWindowProperty::Pointer prop; prop = mitk::LevelWindowProperty::New(levelWindow); GetPropertyList(renderer)->SetProperty(propertyKey, prop); } void mitk::DataNode::SetIntProperty(const char* propertyKey, int intValue, mitk::BaseRenderer* renderer) { GetPropertyList(renderer)->SetProperty(propertyKey, mitk::IntProperty::New(intValue)); } void mitk::DataNode::SetBoolProperty( const char* propertyKey, bool boolValue, mitk::BaseRenderer* renderer/*=NULL*/ ) { GetPropertyList(renderer)->SetProperty(propertyKey, mitk::BoolProperty::New(boolValue)); } void mitk::DataNode::SetFloatProperty( const char* propertyKey, float floatValue, mitk::BaseRenderer* renderer/*=NULL*/ ) { GetPropertyList(renderer)->SetProperty(propertyKey, mitk::FloatProperty::New(floatValue)); } void mitk::DataNode::SetStringProperty( const char* propertyKey, const char* stringValue, mitk::BaseRenderer* renderer/*=NULL*/ ) { GetPropertyList(renderer)->SetProperty(propertyKey, mitk::StringProperty::New(stringValue)); } void mitk::DataNode::SetProperty(const char *propertyKey, BaseProperty* propertyValue, const mitk::BaseRenderer* renderer) { GetPropertyList(renderer)->SetProperty(propertyKey, propertyValue); } void mitk::DataNode::ReplaceProperty(const char *propertyKey, BaseProperty* propertyValue, const mitk::BaseRenderer* renderer) { GetPropertyList(renderer)->ReplaceProperty(propertyKey, propertyValue); } void mitk::DataNode::AddProperty(const char *propertyKey, BaseProperty* propertyValue, const mitk::BaseRenderer* renderer, bool overwrite) { if((overwrite) || (GetProperty(propertyKey, renderer) == NULL)) { SetProperty(propertyKey, propertyValue, renderer); } } vtkLinearTransform* mitk::DataNode::GetVtkTransform(int t) const { assert(m_Data.IsNotNull()); - mitk::Geometry3D* geometry = m_Data->GetGeometry(t); + mitk::BaseGeometry* geometry = m_Data->GetGeometry(t); if(geometry == NULL) return NULL; return geometry->GetVtkTransform(); } unsigned long mitk::DataNode::GetMTime() const { unsigned long time = Superclass::GetMTime(); if(m_Data.IsNotNull()) { if((time < m_Data->GetMTime()) || ((m_Data->GetSource().IsNotNull()) && (time < m_Data->GetSource()->GetMTime())) ) { Modified(); return Superclass::GetMTime(); } } return time; } void mitk::DataNode::SetSelected(bool selected, mitk::BaseRenderer* renderer) { mitk::BoolProperty::Pointer selectedProperty = dynamic_cast(GetProperty("selected")); if ( selectedProperty.IsNull() ) { selectedProperty = mitk::BoolProperty::New(); selectedProperty->SetValue(false); SetProperty("selected", selectedProperty, renderer); } if( selectedProperty->GetValue() != selected ) { selectedProperty->SetValue(selected); itk::ModifiedEvent event; InvokeEvent( event ); } } /* class SelectedEvent : public itk::ModifiedEvent { public: typedef SelectedEvent Self; typedef itk::ModifiedEvent Superclass; SelectedEvent(DataNode* dataNode) { m_DataNode = dataNode; }; DataNode* GetDataNode() { return m_DataNode; }; virtual const char * GetEventName() const { return "SelectedEvent"; } virtual bool CheckEvent(const ::itk::EventObject* e) const { return dynamic_cast(e); } virtual ::itk::EventObject* MakeObject() const { return new Self(m_DataNode); } private: DataNode* m_DataNode; SelectedEvent(const Self& event) { m_DataNode = event.m_DataNode; }; void operator=(const Self& event) { m_DataNode = event.m_DataNode; } }; */ bool mitk::DataNode::IsSelected(mitk::BaseRenderer* renderer) { bool selected; if ( !GetBoolProperty("selected", selected, renderer) ) return false; return selected; } void mitk::DataNode::SetInteractorEnabled( const bool& enabled ) { if ( m_Interactor.IsNull() ) { itkWarningMacro("Interactor is NULL. Couldn't enable or disable interaction."); return; } if ( enabled ) mitk::GlobalInteraction::GetInstance()->AddInteractor( m_Interactor.GetPointer() ); else mitk::GlobalInteraction::GetInstance()->RemoveInteractor( m_Interactor.GetPointer() ); } void mitk::DataNode::EnableInteractor() { SetInteractorEnabled( true ); } void mitk::DataNode::DisableInteractor() { SetInteractorEnabled( false ); } bool mitk::DataNode::IsInteractorEnabled() const { return mitk::GlobalInteraction::GetInstance()->InteractorRegistered( m_Interactor.GetPointer() ); } void mitk::DataNode::SetDataInteractor(const DataInteractor::Pointer& interactor) { m_DataInteractor = interactor; Modified(); // the interactor has changed, so we have ti invoke an InteractorChangedEvent const mitk::DataNode::InteractorChangedEvent changedEvent; InvokeEvent( changedEvent ); } mitk::DataInteractor::Pointer mitk::DataNode::GetDataInteractor() const { return m_DataInteractor; } void mitk::DataNode::PropertyListModified( const itk::Object* /*caller*/, const itk::EventObject& ) { Modified(); } diff --git a/Core/Code/DataManagement/mitkDisplayGeometry.cpp b/Core/Code/DataManagement/mitkDisplayGeometry.cpp index 39fac88de3..141fb19c1c 100644 --- a/Core/Code/DataManagement/mitkDisplayGeometry.cpp +++ b/Core/Code/DataManagement/mitkDisplayGeometry.cpp @@ -1,637 +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."); + // itkExceptionMacro(<<"calling mitk::DisplayGeometry::Clone does not make much sense."); DisplayGeometry* returnValue = const_cast(this); return returnValue; } bool mitk::DisplayGeometry::IsValid() const { - return m_Valid && m_WorldGeometry.IsNotNull() && m_WorldGeometry->IsValid(); + 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 m_TimeBounds; + 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->GetParametricExtentInMM(0); - const ScalarType& heightInMM = m_WorldGeometry->GetParametricExtentInMM(1); + 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; - } + 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_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->GetParametricExtentInMM(0); - float worldHeightMM = m_WorldGeometry->GetParametricExtentInMM(1); + 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; - } + 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 + // make sure bottom display border inside our world else - if (displayYMM < 0) - { - newOrigin[1] = 0; - correctPanning = true; - } - + if (displayYMM < 0) + { + newOrigin[1] = 0; + correctPanning = true; + } } - if (correctPanning) + 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 d463fa37e2..613e1dd9d2 100644 --- a/Core/Code/DataManagement/mitkDisplayGeometry.h +++ b/Core/Code/DataManagement/mitkDisplayGeometry.h @@ -1,241 +1,230 @@ /*=================================================================== 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. itkFactorylessNewMacro(Self) itkCloneMacro(Self) /// \brief duplicates the geometry, NOT useful for this sub-class virtual itk::LightObject::Pointer InternalClone() const; - virtual bool IsValid() 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 deleted file mode 100644 index 6f23c91710..0000000000 --- a/Core/Code/DataManagement/mitkGeometry2D.cpp +++ /dev/null @@ -1,284 +0,0 @@ -/*=================================================================== - -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) - : Geometry3D(other), m_ScaleFactorMMPerUnitX( other.m_ScaleFactorMMPerUnitX), - m_ScaleFactorMMPerUnitY( other.m_ScaleFactorMMPerUnitY), - m_ReferenceGeometry( other.m_ReferenceGeometry ) -{ -} - - - -mitk::Geometry2D::~Geometry2D() -{ -} - - -void -mitk::Geometry2D::SetIndexToWorldTransform( - mitk::AffineTransform3D* transform) -{ - Superclass::SetIndexToWorldTransform(transform); - - m_ScaleFactorMMPerUnitX=GetExtentInMM(0)/GetExtent(0); - m_ScaleFactorMMPerUnitY=GetExtentInMM(1)/GetExtent(1); - - assert(m_ScaleFactorMMPerUnitX(m_BoundingBox.GetPointer())->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 = GetParametricTransform()->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(m_BoundingBox.IsNotNull()); - - Point3D pt3d_units; - BackTransform(pt3d_mm, pt3d_units); - pt3d_units[2] = 0; - projectedPt3d_mm = GetParametricTransform()->TransformPoint(pt3d_units); - return const_cast(m_BoundingBox.GetPointer())->IsInside(pt3d_units); -} - -bool -mitk::Geometry2D::Project(const mitk::Vector3D &vec3d_mm, mitk::Vector3D &projectedVec3d_mm) const -{ - assert(m_BoundingBox.IsNotNull()); - - Vector3D vec3d_units; - BackTransform(vec3d_mm, vec3d_units); - vec3d_units[2] = 0; - projectedVec3d_mm = GetParametricTransform()->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(m_BoundingBox.IsNotNull()); - - Vector3D vec3d_units; - BackTransform(atPt3d_mm, vec3d_mm, vec3d_units); - vec3d_units[2] = 0; - projectedVec3d_mm = GetParametricTransform()->TransformVector(vec3d_units); - - Point3D pt3d_units; - BackTransform(atPt3d_mm, pt3d_units); - return const_cast(m_BoundingBox.GetPointer())->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; - Geometry3D::WorldToIndex(pt3d_mm, pt3d_units); - return (pt3d_units[2] > m_BoundingBox->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::Geometry3D *geometry ) -{ - m_ReferenceGeometry = geometry; -} - -mitk::Geometry3D * -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 deleted file mode 100644 index 4b5d912522..0000000000 --- a/Core/Code/DataManagement/mitkGeometry2D.h +++ /dev/null @@ -1,274 +0,0 @@ -/*=================================================================== - -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 "mitkGeometry3D.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::Geometry3D -{ -public: - mitkClassMacro(Geometry2D, mitk::Geometry3D); - itkFactorylessNewMacro(Self) - itkCloneMacro(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 void SetIndexToWorldTransform(mitk::AffineTransform3D* transform); - - virtual void SetExtentInMM(int direction, ScalarType extentInMM); - - 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::Geometry3D *geometry ); - - /** - * \brief Get the geometrical frame of reference for this Geometry2D. - */ - Geometry3D *GetReferenceGeometry() const; - bool HasReferenceGeometry() const; - - -protected: - Geometry2D(); - - Geometry2D(const Geometry2D& other); - - virtual ~Geometry2D(); - - virtual void PrintSelf(std::ostream& os, itk::Indent indent) const; - - /** - * \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::Geometry3D *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 3d87886121..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::Geometry3D *geometry) +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 ed1d5e7a17..1ff003c1b6 100644 --- a/Core/Code/DataManagement/mitkGeometry2DData.h +++ b/Core/Code/DataManagement/mitkGeometry2DData.h @@ -1,78 +1,78 @@ /*=================================================================== 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); itkFactorylessNewMacro(Self) itkCloneMacro(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. - virtual void SetGeometry(mitk::Geometry3D *geometry); + //## @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/mitkGeometry3D.cpp b/Core/Code/DataManagement/mitkGeometry3D.cpp index 70789582b1..50269ca660 100644 --- a/Core/Code/DataManagement/mitkGeometry3D.cpp +++ b/Core/Code/DataManagement/mitkGeometry3D.cpp @@ -1,978 +1,48 @@ /*=================================================================== 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 "mitkGeometry3D.h" -#include "mitkMatrixConvert.h" + #include "mitkRotationOperation.h" #include "mitkRestorePlanePositionOperation.h" #include "mitkApplyTransformMatrixOperation.h" #include "mitkPointOperation.h" #include "mitkInteractionConst.h" - #include #include +#include "mitkMatrixConvert.h" // Standard constructor for the New() macro. Sets the geometry to 3 dimensions -mitk::Geometry3D::Geometry3D() - : m_ParametricBoundingBox(NULL), - m_ImageGeometry(false), m_Valid(true), m_FrameOfReferenceID(0), m_IndexToWorldTransformLastModified(0) +mitk::Geometry3D::Geometry3D() : BaseGeometry() { - FillVector3D(m_FloatSpacing, 1,1,1); - m_VtkMatrix = vtkMatrix4x4::New(); - m_VtkIndexToWorldTransform = vtkMatrixToLinearTransform::New(); - m_VtkIndexToWorldTransform->SetInput(m_VtkMatrix); - Initialize(); } -mitk::Geometry3D::Geometry3D(const Geometry3D& other) : Superclass(), mitk::OperationActor(), m_ParametricBoundingBox(other.m_ParametricBoundingBox),m_TimeBounds(other.m_TimeBounds), - m_ImageGeometry(other.m_ImageGeometry), m_Valid(other.m_Valid), m_FrameOfReferenceID(other.m_FrameOfReferenceID), m_IndexToWorldTransformLastModified(other.m_IndexToWorldTransformLastModified), m_RotationQuaternion( other.m_RotationQuaternion ) , m_Origin(other.m_Origin) +mitk::Geometry3D::Geometry3D(const Geometry3D& other) : BaseGeometry(other) { - // AffineGeometryFrame - SetBounds(other.GetBounds()); - //SetIndexToObjectTransform(other.GetIndexToObjectTransform()); - //SetObjectToNodeTransform(other.GetObjectToNodeTransform()); - //SetIndexToWorldTransform(other.GetIndexToWorldTransform()); - // this is not used in AffineGeometryFrame of ITK, thus there are not Get and Set methods - // m_IndexToNodeTransform = other.m_IndexToNodeTransform; - // m_InvertedTransform = TransformType::New(); - // m_InvertedTransform = TransformType::New(); - // m_InvertedTransform->DeepCopy(other.m_InvertedTransform); - m_VtkMatrix = vtkMatrix4x4::New(); - m_VtkMatrix->DeepCopy(other.m_VtkMatrix); - if (other.m_ParametricBoundingBox.IsNotNull()) - { - m_ParametricBoundingBox = other.m_ParametricBoundingBox->DeepCopy(); - } - FillVector3D(m_FloatSpacing,other.m_FloatSpacing[0],other.m_FloatSpacing[1],other.m_FloatSpacing[2]); - m_VtkIndexToWorldTransform = vtkMatrixToLinearTransform::New(); - m_VtkIndexToWorldTransform->DeepCopy(other.m_VtkIndexToWorldTransform); - m_VtkIndexToWorldTransform->SetInput(m_VtkMatrix); - other.InitializeGeometry(this); } mitk::Geometry3D::~Geometry3D() { - m_VtkMatrix->Delete(); - m_VtkIndexToWorldTransform->Delete(); -} - - -static void CopySpacingFromTransform(mitk::AffineTransform3D* transform, mitk::Vector3D& spacing, float floatSpacing[3]) -{ - mitk::AffineTransform3D::MatrixType::InternalMatrixType vnlmatrix; - vnlmatrix = transform->GetMatrix().GetVnlMatrix(); - - spacing[0]=vnlmatrix.get_column(0).magnitude(); - spacing[1]=vnlmatrix.get_column(1).magnitude(); - spacing[2]=vnlmatrix.get_column(2).magnitude(); - floatSpacing[0]=spacing[0]; - floatSpacing[1]=spacing[1]; - floatSpacing[2]=spacing[2]; -} - - -void mitk::Geometry3D::Initialize() -{ - float b[6] = {0,1,0,1,0,1}; - SetFloatBounds(b); - - if(m_IndexToWorldTransform.IsNull()) - m_IndexToWorldTransform = TransformType::New(); - else - m_IndexToWorldTransform->SetIdentity(); - CopySpacingFromTransform(m_IndexToWorldTransform, m_Spacing, m_FloatSpacing); - vtk2itk(m_IndexToWorldTransform->GetOffset(), m_Origin); - - m_VtkMatrix->Identity(); - - m_TimeBounds[0]=ScalarTypeNumericTraits::NonpositiveMin(); m_TimeBounds[1]=ScalarTypeNumericTraits::max(); - - m_FrameOfReferenceID = 0; - - m_ImageGeometry = false; -} - -void mitk::Geometry3D::TransferItkToVtkTransform() -{ - // copy m_IndexToWorldTransform into m_VtkIndexToWorldTransform - TransferItkTransformToVtkMatrix(m_IndexToWorldTransform.GetPointer(), m_VtkMatrix); - m_VtkIndexToWorldTransform->Modified(); -} - -void mitk::Geometry3D::TransferVtkToItkTransform() -{ - TransferVtkMatrixToItkTransform(m_VtkMatrix, m_IndexToWorldTransform.GetPointer()); - CopySpacingFromTransform(m_IndexToWorldTransform, m_Spacing, m_FloatSpacing); - vtk2itk(m_IndexToWorldTransform->GetOffset(), m_Origin); -} - -void mitk::Geometry3D::SetIndexToWorldTransformByVtkMatrix(vtkMatrix4x4* vtkmatrix) -{ - m_VtkMatrix->DeepCopy(vtkmatrix); - TransferVtkToItkTransform(); -} - -void mitk::Geometry3D::SetTimeBounds(const TimeBounds& timebounds) -{ - if(m_TimeBounds != timebounds) - { - m_TimeBounds = timebounds; - Modified(); - } -} - -void mitk::Geometry3D::SetFloatBounds(const float bounds[6]) -{ - mitk::BoundingBox::BoundsArrayType b; - const float *input = bounds; - int i=0; - for(mitk::BoundingBox::BoundsArrayType::Iterator it = b.Begin(); i < 6 ;++i) *it++ = (mitk::ScalarType)*input++; - SetBounds(b); -} - -void mitk::Geometry3D::SetFloatBounds(const double bounds[6]) -{ - mitk::BoundingBox::BoundsArrayType b; - const double *input = bounds; - int i=0; - for(mitk::BoundingBox::BoundsArrayType::Iterator it = b.Begin(); i < 6 ;++i) *it++ = (mitk::ScalarType)*input++; - SetBounds(b); -} - -void mitk::Geometry3D::SetParametricBounds(const BoundingBox::BoundsArrayType& bounds) -{ - m_ParametricBoundingBox = BoundingBoxType::New(); - - BoundingBoxType::PointsContainer::Pointer pointscontainer = - BoundingBoxType::PointsContainer::New(); - BoundingBoxType::PointType p; - BoundingBoxType::PointIdentifier pointid; - - for(pointid=0; pointid<2;++pointid) - { - unsigned int i; - for(i=0; iInsertElement(pointid, p); - } - - m_ParametricBoundingBox->SetPoints(pointscontainer); - m_ParametricBoundingBox->ComputeBoundingBox(); - this->Modified(); -} - -void mitk::Geometry3D::WorldToIndex(const mitk::Point3D &pt_mm, mitk::Point3D &pt_units) const -{ - BackTransform(pt_mm, pt_units); -} - -void mitk::Geometry3D::IndexToWorld(const mitk::Point3D &pt_units, mitk::Point3D &pt_mm) const -{ - pt_mm = m_IndexToWorldTransform->TransformPoint(pt_units); -} - -void mitk::Geometry3D::WorldToIndex(const mitk::Point3D & /*atPt3d_mm*/, const mitk::Vector3D &vec_mm, mitk::Vector3D &vec_units) const -{ - MITK_WARN<<"Warning! Call of the deprecated function Geometry3D::WorldToIndex(point, vec, vec). Use Geometry3D::WorldToIndex(vec, vec) instead!"; - //BackTransform(atPt3d_mm, vec_mm, vec_units); - this->WorldToIndex(vec_mm, vec_units); -} - -void mitk::Geometry3D::WorldToIndex( const mitk::Vector3D &vec_mm, mitk::Vector3D &vec_units) const -{ - BackTransform( vec_mm, vec_units); -} - -void mitk::Geometry3D::IndexToWorld(const mitk::Point3D &/*atPt3d_units*/, const mitk::Vector3D &vec_units, mitk::Vector3D &vec_mm) const -{ - MITK_WARN<<"Warning! Call of the deprecated function Geometry3D::IndexToWorld(point, vec, vec). Use Geometry3D::IndexToWorld(vec, vec) instead!"; - //vec_mm = m_IndexToWorldTransform->TransformVector(vec_units); - this->IndexToWorld(vec_units, vec_mm); -} - -void mitk::Geometry3D::IndexToWorld(const mitk::Vector3D &vec_units, mitk::Vector3D &vec_mm) const -{ - vec_mm = m_IndexToWorldTransform->TransformVector(vec_units); -} - -void mitk::Geometry3D::SetIndexToWorldTransform(mitk::AffineTransform3D* transform) -{ - if(m_IndexToWorldTransform.GetPointer() != transform) - { - m_IndexToWorldTransform = transform; - CopySpacingFromTransform(m_IndexToWorldTransform, m_Spacing, m_FloatSpacing); - vtk2itk(m_IndexToWorldTransform->GetOffset(), m_Origin); - TransferItkToVtkTransform(); - Modified(); - } } itk::LightObject::Pointer mitk::Geometry3D::InternalClone() const { Self::Pointer newGeometry = new Self(*this); newGeometry->UnRegister(); return newGeometry.GetPointer(); } -/* -void mitk::Geometry3D::InitializeGeometry(Geometry3D * newGeometry) const -{ - Superclass::InitializeGeometry(newGeometry); - - newGeometry->SetTimeBounds(m_TimeBounds); - - //newGeometry->GetVtkTransform()->SetMatrix(m_VtkIndexToWorldTransform->GetMatrix()); IW - //newGeometry->TransferVtkToItkTransform(); //MH - - newGeometry->SetFrameOfReferenceID(GetFrameOfReferenceID()); - newGeometry->m_ImageGeometry = m_ImageGeometry; -} -*/ -void mitk::Geometry3D::SetExtentInMM(int direction, ScalarType extentInMM) -{ - ScalarType len = GetExtentInMM(direction); - if(fabs(len - extentInMM)>=mitk::eps) - { - AffineTransform3D::MatrixType::InternalMatrixType vnlmatrix; - vnlmatrix = m_IndexToWorldTransform->GetMatrix().GetVnlMatrix(); - if(len>extentInMM) - vnlmatrix.set_column(direction, vnlmatrix.get_column(direction)/len*extentInMM); - else - vnlmatrix.set_column(direction, vnlmatrix.get_column(direction)*extentInMM/len); - Matrix3D matrix; - matrix = vnlmatrix; - m_IndexToWorldTransform->SetMatrix(matrix); - Modified(); - } -} - -mitk::BoundingBox::Pointer mitk::Geometry3D::CalculateBoundingBoxRelativeToTransform(const mitk::AffineTransform3D* transform) const -{ - mitk::BoundingBox::PointsContainer::Pointer pointscontainer=mitk::BoundingBox::PointsContainer::New(); - - mitk::BoundingBox::PointIdentifier pointid=0; - - unsigned char i; - if(transform!=NULL) - { - mitk::AffineTransform3D::Pointer inverse = mitk::AffineTransform3D::New(); - transform->GetInverse(inverse); - for(i=0; i<8; ++i) - pointscontainer->InsertElement( pointid++, inverse->TransformPoint( GetCornerPoint(i) )); - } - else - { - for(i=0; i<8; ++i) - pointscontainer->InsertElement( pointid++, GetCornerPoint(i) ); - } - - mitk::BoundingBox::Pointer result = mitk::BoundingBox::New(); - result->SetPoints(pointscontainer); - result->ComputeBoundingBox(); - - return result; -} - -#include -void mitk::Geometry3D::ExecuteOperation(Operation* operation) -{ - vtkTransform *vtktransform = vtkTransform::New(); - vtktransform->SetMatrix(m_VtkMatrix); - switch (operation->GetOperationType()) - { - case OpNOTHING: - break; - case OpMOVE: - { - mitk::PointOperation *pointOp = dynamic_cast(operation); - if (pointOp == NULL) - { - //mitk::StatusBar::GetInstance()->DisplayText("received wrong type of operation!See mitkAffineInteractor.cpp", 10000); - return; - } - mitk::Point3D newPos = pointOp->GetPoint(); - ScalarType data[3]; - vtktransform->GetPosition(data); - vtktransform->PostMultiply(); - vtktransform->Translate(newPos[0], newPos[1], newPos[2]); - vtktransform->PreMultiply(); - break; - } - case OpSCALE: - { - mitk::PointOperation *pointOp = dynamic_cast(operation); - if (pointOp == NULL) - { - //mitk::StatusBar::GetInstance()->DisplayText("received wrong type of operation!See mitkAffineInteractor.cpp", 10000); - return; - } - mitk::Point3D newScale = pointOp->GetPoint(); - ScalarType data[3]; - /* calculate new scale: newscale = oldscale * (oldscale + scaletoadd)/oldscale */ - data[0] = 1 + (newScale[0] / GetMatrixColumn(0).magnitude()); - data[1] = 1 + (newScale[1] / GetMatrixColumn(1).magnitude()); - data[2] = 1 + (newScale[2] / GetMatrixColumn(2).magnitude()); - - mitk::Point3D center = const_cast(m_BoundingBox.GetPointer())->GetCenter(); - ScalarType pos[3]; - vtktransform->GetPosition(pos); - vtktransform->PostMultiply(); - vtktransform->Translate(-pos[0], -pos[1], -pos[2]); - vtktransform->Translate(-center[0], -center[1], -center[2]); - vtktransform->PreMultiply(); - vtktransform->Scale(data[0], data[1], data[2]); - vtktransform->PostMultiply(); - vtktransform->Translate(+center[0], +center[1], +center[2]); - vtktransform->Translate(pos[0], pos[1], pos[2]); - vtktransform->PreMultiply(); - break; - } - case OpROTATE: - { - mitk::RotationOperation *rotateOp = dynamic_cast(operation); - if (rotateOp == NULL) - { - //mitk::StatusBar::GetInstance()->DisplayText("received wrong type of operation!See mitkAffineInteractor.cpp", 10000); - return; - } - Vector3D rotationVector = rotateOp->GetVectorOfRotation(); - Point3D center = rotateOp->GetCenterOfRotation(); - ScalarType angle = rotateOp->GetAngleOfRotation(); - vtktransform->PostMultiply(); - vtktransform->Translate(-center[0], -center[1], -center[2]); - vtktransform->RotateWXYZ(angle, rotationVector[0], rotationVector[1], rotationVector[2]); - vtktransform->Translate(center[0], center[1], center[2]); - vtktransform->PreMultiply(); - break; - } - case OpRESTOREPLANEPOSITION: - { - //Copy necessary to avoid vtk warning - vtkMatrix4x4* matrix = vtkMatrix4x4::New(); - TransferItkTransformToVtkMatrix(dynamic_cast(operation)->GetTransform().GetPointer(), matrix); - vtktransform->SetMatrix(matrix); - break; - } - case OpAPPLYTRANSFORMMATRIX: - { - ApplyTransformMatrixOperation *applyMatrixOp = dynamic_cast< ApplyTransformMatrixOperation* >( operation ); - vtktransform->SetMatrix(applyMatrixOp->GetMatrix()); - break; - } - default: - vtktransform->Delete(); - return; - } - m_VtkMatrix->DeepCopy(vtktransform->GetMatrix()); - TransferVtkToItkTransform(); - Modified(); - vtktransform->Delete(); -} - -void mitk::Geometry3D::BackTransform(const mitk::Point3D &in, mitk::Point3D& out) const -{ - ScalarType temp[3]; - unsigned int i, j; - const TransformType::OffsetType& offset = m_IndexToWorldTransform->GetOffset(); - - // Remove offset - for (j = 0; j < 3; j++) - { - temp[j] = in[j] - offset[j]; - } - - // Get WorldToIndex transform - if (m_IndexToWorldTransformLastModified != m_IndexToWorldTransform->GetMTime()) - { - m_InvertedTransform = TransformType::New(); - if (!m_IndexToWorldTransform->GetInverse( m_InvertedTransform.GetPointer() )) - { - itkExceptionMacro( "Internal ITK matrix inversion error, cannot proceed." ); - } - m_IndexToWorldTransformLastModified = m_IndexToWorldTransform->GetMTime(); - } - - // Check for valid matrix inversion - const TransformType::MatrixType& inverse = m_InvertedTransform->GetMatrix(); - if(inverse.GetVnlMatrix().has_nans()) - { - itkExceptionMacro( "Internal ITK matrix inversion error, cannot proceed. Matrix was: " << std::endl - << m_IndexToWorldTransform->GetMatrix() << "Suggested inverted matrix is:" << std::endl - << inverse ); - } - - // Transform point - for (i = 0; i < 3; i++) - { - out[i] = 0.0; - for (j = 0; j < 3; j++) - { - out[i] += inverse[i][j]*temp[j]; - } - } -} - -void mitk::Geometry3D::BackTransform(const mitk::Point3D &/*at*/, const mitk::Vector3D &in, mitk::Vector3D& out) const -{ - MITK_INFO<<"Warning! Call of the deprecated function Geometry3D::BackTransform(point, vec, vec). Use Geometry3D::BackTransform(vec, vec) instead!"; - //// Get WorldToIndex transform - //if (m_IndexToWorldTransformLastModified != m_IndexToWorldTransform->GetMTime()) - //{ - // m_InvertedTransform = TransformType::New(); - // if (!m_IndexToWorldTransform->GetInverse( m_InvertedTransform.GetPointer() )) - // { - // itkExceptionMacro( "Internal ITK matrix inversion error, cannot proceed." ); - // } - // m_IndexToWorldTransformLastModified = m_IndexToWorldTransform->GetMTime(); - //} - - //// Check for valid matrix inversion - //const TransformType::MatrixType& inverse = m_InvertedTransform->GetMatrix(); - //if(inverse.GetVnlMatrix().has_nans()) - //{ - // itkExceptionMacro( "Internal ITK matrix inversion error, cannot proceed. Matrix was: " << std::endl - // << m_IndexToWorldTransform->GetMatrix() << "Suggested inverted matrix is:" << std::endl - // << inverse ); - //} - - //// Transform vector - //for (unsigned int i = 0; i < 3; i++) - //{ - // out[i] = 0.0; - // for (unsigned int j = 0; j < 3; j++) - // { - // out[i] += inverse[i][j]*in[j]; - // } - //} - this->BackTransform(in, out); -} - -void mitk::Geometry3D::BackTransform(const mitk::Vector3D& in, mitk::Vector3D& out) const -{ - // Get WorldToIndex transform - if (m_IndexToWorldTransformLastModified != m_IndexToWorldTransform->GetMTime()) - { - m_InvertedTransform = TransformType::New(); - if (!m_IndexToWorldTransform->GetInverse( m_InvertedTransform.GetPointer() )) - { - itkExceptionMacro( "Internal ITK matrix inversion error, cannot proceed." ); - } - m_IndexToWorldTransformLastModified = m_IndexToWorldTransform->GetMTime(); - } - - // Check for valid matrix inversion - const TransformType::MatrixType& inverse = m_InvertedTransform->GetMatrix(); - if(inverse.GetVnlMatrix().has_nans()) - { - itkExceptionMacro( "Internal ITK matrix inversion error, cannot proceed. Matrix was: " << std::endl - << m_IndexToWorldTransform->GetMatrix() << "Suggested inverted matrix is:" << std::endl - << inverse ); - } - - // Transform vector - for (unsigned int i = 0; i < 3; i++) - { - out[i] = 0.0; - for (unsigned int j = 0; j < 3; j++) - { - out[i] += inverse[i][j]*in[j]; - } - } -} - -const float* mitk::Geometry3D::GetFloatSpacing() const -{ - return m_FloatSpacing; -} - -void mitk::Geometry3D::SetSpacing(const mitk::Vector3D& aSpacing) -{ - if(mitk::Equal(m_Spacing, aSpacing) == false) - { - assert(aSpacing[0]>0 && aSpacing[1]>0 && aSpacing[2]>0); - - m_Spacing = aSpacing; - - AffineTransform3D::MatrixType::InternalMatrixType vnlmatrix; - - vnlmatrix = m_IndexToWorldTransform->GetMatrix().GetVnlMatrix(); - - mitk::VnlVector col; - col = vnlmatrix.get_column(0); col.normalize(); col*=aSpacing[0]; vnlmatrix.set_column(0, col); - col = vnlmatrix.get_column(1); col.normalize(); col*=aSpacing[1]; vnlmatrix.set_column(1, col); - col = vnlmatrix.get_column(2); col.normalize(); col*=aSpacing[2]; vnlmatrix.set_column(2, col); - - Matrix3D matrix; - matrix = vnlmatrix; - - AffineTransform3D::Pointer transform = AffineTransform3D::New(); - transform->SetMatrix(matrix); - transform->SetOffset(m_IndexToWorldTransform->GetOffset()); - - SetIndexToWorldTransform(transform.GetPointer()); - - itk2vtk(m_Spacing, m_FloatSpacing); - } -} - -void mitk::Geometry3D::SetOrigin(const Point3D & origin) -{ - if(origin!=GetOrigin()) - { - m_Origin = origin; - m_IndexToWorldTransform->SetOffset(m_Origin.GetVectorFromOrigin()); - Modified(); - TransferItkToVtkTransform(); - } -} - -void mitk::Geometry3D::Translate(const Vector3D & vector) -{ - if((vector[0] != 0) || (vector[1] != 0) || (vector[2] != 0)) - { - this->SetOrigin(m_Origin + vector); -// m_IndexToWorldTransform->SetOffset(m_IndexToWorldTransform->GetOffset()+vector); -// TransferItkToVtkTransform(); -// Modified(); - } -} - -void mitk::Geometry3D::SetIdentity() -{ - m_IndexToWorldTransform->SetIdentity(); - m_Origin.Fill(0); - Modified(); - TransferItkToVtkTransform(); -} - -void mitk::Geometry3D::Compose( const mitk::Geometry3D::TransformType * other, bool pre ) -{ - m_IndexToWorldTransform->Compose(other, pre); - CopySpacingFromTransform(m_IndexToWorldTransform, m_Spacing, m_FloatSpacing); - vtk2itk(m_IndexToWorldTransform->GetOffset(), m_Origin); - Modified(); - TransferItkToVtkTransform(); -} - -void mitk::Geometry3D::Compose( const vtkMatrix4x4 * vtkmatrix, bool pre ) -{ - mitk::Geometry3D::TransformType::Pointer itkTransform = mitk::Geometry3D::TransformType::New(); - TransferVtkMatrixToItkTransform(vtkmatrix, itkTransform.GetPointer()); - Compose(itkTransform, pre); -} - -const std::string mitk::Geometry3D::GetTransformAsString( TransformType* transformType ) -{ - std::ostringstream out; - - out << '['; - - for( int i=0; i<3; ++i ) - { - out << '['; - for( int j=0; j<3; ++j ) - out << transformType->GetMatrix().GetVnlMatrix().get(i, j) << ' '; - out << ']'; - } - - out << "]["; - - for( int i=0; i<3; ++i ) - out << transformType->GetOffset()[i] << ' '; - - out << "]\0"; - - return out.str(); -} - -void mitk::Geometry3D::PrintSelf(std::ostream& os, itk::Indent indent) const -{ - os << indent << " IndexToWorldTransform: "; - if(m_IndexToWorldTransform.IsNull()) - os << "NULL" << std::endl; - else - { - // from itk::MatrixOffsetTransformBase - unsigned int i, j; - os << std::endl; - os << indent << "Matrix: " << std::endl; - for (i = 0; i < 3; i++) - { - os << indent.GetNextIndent(); - for (j = 0; j < 3; j++) - { - os << m_IndexToWorldTransform->GetMatrix()[i][j] << " "; - } - os << std::endl; - } - - os << indent << "Offset: " << m_IndexToWorldTransform->GetOffset() << std::endl; - os << indent << "Center: " << m_IndexToWorldTransform->GetCenter() << std::endl; - os << indent << "Translation: " << m_IndexToWorldTransform->GetTranslation() << std::endl; - - os << indent << "Inverse: " << std::endl; - for (i = 0; i < 3; i++) - { - os << indent.GetNextIndent(); - for (j = 0; j < 3; j++) - { - os << m_IndexToWorldTransform->GetInverseMatrix()[i][j] << " "; - } - os << std::endl; - } - - // from itk::ScalableAffineTransform - os << indent << "Scale : "; - for (i = 0; i < 3; i++) - { - os << m_IndexToWorldTransform->GetScale()[i] << " "; - } - os << std::endl; - } - - os << indent << " BoundingBox: "; - if(m_BoundingBox.IsNull()) - os << "NULL" << std::endl; - else - { - os << indent << "( "; - for (unsigned int i=0; i<3; i++) - { - os << m_BoundingBox->GetBounds()[2*i] << "," << m_BoundingBox->GetBounds()[2*i+1] << " "; - } - os << " )" << std::endl; - } - - os << indent << " Origin: " << m_Origin << std::endl; - os << indent << " ImageGeometry: " << m_ImageGeometry << std::endl; - os << indent << " Spacing: " << m_Spacing << std::endl; - os << indent << " TimeBounds: " << m_TimeBounds << std::endl; -} - -mitk::Point3D mitk::Geometry3D::GetCornerPoint(int id) const -{ - assert(id >= 0); - assert(m_BoundingBox.IsNotNull()); - - BoundingBox::BoundsArrayType bounds = m_BoundingBox->GetBounds(); - - Point3D cornerpoint; - switch(id) - { - case 0: FillVector3D(cornerpoint, bounds[0],bounds[2],bounds[4]); break; - case 1: FillVector3D(cornerpoint, bounds[0],bounds[2],bounds[5]); break; - case 2: FillVector3D(cornerpoint, bounds[0],bounds[3],bounds[4]); break; - case 3: FillVector3D(cornerpoint, bounds[0],bounds[3],bounds[5]); break; - case 4: FillVector3D(cornerpoint, bounds[1],bounds[2],bounds[4]); break; - case 5: FillVector3D(cornerpoint, bounds[1],bounds[2],bounds[5]); break; - case 6: FillVector3D(cornerpoint, bounds[1],bounds[3],bounds[4]); break; - case 7: FillVector3D(cornerpoint, bounds[1],bounds[3],bounds[5]); break; - default: - { - itkExceptionMacro(<<"A cube only has 8 corners. These are labeled 0-7."); - } - } - if(m_ImageGeometry) - { - // Here i have to adjust the 0.5 offset manually, because the cornerpoint is the corner of the - // bounding box. The bounding box itself is no image, so it is corner-based - FillVector3D(cornerpoint, cornerpoint[0]-0.5, cornerpoint[1]-0.5, cornerpoint[2]-0.5); - } - return m_IndexToWorldTransform->TransformPoint(cornerpoint); -} - -mitk::Point3D mitk::Geometry3D::GetCornerPoint(bool xFront, bool yFront, bool zFront) const -{ - assert(m_BoundingBox.IsNotNull()); - BoundingBox::BoundsArrayType bounds = m_BoundingBox->GetBounds(); - - Point3D cornerpoint; - cornerpoint[0] = (xFront ? bounds[0] : bounds[1]); - cornerpoint[1] = (yFront ? bounds[2] : bounds[3]); - cornerpoint[2] = (zFront ? bounds[4] : bounds[5]); - if(m_ImageGeometry) - { - // Here i have to adjust the 0.5 offset manually, because the cornerpoint is the corner of the - // bounding box. The bounding box itself is no image, so it is corner-based - FillVector3D(cornerpoint, cornerpoint[0]-0.5, cornerpoint[1]-0.5, cornerpoint[2]-0.5); - } - - return m_IndexToWorldTransform->TransformPoint(cornerpoint); -} - -void -mitk::Geometry3D::ResetSubTransforms() -{ -} - -void -mitk::Geometry3D::ChangeImageGeometryConsideringOriginOffset( const bool isAnImageGeometry ) -{ - // If Geometry is switched to ImageGeometry, you have to put an offset to the origin, because - // imageGeometries origins are pixel-center-based - // ... and remove the offset, if you switch an imageGeometry back to a normal geometry - // For more information please see the Geometry documentation page - - if(m_ImageGeometry == isAnImageGeometry) - return; - - const BoundingBox::BoundsArrayType& boundsarray = - this->GetBoundingBox()->GetBounds(); - - Point3D originIndex; - FillVector3D(originIndex, boundsarray[0], boundsarray[2], boundsarray[4]); - - if(isAnImageGeometry == true) - FillVector3D( originIndex, - originIndex[0] + 0.5, - originIndex[1] + 0.5, - originIndex[2] + 0.5 ); - else - FillVector3D( originIndex, - originIndex[0] - 0.5, - originIndex[1] - 0.5, - originIndex[2] - 0.5 ); - - Point3D originWorld; - - originWorld = GetIndexToWorldTransform() - ->TransformPoint( originIndex ); - // instead could as well call IndexToWorld(originIndex,originWorld); - - SetOrigin(originWorld); - - this->SetImageGeometry(isAnImageGeometry); -} - -bool mitk::Geometry3D::Is2DConvertable() -{ - bool isConvertableWithoutLoss = true; - do - { - if (this->GetSpacing()[2] != 1) - { - isConvertableWithoutLoss = false; - break; - } - if (this->GetOrigin()[2] != 0) - { - isConvertableWithoutLoss = false; - break; - } - mitk::Vector3D col0, col1, col2; - col0.SetVnlVector(this->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(0)); - col1.SetVnlVector(this->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(1)); - col2.SetVnlVector(this->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(2)); - - if ((col0[2] != 0) || (col1[2] != 0) || (col2[0] != 0) || (col2[1] != 0) || (col2[2] != 1)) - { - isConvertableWithoutLoss = false; - break; - } - } while (0); - - return isConvertableWithoutLoss; -} - -bool mitk::Equal( const mitk::Geometry3D::BoundingBoxType *leftHandSide, const mitk::Geometry3D::BoundingBoxType *rightHandSide, ScalarType eps, bool verbose ) -{ - if(( leftHandSide == NULL) || ( rightHandSide == NULL )) - { - MITK_ERROR << "mitk::Equal( const mitk::Geometry3D::BoundingBoxType *leftHandSide, const mitk::Geometry3D::BoundingBoxType *rightHandSide, ScalarType eps, bool verbose ) does not with NULL pointer input."; - return false; - } - return Equal( *leftHandSide, *rightHandSide, eps, verbose); -} - -bool mitk::Equal( const mitk::Geometry3D::BoundingBoxType& leftHandSide, const mitk::Geometry3D::BoundingBoxType& rightHandSide, ScalarType eps, bool verbose ) -{ - bool result = true; - - Geometry3D::BoundsArrayType rightBounds = rightHandSide.GetBounds(); - Geometry3D::BoundsArrayType leftBounds = leftHandSide.GetBounds(); - Geometry3D::BoundsArrayType::Iterator itLeft = leftBounds.Begin(); - for( Geometry3D::BoundsArrayType::Iterator itRight = rightBounds.Begin(); itRight != rightBounds.End(); ++itRight) - { - if(( !mitk::Equal( *itLeft, *itRight, eps )) ) - { - if(verbose) - { - MITK_INFO << "[( Geometry3D::BoundingBoxType )] bounds are not equal."; - MITK_INFO << "rightHandSide is " << setprecision(12) << *itRight << " : leftHandSide is " << *itLeft << " and tolerance is " << eps; - } - result = false; - } - itLeft++; - } - return result; -} - -bool mitk::Equal(const mitk::Geometry3D *leftHandSide, const mitk::Geometry3D *rightHandSide, ScalarType eps, bool verbose) -{ - if(( leftHandSide == NULL) || ( rightHandSide == NULL )) - { - MITK_ERROR << "mitk::Equal(const mitk::Geometry3D *leftHandSide, const mitk::Geometry3D *rightHandSide, ScalarType eps, bool verbose) does not with NULL pointer input."; - return false; - } - return Equal( *leftHandSide, *rightHandSide, eps, verbose); -} - -bool mitk::Equal(const mitk::Geometry3D& leftHandSide, const mitk::Geometry3D& rightHandSide, ScalarType eps, bool verbose) -{ - bool result = true; - - //Compare spacings - if( !mitk::Equal( leftHandSide.GetSpacing(), rightHandSide.GetSpacing(), eps ) ) - { - if(verbose) - { - MITK_INFO << "[( Geometry3D )] Spacing differs."; - MITK_INFO << "rightHandSide is " << setprecision(12) << rightHandSide.GetSpacing() << " : leftHandSide is " << leftHandSide.GetSpacing() << " and tolerance is " << eps; - } - result = false; - } - - //Compare Origins - if( !mitk::Equal( leftHandSide.GetOrigin(), rightHandSide.GetOrigin(), eps ) ) - { - if(verbose) - { - MITK_INFO << "[( Geometry3D )] Origin differs."; - MITK_INFO << "rightHandSide is " << setprecision(12) << rightHandSide.GetOrigin() << " : leftHandSide is " << leftHandSide.GetOrigin() << " and tolerance is " << eps; - } - result = false; - } - - //Compare Axis and Extents - for( unsigned int i=0; i<3; ++i) - { - if( !mitk::Equal( leftHandSide.GetAxisVector(i), rightHandSide.GetAxisVector(i), eps)) - { - if(verbose) - { - MITK_INFO << "[( Geometry3D )] AxisVector #" << i << " differ"; - MITK_INFO << "rightHandSide is " << setprecision(12) << rightHandSide.GetAxisVector(i) << " : leftHandSide is " << leftHandSide.GetAxisVector(i) << " and tolerance is " << eps; - } - result = false; - } - - if( !mitk::Equal( leftHandSide.GetExtent(i), rightHandSide.GetExtent(i), eps) ) - { - if(verbose) - { - MITK_INFO << "[( Geometry3D )] Extent #" << i << " differ"; - MITK_INFO << "rightHandSide is " << setprecision(12) << rightHandSide.GetExtent(i) << " : leftHandSide is " << leftHandSide.GetExtent(i) << " and tolerance is " << eps; - } - result = false; - } - } - - //Compare ImageGeometry Flag - if( rightHandSide.GetImageGeometry() != leftHandSide.GetImageGeometry() ) - { - if(verbose) - { - MITK_INFO << "[( Geometry3D )] GetImageGeometry is different."; - MITK_INFO << "rightHandSide is " << rightHandSide.GetImageGeometry() << " : leftHandSide is " << leftHandSide.GetImageGeometry(); - } - result = false; - } - - //Compare BoundingBoxes - if( !mitk::Equal( *leftHandSide.GetBoundingBox(), *rightHandSide.GetBoundingBox(), eps, verbose) ) - { - result = false; - } - - //Compare IndexToWorldTransform Matrix - if( !mitk::Equal( *leftHandSide.GetIndexToWorldTransform(), *rightHandSide.GetIndexToWorldTransform(), eps, verbose) ) - { - result = false; - } - return result; -} - -bool mitk::Equal(const Geometry3D::TransformType *leftHandSide, const Geometry3D::TransformType *rightHandSide, ScalarType eps, bool verbose ) -{ - if(( leftHandSide == NULL) || ( rightHandSide == NULL )) - { - MITK_ERROR << "mitk::Equal(const Geometry3D::TransformType *leftHandSide, const Geometry3D::TransformType *rightHandSide, ScalarType eps, bool verbose ) does not with NULL pointer input."; - return false; - } - return Equal( *leftHandSide, *rightHandSide, eps, verbose); -} - -bool mitk::Equal(const Geometry3D::TransformType& leftHandSide, const Geometry3D::TransformType& rightHandSide, ScalarType eps, bool verbose ) -{ - //Compare IndexToWorldTransform Matrix - if( !mitk::MatrixEqualElementWise( leftHandSide.GetMatrix(), - rightHandSide.GetMatrix() ) ) - { - if(verbose) - { - MITK_INFO << "[( Geometry3D::TransformType )] Index to World Transformation matrix differs."; - MITK_INFO << "rightHandSide is " << setprecision(12) << rightHandSide.GetMatrix() << " : leftHandSide is " << leftHandSide.GetMatrix() << " and tolerance is " << eps; - } - return false; - } - return true; -} - -/** Initialize the geometry */ -void -mitk::Geometry3D::InitializeGeometry(Geometry3D* newGeometry) const -{ - newGeometry->SetBounds(m_BoundingBox->GetBounds()); - // we have to create a new transform!! - - if(m_IndexToWorldTransform) - { - TransformType::Pointer indexToWorldTransform = TransformType::New(); - indexToWorldTransform->SetCenter( m_IndexToWorldTransform->GetCenter() ); - indexToWorldTransform->SetMatrix( m_IndexToWorldTransform->GetMatrix() ); - indexToWorldTransform->SetOffset( m_IndexToWorldTransform->GetOffset() ); - newGeometry->SetIndexToWorldTransform(indexToWorldTransform); - } -} - -/** Set the bounds */ -void mitk::Geometry3D::SetBounds(const BoundsArrayType& bounds) -{ - m_BoundingBox = BoundingBoxType::New(); - - BoundingBoxType::PointsContainer::Pointer pointscontainer = - BoundingBoxType::PointsContainer::New(); - BoundingBoxType::PointType p; - BoundingBoxType::PointIdentifier pointid; - - for(pointid=0; pointid<2;++pointid) - { - unsigned int i; - for(i=0; iInsertElement(pointid, p); - } - - m_BoundingBox->SetPoints(pointscontainer); - m_BoundingBox->ComputeBoundingBox(); - this->Modified(); -} diff --git a/Core/Code/DataManagement/mitkGeometry3D.h b/Core/Code/DataManagement/mitkGeometry3D.h index 0cfc5a2ac0..9266f9ad84 100644 --- a/Core/Code/DataManagement/mitkGeometry3D.h +++ b/Core/Code/DataManagement/mitkGeometry3D.h @@ -1,826 +1,69 @@ /*=================================================================== 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 GEOMETRY3D_H_HEADER_INCLUDED_C1EBD0AD #define GEOMETRY3D_H_HEADER_INCLUDED_C1EBD0AD #include #include -#include "mitkVector.h" -#include "mitkOperationActor.h" -#include -#include -#include -#include #include "itkScalableAffineTransform.h" -#include "itkBoundingBox.h" +#include + +#include "mitkBaseGeometry.h" class vtkLinearTransform; -class vtkMatrixToLinearTransform; -class vtkMatrix4x4; namespace mitk { - //##Documentation - //## @brief Standard 3D-BoundingBox typedef - //## - //## Standard 3D-BoundingBox typedef to get rid of template arguments (3D, type). - typedef itk::BoundingBox BoundingBox; - - //##Documentation - //## @brief Standard typedef for time-bounds - typedef itk::FixedArray TimeBounds; - typedef itk::FixedArray FixedArrayType; - - typedef itk::AffineGeometryFrame AffineGeometryFrame3D; - - //##Documentation - //## @brief Describes the geometry of a data object - //## - //## At least, it can return the bounding box of the data object. - //## - //## The class holds - //## \li a bounding box which is axes-parallel in intrinsic coordinates - //## (often integer indices of pixels), to be accessed by - //## GetBoundingBox() - //## \li a transform to convert intrinsic coordinates into a - //## world-coordinate system with coordinates in millimeters - //## and milliseconds (all are floating point values), to - //## be accessed by GetIndexToWorldTransform() - //## \li a life span, i.e. a bounding box in time in ms (with - //## start and end time), to be accessed by GetTimeBounds(). - //## The default is minus infinity to plus infinity. - //## - //## Geometry3D and its sub-classes allow converting between - //## intrinsic coordinates (called index or unit coordinates) - //## and world-coordinates (called world or mm coordinates), - //## e.g. WorldToIndex. - //## In case you need integer index coordinates, provide an - //## mitk::Index3D (or itk::Index) as target variable to - //## WorldToIndex, otherwise you will get a continuous index - //## (floating point values). - //## - //## An important sub-class is SlicedGeometry3D, which descibes - //## data objects consisting of slices, e.g., objects of type Image. - //## Conversions between world coordinates (in mm) and unit coordinates - //## (e.g., pixels in the case of an Image) can be performed. - //## - //## For more information on related classes, see \ref Geometry. - //## - //## Geometry3D instances referring to an Image need a slightly - //## different definition of corners, see SetImageGeometry. This - //## is usualy automatically called by Image. - //## - //## Geometry3D 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 - //## itk::ProcessObject::GenerateOutputInformation(), - //## itk::DataObject::CopyInformation() and - //## itk::DataObject::UpdateOutputInformation(). - //## - //## Rule: everything is in mm (ms) if not stated otherwise. - //## @ingroup Geometry - class MITK_CORE_EXPORT Geometry3D : public itk::Object, public OperationActor - { - public: - mitkClassMacro(Geometry3D, itk::Object); - - typedef itk::QuaternionRigidTransform< ScalarType > QuaternionTransformType; - typedef QuaternionTransformType::VnlQuaternionType VnlQuaternionType; - - /** Method for creation through the object factory. */ - itkFactorylessNewMacro(Self) - itkCloneMacro(Self) - - typedef itk::ScalableAffineTransform TransformType; - typedef itk::BoundingBox BoundingBoxType; - typedef BoundingBoxType::BoundsArrayType BoundsArrayType; - typedef BoundingBoxType::Pointer BoundingBoxPointer; - - // a bit of a misuse, but we want only doxygen to see the following: -#ifdef DOXYGEN_SKIP - //##Documentation - //## @brief Get the transformation used to convert from index - //## to world coordinates - itkGetObjectMacro(IndexToWorldTransform, AffineTransform3D); -#endif - //## @brief Set the transformation used to convert from index - //## to world coordinates - virtual void SetIndexToWorldTransform(mitk::AffineTransform3D* transform); - //##Documentation - //## @brief Convenience method for setting the ITK transform - //## (m_IndexToWorldTransform) via an vtkMatrix4x4 - //## \sa SetIndexToWorldTransform - virtual void SetIndexToWorldTransformByVtkMatrix(vtkMatrix4x4* vtkmatrix); - -#ifdef DOXYGEN_SKIP - //##Documentation - //## @brief Get bounding box (in index/unit coordinates) - itkGetConstObjectMacro(BoundingBox, BoundingBoxType); - //##Documentation - //## @brief Get bounding box (in index/unit coordinates) as a BoundsArrayType - const BoundsArrayType GetBounds() const - { - assert(m_BoundingBox.IsNotNull()); - return m_BoundingBox->GetBounds(); - } -#endif - //##Documentation - //## \brief Set the bounding box (in index/unit coordinates) - //## - //## Only possible via the BoundsArray to make clear that a - //## copy of the bounding-box is stored, not a reference to it. - virtual void SetBounds(const BoundsArrayType& bounds); - //##Documentation - //## @brief Set the bounding box (in index/unit coordinates) via a float array - virtual void SetFloatBounds(const float bounds[6]); - //##Documentation - //## @brief Set the bounding box (in index/unit coordinates) via a double array - virtual void SetFloatBounds(const double bounds[6]); - - //##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 ); - - //##Documentation - //## @brief Checks, if the given geometry can be converted to 2D without information loss - //## e.g. when a 2D image is saved, the matrix is usually cropped to 2x2, and when you load it back to MITK - //## it will be filled with standard values. This function checks, if information would be lost during this - //## procedure - virtual bool Is2DConvertable(); - - //##Documentation - //## @brief Get the time bounds (in ms) - itkGetConstReferenceMacro(TimeBounds, TimeBounds); - //##Documentation - //## @brief Set the time bounds (in ms) - virtual void SetTimeBounds(const TimeBounds& timebounds); - - //##Documentation - //## @brief Get the position of the corner number \a id (in world coordinates) - //## - //## See SetImageGeometry for how a corner is defined on images. - Point3D GetCornerPoint(int id) const; - - //##Documentation - //## @brief Get the position of a corner (in world coordinates) - //## - //## See SetImageGeometry for how a corner is defined on images. - Point3D GetCornerPoint(bool xFront=true, bool yFront=true, bool zFront=true) const; - - //##Documentation - //## @brief Get vector along bounding-box in the specified @a direction in mm - //## - //## The length of the vector is the size of the bounding-box in the - //## specified @a direction in mm - //## \sa GetMatrixColumn - Vector3D GetAxisVector(unsigned int direction) const - { - Vector3D frontToBack; - frontToBack.SetVnlVector(m_IndexToWorldTransform->GetMatrix().GetVnlMatrix().get_column(direction)); - frontToBack *= GetExtent(direction); - return frontToBack; - } - - //##Documentation - //## @brief Get the center of the bounding-box in mm - //## - Point3D GetCenter() const - { - assert(m_BoundingBox.IsNotNull()); - return m_IndexToWorldTransform->TransformPoint(m_BoundingBox->GetCenter()); - } - - //##Documentation - //## @brief Get the squared length of the diagonal of the bounding-box in mm - //## - double GetDiagonalLength2() const - { - Vector3D diagonalvector = GetCornerPoint()-GetCornerPoint(false, false, false); - return diagonalvector.GetSquaredNorm(); - } - - //##Documentation - //## @brief Get the length of the diagonal of the bounding-box in mm - //## - double GetDiagonalLength() const - { - return sqrt(GetDiagonalLength2()); - } - - //##Documentation - //## @brief Get a VnlVector along bounding-box in the specified - //## @a direction, length is spacing - //## - //## \sa GetAxisVector - VnlVector GetMatrixColumn(unsigned int direction) const - { - return m_IndexToWorldTransform->GetMatrix().GetVnlMatrix().get_column(direction); - } - -#ifdef DOXYGEN_SKIP - //##Documentation - //## @brief Get the extent of the bounding box (in index/unit coordinates) - //## - //## To access the extent in mm use GetExtentInMM - ScalarType GetExtent(unsigned int direction) const; -#endif - - //##Documentation - //## @brief Get the extent of the bounding-box in the specified @a direction in mm - //## - //## Equals length of GetAxisVector(direction). - ScalarType GetExtentInMM(int direction) const - { - return m_IndexToWorldTransform->GetMatrix().GetVnlMatrix().get_column(direction).magnitude()*GetExtent(direction); - } - - //##Documentation - //## @brief Set the extent of the bounding-box in the specified @a direction in mm - //## - //## @note This changes the matrix in the transform, @a not the bounds, which are given in units! - virtual void SetExtentInMM(int direction, ScalarType extentInMM); - - //##Documentation - //## @brief Get the m_IndexToWorldTransform as a vtkLinearTransform - vtkLinearTransform* GetVtkTransform() const - { - return (vtkLinearTransform*)m_VtkIndexToWorldTransform; - } - - //##Documentation - //## @brief Set the origin, i.e. the upper-left corner of the plane - //## - virtual void SetOrigin(const Point3D& origin); - - //##Documentation - //## @brief Translate the origin by a vector - //## - virtual void Translate(const Vector3D& vector); - - //##Documentation - //## @brief Set the transform to identity - //## - virtual void SetIdentity(); - - //##Documentation - //## @brief Compose new IndexToWorldTransform with a given transform. - //## - //## This method composes m_IndexToWorldTransform with another transform, - //## modifying self to be the composition of self and other. - //## If the argument pre is true, then other is precomposed with self; - //## that is, the resulting transformation consists of first applying - //## other to the source, followed by self. If pre is false or omitted, - //## then other is post-composed with self; that is the resulting - //## transformation consists of first applying self to the source, - //## followed by other. - virtual void Compose( const Geometry3D::TransformType * other, bool pre = 0 ); - - //##Documentation - //## @brief Compose new IndexToWorldTransform with a given vtkMatrix4x4. - //## - //## Converts the vtkMatrix4x4 into a itk-transform and calls the previous method. - virtual void Compose( const vtkMatrix4x4 * vtkmatrix, bool pre = 0 ); - - //##Documentation - //## @brief Get the origin, e.g. the upper-left corner of the plane - const Point3D& GetOrigin() const - { - return m_Origin; - } + //##Documentation + //## @brief Standard implementation of BaseGeometry. + //## + //## @ingroup Geometry + class MITK_CORE_EXPORT Geometry3D : public BaseGeometry + { + public: + mitkClassMacro(Geometry3D, mitk::BaseGeometry); - //##Documentation - //## @brief Get the origin as VnlVector - //## - //## \sa GetOrigin - VnlVector GetOriginVnl() const - { - return const_cast(this)->m_Origin.GetVnlVector(); - } - - //##Documentation - //## @brief Convert world coordinates (in mm) of a \em point to (continuous!) index coordinates - //## \warning If you need (discrete) integer index coordinates (e.g., for iterating easily over an image), - //## use WorldToIndex(const mitk::Point3D& pt_mm, itk::Index &index). - //## For further information about coordinates types, please see the Geometry documentation - void WorldToIndex(const mitk::Point3D& pt_mm, mitk::Point3D& pt_units) const; + typedef itk::QuaternionRigidTransform< ScalarType > QuaternionTransformType; + typedef QuaternionTransformType::VnlQuaternionType VnlQuaternionType; - //##Documentation - //## @brief Convert (continuous or discrete) index coordinates of a \em point to world coordinates (in mm) - //## For further information about coordinates types, please see the Geometry documentation - void IndexToWorld(const mitk::Point3D& pt_units, mitk::Point3D& pt_mm) const; + /** Method for creation through the object factory. */ + itkFactorylessNewMacro(Self) + mitkNewMacro1Param(Self,Self); - //##Documentation - //## @brief Convert (discrete) index coordinates of a \em point to world coordinates (in mm) - //## For further information about coordinates types, please see the Geometry documentation - template - void IndexToWorld(const itk::Index &index, mitk::Point3D& pt_mm ) const - { - mitk::Point3D pt_units; - pt_units.Fill(0); - int i, dim=index.GetIndexDimension(); - if(dim>3) - { - dim=3; - } - for(i=0;i - void WorldToIndex(const mitk::Point3D& pt_mm, itk::Index &index) const - { - typedef itk::Index IndexType; - mitk::Point3D pt_units; - this->WorldToIndex(pt_mm, pt_units); - int i, dim=index.GetIndexDimension(); - if(dim>3) - { - index.Fill(0); - dim=3; - } - for(i=0;i( pt_units[i] ); - } - } - - //##Documentation - //## @brief Deprecated for use with ITK version 3.10 or newer. - //## Convert world coordinates (in mm) of a \em point to - //## ITK physical coordinates (in mm, but without a possible rotation) - //## - //## This method is useful if you have want to access an mitk::Image - //## via an itk::Image. ITK v3.8 and older did not support rotated (tilted) - //## images, i.e., ITK images are always parallel to the coordinate axes. - //## When accessing a (possibly rotated) mitk::Image via an itk::Image - //## the rotational part of the transformation in the Geometry3D is - //## simply discarded; in other word: only the origin and spacing is - //## used by ITK, not the complete matrix available in MITK. - //## With WorldToItkPhysicalPoint you can convert an MITK world - //## coordinate (including the rotation) into a coordinate that - //## can be used with the ITK image as a ITK physical coordinate - //## (excluding the rotation). - template - void WorldToItkPhysicalPoint(const mitk::Point3D& pt_mm, - itk::Point& itkPhysicalPoint) const - { - mitk::vtk2itk(pt_mm, itkPhysicalPoint); - } - - //##Documentation - //## @brief Deprecated for use with ITK version 3.10 or newer. - //## Convert ITK physical coordinates of a \em point (in mm, - //## but without a rotation) into MITK world coordinates (in mm) - //## - //## For more information, see WorldToItkPhysicalPoint. - template - void ItkPhysicalPointToWorld(const itk::Point& itkPhysicalPoint, - mitk::Point3D& pt_mm) const - { - mitk::vtk2itk(itkPhysicalPoint, pt_mm); - } - - //##Documentation - //## @brief Initialize the Geometry3D - virtual void Initialize(); - - //##Documentation - //## @brief Is this an ImageGeometry? - //## - //## For more information, see SetImageGeometry - itkGetConstMacro(ImageGeometry, bool); - //##Documentation - //## @brief Define that this Geometry3D is refering to an Image - //## - //## A geometry referring to an Image needs a slightly different - //## definition of the position of the corners (see GetCornerPoint). - //## The position of a voxel is defined by the position of its center. - //## If we would use the origin (position of the (center of) the first - //## voxel) as a corner and display this point, it would seem to be - //## \em not at the corner but a bit within the image. Even worse for - //## the opposite corner of the image: here the corner would appear - //## outside the image (by half of the voxel diameter). Thus, we have - //## to correct for this and to be able to do that, we need to know - //## that the Geometry3D is referring to an Image. - itkSetMacro(ImageGeometry, bool); - itkBooleanMacro(ImageGeometry); - - //##Documentation - //## @brief Is this Geometry3D in a state that is valid? - virtual bool IsValid() const - { - return m_Valid; - } - - //##Documentation - //## @brief Test whether the point \a p (world coordinates in mm) is - //## inside the bounding box - bool IsInside(const mitk::Point3D& p) const - { - mitk::Point3D index; - WorldToIndex(p, index); - return IsIndexInside(index); - } - - //##Documentation - //## @brief Test whether the point \a p ((continous!)index coordinates in units) is - //## inside the bounding box - bool IsIndexInside(const mitk::Point3D& index) const - { - bool inside = false; - //if it is an image geometry, we need to convert the index to discrete values - //this is done by applying the rounding function also used in WorldToIndex (see line 323) - if (m_ImageGeometry) - { - mitk::Point3D discretIndex; - discretIndex[0]=itk::Math::RoundHalfIntegerUp( index[0] ); - discretIndex[1]=itk::Math::RoundHalfIntegerUp( index[1] ); - discretIndex[2]=itk::Math::RoundHalfIntegerUp( index[2] ); - - inside = m_BoundingBox->IsInside(discretIndex); - //we have to check if the index is at the upper border of each dimension, - // because the boundingbox is not centerbased - if (inside) - { - const BoundingBox::BoundsArrayType& bounds = m_BoundingBox->GetBounds(); - if((discretIndex[0] == bounds[1]) || - (discretIndex[1] == bounds[3]) || - (discretIndex[2] == bounds[5])) - inside = false; - } - } - else - inside = m_BoundingBox->IsInside(index); - - return inside; - } - - //##Documentation - //## @brief Convenience method for working with ITK indices - template - bool IsIndexInside(const itk::Index &index) const - { - int i, dim=index.GetIndexDimension(); - Point3D pt_index; - pt_index.Fill(0); - for ( i = 0; i < dim; ++i ) - { - pt_index[i] = index[i]; - } - return IsIndexInside(pt_index); - } - - //##Documentation - //## @brief Get the spacing (size of a pixel). - //## - itkGetConstReferenceMacro(Spacing, mitk::Vector3D); - - //##Documentation - //## @brief Get the spacing as a float[3] array. - const float* GetFloatSpacing() const; - - //##Documentation - //## @brief Set the spacing (m_Spacing) - virtual void SetSpacing(const mitk::Vector3D& aSpacing); - - //##Documentation - //## @brief Get the DICOM FrameOfReferenceID referring to the - //## used world coordinate system - itkGetConstMacro(FrameOfReferenceID, unsigned int); - //##Documentation - //## @brief Set the DICOM FrameOfReferenceID referring to the - //## used world coordinate system - itkSetMacro(FrameOfReferenceID, unsigned int); - - //##Documentation - //## @brief Copy the ITK transform - //## (m_IndexToWorldTransform) to the VTK transform - //## \sa SetIndexToWorldTransform - void TransferItkToVtkTransform(); - - //##Documentation - //## @brief Copy the VTK transform - //## to the ITK transform (m_IndexToWorldTransform) - //## \sa SetIndexToWorldTransform - void TransferVtkToItkTransform(); - - //##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 - { - assert(m_ParametricBoundingBox.IsNotNull()); - return m_ParametricBoundingBox->GetBounds(); - } - - //##Documentation - //## @brief Get the parametric extent - //## - //## See AbstractTransformGeometry for an example usage of this. - mitk::ScalarType GetParametricExtent(int direction) const - { - if (direction < 0 || direction>=3) - mitkThrow() << "Invalid direction. Must be between either 0, 1 or 2. "; - assert(m_ParametricBoundingBox.IsNotNull()); - - BoundingBoxType::BoundsArrayType bounds = m_ParametricBoundingBox->GetBounds(); - return bounds[direction*2+1]-bounds[direction*2]; - } - - //##Documentation - //## @brief Get the parametric extent in mm - //## - //## See AbstractTransformGeometry for an example usage of this. - virtual mitk::ScalarType GetParametricExtentInMM(int direction) const - { - return GetExtentInMM(direction); - } - - //##Documentation - //## @brief Get the parametric transform - //## - //## See AbstractTransformGeometry for an example usage of this. - virtual const Transform3D* GetParametricTransform() const - { - return m_IndexToWorldTransform; - } - - //##Documentation - //## @brief Calculates a bounding-box around the geometry relative - //## to a coordinate system defined by a transform - //## - mitk::BoundingBox::Pointer CalculateBoundingBoxRelativeToTransform(const mitk::AffineTransform3D* transform) const; + itkCloneMacro(Self) //##Documentation //## @brief clones the geometry //## //## Overwrite in all sub-classes. //## Normally looks like: //## \code //## Self::Pointer newGeometry = new Self(*this); //## newGeometry->UnRegister(); //## return newGeometry.GetPointer(); //## \endcode virtual itk::LightObject::Pointer InternalClone() const; - //##Documentation - //##@brief executes affine operations (translate, rotate, scale) - virtual void ExecuteOperation(Operation* operation); - - /** Set/Get the IndexToWorldTransform */ - itkGetConstObjectMacro(IndexToWorldTransform, AffineTransform3D); - itkGetObjectMacro(IndexToWorldTransform, AffineTransform3D); - /** Get the bounding box */ - itkGetConstObjectMacro(BoundingBox, BoundingBoxType); - - const BoundsArrayType GetBounds() const - { - assert(m_BoundingBox.IsNotNull()); - return m_BoundingBox->GetBounds(); - } - - /** Get the extent of the bounding box */ - ScalarType GetExtent(unsigned int direction) const - { - assert(m_BoundingBox.IsNotNull()); - if (direction>=NDimensions) - mitkThrow() << "Direction is too big. This geometry is for 3D Data"; - BoundsArrayType bounds = m_BoundingBox->GetBounds(); - return bounds[direction*2+1]-bounds[direction*2]; - } - protected: - Geometry3D(); - Geometry3D(const Geometry3D& other); - - virtual void InitializeGeometry(Self * newGeometry) const; - - static const std::string GetTransformAsString( TransformType* transformType ); - static const unsigned int NDimensions = 3; - - virtual ~Geometry3D(); - - virtual void PrintSelf(std::ostream& os, itk::Indent indent) const; - - virtual void BackTransform(const mitk::Point3D& in, mitk::Point3D& out) const; - //##Documentation - //## @brief Deprecated - virtual void BackTransform(const mitk::Point3D& at, const mitk::Vector3D& in, mitk::Vector3D& out) const; - - //Without redundant parameter Point3D - virtual void BackTransform(const mitk::Vector3D& in, mitk::Vector3D& out) const; - - //##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); - - /** Resets sub-transforms that compose m_IndexToWorldTransform, by using - * the current value of m_IndexToWorldTransform and setting the rotation - * component to zero. */ - virtual void ResetSubTransforms(); - - mutable mitk::BoundingBox::Pointer m_ParametricBoundingBox; - - mutable mitk::TimeBounds m_TimeBounds; - - vtkMatrix4x4* m_VtkMatrix; - - bool m_ImageGeometry; - - AffineTransform3D::Pointer m_IndexToWorldTransform; - mutable BoundingBoxPointer m_BoundingBox; - - //##Documentation - //## @brief Spacing of the data. Only significant if the geometry describes - //## an Image (m_ImageGeometry==true). - mitk::Vector3D m_Spacing; - - bool m_Valid; - - unsigned int m_FrameOfReferenceID; - - static const std::string INDEX_TO_OBJECT_TRANSFORM; - static const std::string OBJECT_TO_NODE_TRANSFORM; - static const std::string INDEX_TO_NODE_TRANSFORM; - static const std::string INDEX_TO_WORLD_TRANSFORM; - - private: - mutable TransformType::Pointer m_InvertedTransform; - mutable unsigned long m_IndexToWorldTransformLastModified; - - VnlQuaternionType m_RotationQuaternion; - - float m_FloatSpacing[3]; - vtkMatrixToLinearTransform* m_VtkIndexToWorldTransform; - - //##Documentation - //## @brief Origin, i.e. upper-left corner of the plane - //## - Point3D m_Origin; - }; - - // - // Static compare functions mainly for testing - // - /** - * @brief Equal A function comparing two geometries for beeing identical. - * @warning This method is deprecated and will not be available in the future. Use the \a bool mitk::Equal(const mitk::mitk::Geometry3D& g1, const mitk::Geometry3D& g2) instead. - * - * @ingroup MITKTestingAPI - * - * The function compares the spacing, origin, axisvectors, extents, the matrix of the - * IndexToWorldTransform (elementwise), the bounding (elementwise) and the ImageGeometry flag. - * - * The parameter eps is a tolarence value for all methods which are internally used for comparion. - * If you want to use different tolarance values for different parts of the geometry, feel free to use - * the other comparison methods and write your own implementation of Equal. - * @param rightHandSide Compare this against leftHandSide. - * @param leftHandSide Compare this against rightHandSide. - * @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 comparison are true. False in any other case. - */ - DEPRECATED( MITK_CORE_EXPORT bool Equal(const mitk::Geometry3D* leftHandSide, const mitk::Geometry3D* rightHandSide, ScalarType eps, bool verbose)); - - /** - * @brief Equal A function comparing two geometries for beeing identical. - * - * @ingroup MITKTestingAPI - * - * The function compares the spacing, origin, axisvectors, extents, the matrix of the - * IndexToWorldTransform (elementwise), the bounding (elementwise) and the ImageGeometry flag. - * - * The parameter eps is a tolarence value for all methods which are internally used for comparion. - * If you want to use different tolarance values for different parts of the geometry, feel free to use - * the other comparison methods and write your own implementation of Equal. - * @param rightHandSide Compare this against leftHandSide. - * @param leftHandSide Compare this against rightHandSide. - * @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 comparison are true. False in any other case. - */ - MITK_CORE_EXPORT bool Equal(const mitk::Geometry3D& leftHandSide, const mitk::Geometry3D& rightHandSide, ScalarType eps, bool verbose); - - /** - * @brief Equal A function comparing two transforms (TransformType) for beeing identical. - * @warning This method is deprecated and will not be available in the future. Use the \a bool mitk::Equal(const mitk::mitk::Geometry3D::TransformType& t1, const mitk::Geometry3D::TransformType& t2) instead. - * - * @ingroup MITKTestingAPI - * - * The function compares the IndexToWorldTransform (elementwise). - * - * The parameter eps is a tolarence value for all methods which are internally used for comparion. - * @param rightHandSide Compare this against leftHandSide. - * @param leftHandSide Compare this against rightHandSide. - * @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 comparison are true. False in any other case. - */ - DEPRECATED( MITK_CORE_EXPORT bool Equal(const mitk::Geometry3D::TransformType *leftHandSide, const mitk::Geometry3D::TransformType *rightHandSide, ScalarType eps, bool verbose)); - - /** - * @brief Equal A function comparing two transforms (TransformType) for beeing identical. - * - * @ingroup MITKTestingAPI - * - * The function compares the IndexToWorldTransform (elementwise). - * - * The parameter eps is a tolarence value for all methods which are internally used for comparion. - * @param rightHandSide Compare this against leftHandSide. - * @param leftHandSide Compare this against rightHandSide. - * @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 comparison are true. False in any other case. - */ - MITK_CORE_EXPORT bool Equal(const mitk::Geometry3D::TransformType& leftHandSide, const mitk::Geometry3D::TransformType& rightHandSide, ScalarType eps, bool verbose); - - /** - * @brief Equal A function comparing two bounding boxes (BoundingBoxType) for beeing identical. - * @warning This method is deprecated and will not be available in the future. Use the \a bool mitk::Equal(const mitk::mitk::Geometry3D::BoundingBoxType& b1, const mitk::Geometry3D::BoundingBoxType& b2) instead. - * - * @ingroup MITKTestingAPI - * - * The function compares the bounds (elementwise). - * - * The parameter eps is a tolarence value for all methods which are internally used for comparion. - * @param rightHandSide Compare this against leftHandSide. - * @param leftHandSide Compare this against rightHandSide. - * @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 comparison are true. False in any other case. - */ - DEPRECATED( MITK_CORE_EXPORT bool Equal( const mitk::Geometry3D::BoundingBoxType *leftHandSide, const mitk::Geometry3D::BoundingBoxType *rightHandSide, ScalarType eps, bool verbose)); + protected: + Geometry3D(); + Geometry3D(const Geometry3D& other); - /** - * @brief Equal A function comparing two bounding boxes (BoundingBoxType) for beeing identical. - * - * @ingroup MITKTestingAPI - * - * The function compares the bounds (elementwise). - * - * The parameter eps is a tolarence value for all methods which are internally used for comparion. - * @param rightHandSide Compare this against leftHandSide. - * @param leftHandSide Compare this against rightHandSide. - * @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 comparison are true. False in any other case. - */ - MITK_CORE_EXPORT bool Equal( const mitk::Geometry3D::BoundingBoxType& leftHandSide, const mitk::Geometry3D::BoundingBoxType& rightHandSide, ScalarType eps, bool verbose); + virtual ~Geometry3D(); + }; } // namespace mitk #endif /* GEOMETRY3D_H_HEADER_INCLUDED_C1EBD0AD */ diff --git a/Core/Code/DataManagement/mitkITKImageImport.h b/Core/Code/DataManagement/mitkITKImageImport.h index c6176af868..f9b3d7f9a5 100644 --- a/Core/Code/DataManagement/mitkITKImageImport.h +++ b/Core/Code/DataManagement/mitkITKImageImport.h @@ -1,195 +1,195 @@ /*=================================================================== 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 MITKITKIMAGEIMPORT_H_HEADER_INCLUDED_C1E4861D #define MITKITKIMAGEIMPORT_H_HEADER_INCLUDED_C1E4861D #include #include "mitkImageSource.h" #include "itkImageToImageFilterDetail.h" namespace mitk { /** * @brief Pipelined import of itk::Image * * The image data contained in the itk::Image is referenced, * not copied. * The easiest way of use is by the function * mitk::ImportItkImage * \code * mitkImage = mitk::ImportItkImage(itkImage); * \endcode * \sa ImportItkImage * @ingroup Adaptor */ template class MITK_EXPORT ITKImageImport : public ImageSource { public: mitkClassMacro(ITKImageImport,ImageSource) itkFactorylessNewMacro(Self) itkCloneMacro(Self) /// \brief The type of the input image. typedef TInputImage InputImageType; typedef typename InputImageType::Pointer InputImagePointer; typedef typename InputImageType::ConstPointer InputImageConstPointer; typedef typename InputImageType::RegionType InputImageRegionType; typedef typename InputImageType::PixelType InputImagePixelType; /** ImageDimension constants */ itkStaticConstMacro(InputImageDimension, unsigned int, TInputImage::ImageDimension); itkStaticConstMacro(RegionDimension, unsigned int, mitk::SlicedData::RegionDimension); /** \brief Set the input itk::Image of this image importer. */ InputImageType * GetInput(void); /** \brief Set the input itk::Image of this image importer. */ void SetInput(const InputImageType*); using itk::ProcessObject::SetInput; /** * \brief Set the Geometry of the result image (optional) * * The Geometry has to fit the dimension and size of * the input image. The Geometry will be cloned, not * referenced! * * Providing the Geometry is optional. * The default behavior is to set the geometry by * the itk::Image::GetDirection() information. */ - void SetGeometry(const Geometry3D* geometry); + void SetGeometry(const BaseGeometry* geometry); protected: ITKImageImport(); virtual ~ITKImageImport(); virtual void GenerateOutputInformation(); virtual void GenerateInputRequestedRegion(); virtual void GenerateData(); virtual void SetNthOutput(DataObjectPointerArraySizeType num, itk::DataObject *output); /** Typedef for the region copier function object that converts an * output region to an input region. */ typedef itk::ImageToImageFilterDetail::ImageRegionCopier OutputToInputRegionCopierType; - Geometry3D::Pointer m_Geometry; + BaseGeometry::Pointer m_Geometry; }; /** * @brief Imports an itk::Image (with a specific type) as an mitk::Image. * @ingroup Adaptor * * Instantiates instance of ITKImageImport. * mitk::ITKImageImport does not cast pixel types etc., it just imports * image data. If you get a compile error, try image.GetPointer(). * * \param update: if \a true, fill mitk::Image, which will execute the * up-stream pipeline connected to the input itk::Image. Otherwise you * need to make sure that Update() is called on the mitk::Image before * its data is being used, e.g., by connecting it to an mitk-pipeline * and call Update of a downstream filter at some time. * \sa itk::Image::CastToMitkImage */ template -Image::Pointer ImportItkImage(const itk::SmartPointer& itkimage, const Geometry3D* geometry = NULL, bool update = true); +Image::Pointer ImportItkImage(const itk::SmartPointer& itkimage, const BaseGeometry* geometry = NULL, bool update = true); /** * @brief Imports an itk::Image (with a specific type) as an mitk::Image. * @ingroup Adaptor * * Instantiates instance of ITKImageImport * mitk::ITKImageImport does not cast pixel types etc., it just imports * image data. If you get a compile error, try image.GetPointer(). * * \param update: if \a true, fill mitk::Image, which will execute the * up-stream pipeline connected to the input itk::Image. Otherwise you * need to make sure that Update() is called on the mitk::Image before * its data is being used, e.g., by connecting it to an mitk-pipeline * and call Update of a downstream filter at some time. * * * \note If the source (itk image) and the target (mitk image) do not share the same scope, the mitk::GrabItkImageMemory function * has to be used instead. Otherwise the image memory managed by the itk image is lost at a scope level change. This affects especially the * usage in combination with AccessByItk macros as in following example code * * \snippet Testing/mitkGrabItkImageMemoryTest.cpp OutOfScopeCall * * which calls an ITK-like filter * * \snippet Testing/mitkGrabItkImageMemoryTest.cpp ItkThresholdFilter * * * \sa itk::Image::CastToMitkImage * \sa GrabItkImageMemory */ template -Image::Pointer ImportItkImage(const ItkOutputImageType* itkimage, const Geometry3D* geometry = NULL, bool update = true); +Image::Pointer ImportItkImage(const ItkOutputImageType* itkimage, const BaseGeometry* geometry = NULL, bool update = true); /** * @brief Grabs the memory of an itk::Image (with a specific type) * and puts it into an mitk::Image. * @ingroup Adaptor * * The memory is managed by the mitk::Image after calling this * function. The itk::Image remains valid until the mitk::Image * decides to free the memory. * \param update: if \a true, fill mitk::Image, which will execute the * up-stream pipeline connected to the input itk::Image. Otherwise you * need to make sure that Update() is called on the mitk::Image before * its data is being used, e.g., by connecting it to an mitk-pipeline * and call Update of a downstream filter at some time. * \sa ImportItkImage */ template -Image::Pointer GrabItkImageMemory(itk::SmartPointer& itkimage, mitk::Image* mitkImage = NULL, const Geometry3D* geometry = NULL, bool update = true); +Image::Pointer GrabItkImageMemory(itk::SmartPointer& itkimage, mitk::Image* mitkImage = NULL, const BaseGeometry* geometry = NULL, bool update = true); /** * @brief Grabs the memory of an itk::Image (with a specific type) * and puts it into an mitk::Image. * @ingroup Adaptor * * The memory is managed by the mitk::Image after calling this * function. The itk::Image remains valid until the mitk::Image * decides to free the memory. * \param update: if \a true, fill mitk::Image, which will execute the * up-stream pipeline connected to the input itk::Image. Otherwise you * need to make sure that Update() is called on the mitk::Image before * its data is being used, e.g., by connecting it to an mitk-pipeline * and call Update of a downstream filter at some time. * \sa ImportItkImage */ template -Image::Pointer GrabItkImageMemory(ItkOutputImageType* itkimage, mitk::Image* mitkImage = NULL, const Geometry3D* geometry = NULL, bool update = true); +Image::Pointer GrabItkImageMemory(ItkOutputImageType* itkimage, mitk::Image* mitkImage = NULL, const BaseGeometry* geometry = NULL, bool update = true); } // namespace mitk #ifndef MITK_MANUAL_INSTANTIATION #include "mitkITKImageImport.txx" #endif #endif /* MITKITKIMAGEIMPORT_H_HEADER_INCLUDED_C1E4861D */ diff --git a/Core/Code/DataManagement/mitkITKImageImport.txx b/Core/Code/DataManagement/mitkITKImageImport.txx index 4123b34f20..5d7867115f 100644 --- a/Core/Code/DataManagement/mitkITKImageImport.txx +++ b/Core/Code/DataManagement/mitkITKImageImport.txx @@ -1,177 +1,177 @@ /*=================================================================== 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 __mitkITKImageImport_txx #define __mitkITKImageImport_txx #include "mitkITKImageImport.h" template mitk::ITKImageImport::ITKImageImport() { } template mitk::ITKImageImport::~ITKImageImport() { } template typename mitk::ITKImageImport::InputImageType * mitk::ITKImageImport::GetInput(void) { return static_cast( this->ProcessObject::GetInput(0)); } template void mitk::ITKImageImport::SetInput(const InputImageType* input) { this->ProcessObject::SetNthInput(0, const_cast(input) ); } template -void mitk::ITKImageImport::SetGeometry(const Geometry3D* geometry) +void mitk::ITKImageImport::SetGeometry(const BaseGeometry* geometry) { if(geometry != NULL) { - m_Geometry = static_cast(geometry->Clone().GetPointer()); + m_Geometry = static_cast(geometry->Clone().GetPointer()); } else { m_Geometry = NULL; } Modified(); } template void mitk::ITKImageImport::GenerateOutputInformation() { InputImageConstPointer input = this->GetInput(); mitk::Image::Pointer output = this->GetOutput(); itkDebugMacro(<<"GenerateOutputInformation()"); output->InitializeByItk(input.GetPointer()); if(m_Geometry.IsNotNull()) { output->SetGeometry(m_Geometry); } } template void mitk::ITKImageImport::GenerateData() { InputImageConstPointer input = this->GetInput(); mitk::Image::Pointer output = this->GetOutput(); output->SetImportChannel((void*)input->GetBufferPointer(), 0, mitk::Image::ReferenceMemory); } template void mitk::ITKImageImport::GenerateInputRequestedRegion() { Superclass::GenerateInputRequestedRegion(); // Input is an image, cast away the constness so we can set // the requested region. InputImagePointer input = const_cast< TInputImage * > ( this->GetInput() ); // Use the function object RegionCopier to copy the output region // to the input. The default region copier has default implementations // to handle the cases where the input and output are the same // dimension, the input a higher dimension than the output, and the // input a lower dimension than the output. InputImageRegionType inputRegion; OutputToInputRegionCopierType regionCopier; regionCopier(inputRegion, this->GetOutput()->GetRequestedRegion()); input->SetRequestedRegion( inputRegion ); } template void mitk::ITKImageImport::SetNthOutput(DataObjectPointerArraySizeType idx, itk::DataObject *output) { if((output == NULL) && (idx == 0)) { // we are disconnected from our output: // copy buffer of input to output, because we // cannot guarantee that the input (to which our // output is refering) will stay alive. InputImageConstPointer input = this->GetInput(); mitk::Image::Pointer currentOutput = this->GetOutput(); if(input.IsNotNull() && currentOutput.IsNotNull()) currentOutput->SetChannel(input->GetBufferPointer()); } Superclass::SetNthOutput(idx, output); } template -mitk::Image::Pointer mitk::ImportItkImage(const itk::SmartPointer& itkimage, const Geometry3D* geometry, bool update) +mitk::Image::Pointer mitk::ImportItkImage(const itk::SmartPointer& itkimage, const BaseGeometry* geometry, bool update) { typename mitk::ITKImageImport::Pointer importer = mitk::ITKImageImport::New(); importer->SetInput(itkimage); importer->SetGeometry(geometry); if(update) importer->Update(); return importer->GetOutput(); } template -mitk::Image::Pointer mitk::ImportItkImage(const ItkOutputImageType* itkimage, const Geometry3D* geometry, bool update) +mitk::Image::Pointer mitk::ImportItkImage(const ItkOutputImageType* itkimage, const BaseGeometry* geometry, bool update) { typename mitk::ITKImageImport::Pointer importer = mitk::ITKImageImport::New(); importer->SetInput(itkimage); importer->SetGeometry(geometry); if(update) importer->Update(); return importer->GetOutput(); } template -mitk::Image::Pointer mitk::GrabItkImageMemory(itk::SmartPointer& itkimage, mitk::Image* mitkImage, const Geometry3D* geometry, bool update) +mitk::Image::Pointer mitk::GrabItkImageMemory(itk::SmartPointer& itkimage, mitk::Image* mitkImage, const BaseGeometry* geometry, bool update) { return GrabItkImageMemory( itkimage.GetPointer(), mitkImage, geometry, update ); } template -mitk::Image::Pointer mitk::GrabItkImageMemory(ItkOutputImageType* itkimage, mitk::Image* mitkImage, const Geometry3D* geometry, bool update) +mitk::Image::Pointer mitk::GrabItkImageMemory(ItkOutputImageType* itkimage, mitk::Image* mitkImage, const BaseGeometry* geometry, bool update) { if(update) itkimage->Update(); mitk::Image::Pointer resultImage; if(mitkImage != NULL) { resultImage = mitkImage; } else { resultImage = mitk::Image::New(); } resultImage->InitializeByItk( itkimage ); resultImage->SetImportVolume( itkimage->GetBufferPointer(), 0, 0, Image::ManageMemory ); itkimage->GetPixelContainer()->ContainerManageMemoryOff(); if(geometry != NULL) - resultImage->SetGeometry(static_cast(geometry->Clone().GetPointer())); + resultImage->SetGeometry(static_cast(geometry->Clone().GetPointer())); return resultImage; } #endif //__mitkITKImageImport_txx diff --git a/Core/Code/DataManagement/mitkImage.cpp b/Core/Code/DataManagement/mitkImage.cpp index bf86a0dad7..a7a10c1487 100644 --- a/Core/Code/DataManagement/mitkImage.cpp +++ b/Core/Code/DataManagement/mitkImage.cpp @@ -1,1383 +1,1383 @@ /*=================================================================== 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]}; + const mitk::Vector3D vspacing = (geom3d->GetSpacing()); + double dspacing[3] = {vspacing[0],vspacing[1],vspacing[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::Geometry3D& geometry, unsigned int channels, int tDim ) +void mitk::Image::Initialize(const mitk::PixelType& type, const mitk::BaseGeometry& geometry, unsigned int channels, int tDim ) { mitk::ProportionalTimeGeometry::Pointer timeGeometry = ProportionalTimeGeometry::New(); - Geometry3D::Pointer geometry3D = geometry.Clone(); - timeGeometry->Initialize(geometry3D.GetPointer(), tDim); + itk::LightObject::Pointer lopointer = geometry.Clone(); + timeGeometry->Initialize(dynamic_cast(lopointer.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(Geometry3D* aGeometry3D) +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::Geometry3D* geo = this->GetGeometry(); + 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* leftHandSide, const mitk::Image* rightHandSide, ScalarType eps, bool verbose) { if((leftHandSide == NULL) || (rightHandSide == NULL)) { MITK_ERROR << "mitk::Equal(const mitk::Image* leftHandSide, const mitk::Image* rightHandSide, ScalarType eps, bool verbose) does not work with NULL pointer input."; return false; } return mitk::Equal( *leftHandSide, *rightHandSide, eps, verbose); } bool mitk::Equal(const mitk::Image& leftHandSide, const mitk::Image& rightHandSide, ScalarType eps, bool verbose) { bool returnValue = true; // Dimensionality if( rightHandSide.GetDimension() != leftHandSide.GetDimension() ) { if(verbose) { MITK_INFO << "[( Image )] Dimensionality differs."; MITK_INFO << "leftHandSide is " << leftHandSide.GetDimension() << "rightHandSide is " << rightHandSide.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 << "leftHandSide->GetDimension("<GetDimension("<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 06e8bbbe4a..3f16d79702 100644 --- a/Core/Code/DataManagement/mitkImage.h +++ b/Core/Code/DataManagement/mitkImage.h @@ -1,735 +1,735 @@ /*=================================================================== 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); itkFactorylessNewMacro(Self) itkCloneMacro(Self) /** 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::Geometry3D& geometry, unsigned int channels = 1, int tDim=1); + 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(Geometry3D* aGeometry3D); + 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: mitkCloneMacro(Self); 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 * @warning This method is deprecated and will not be available in the future. Use the \a bool mitk::Equal(const mitk::Image& i1, const mitk::Image& i2) instead. * * @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 */ DEPRECATED (MITK_CORE_EXPORT bool Equal( const mitk::Image* leftHandSide, const mitk::Image* rightHandSide, ScalarType eps, bool verbose )); /** * @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/mitkImagePixelAccessor.h b/Core/Code/DataManagement/mitkImagePixelAccessor.h index 52895dc5bb..5bfea1638e 100644 --- a/Core/Code/DataManagement/mitkImagePixelAccessor.h +++ b/Core/Code/DataManagement/mitkImagePixelAccessor.h @@ -1,117 +1,117 @@ /*=================================================================== 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 MITKIMAGEPIXELACCESSOR_H #define MITKIMAGEPIXELACCESSOR_H #include #include #include #include #include #include "mitkImageAccessorBase.h" #include "mitkImageDataItem.h" #include "mitkPixelType.h" #include "mitkImage.h" namespace mitk { class Image; //##Documentation //## @brief Provides templated image access for all inheriting classes //## @tparam TPixel defines the PixelType //## @tparam VDimension defines the dimension for accessing data //## @ingroup Data template class ImagePixelAccessor { friend class Image; public: typedef itk::Index IndexType; typedef ImagePixelAccessor ImagePixelAccessorType; /** Get Dimensions from ImageDataItem */ int GetDimension (int i) const { return m_ImageDataItem->GetDimension(i); } protected: /** \param ImageDataItem* specifies the allocated image part */ ImagePixelAccessor(mitk::Image::Pointer iP, mitk::ImageDataItem* iDI) : m_ImageDataItem(iDI) { if(iDI == NULL) m_ImageDataItem = iP->GetChannelData(); } /** Destructor */ virtual ~ImagePixelAccessor() { } protected: // protected members /** Holds the specified ImageDataItem */ ImageDataItem* m_ImageDataItem; /** \brief Pointer to the used Geometry. * Since Geometry can be different to the Image (if memory was forced to be coherent) it is necessary to store Geometry separately. */ - Geometry3D::Pointer m_Geometry; + BaseGeometry::Pointer m_Geometry; /** \brief A Subregion defines an arbitrary area within the image. * If no SubRegion is defined, the whole ImageDataItem or Image is regarded. * A subregion (e.g. subvolume) can lead to non-coherent memory access where every dimension has a start- and end-offset. */ itk::ImageRegion* m_SubRegion; /** \brief Stores all extended properties of an ImageAccessor. * The different flags in mitk::ImageAccessorBase::Options can be unified by bitwise operations. */ int m_Options; /** Get memory offset for a given image index */ unsigned int GetOffset(const IndexType & idx) const { const unsigned int * imageDims = m_ImageDataItem->m_Dimensions; unsigned int offset = 0; switch(VDimension) { case 4: offset += idx[3]*imageDims[0]*imageDims[1]*imageDims[2]; case 3: offset += idx[2]*imageDims[0]*imageDims[1]; case 2: offset += idx[0] + idx[1]*imageDims[0]; break; } return offset; } private: }; } #endif // MITKIMAGEACCESSOR_H diff --git a/Core/Code/DataManagement/mitkLandmarkBasedCurvedGeometry.cpp b/Core/Code/DataManagement/mitkLandmarkBasedCurvedGeometry.cpp deleted file mode 100644 index 9006e05040..0000000000 --- a/Core/Code/DataManagement/mitkLandmarkBasedCurvedGeometry.cpp +++ /dev/null @@ -1,35 +0,0 @@ -/*=================================================================== - -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 "mitkLandmarkBasedCurvedGeometry.h" -#include - -mitk::LandmarkBasedCurvedGeometry::LandmarkBasedCurvedGeometry() - : m_TargetLandmarks(NULL) -{ -} - -mitk::LandmarkBasedCurvedGeometry::LandmarkBasedCurvedGeometry(const LandmarkBasedCurvedGeometry& other) - : Superclass(other) -{ - SetTargetLandmarks(other.m_TargetLandmarks); -} - -mitk::LandmarkBasedCurvedGeometry::~LandmarkBasedCurvedGeometry() -{ - -} diff --git a/Core/Code/DataManagement/mitkLandmarkBasedCurvedGeometry.h b/Core/Code/DataManagement/mitkLandmarkBasedCurvedGeometry.h deleted file mode 100644 index f7363d421f..0000000000 --- a/Core/Code/DataManagement/mitkLandmarkBasedCurvedGeometry.h +++ /dev/null @@ -1,60 +0,0 @@ -/*=================================================================== - -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 MITKLANDMARKBASEDCURVEDGEOMETRY_H_HEADER_INCLUDED_C1C68A2C -#define MITKLANDMARKBASEDCURVEDGEOMETRY_H_HEADER_INCLUDED_C1C68A2C - -#include "mitkAbstractTransformGeometry.h" -#include "mitkPointSet.h" - -namespace mitk { - -//##Documentation -//## @brief Superclass of AbstractTransformGeometry sub-classes defined -//## by a set of landmarks. -//## -//## @ingroup Geometry -class MITK_CORE_EXPORT LandmarkBasedCurvedGeometry : public AbstractTransformGeometry -{ -public: - mitkClassMacro(LandmarkBasedCurvedGeometry, AbstractTransformGeometry); - - //##Documentation - //## @brief Set the landmarks through which the geometry shall pass - itkSetConstObjectMacro(TargetLandmarks, mitk::PointSet::DataType::PointsContainer); - //##Documentation - //## @brief Get the landmarks through which the geometry shall pass - itkGetConstObjectMacro(TargetLandmarks, mitk::PointSet::DataType::PointsContainer); - - virtual void ComputeGeometry() = 0; - - virtual itk::LightObject::Pointer InternalClone() const = 0; - -protected: - LandmarkBasedCurvedGeometry(); - - LandmarkBasedCurvedGeometry(const LandmarkBasedCurvedGeometry& other); - - virtual ~LandmarkBasedCurvedGeometry(); - - mitk::PointSet::DataType::PointsContainer::ConstPointer m_TargetLandmarks; - -}; - -} // namespace mitk - -#endif diff --git a/Core/Code/DataManagement/mitkLandmarkProjector.h b/Core/Code/DataManagement/mitkLandmarkProjector.h index fbb0be163f..0637a83da2 100644 --- a/Core/Code/DataManagement/mitkLandmarkProjector.h +++ b/Core/Code/DataManagement/mitkLandmarkProjector.h @@ -1,117 +1,117 @@ /*=================================================================== 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 MITKLANDMARKPROJECTOR_H_HEADER_INCLUDED_C1C68A2C #define MITKLANDMARKPROJECTOR_H_HEADER_INCLUDED_C1C68A2C #include #include "itkObject.h" #include "mitkPlaneGeometry.h" #include "mitkPointSet.h" class vtkAbstractTransform; namespace mitk { //##Documentation //## @brief Base-class of landmark-projectors, which project the target landmarks //## to create source landmarks. //## //## @ingroup Geometry class MITK_CORE_EXPORT LandmarkProjector : public itk::Object { public: mitkClassMacro(LandmarkProjector, itk::Object); //##Documentation //## @brief Set the interpolating (world-space-to-world-space) transform, //## which uses the landmarks. //## //## \sa GetCompleteAbstractTransform virtual void SetInterpolatingAbstractTransform(vtkAbstractTransform* anInterpolatingAbstractTransform); //##Documentation //## @brief Get the interpolating (world-space-to-world-space) transform, //## which uses the landmarks. //## //## \sa GetCompleteAbstractTransform //## \sa ComputeCompleteAbstractTransform itkGetConstMacro(InterpolatingAbstractTransform, vtkAbstractTransform*); //##Documentation //## @brief Set frame geometry within which the interpolation shall occur. //## //## Used as a hint, may be ignored depending on the concrete sub-classes. - itkSetConstObjectMacro(FrameGeometry, mitk::Geometry3D); + itkSetConstObjectMacro(FrameGeometry, mitk::BaseGeometry); //##Documentation //## @brief Get frame geometry within which the interpolation shall occur. //## //## Used as a hint, may be ignored depending on the concrete sub-classes. - itkGetConstObjectMacro(FrameGeometry, mitk::Geometry3D); + itkGetConstObjectMacro(FrameGeometry, mitk::BaseGeometry); //##Documentation //## @brief Get the parameter plane for use in AbstractTransformGeometry::SetPlane. //## itkGetConstObjectMacro(ParameterPlane, mitk::PlaneGeometry); //##Documentation //## @brief Get the projected landmarks. //## //## @note Valid only after calling ProjectLandmarks. itkGetConstObjectMacro(ProjectedLandmarks, mitk::PointSet::DataType::PointsContainer); //##Documentation //## @brief Get the final target landmarks to use for the interpolating transform. //## //## @note Valid only after calling ProjectLandmarks. itkGetConstObjectMacro(FinalTargetLandmarks, mitk::PointSet::DataType::PointsContainer); //##Documentation //## @brief Get the transform from parameter space to world space incorporating //## the given interpolating transform, which uses the landmarks. //## //## \sa ComputeCompleteAbstractTransform //## \sa SetInterpolatingAbstractTransform virtual vtkAbstractTransform* GetCompleteAbstractTransform() const; virtual void ProjectLandmarks(const mitk::PointSet::DataType::PointsContainer* targetLandmarks) = 0; protected: LandmarkProjector(); virtual ~LandmarkProjector(); //##Documentation //## @brief Compute the transform from parameter space to world space incorporating //## the given interpolating transform, which uses the landmarks. //## //## Called after a new interpolating transform is set via //## SetInterpolatingAbstractTransform(). //## \sa SetInterpolatingAbstractTransform //## \sa GetCompleteAbstractTransform virtual void ComputeCompleteAbstractTransform() = 0; vtkAbstractTransform* m_InterpolatingAbstractTransform; vtkAbstractTransform* m_CompleteAbstractTransform; - mitk::Geometry3D::ConstPointer m_FrameGeometry; + mitk::BaseGeometry::ConstPointer m_FrameGeometry; mutable mitk::PlaneGeometry::ConstPointer m_ParameterPlane; mitk::PointSet::DataType::PointsContainer::Pointer m_WritableFinalTargetLandmarks; mitk::PointSet::DataType::PointsContainer::ConstPointer m_FinalTargetLandmarks; mitk::PointSet::DataType::PointsContainer::Pointer m_ProjectedLandmarks; }; } // namespace mitk #endif /* MITKLANDMARKPROJECTOR_H_HEADER_INCLUDED_C1C68A2C */ diff --git a/Core/Code/DataManagement/mitkLandmarkProjectorBasedCurvedGeometry.cpp b/Core/Code/DataManagement/mitkLandmarkProjectorBasedCurvedGeometry.cpp index c83f37ee82..9e8ac72940 100644 --- a/Core/Code/DataManagement/mitkLandmarkProjectorBasedCurvedGeometry.cpp +++ b/Core/Code/DataManagement/mitkLandmarkProjectorBasedCurvedGeometry.cpp @@ -1,82 +1,83 @@ /*=================================================================== 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 "mitkLandmarkProjectorBasedCurvedGeometry.h" #include mitk::LandmarkProjectorBasedCurvedGeometry::LandmarkProjectorBasedCurvedGeometry() - : m_LandmarkProjector(NULL), m_InterpolatingAbstractTransform(NULL) + : m_LandmarkProjector(NULL), m_InterpolatingAbstractTransform(NULL), m_TargetLandmarks(NULL) { } mitk::LandmarkProjectorBasedCurvedGeometry::LandmarkProjectorBasedCurvedGeometry(const mitk::LandmarkProjectorBasedCurvedGeometry& other) : Superclass(other) { + SetTargetLandmarks(other.m_TargetLandmarks); this->SetLandmarkProjector(other.m_LandmarkProjector); this->ComputeGeometry(); } mitk::LandmarkProjectorBasedCurvedGeometry::~LandmarkProjectorBasedCurvedGeometry() { if(m_InterpolatingAbstractTransform!=NULL) m_InterpolatingAbstractTransform->Delete(); } void mitk::LandmarkProjectorBasedCurvedGeometry::SetLandmarkProjector(mitk::LandmarkProjector* aLandmarkProjector) { itkDebugMacro("setting LandmarkProjector to " << aLandmarkProjector ); if(m_LandmarkProjector != aLandmarkProjector) { m_LandmarkProjector = aLandmarkProjector; if(m_LandmarkProjector.IsNotNull()) { if(m_FrameGeometry.IsNotNull()) m_LandmarkProjector->SetFrameGeometry(m_FrameGeometry); if(m_InterpolatingAbstractTransform == NULL) { itkWarningMacro(<<"m_InterpolatingAbstractTransform not set."); } m_LandmarkProjector->SetInterpolatingAbstractTransform(GetInterpolatingAbstractTransform()); SetVtkAbstractTransform(m_LandmarkProjector->GetCompleteAbstractTransform()); } Modified(); } } -void mitk::LandmarkProjectorBasedCurvedGeometry::SetFrameGeometry(const mitk::Geometry3D* frameGeometry) +void mitk::LandmarkProjectorBasedCurvedGeometry::SetFrameGeometry(const mitk::BaseGeometry* frameGeometry) { Superclass::SetFrameGeometry(frameGeometry); if(m_LandmarkProjector.IsNotNull()) m_LandmarkProjector->SetFrameGeometry(frameGeometry); } void mitk::LandmarkProjectorBasedCurvedGeometry::ComputeGeometry() { if(m_LandmarkProjector.IsNull()) { itkExceptionMacro(<< "m_LandmarkProjector is not set."); } m_LandmarkProjector->ProjectLandmarks(m_TargetLandmarks); SetPlane(m_LandmarkProjector->GetParameterPlane()); } itk::LightObject::Pointer mitk::LandmarkProjectorBasedCurvedGeometry::InternalClone() const { - mitk::Geometry3D::Pointer newGeometry = new LandmarkProjectorBasedCurvedGeometry(*this); + mitk::BaseGeometry::Pointer newGeometry = new LandmarkProjectorBasedCurvedGeometry(*this); newGeometry->UnRegister(); return newGeometry.GetPointer(); } diff --git a/Core/Code/DataManagement/mitkLandmarkProjectorBasedCurvedGeometry.h b/Core/Code/DataManagement/mitkLandmarkProjectorBasedCurvedGeometry.h index b3b94d60c9..d2b5217475 100644 --- a/Core/Code/DataManagement/mitkLandmarkProjectorBasedCurvedGeometry.h +++ b/Core/Code/DataManagement/mitkLandmarkProjectorBasedCurvedGeometry.h @@ -1,61 +1,72 @@ /*=================================================================== 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 MITKLANDMARKPROJECTORBASEDCURVEDGEOMETRY_H_HEADER_INCLUDED_C1C68A2C #define MITKLANDMARKPROJECTORBASEDCURVEDGEOMETRY_H_HEADER_INCLUDED_C1C68A2C -#include "mitkLandmarkBasedCurvedGeometry.h" #include "mitkLandmarkProjector.h" +#include "mitkAbstractTransformGeometry.h" +#include "mitkPointSet.h" + namespace mitk { //##Documentation //## @brief Superclass of AbstractTransformGeometry sub-classes defined //## by a set of landmarks. //## //## @ingroup Geometry -class MITK_CORE_EXPORT LandmarkProjectorBasedCurvedGeometry : public LandmarkBasedCurvedGeometry +class MITK_CORE_EXPORT LandmarkProjectorBasedCurvedGeometry : public AbstractTransformGeometry { public: - mitkClassMacro(LandmarkProjectorBasedCurvedGeometry, LandmarkBasedCurvedGeometry); + mitkClassMacro(LandmarkProjectorBasedCurvedGeometry, AbstractTransformGeometry); void SetLandmarkProjector(mitk::LandmarkProjector* aLandmarkProjector); itkGetConstObjectMacro(LandmarkProjector, mitk::LandmarkProjector); - virtual void SetFrameGeometry(const mitk::Geometry3D* frameGeometry); + virtual void SetFrameGeometry(const mitk::BaseGeometry* frameGeometry); virtual void ComputeGeometry(); itkGetConstMacro(InterpolatingAbstractTransform, vtkAbstractTransform*); itk::LightObject::Pointer InternalClone() const; + //##Documentation + //## @brief Set the landmarks through which the geometry shall pass + itkSetConstObjectMacro(TargetLandmarks, mitk::PointSet::DataType::PointsContainer); + //##Documentation + //## @brief Get the landmarks through which the geometry shall pass + itkGetConstObjectMacro(TargetLandmarks, mitk::PointSet::DataType::PointsContainer); + protected: LandmarkProjectorBasedCurvedGeometry(); LandmarkProjectorBasedCurvedGeometry(const LandmarkProjectorBasedCurvedGeometry& other); virtual ~LandmarkProjectorBasedCurvedGeometry(); mitk::LandmarkProjector::Pointer m_LandmarkProjector; vtkAbstractTransform* m_InterpolatingAbstractTransform; + + mitk::PointSet::DataType::PointsContainer::ConstPointer m_TargetLandmarks; }; } // namespace mitk #endif /* MITKLANDMARKPROJECTORBASEDCURVEDGEOMETRY_H_HEADER_INCLUDED_C1C68A2C */ diff --git a/Core/Code/DataManagement/mitkMatrixConvert.h b/Core/Code/DataManagement/mitkMatrixConvert.h index e7ff867d3d..b549a10fbc 100644 --- a/Core/Code/DataManagement/mitkMatrixConvert.h +++ b/Core/Code/DataManagement/mitkMatrixConvert.h @@ -1,151 +1,152 @@ /*=================================================================== 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 MITKMATRIXCONVERT_H_HEADER_INCLUDED_C1EBD0AD #define MITKMATRIXCONVERT_H_HEADER_INCLUDED_C1EBD0AD #include "mitkGeometry3D.h" +#include "mitkBaseGeometry.h" #include "mitkItkMatrixHack.h" #include namespace mitk { template void TransferVtkMatrixToItkTransform(const vtkMatrix4x4* vtkmatrix, TTransformType * itkTransform) { if(itkTransform==NULL) return; typename TTransformType::MatrixType::InternalMatrixType& vnlMatrix = const_cast(itkTransform->GetMatrix().GetVnlMatrix()); for ( int i=0; i < 3; ++i) for( int j=0; j < 3; ++j ) vnlMatrix[i][j] = vtkmatrix->GetElement( i, j ); // *This* ensures m_MatrixMTime.Modified(), which is therewith not equal to // m_InverseMatrixMTime, thus a new inverse will be calculated (when // requested). static_cast*>(itkTransform)->MatrixChanged(); typename TTransformType::OffsetType offset; offset[0] = vtkmatrix->GetElement( 0, 3 ); offset[1] = vtkmatrix->GetElement( 1, 3 ); offset[2] = vtkmatrix->GetElement( 2, 3 ); itkTransform->SetOffset( offset ); } template void TransferItkTransformToVtkMatrix(const TTransformType * itkTransform, vtkMatrix4x4* vtkmatrix) { int i,j; for(i=0;i<3;++i) for(j=0;j<3;++j) vtkmatrix->SetElement(i, j, itkTransform->GetMatrix().GetVnlMatrix().get(i, j)); for(i=0;i<3;++i) vtkmatrix->SetElement(i, 3, itkTransform->GetOffset()[i]); for(i=0;i<3;++i) vtkmatrix->SetElement(3, i, 0.0); vtkmatrix->SetElement(3, 3, 1); } template void ConvertItkTransform(const TTransformType1* sourceTransform, TTransformType2* destTransform) { if((sourceTransform==NULL) || (destTransform==NULL)) return; // transfer offset const typename TTransformType1::OutputVectorType& sourceOffset = sourceTransform->GetOffset(); typename TTransformType2::OutputVectorType offset; offset[0] = sourceOffset[0]; offset[1] = sourceOffset[1]; offset[2] = sourceOffset[2]; destTransform->SetOffset( offset ); typename TTransformType1::MatrixType::InternalMatrixType& sourceVnlMatrix = const_cast(sourceTransform->GetMatrix().GetVnlMatrix()); //transfer matrix typename TTransformType2::MatrixType::InternalMatrixType& destVnlMatrix = const_cast(destTransform->GetMatrix().GetVnlMatrix()); for ( int i=0; i < 3; ++i) for( int j=0; j < 3; ++j ) destVnlMatrix[i][j] = sourceVnlMatrix[i][j]; // *This* ensures m_MatrixMTime.Modified(), which is therewith not equal to // m_InverseMatrixMTime, thus a new inverse will be calculated (when // requested). static_cast*>(destTransform)->MatrixChanged(); } template - void GetRotation(const mitk::Geometry3D * geometry, TMatrixType& itkmatrix) + void GetRotation(const mitk::BaseGeometry * geometry, TMatrixType& itkmatrix) { const mitk::Vector3D& spacing = geometry->GetSpacing(); - typename mitk::Geometry3D::TransformType::MatrixType::InternalMatrixType& geometryVnlMatrix = - const_cast(geometry->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix()); + typename mitk::BaseGeometry::TransformType::MatrixType::InternalMatrixType& geometryVnlMatrix = + const_cast(geometry->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix()); typename TMatrixType::InternalMatrixType& outputVnlMatrix = const_cast(itkmatrix.GetVnlMatrix()); for ( int i=0; i < 3; ++i) for( int j=0; j < 3; ++j ) outputVnlMatrix [i][j] = geometryVnlMatrix [i][j] / spacing[j]; } template void GetWorldToItkPhysicalTransform(const mitk::Geometry3D * geometry, TTransformType* itkTransform) { if(itkTransform==NULL) return; // get rotation matrix and offset from Geometry and transfer in TTransformType types typename TTransformType::MatrixType rotationMatrix; GetRotation(geometry, rotationMatrix); const typename mitk::Geometry3D::TransformType::OffsetType& geometryOffset = geometry->GetIndexToWorldTransform()->GetOffset(); vnl_vector vnlOffset(3); vnlOffset[0] = geometryOffset[0]; vnlOffset[1] = geometryOffset[1]; vnlOffset[2] = geometryOffset[2]; // do calculations typename TTransformType::MatrixType::InternalMatrixType inverseRotationVnlMatrix = rotationMatrix.GetTranspose(); vnlOffset -= inverseRotationVnlMatrix*vnlOffset; typename TTransformType::OutputVectorType offset;//vnl_vector offset; offset[0] = vnlOffset[0]; offset[1] = vnlOffset[1]; offset[2] = vnlOffset[2]; itkTransform->SetOffset( offset ); // copy in destination itkTransform typename TTransformType::MatrixType::InternalMatrixType& destVnlMatrix = const_cast(itkTransform->GetMatrix().GetVnlMatrix()); for ( int i=0; i < 3; ++i) for( int j=0; j < 3; ++j ) destVnlMatrix[i][j] = inverseRotationVnlMatrix[i][j]; // *This* ensures m_MatrixMTime.Modified(), which is therewith not equal to // m_InverseMatrixMTime, thus a new inverse will be calculated (when // requested). static_cast*>(itkTransform)->MatrixChanged(); } } #endif /* MITKMATRIXCONVERT_H_HEADER_INCLUDED_C1EBD0AD */ diff --git a/Core/Code/DataManagement/mitkPlaneGeometry.cpp b/Core/Code/DataManagement/mitkPlaneGeometry.cpp index 364fe98271..f2ff00020f 100644 --- a/Core/Code/DataManagement/mitkPlaneGeometry.cpp +++ b/Core/Code/DataManagement/mitkPlaneGeometry.cpp @@ -1,777 +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(); + } -mitk::PlaneGeometry::PlaneGeometry() -{ - Initialize(); -} - - -mitk::PlaneGeometry::~PlaneGeometry() -{ -} - - -void -PlaneGeometry::Initialize() -{ - Superclass::Initialize(); -} - - -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::SetIndexToWorldTransform(mitk::AffineTransform3D *transform) -{ - EnsurePerpendicularNormal(transform); - - Superclass::SetIndexToWorldTransform(transform); -} - - -void -PlaneGeometry::SetBounds(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); + PlaneGeometry::~PlaneGeometry() + { + } - Superclass::SetBounds(bounds); -} + 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::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::PreSetIndexToWorldTransform(mitk::AffineTransform3D *transform) + { + EnsurePerpendicularNormal(transform); + } + void + PlaneGeometry::PreSetBounds(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::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 &pt_units, Point2D &pt_mm ) const + { + pt_mm[0]=m_ScaleFactorMMPerUnitX*pt_units[0]; + pt_mm[1]=m_ScaleFactorMMPerUnitY*pt_units[1]; + } -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::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 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::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::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::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 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::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(); + 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); + } - //construct standard view - Point3D origin; - VnlVector rightDV(3), bottomDV(3); - origin.Fill(0); - int normalDirection; - switch(planeorientation) + 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 ); - } + } + 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 ); + 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() + if ( transform == NULL ) + { + this->SetMatrixByVectors( rightDV, bottomDV ); + } + else + { + this->SetMatrixByVectors( + rightDV, bottomDV, + transform->GetMatrix().GetVnlMatrix() .get_column(normalDirection).magnitude() - ); - } - - this->SetOrigin(origin); -} + ); + } + this->SetOrigin(origin); + } -void -PlaneGeometry::InitializeStandardPlane( const Geometry3D *geometry3D, - PlaneOrientation planeorientation, ScalarType zPosition, - bool frontside, bool rotated ) -{ - this->SetReferenceGeometry( const_cast< Geometry3D * >( geometry3D ) ); + void + PlaneGeometry::InitializeStandardPlane( const BaseGeometry *geometry3D, + PlaneOrientation planeorientation, ScalarType zPosition, + bool frontside, bool rotated ) + { + this->SetReferenceGeometry( const_cast< BaseGeometry * >( geometry3D ) ); - ScalarType width, height; + ScalarType width, height; - const BoundingBox::BoundsArrayType& boundsarray = - geometry3D->GetBoundingBox()->GetBounds(); + const BoundingBox::BoundsArrayType& boundsarray = + geometry3D->GetBoundingBox()->GetBounds(); - Vector3D originVector; - FillVector3D(originVector, boundsarray[0], boundsarray[2], boundsarray[4]); + 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) - { + 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 ); + InitializeStandardPlane( width, height, + geometry3D->GetIndexToWorldTransform(), + planeorientation, zPosition, frontside, rotated ); - origin = GetOrigin() + originVector; - SetOrigin(origin); -} + ScalarType bounds[6]= { 0, width, 0, height, 0, 1 }; + this->SetBounds( bounds ); + Point3D origin; + originVector = geometry3D->GetIndexToWorldTransform() + ->TransformVector( originVector ); -void -PlaneGeometry::InitializeStandardPlane( const Geometry3D *geometry3D, - bool top, PlaneOrientation planeorientation, bool frontside, bool rotated ) -{ - ScalarType zPosition; + origin = GetOrigin() + originVector; + SetOrigin(origin); + } - switch(planeorientation) + 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 ); } - 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(); -void -PlaneGeometry::InitializeStandardPlane( const Vector3D &rightVector, - const Vector3D &downVector, const Vector3D *spacing ) -{ - InitializeStandardPlane( rightVector.GetVnlVector(), - downVector.GetVnlVector(), spacing ); -} + 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( const VnlVector& rightVector, - const VnlVector &downVector, const Vector3D *spacing ) -{ - ScalarType width = rightVector.magnitude(); - ScalarType height = downVector.magnitude(); + void + PlaneGeometry::InitializeStandardPlane( + mitk::ScalarType width, ScalarType height, + const VnlVector &rightVector, const VnlVector &downVector, + const Vector3D *spacing ) + { + assert(width > 0); + assert(height > 0); - InitializeStandardPlane( width, height, rightVector, downVector, spacing ); -} + 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]; + } -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); + 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()); - VnlVector rightDV = rightVector; rightDV.normalize(); - VnlVector downDV = downVector; downDV.normalize(); - VnlVector normal = vnl_cross_3d(rightVector, downVector); - normal.normalize(); + ScalarType bounds[6] = { 0, width, 0, height, 0, 1 }; + this->SetBounds( bounds ); - if(spacing!=NULL) - { - rightDV *= (*spacing)[0]; - downDV *= (*spacing)[1]; - normal *= (*spacing)[2]; + this->SetIndexToWorldTransform( transform ); } - 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(m_IndexToWorldTransform->GetOffset()); - - ScalarType bounds[6] = { 0, width, 0, height, 0, 1 }; - this->SetBounds( bounds ); + void + PlaneGeometry::InitializePlane( const Point3D &origin, + const Vector3D &normal ) + { + VnlVector rightVectorVnl(3), downVectorVnl; - this->SetIndexToWorldTransform( transform ); -} + 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 ); -void -PlaneGeometry::InitializePlane( const Point3D &origin, - const Vector3D &normal ) -{ - VnlVector rightVectorVnl(3), downVectorVnl; + SetOrigin(origin); + } - if( Equal( normal[1], 0.0f ) == false ) + void + PlaneGeometry::SetMatrixByVectors( const VnlVector &rightVector, + const VnlVector &downVector, ScalarType thickness ) { - FillVector3D( rightVectorVnl, 1.0f, -normal[0]/normal[1], 0.0f ); - rightVectorVnl.normalize(); + 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); } - else + + Vector3D + PlaneGeometry::GetNormal() const { - FillVector3D( rightVectorVnl, 0.0f, 1.0f, 0.0f ); + Vector3D frontToBack; + frontToBack.SetVnlVector( this->GetIndexToWorldTransform() + ->GetMatrix().GetVnlMatrix().get_column(2) ); + + return frontToBack; } - downVectorVnl = vnl_cross_3d( normal.GetVnlVector(), rightVectorVnl ); - downVectorVnl.normalize(); - InitializeStandardPlane( rightVectorVnl, downVectorVnl ); + VnlVector + PlaneGeometry::GetNormalVnl() const + { + return this->GetIndexToWorldTransform() + ->GetMatrix().GetVnlMatrix().get_column(2); + } - SetOrigin(origin); -} + 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); + } -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(m_IndexToWorldTransform->GetOffset()); - SetIndexToWorldTransform(transform); -} - - -Vector3D -PlaneGeometry::GetNormal() const -{ - Vector3D frontToBack; - frontToBack.SetVnlVector( m_IndexToWorldTransform - ->GetMatrix().GetVnlMatrix().get_column(2) ); + //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(); - return frontToBack; -} + // if(IsAbove(pt3d_mm) == false) + // distance*=-1.0; + // return distance; + //} -VnlVector -PlaneGeometry::GetNormalVnl() const -{ - return m_IndexToWorldTransform - ->GetMatrix().GetVnlMatrix().get_column(2); -} + bool + PlaneGeometry::IsAbove( const Point3D &pt3d_mm , bool considerBoundingBox) const + { + 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(); -ScalarType -PlaneGeometry::DistanceFromPlane( const Point3D &pt3d_mm ) const -{ - return fabs(SignedDistance( pt3d_mm )); -} + Vector3D planeNormal = plane->GetNormal(); + planeNormal.Normalize(); + Vector3D direction = itk::CrossProduct( normal, planeNormal ); -ScalarType -PlaneGeometry::SignedDistance( const Point3D &pt3d_mm ) const -{ - return SignedDistanceFromPlane(pt3d_mm); -} + if ( direction.GetSquaredNorm() < eps ) + return false; + crossline.SetDirection( direction ); -bool -PlaneGeometry::IsAbove( const Point3D &pt3d_mm ) const -{ - return SignedDistanceFromPlane(pt3d_mm) > 0; -} + double N1dN2 = normal * planeNormal; + double determinant = 1.0 - N1dN2 * N1dN2; + Vector3D origin = this->GetOrigin().GetVectorFromOrigin(); + Vector3D planeOrigin = plane->GetOrigin().GetVectorFromOrigin(); -bool -PlaneGeometry::IntersectionLine( - const PlaneGeometry* plane, Line3D& crossline ) const -{ - Vector3D normal = this->GetNormal(); - normal.Normalize(); + double d1 = normal * origin; + double d2 = planeNormal * planeOrigin; - Vector3D planeNormal = plane->GetNormal(); - planeNormal.Normalize(); + double c1 = ( d1 - d2 * N1dN2 ) / determinant; + double c2 = ( d2 - d1 * N1dN2 ) / determinant; - Vector3D direction = itk::CrossProduct( normal, planeNormal ); + Vector3D p = normal * c1 + planeNormal * c2; + crossline.GetPoint().GetVnlVector() = p.GetVnlVector(); - if ( direction.GetSquaredNorm() < eps ) - return false; + return true; + } - crossline.SetDirection( direction ); + unsigned int + PlaneGeometry::IntersectWithPlane2D( + const PlaneGeometry* plane, Point2D& lineFrom, Point2D &lineTo ) const + { + Line3D crossline; + if ( this->IntersectionLine( plane, crossline ) == false ) + return 0; - double N1dN2 = normal * planeNormal; - double determinant = 1.0 - N1dN2 * N1dN2; + Point2D point2; + Vector2D direction2; - Vector3D origin = this->GetOrigin().GetVectorFromOrigin(); - Vector3D planeOrigin = plane->GetOrigin().GetVectorFromOrigin(); + this->Map( crossline.GetPoint(), point2 ); + this->Map( crossline.GetPoint(), crossline.GetDirection(), direction2 ); - double d1 = normal * origin; - double d2 = planeNormal * planeOrigin; + return + Line3D::RectangleLineIntersection( + 0, 0, GetExtentInMM(0), GetExtentInMM(1), + point2, direction2, lineFrom, lineTo ); + } - double c1 = ( d1 - d2 * N1dN2 ) / determinant; - double c2 = ( d2 - d1 * N1dN2 ) / determinant; + double PlaneGeometry::Angle( const PlaneGeometry *plane ) const + { + return angle(plane->GetMatrixColumn(2), GetMatrixColumn(2)); + } - Vector3D p = normal * c1 + planeNormal * c2; - crossline.GetPoint().GetVnlVector() = p.GetVnlVector(); + double PlaneGeometry::Angle( const Line3D &line ) const + { + return vnl_math::pi_over_2 + - angle( line.GetDirection().GetVnlVector(), GetMatrixColumn(2) ); + } - return true; -} + bool PlaneGeometry::IntersectionPoint( + const Line3D &line, Point3D &intersectionPoint ) const + { + Vector3D planeNormal = this->GetNormal(); + planeNormal.Normalize(); + Vector3D lineDirection = line.GetDirection(); + lineDirection.Normalize(); -unsigned int -PlaneGeometry::IntersectWithPlane2D( - const PlaneGeometry* plane, Point2D& lineFrom, Point2D &lineTo ) const -{ - Line3D crossline; - if ( this->IntersectionLine( plane, crossline ) == false ) - return 0; + double t = planeNormal * lineDirection; + if ( fabs( t ) < eps ) + { + return false; + } - Point2D point2; - Vector2D direction2; + Vector3D diff; + diff = this->GetOrigin() - line.GetPoint(); + t = ( planeNormal * diff ) / t; - this->Map( crossline.GetPoint(), point2 ); - this->Map( crossline.GetPoint(), crossline.GetDirection(), direction2 ); + intersectionPoint = line.GetPoint() + lineDirection * t; + return true; + } - return - Line3D::RectangleLineIntersection( - 0, 0, GetExtentInMM(0), GetExtentInMM(1), - point2, direction2, lineFrom, lineTo ); -} + bool + PlaneGeometry::IntersectionPointParam( const Line3D &line, double &t ) const + { + Vector3D planeNormal = this->GetNormal(); + Vector3D lineDirection = line.GetDirection(); -double PlaneGeometry::Angle( const PlaneGeometry *plane ) const -{ - return angle(plane->GetMatrixColumn(2), GetMatrixColumn(2)); -} + t = planeNormal * lineDirection; + if ( fabs( t ) < eps ) + { + return false; + } -double PlaneGeometry::Angle( const Line3D &line ) const -{ - return vnl_math::pi_over_2 - - angle( line.GetDirection().GetVnlVector(), GetMatrixColumn(2) ); -} + 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::IntersectionPoint( - const Line3D &line, Point3D &intersectionPoint ) const -{ - Vector3D planeNormal = this->GetNormal(); - planeNormal.Normalize(); + bool + PlaneGeometry::IsOnPlane( const Point3D &point ) const + { + return Distance(point) < eps; + } - Vector3D lineDirection = line.GetDirection(); - lineDirection.Normalize(); + bool + PlaneGeometry::IsOnPlane( const Line3D &line ) const + { + return ( (Distance( line.GetPoint() ) < eps) + && (Distance( line.GetPoint2() ) < eps) ); + } - double t = planeNormal * lineDirection; - if ( fabs( t ) < eps ) + bool + PlaneGeometry::IsOnPlane( const PlaneGeometry *plane ) const { - return false; + return ( IsParallel( plane ) && (Distance( plane->GetOrigin() ) < eps) ); } - Vector3D diff; - diff = this->GetOrigin() - line.GetPoint(); - t = ( planeNormal * diff ) / t; + Point3D + PlaneGeometry::ProjectPointOntoPlane( const Point3D& pt ) const + { + ScalarType len = this->GetNormalVnl().two_norm(); + return pt - this->GetNormal() * this->SignedDistanceFromPlane( pt ) / len; + } - intersectionPoint = line.GetPoint() + lineDirection * t; - return true; -} + 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()); -bool -PlaneGeometry::IntersectionPointParam( const Line3D &line, double &t ) const -{ - Vector3D planeNormal = this->GetNormal(); + switch ( operation->GetOperationType() ) + { + case OpORIENT: + { + mitk::PlaneOperation *planeOp = dynamic_cast< mitk::PlaneOperation * >( operation ); + if ( planeOp == NULL ) + { + return; + } - Vector3D lineDirection = line.GetDirection(); + Point3D center = planeOp->GetPoint(); - t = planeNormal * lineDirection; + Vector3D orientationVector = planeOp->GetNormal(); + Vector3D defaultVector; + FillVector3D( defaultVector, 0.0, 0.0, 1.0 ); - if ( fabs( t ) < eps ) - { - return false; - } + Vector3D rotationAxis = itk::CrossProduct( orientationVector, defaultVector ); + //double rotationAngle = acos( orientationVector[2] / orientationVector.GetNorm() ); - Vector3D diff; - diff = this->GetOrigin() - line.GetPoint(); - t = ( planeNormal * diff ) / t; - return true; -} + 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; + } -bool -PlaneGeometry::IsParallel( const PlaneGeometry *plane ) const -{ - return ( (Angle(plane) < 10.0 * mitk::sqrteps ) || ( Angle(plane) > ( vnl_math::pi - 10.0 * sqrteps ) ) ) ; -} + 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(); + } -bool -PlaneGeometry::IsOnPlane( const Point3D &point ) const -{ - return Distance(point) < eps; -} + 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::PostSetIndexToWorldTransform( + mitk::AffineTransform3D* transform) + { + m_ScaleFactorMMPerUnitX=GetExtentInMM(0)/GetExtent(0); + m_ScaleFactorMMPerUnitY=GetExtentInMM(1)/GetExtent(1); -bool -PlaneGeometry::IsOnPlane( const Line3D &line ) const -{ - return ( (Distance( line.GetPoint() ) < eps) - && (Distance( line.GetPoint2() ) < eps) ); -} + assert(m_ScaleFactorMMPerUnitXGetOrigin() ) < eps) ); -} + 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); + } -Point3D -PlaneGeometry::ProjectPointOntoPlane( const Point3D& pt ) const -{ - ScalarType len = this->GetNormalVnl().two_norm(); - return pt - this->GetNormal() * this->SignedDistanceFromPlane( pt ) / len; -} + 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); + } -itk::LightObject::Pointer -PlaneGeometry::InternalClone() const -{ - Self::Pointer newGeometry = new PlaneGeometry(*this); - newGeometry->UnRegister(); - return newGeometry.GetPointer(); -} + bool + PlaneGeometry::Project( + const mitk::Point3D &pt3d_mm, mitk::Point3D &projectedPt3d_mm) const + { + assert(this->IsBoundingBoxNull()==false); -void -PlaneGeometry::ExecuteOperation( Operation *operation ) -{ - vtkTransform *transform = vtkTransform::New(); - transform->SetMatrix( m_VtkMatrix ); + 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); + } - switch ( operation->GetOperationType() ) + bool + PlaneGeometry::Project(const mitk::Vector3D &vec3d_mm, mitk::Vector3D &projectedVec3d_mm) const { - case OpORIENT: - { - mitk::PlaneOperation *planeOp = dynamic_cast< mitk::PlaneOperation * >( operation ); - if ( planeOp == NULL ) - { - return; - } + assert(this->IsBoundingBoxNull()==false); - Point3D center = planeOp->GetPoint(); - - Vector3D orientationVector = planeOp->GetNormal(); - Vector3D defaultVector; - FillVector3D( defaultVector, 0.0, 0.0, 1.0 ); + Vector3D vec3d_units; + BackTransform(vec3d_mm, vec3d_units); + vec3d_units[2] = 0; + projectedVec3d_mm = GetIndexToWorldTransform()->TransformVector(vec3d_units); + return true; + } - Vector3D rotationAxis = itk::CrossProduct( orientationVector, defaultVector ); - //double rotationAngle = acos( orientationVector[2] / orientationVector.GetNorm() ); + 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); - double rotationAngle = atan2( (double) rotationAxis.GetNorm(), (double) (orientationVector * defaultVector) ); - rotationAngle *= 180.0 / vnl_math::pi; + Vector3D vec3d_units; + BackTransform(atPt3d_mm, vec3d_mm, vec3d_units); + vec3d_units[2] = 0; + projectedVec3d_mm = GetIndexToWorldTransform()->TransformVector(vec3d_units); - 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; - } + Point3D pt3d_units; + BackTransform(atPt3d_mm, pt3d_units); + return const_cast(this->GetBoundingBox())->IsInside(pt3d_units); + } - 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; + 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; + } - } - default: - Superclass::ExecuteOperation( operation ); - transform->Delete(); - return; + 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); } - m_VtkMatrix->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 << " Normal: " << GetNormal() << std::endl; -} + 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 ed8fd8d6e7..abf20b60c4 100644 --- a/Core/Code/DataManagement/mitkPlaneGeometry.h +++ b/Core/Code/DataManagement/mitkPlaneGeometry.h @@ -1,428 +1,566 @@ /*=================================================================== 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. ===================================================================*/ + /** + * \brief Describes the geometry of a plane object + * + * Describes a two-dimensional manifold, i.e., to put it simply, + * an object that can be described using a 2D coordinate-system. + * + * PlaneGeometry 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 PlaneGeometry describes the 2D representation within a 3D object and is + * therefore itself a Geometry3D (derived from BaseGeometry). 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. + * + * + * Optionally, a reference BaseGeometry 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 PlaneGeometry) with the bounding box of the associated + * BaseGeometry. + * + * \warning The PlaneGeometry 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 PlaneGeometry 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; -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 -{ -public: - mitkClassMacro(PlaneGeometry,Geometry2D); + /** + * \brief Describes a two-dimensional, rectangular plane + * + * \ingroup Geometry + */ + class MITK_CORE_EXPORT PlaneGeometry : public BaseGeometry + { + public: + mitkClassMacro(PlaneGeometry,BaseGeometry); - /** Method for creation through the object factory. */ + /** Method for creation through the object factory. */ itkFactorylessNewMacro(Self) itkCloneMacro(Self) - enum PlaneOrientation - { + enum PlaneOrientation + { Axial, - Sagittal, - Frontal - }; - - 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; - - - virtual void Initialize(); - - /** - * \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 Geometry3D* 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 Geometry3D* 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; + Sagittal, + Frontal + }; + + 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 BaseGeometry (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 BaseGeometry (default: identity). + * Spacing also taken from \a BaseGeometry. + * + * \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; + + /** + * \brief Calculates, whether a point is below or above the plane. There are two different + *calculation methods, with or without consideration of the bounding box. + */ + 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; - /** - * \brief Distance of the point from the plane - * (bounding-box \em not considered) - * - */ - ScalarType DistanceFromPlane( const Point3D& pt3d_mm ) const ; - + return (pt3d_mm-GetOrigin())*GetNormal() / len; + } - /** - * \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(); + /** + * \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)); + } - if( len == 0 ) + /** + * \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; + } - 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)) + /** + * \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 ); + + /** + * \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 SignedDistance(plane->GetOrigin()); + return fabs(SignedDistance(pt3d_mm)); } - 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 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 Returns the lot from the point to the plane - */ - Point3D ProjectPointOntoPlane( const Point3D &pt ) const; + /** + * \brief Get the geometrical frame of reference for this PlaneGeometry. + */ + BaseGeometry *GetReferenceGeometry() const; + bool HasReferenceGeometry() const; - virtual void SetIndexToWorldTransform( AffineTransform3D *transform); + protected: + PlaneGeometry(); + PlaneGeometry(const PlaneGeometry& other); - virtual void SetBounds( const BoundingBox::BoundsArrayType &bounds ); + virtual ~PlaneGeometry(); + virtual void PrintSelf( std::ostream &os, itk::Indent indent ) const; - virtual itk::LightObject::Pointer InternalClone() const; + virtual void PreSetBounds( const BoundingBox::BoundsArrayType &bounds ); + virtual void PreSetIndexToWorldTransform( AffineTransform3D *transform); - /** Implements operation to re-orient the plane */ - virtual void ExecuteOperation( Operation *operation ); + virtual void PostSetExtentInMM(int direction, ScalarType extentInMM); + virtual void PostSetIndexToWorldTransform(mitk::AffineTransform3D* transform); -protected: - PlaneGeometry(); + /** + * \brief factor to convert x-coordinates from mm to units and vice versa + * + */ + mutable mitk::ScalarType m_ScaleFactorMMPerUnitX; - virtual ~PlaneGeometry(); + /** + * \brief factor to convert y-coordinates from mm to units and vice versa + * + */ + mutable mitk::ScalarType m_ScaleFactorMMPerUnitY; - virtual void PrintSelf( std::ostream &os, itk::Indent indent ) const; + 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; }; - -}; + 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/mitkPointSet.cpp b/Core/Code/DataManagement/mitkPointSet.cpp index 1040beae0f..e4dc354337 100755 --- a/Core/Code/DataManagement/mitkPointSet.cpp +++ b/Core/Code/DataManagement/mitkPointSet.cpp @@ -1,880 +1,880 @@ /*=================================================================== 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 "mitkPointSet.h" #include "mitkPointOperation.h" #include "mitkInteractionConst.h" #include #include mitk::PointSet::PointSet() { this->InitializeEmpty(); } mitk::PointSet::PointSet(const PointSet &other) : BaseData(other) , m_PointSetSeries(other.GetPointSetSeriesSize()) , m_CalculateBoundingBox(true) { // Copy points for (std::size_t t = 0; t < m_PointSetSeries.size(); ++t) { m_PointSetSeries[t] = DataType::New(); DataType::Pointer otherPts = other.GetPointSet(t); for (PointsConstIterator i = other.Begin(t); i != other.End(t); ++i) { m_PointSetSeries[t]->SetPoint(i.Index(), i.Value()); PointDataType pointData; if (otherPts->GetPointData(i.Index(), &pointData)) { m_PointSetSeries[t]->SetPointData(i.Index(), pointData); } } } } mitk::PointSet::~PointSet() { this->ClearData(); } void mitk::PointSet::ClearData() { m_PointSetSeries.clear(); Superclass::ClearData(); } void mitk::PointSet::InitializeEmpty() { m_PointSetSeries.resize( 1 ); m_PointSetSeries[0] = DataType::New(); PointDataContainer::Pointer pointData = PointDataContainer::New(); m_PointSetSeries[0]->SetPointData( pointData ); m_CalculateBoundingBox = false; Superclass::InitializeTimeGeometry(1); m_Initialized = true; } bool mitk::PointSet::IsEmptyTimeStep(unsigned int t) const { return IsInitialized() && (GetSize(t) == 0); } void mitk::PointSet::Expand( unsigned int timeSteps ) { // Check if the vector is long enough to contain the new element // at the given position. If not, expand it with sufficient pre-initialized // elements. // // NOTE: This method will never REDUCE the vector size; it should only // be used to make sure that the vector has enough elements to include the // specified time step. unsigned int oldSize = m_PointSetSeries.size(); if ( timeSteps > oldSize ) { Superclass::Expand( timeSteps ); m_PointSetSeries.resize( timeSteps ); for ( unsigned int i = oldSize; i < timeSteps; ++i ) { m_PointSetSeries[i] = DataType::New(); PointDataContainer::Pointer pointData = PointDataContainer::New(); m_PointSetSeries[i]->SetPointData( pointData ); } //if the size changes, then compute the bounding box m_CalculateBoundingBox = true; this->InvokeEvent( PointSetExtendTimeRangeEvent() ); } } unsigned int mitk::PointSet::GetPointSetSeriesSize() const { return m_PointSetSeries.size(); } int mitk::PointSet::GetSize( unsigned int t ) const { if ( t < m_PointSetSeries.size() ) { return m_PointSetSeries[t]->GetNumberOfPoints(); } else { return 0; } } mitk::PointSet::DataType::Pointer mitk::PointSet::GetPointSet( int t ) const { if ( t < (int)m_PointSetSeries.size() ) { return m_PointSetSeries[t]; } else { return NULL; } } mitk::PointSet::PointsIterator mitk::PointSet::Begin( int t ) { if (t >= 0 && t < static_cast(m_PointSetSeries.size())) { return m_PointSetSeries[t]->GetPoints()->Begin(); } return PointsIterator(); } mitk::PointSet::PointsConstIterator mitk::PointSet::Begin(int t) const { if (t >= 0 && t < static_cast(m_PointSetSeries.size())) { return m_PointSetSeries[t]->GetPoints()->Begin(); } return PointsConstIterator(); } mitk::PointSet::PointsIterator mitk::PointSet::End( int t ) { if (t >= 0 && t < static_cast(m_PointSetSeries.size())) { return m_PointSetSeries[t]->GetPoints()->End(); } return PointsIterator(); } mitk::PointSet::PointsConstIterator mitk::PointSet::End(int t) const { if (t >= 0 && t < static_cast(m_PointSetSeries.size())) { return m_PointSetSeries[t]->GetPoints()->End(); } return PointsConstIterator(); } int mitk::PointSet::SearchPoint( Point3D point, ScalarType distance, int t ) const { if ( t >= (int)m_PointSetSeries.size() ) { return -1; } // Out is the point which is checked to be the searched point PointType out; out.Fill( 0 ); PointType indexPoint; this->GetGeometry( t )->WorldToIndex(point, indexPoint); // Searching the first point in the Set, that is +- distance far away fro // the given point unsigned int i; PointsContainer::Iterator it, end; end = m_PointSetSeries[t]->GetPoints()->End(); int bestIndex = -1; distance = distance * distance; // To correct errors from converting index to world and world to index if (distance == 0.0) { distance = 0.000001; } ScalarType bestDist = distance; ScalarType dist, tmp; for ( it = m_PointSetSeries[t]->GetPoints()->Begin(), i = 0; it != end; ++it, ++i ) { bool ok = m_PointSetSeries[t]->GetPoints() ->GetElementIfIndexExists( it->Index(), &out ); if ( !ok ) { return -1; } else if ( indexPoint == out ) //if totally equal { return it->Index(); } //distance calculation tmp = out[0] - indexPoint[0]; dist = tmp * tmp; tmp = out[1] - indexPoint[1]; dist += tmp * tmp; tmp = out[2] - indexPoint[2]; dist += tmp * tmp; if ( dist < bestDist ) { bestIndex = it->Index(); bestDist = dist; } } return bestIndex; } mitk::PointSet::PointType mitk::PointSet::GetPoint( PointIdentifier id, int t ) const { PointType out; out.Fill(0); if ( (unsigned int) t >= m_PointSetSeries.size() ) { return out; } if ( m_PointSetSeries[t]->GetPoints()->IndexExists(id) ) { m_PointSetSeries[t]->GetPoint( id, &out ); this->GetGeometry(t)->IndexToWorld( out, out ); return out; } else { return out; } } bool mitk::PointSet ::GetPointIfExists( PointIdentifier id, PointType* point, int t ) const { if ( (unsigned int) t >= m_PointSetSeries.size() ) { return false; } if ( m_PointSetSeries[t]->GetPoints()->GetElementIfIndexExists(id, point) ) { this->GetGeometry( t )->IndexToWorld( *point, *point ); return true; } else { return false; } } void mitk::PointSet::SetPoint( PointIdentifier id, PointType point, int t ) { // Adapt the size of the data vector if necessary this->Expand( t+1 ); mitk::Point3D indexPoint; this->GetGeometry( t )->WorldToIndex( point, indexPoint ); m_PointSetSeries[t]->SetPoint( id, indexPoint ); PointDataType defaultPointData; defaultPointData.id = id; defaultPointData.selected = false; defaultPointData.pointSpec = mitk::PTUNDEFINED; m_PointSetSeries[t]->SetPointData( id, defaultPointData ); //boundingbox has to be computed anyway m_CalculateBoundingBox = true; this->Modified(); } void mitk::PointSet::SetPoint( PointIdentifier id, PointType point, PointSpecificationType spec, int t ) { // Adapt the size of the data vector if necessary this->Expand( t+1 ); mitk::Point3D indexPoint; this->GetGeometry( t )->WorldToIndex( point, indexPoint ); m_PointSetSeries[t]->SetPoint( id, indexPoint ); PointDataType defaultPointData; defaultPointData.id = id; defaultPointData.selected = false; defaultPointData.pointSpec = spec; m_PointSetSeries[t]->SetPointData( id, defaultPointData ); //boundingbox has to be computed anyway m_CalculateBoundingBox = true; this->Modified(); } void mitk::PointSet::InsertPoint( PointIdentifier id, PointType point, int t ) { this->InsertPoint(id, point, mitk::PTUNDEFINED, t); } void mitk::PointSet::InsertPoint( PointIdentifier id, PointType point, PointSpecificationType spec, int t ) { if ( (unsigned int) t < m_PointSetSeries.size() ) { mitk::Point3D indexPoint; - mitk::Geometry3D* tempGeometry = this->GetGeometry( t ); + mitk::BaseGeometry* tempGeometry = this->GetGeometry( t ); if (tempGeometry == NULL) { MITK_INFO<< __FILE__ << ", l." << __LINE__ << ": GetGeometry of "<< t <<" returned NULL!" << std::endl; return; } tempGeometry->WorldToIndex( point, indexPoint ); m_PointSetSeries[t]->GetPoints()->InsertElement( id, indexPoint ); PointDataType defaultPointData; defaultPointData.id = id; defaultPointData.selected = false; defaultPointData.pointSpec = spec; m_PointSetSeries[t]->GetPointData()->InsertElement(id, defaultPointData); //boundingbox has to be computed anyway m_CalculateBoundingBox = true; this->Modified(); } } bool mitk::PointSet::SwapPointPosition( PointIdentifier id, bool moveUpwards, int t ) { if(IndexExists(id, t) ) { PointType point = GetPoint(id,t); if(moveUpwards) {//up if(IndexExists(id-1,t)) { InsertPoint(id, GetPoint(id - 1, t), t); InsertPoint(id-1,point,t); this->Modified(); return true; } } else {//down if(IndexExists(id+1,t)) { InsertPoint(id, GetPoint(id + 1, t), t); InsertPoint(id+1,point,t); this->Modified(); return true; } } } return false; } bool mitk::PointSet::IndexExists( int position, int t ) const { if ( (unsigned int) t < m_PointSetSeries.size() ) { return m_PointSetSeries[t]->GetPoints()->IndexExists( position ); } else { return false; } } bool mitk::PointSet::GetSelectInfo( int position, int t ) const { if ( this->IndexExists( position, t ) ) { PointDataType pointData = { 0, false, PTUNDEFINED }; m_PointSetSeries[t]->GetPointData( position, &pointData ); return pointData.selected; } else { return false; } } void mitk::PointSet::SetSelectInfo( int position, bool selected, int t ) { if ( this->IndexExists( position, t ) ) { // timeStep to ms TimePointType timeInMS = this->GetTimeGeometry()->TimeStepToTimePoint( t ); // point Point3D point = this->GetPoint( position, t ); std::auto_ptr op; if (selected) { op.reset(new mitk::PointOperation(OpSELECTPOINT, timeInMS, point, position )); } else { op.reset(new mitk::PointOperation(OpDESELECTPOINT, timeInMS, point, position )); } this->ExecuteOperation( op.get() ); } } mitk::PointSpecificationType mitk::PointSet::GetSpecificationTypeInfo( int position, int t ) const { if ( this->IndexExists( position, t ) ) { PointDataType pointData = { 0, false, PTUNDEFINED }; m_PointSetSeries[t]->GetPointData( position, &pointData ); return pointData.pointSpec; } else { return PTUNDEFINED; } } int mitk::PointSet::GetNumberOfSelected( int t ) const { if ( (unsigned int) t >= m_PointSetSeries.size() ) { return 0; } int numberOfSelected = 0; PointDataIterator it; for ( it = m_PointSetSeries[t]->GetPointData()->Begin(); it != m_PointSetSeries[t]->GetPointData()->End(); it++ ) { if (it->Value().selected == true) { ++numberOfSelected; } } return numberOfSelected; } int mitk::PointSet::SearchSelectedPoint( int t ) const { if ( (unsigned int) t >= m_PointSetSeries.size() ) { return -1; } PointDataIterator it; for ( it = m_PointSetSeries[t]->GetPointData()->Begin(); it != m_PointSetSeries[t]->GetPointData()->End(); it++ ) { if ( it->Value().selected == true ) { return it->Index(); } } return -1; } void mitk::PointSet::ExecuteOperation( Operation* operation ) { int timeStep = -1; mitkCheckOperationTypeMacro(PointOperation, operation, pointOp); if ( pointOp ) { timeStep = this->GetTimeGeometry()->TimePointToTimeStep( pointOp->GetTimeInMS() ); } if ( timeStep < 0 ) { MITK_ERROR << "Time step (" << timeStep << ") outside of PointSet time bounds" << std::endl; return; } switch (operation->GetOperationType()) { case OpNOTHING: break; case OpINSERT://inserts the point at the given position and selects it. { int position = pointOp->GetIndex(); PointType pt; pt.CastFrom(pointOp->GetPoint()); //transfer from world to index coordinates - mitk::Geometry3D* geometry = this->GetGeometry( timeStep ); + mitk::BaseGeometry* geometry = this->GetGeometry( timeStep ); if (geometry == NULL) { MITK_INFO<<"GetGeometry returned NULL!\n"; return; } geometry->WorldToIndex(pt, pt); m_PointSetSeries[timeStep]->GetPoints()->InsertElement(position, pt); PointDataType pointData = { static_cast(pointOp->GetIndex()), pointOp->GetSelected(), pointOp->GetPointType() }; m_PointSetSeries[timeStep]->GetPointData() ->InsertElement(position, pointData); this->Modified(); //boundingbox has to be computed m_CalculateBoundingBox = true; this->InvokeEvent( PointSetAddEvent() ); this->OnPointSetChange(); } break; case OpMOVE://moves the point given by index { PointType pt; pt.CastFrom(pointOp->GetPoint()); //transfer from world to index coordinates this->GetGeometry( timeStep )->WorldToIndex(pt, pt); // Copy new point into container m_PointSetSeries[timeStep]->SetPoint(pointOp->GetIndex(), pt); // Insert a default point data object to keep the containers in sync // (if no point data object exists yet) PointDataType pointData; if ( !m_PointSetSeries[timeStep]->GetPointData( pointOp->GetIndex(), &pointData ) ) { m_PointSetSeries[timeStep]->SetPointData( pointOp->GetIndex(), pointData ); } this->OnPointSetChange(); this->Modified(); //boundingbox has to be computed anyway m_CalculateBoundingBox = true; this->InvokeEvent( PointSetMoveEvent() ); } break; case OpREMOVE://removes the point at given by position { m_PointSetSeries[timeStep]->GetPoints()->DeleteIndex((unsigned)pointOp->GetIndex()); m_PointSetSeries[timeStep]->GetPointData()->DeleteIndex((unsigned)pointOp->GetIndex()); this->OnPointSetChange(); this->Modified(); //boundingbox has to be computed anyway m_CalculateBoundingBox = true; this->InvokeEvent( PointSetRemoveEvent() ); } break; case OpSELECTPOINT://select the given point { PointDataType pointData = {0, false, PTUNDEFINED}; m_PointSetSeries[timeStep]->GetPointData(pointOp->GetIndex(), &pointData); pointData.selected = true; m_PointSetSeries[timeStep]->SetPointData(pointOp->GetIndex(), pointData); this->Modified(); } break; case OpDESELECTPOINT://unselect the given point { PointDataType pointData = {0, false, PTUNDEFINED}; m_PointSetSeries[timeStep]->GetPointData(pointOp->GetIndex(), &pointData); pointData.selected = false; m_PointSetSeries[timeStep]->SetPointData(pointOp->GetIndex(), pointData); this->Modified(); } break; case OpSETPOINTTYPE: { PointDataType pointData = {0, false, PTUNDEFINED}; m_PointSetSeries[timeStep]->GetPointData(pointOp->GetIndex(), &pointData); pointData.pointSpec = pointOp->GetPointType(); m_PointSetSeries[timeStep]->SetPointData(pointOp->GetIndex(), pointData); this->Modified(); } break; case OpMOVEPOINTUP: // swap content of point with ID pointOp->GetIndex() with the point preceding it in the container // move point position within the pointset { PointIdentifier currentID = pointOp->GetIndex(); /* search for point with this id and point that precedes this one in the data container */ PointsContainer::STLContainerType points = m_PointSetSeries[timeStep]->GetPoints()->CastToSTLContainer(); PointsContainer::STLContainerType::iterator it = points.find(currentID); if (it == points.end()) // ID not found break; if (it == points.begin()) // we are at the first element, there is no previous element break; /* get and cache current point & pointdata and previous point & pointdata */ --it; PointIdentifier prevID = it->first; if (this->SwapPointContents(prevID, currentID, timeStep) == true) this->Modified(); } break; case OpMOVEPOINTDOWN: // move point position within the pointset { PointIdentifier currentID = pointOp->GetIndex(); /* search for point with this id and point that succeeds this one in the data container */ PointsContainer::STLContainerType points = m_PointSetSeries[timeStep]->GetPoints()->CastToSTLContainer(); PointsContainer::STLContainerType::iterator it = points.find(currentID); if (it == points.end()) // ID not found break; ++it; if (it == points.end()) // ID is already the last element, there is no succeeding element break; /* get and cache current point & pointdata and previous point & pointdata */ PointIdentifier nextID = it->first; if (this->SwapPointContents(nextID, currentID, timeStep) == true) this->Modified(); } break; default: itkWarningMacro("mitkPointSet could not understrand the operation. Please check!"); break; } //to tell the mappers, that the data is modified and has to be updated //only call modified if anything is done, so call in cases //this->Modified(); mitk::OperationEndEvent endevent(operation); ((const itk::Object*)this)->InvokeEvent(endevent); //*todo has to be done here, cause of update-pipeline not working yet // As discussed lately, don't mess with the rendering from inside data structures //mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void mitk::PointSet::UpdateOutputInformation() { if ( this->GetSource( ) ) { this->GetSource( )->UpdateOutputInformation( ); } // // first make sure, that the associated time sliced geometry has // the same number of geometry 3d's as PointSets are present // TimeGeometry* timeGeometry = GetTimeGeometry(); if ( timeGeometry->CountTimeSteps() != m_PointSetSeries.size() ) { itkExceptionMacro(<<"timeGeometry->CountTimeSteps() != m_PointSetSeries.size() -- use Initialize(timeSteps) with correct number of timeSteps!"); } // This is needed to detect zero objects mitk::ScalarType nullpoint[]={0,0,0,0,0,0}; BoundingBox::BoundsArrayType itkBoundsNull(nullpoint); // // Iterate over the PointSets and update the Geometry // information of each of the items. // if (m_CalculateBoundingBox) { for ( unsigned int i = 0 ; i < m_PointSetSeries.size() ; ++i ) { const DataType::BoundingBoxType *bb = m_PointSetSeries[i]->GetBoundingBox(); BoundingBox::BoundsArrayType itkBounds = bb->GetBounds(); if ( m_PointSetSeries[i].IsNull() || (m_PointSetSeries[i]->GetNumberOfPoints() == 0) || (itkBounds == itkBoundsNull) ) { itkBounds = itkBoundsNull; continue; } // Ensure minimal bounds of 1.0 in each dimension for ( unsigned int j = 0; j < 3; ++j ) { if ( itkBounds[j*2+1] - itkBounds[j*2] < 1.0 ) { BoundingBox::CoordRepType center = (itkBounds[j*2] + itkBounds[j*2+1]) / 2.0; itkBounds[j*2] = center - 0.5; itkBounds[j*2+1] = center + 0.5; } } this->GetGeometry(i)->SetBounds(itkBounds); } m_CalculateBoundingBox = false; } this->GetTimeGeometry()->Update(); } void mitk::PointSet::SetRequestedRegionToLargestPossibleRegion() { } bool mitk::PointSet::RequestedRegionIsOutsideOfTheBufferedRegion() { return false; } bool mitk::PointSet::VerifyRequestedRegion() { return true; } void mitk::PointSet::SetRequestedRegion(const DataObject * ) { } void mitk::PointSet::PrintSelf( std::ostream& os, itk::Indent indent ) const { Superclass::PrintSelf(os, indent); os << indent << "Number timesteps: " << m_PointSetSeries.size() << "\n"; unsigned int i = 0; for (PointSetSeries::const_iterator it = m_PointSetSeries.begin(); it != m_PointSetSeries.end(); ++it) { os << indent << "Timestep " << i++ << ": \n"; MeshType::Pointer ps = *it; itk::Indent nextIndent = indent.GetNextIndent(); ps->Print(os, nextIndent); MeshType::PointsContainer* points = ps->GetPoints(); MeshType::PointDataContainer* datas = ps->GetPointData(); MeshType::PointDataContainer::Iterator dataIterator = datas->Begin(); for (MeshType::PointsContainer::Iterator pointIterator = points->Begin(); pointIterator != points->End(); ++pointIterator, ++dataIterator) { os << nextIndent << "Point " << pointIterator->Index() << ": ["; os << pointIterator->Value().GetElement(0); for (unsigned int i = 1; i < PointType::GetPointDimension(); ++i) { os << ", " << pointIterator->Value().GetElement(i); } os << "]"; os << ", selected: " << dataIterator->Value().selected << ", point spec: " << dataIterator->Value().pointSpec << "\n"; } } } bool mitk::PointSet::SwapPointContents(PointIdentifier id1, PointIdentifier id2, int timeStep) { /* search and cache contents */ PointType p1; if (m_PointSetSeries[timeStep]->GetPoint(id1, &p1) == false) return false; PointDataType data1; if (m_PointSetSeries[timeStep]->GetPointData(id1, &data1) == false) return false; PointType p2; if (m_PointSetSeries[timeStep]->GetPoint(id2, &p2) == false) return false; PointDataType data2; if (m_PointSetSeries[timeStep]->GetPointData(id2, &data2) == false) return false; /* now swap contents */ m_PointSetSeries[timeStep]->SetPoint(id1, p2); m_PointSetSeries[timeStep]->SetPointData(id1, data2); m_PointSetSeries[timeStep]->SetPoint(id2, p1); m_PointSetSeries[timeStep]->SetPointData(id2, data1); return true; } bool mitk::PointSet::PointDataType::operator ==(const mitk::PointSet::PointDataType &other) const { return id == other.id && selected == other.selected && pointSpec == other.pointSpec; } bool mitk::Equal( const mitk::PointSet* leftHandSide, const mitk::PointSet* rightHandSide, mitk::ScalarType eps, bool verbose ) { if((leftHandSide == NULL) || (rightHandSide == NULL)) { MITK_ERROR << "mitk::Equal( const mitk::PointSet* leftHandSide, const mitk::PointSet* rightHandSide, mitk::ScalarType eps, bool verbose ) does not work with NULL pointer input."; return false; } return Equal( *leftHandSide, *rightHandSide, eps, verbose); } bool mitk::Equal( const mitk::PointSet& leftHandSide, const mitk::PointSet& rightHandSide, mitk::ScalarType eps, bool verbose ) { bool result = true; if( !mitk::Equal( *leftHandSide.GetGeometry(), *rightHandSide.GetGeometry(), eps, verbose) ) { if(verbose) MITK_INFO << "[( PointSet )] Geometries differ."; result = false; } if ( leftHandSide.GetSize() != rightHandSide.GetSize()) { if(verbose) MITK_INFO << "[( PointSet )] Number of points differ."; result = false; } else { //if the size is equal, we compare the point values mitk::Point3D pointLeftHandSide; mitk::Point3D pointRightHandSide; int numberOfIncorrectPoints = 0; //Iterate over both pointsets in order to compare all points pair-wise mitk::PointSet::PointsConstIterator end = leftHandSide.End(); for( mitk::PointSet::PointsConstIterator pointSetIteratorLeft = leftHandSide.Begin(), pointSetIteratorRight = rightHandSide.Begin(); pointSetIteratorLeft != end; ++pointSetIteratorLeft, ++pointSetIteratorRight) //iterate simultaneously over both sets { pointLeftHandSide = pointSetIteratorLeft.Value(); pointRightHandSide = pointSetIteratorRight.Value(); if( !mitk::Equal( pointLeftHandSide, pointRightHandSide, eps, verbose ) ) { if(verbose) MITK_INFO << "[( PointSet )] Point values are different."; result = false; numberOfIncorrectPoints++; } } if((numberOfIncorrectPoints > 0) && verbose) { MITK_INFO << numberOfIncorrectPoints <<" of a total of " << leftHandSide.GetSize() << " points are different."; } } return result; } diff --git a/Core/Code/DataManagement/mitkProportionalTimeGeometry.cpp b/Core/Code/DataManagement/mitkProportionalTimeGeometry.cpp index acb3327944..4f6a5ccd3c 100644 --- a/Core/Code/DataManagement/mitkProportionalTimeGeometry.cpp +++ b/Core/Code/DataManagement/mitkProportionalTimeGeometry.cpp @@ -1,237 +1,241 @@ /*=================================================================== 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 mitk::ProportionalTimeGeometry::ProportionalTimeGeometry() : m_FirstTimePoint(0.0), m_StepDuration(1.0) { } mitk::ProportionalTimeGeometry::~ProportionalTimeGeometry() { } void mitk::ProportionalTimeGeometry::Initialize() { m_FirstTimePoint = 0.0; m_StepDuration = 1.0; m_GeometryVector.resize(1); } mitk::TimeStepType mitk::ProportionalTimeGeometry::CountTimeSteps () const { return static_cast(m_GeometryVector.size() ); } mitk::TimePointType mitk::ProportionalTimeGeometry::GetMinimumTimePoint () const { return m_FirstTimePoint; } mitk::TimePointType mitk::ProportionalTimeGeometry::GetMaximumTimePoint () const { TimePointType timePoint = m_FirstTimePoint + m_StepDuration * CountTimeSteps(); if (timePoint >std::numeric_limits().max()) timePoint = std::numeric_limits().max(); return timePoint; } mitk::TimeBounds mitk::ProportionalTimeGeometry::GetTimeBounds () const { TimeBounds bounds; bounds[0] = this->GetMinimumTimePoint(); bounds[1] = this->GetMaximumTimePoint(); return bounds; } bool mitk::ProportionalTimeGeometry::IsValidTimePoint (TimePointType timePoint) const { return this->GetMinimumTimePoint() <= timePoint && timePoint < this->GetMaximumTimePoint(); } bool mitk::ProportionalTimeGeometry::IsValidTimeStep (TimeStepType timeStep) const { return timeStep < this->CountTimeSteps(); } mitk::TimePointType mitk::ProportionalTimeGeometry::TimeStepToTimePoint( TimeStepType timeStep) const { if (m_FirstTimePoint <= itk::NumericTraits::NonpositiveMin() || m_FirstTimePoint >= itk::NumericTraits::max() || m_StepDuration <= itk::NumericTraits::min() || m_StepDuration >= itk::NumericTraits::max()) { return static_cast(timeStep); } return m_FirstTimePoint + timeStep * m_StepDuration; } mitk::TimeStepType mitk::ProportionalTimeGeometry::TimePointToTimeStep( TimePointType timePoint) const { if (m_FirstTimePoint <= timePoint) return static_cast((timePoint -m_FirstTimePoint) / m_StepDuration); else return 0; } -mitk::Geometry3D::Pointer mitk::ProportionalTimeGeometry::GetGeometryForTimeStep( TimeStepType timeStep) const +mitk::BaseGeometry::Pointer mitk::ProportionalTimeGeometry::GetGeometryForTimeStep( TimeStepType timeStep) const { if (IsValidTimeStep(timeStep)) { - return dynamic_cast(m_GeometryVector[timeStep].GetPointer()); + return dynamic_cast(m_GeometryVector[timeStep].GetPointer()); } else { return 0; } } -mitk::Geometry3D::Pointer mitk::ProportionalTimeGeometry::GetGeometryForTimePoint(TimePointType timePoint) const +mitk::BaseGeometry::Pointer mitk::ProportionalTimeGeometry::GetGeometryForTimePoint(TimePointType timePoint) const { if (this->IsValidTimePoint(timePoint)) { TimeStepType timeStep = this->TimePointToTimeStep(timePoint); return this->GetGeometryForTimeStep(timeStep); } else { return 0; } } -mitk::Geometry3D::Pointer mitk::ProportionalTimeGeometry::GetGeometryCloneForTimeStep( TimeStepType timeStep) const +mitk::BaseGeometry::Pointer mitk::ProportionalTimeGeometry::GetGeometryCloneForTimeStep( TimeStepType timeStep) const { if (timeStep > m_GeometryVector.size()) return 0; - return m_GeometryVector[timeStep]->Clone(); + itk::LightObject::Pointer lopointer = m_GeometryVector[timeStep]->Clone(); + return dynamic_cast(lopointer.GetPointer()); } bool mitk::ProportionalTimeGeometry::IsValid() const { bool isValid = true; isValid &= m_GeometryVector.size() > 0; isValid &= m_StepDuration > 0; return isValid; } void mitk::ProportionalTimeGeometry::ClearAllGeometries() { m_GeometryVector.clear(); } void mitk::ProportionalTimeGeometry::ReserveSpaceForGeometries(TimeStepType numberOfGeometries) { m_GeometryVector.reserve(numberOfGeometries); } void mitk::ProportionalTimeGeometry::Expand(mitk::TimeStepType size) { m_GeometryVector.reserve(size); while (m_GeometryVector.size() < size) { - m_GeometryVector.push_back(Geometry3D::New()); + Geometry3D::Pointer geo3D = Geometry3D::New(); + m_GeometryVector.push_back(dynamic_cast(geo3D.GetPointer())); } } -void mitk::ProportionalTimeGeometry::SetTimeStepGeometry(Geometry3D* geometry, TimeStepType timeStep) +void mitk::ProportionalTimeGeometry::SetTimeStepGeometry(BaseGeometry* geometry, TimeStepType timeStep) { assert(timeStep<=m_GeometryVector.size()); if (timeStep == m_GeometryVector.size()) m_GeometryVector.push_back(geometry); m_GeometryVector[timeStep] = geometry; } itk::LightObject::Pointer mitk::ProportionalTimeGeometry::InternalClone() const { itk::LightObject::Pointer parent = Superclass::InternalClone(); ProportionalTimeGeometry::Pointer newTimeGeometry = dynamic_cast (parent.GetPointer()); newTimeGeometry->m_FirstTimePoint = this->m_FirstTimePoint; newTimeGeometry->m_StepDuration = this->m_StepDuration; newTimeGeometry->m_GeometryVector.clear(); newTimeGeometry->Expand(this->CountTimeSteps()); for (TimeStepType i =0; i < CountTimeSteps(); ++i) { - Geometry3D::Pointer tempGeometry = GetGeometryForTimeStep(i)->Clone(); + itk::LightObject::Pointer lopointer=GetGeometryForTimeStep(i)->Clone(); + BaseGeometry::Pointer tempGeometry = dynamic_cast(lopointer.GetPointer()); newTimeGeometry->SetTimeStepGeometry(tempGeometry.GetPointer(),i); } return parent; } -void mitk::ProportionalTimeGeometry::Initialize (Geometry3D* geometry, TimeStepType timeSteps) +void mitk::ProportionalTimeGeometry::Initialize (BaseGeometry* geometry, TimeStepType timeSteps) { timeSteps = (timeSteps > 0) ? timeSteps : 1; m_FirstTimePoint = geometry->GetTimeBounds()[0]; m_StepDuration = geometry->GetTimeBounds()[1] - geometry->GetTimeBounds()[0]; this->ReserveSpaceForGeometries(timeSteps); try{ for (TimeStepType currentStep = 0; currentStep < timeSteps; ++currentStep) { mitk::TimeBounds timeBounds; if (timeSteps > 1) { timeBounds[0] = m_FirstTimePoint + currentStep * m_StepDuration; timeBounds[1] = m_FirstTimePoint + (currentStep+1) * m_StepDuration; } else { timeBounds = geometry->GetTimeBounds(); } - - Geometry3D::Pointer clonedGeometry = geometry->Clone(); + itk::LightObject::Pointer lopointer=geometry->Clone(); + BaseGeometry::Pointer clonedGeometry = dynamic_cast(lopointer.GetPointer()); this->SetTimeStepGeometry(clonedGeometry.GetPointer(), currentStep); GetGeometryForTimeStep(currentStep)->SetTimeBounds(timeBounds); } } catch (...) { MITK_INFO << "Cloning of geometry produced an error!"; } Update(); } void mitk::ProportionalTimeGeometry::Initialize (TimeStepType timeSteps) { - mitk::Geometry3D::Pointer geometry = mitk::Geometry3D::New(); + mitk::Geometry3D::Pointer geo3D = Geometry3D::New(); + mitk::BaseGeometry::Pointer geometry = dynamic_cast(geo3D.GetPointer()); geometry->Initialize(); if ( timeSteps > 1 ) { mitk::ScalarType timeBounds[] = {0.0, 1.0}; geometry->SetTimeBounds( timeBounds ); } this->Initialize(geometry.GetPointer(), timeSteps); } void mitk::ProportionalTimeGeometry::PrintSelf(std::ostream& os, itk::Indent indent) const { os << indent << " TimeSteps: " << this->CountTimeSteps() << std::endl; os << indent << " FirstTimePoint: " << this->GetFirstTimePoint() << std::endl; os << indent << " StepDuration: " << this->GetStepDuration() << " ms" << std::endl; os << std::endl; os << indent << " GetGeometryForTimeStep(0): "; if(GetGeometryForTimeStep(0).IsNull()) os << "NULL" << std::endl; else GetGeometryForTimeStep(0)->Print(os, indent); } diff --git a/Core/Code/DataManagement/mitkProportionalTimeGeometry.h b/Core/Code/DataManagement/mitkProportionalTimeGeometry.h index 1a4c77d8bd..4f27763f19 100644 --- a/Core/Code/DataManagement/mitkProportionalTimeGeometry.h +++ b/Core/Code/DataManagement/mitkProportionalTimeGeometry.h @@ -1,207 +1,208 @@ /*=================================================================== 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 ProportionalTimeGeometry_h #define ProportionalTimeGeometry_h //MITK #include +#include #include #include namespace mitk { /** * \brief Organizes geometries over proportional time steps * * For this TimeGeometry implementation it is assumed that * the durations of the time steps are equidistant, e.g. * the durations of the time steps in one ProportionalTimeGeometry * are the same. The geometries of the time steps are independent, * and not linked to each other. Since the timeBounds of the * geometries are different for each time step it is not possible * to set the same geometry to different time steps. Instead * copies should be used. * * \addtogroup geometry */ class MITK_CORE_EXPORT ProportionalTimeGeometry : public TimeGeometry { public: mitkClassMacro(ProportionalTimeGeometry, TimeGeometry); ProportionalTimeGeometry(); typedef ProportionalTimeGeometry self; itkFactorylessNewMacro(Self) itkCloneMacro(Self) /** * \brief Returns the number of time steps. * * Returns the number of time steps for which * geometries are saved. The number of time steps * is also the upper bound of the time steps. The * minimum time steps is always 0. */ virtual TimeStepType CountTimeSteps() const; /** * \brief Returns the first time point for which the object is valid. * * Returns the first valid time point for this geometry. If only one * time steps available it usually goes from -max to +max. The time point * is given in ms. */ virtual TimePointType GetMinimumTimePoint () const; /** * \brief Returns the last time point for which the object is valid * * Gives the last time point for which a valid geometrie is saved in * this time geometry. The time point is given in ms. */ virtual TimePointType GetMaximumTimePoint () const; /** * \brief Get the time bounds (in ms) */ virtual TimeBounds GetTimeBounds( ) const; /** * \brief Tests if a given time point is covered by this object * * Returns true if a geometry can be returned for the given time * point and falls if not. The time point must be given in ms. */ virtual bool IsValidTimePoint (TimePointType timePoint) const; /** * \brief Test for the given time step if a geometry is availible * * Returns true if a geometry is defined for the given time step. * Otherwise false is returned. * The time step is defined as positiv number. */ virtual bool IsValidTimeStep (TimeStepType timeStep) const; /** * \brief Converts a time step to a time point * * Converts a time step to a time point in a way that * the new time point indicates the same geometry as the time step. * If the original time steps does not point to a valid geometry, * a time point is calculated that also does not point to a valid * geometry, but no exception is raised. */ virtual TimePointType TimeStepToTimePoint (TimeStepType timeStep) const; /** * \brief Converts a time point to the corresponding time step * * Converts a time point to a time step in a way that * the new time step indicates the same geometry as the time point. * If a negativ invalid time point is given always time step 0 is * returned. If an positiv invalid time step is given an invalid * time step will be returned. */ virtual TimeStepType TimePointToTimeStep (TimePointType timePoint) const; /** * \brief Returns the geometry which corresponds to the given time step * * Returns a clone of the geometry which defines the given time step. If * the given time step is invalid an null-pointer is returned. */ - virtual Geometry3D::Pointer GetGeometryCloneForTimeStep( TimeStepType timeStep) const; + virtual BaseGeometry::Pointer GetGeometryCloneForTimeStep( TimeStepType timeStep) const; /** * \brief Returns the geometry which corresponds to the given time point * * Returns the geometry which defines the given time point. If * the given time point is invalid an null-pointer is returned. * * If the returned geometry is changed this will affect the saved * geometry. */ - virtual Geometry3D::Pointer GetGeometryForTimePoint ( TimePointType timePoint) const; + virtual BaseGeometry::Pointer GetGeometryForTimePoint ( TimePointType timePoint) const; /** * \brief Returns the geometry which corresponds to the given time step * * Returns the geometry which defines the given time step. If * the given time step is invalid an null-pointer is returned. * * If the returned geometry is changed this will affect the saved * geometry. */ - virtual Geometry3D::Pointer GetGeometryForTimeStep ( TimeStepType timeStep) const; + virtual BaseGeometry::Pointer GetGeometryForTimeStep ( TimeStepType timeStep) const; /** * \brief Tests if all necessary informations are set and the object is valid */ virtual bool IsValid () const; /** * \brief Initilizes a new object with one time steps which contains an empty geometry. */ virtual void Initialize(); /** * \brief Expands the time geometry to the given number of time steps. * * Initializes the new time steps with empty geometries. * Shrinking is not supported. */ virtual void Expand(TimeStepType size); /** * \brief Sets the geometry for the given time step * * This method does not afflict other time steps, since the geometry for * each time step is saved individually. */ - virtual void SetTimeStepGeometry(Geometry3D* geometry, TimeStepType timeStep); + virtual void SetTimeStepGeometry(BaseGeometry* geometry, TimeStepType timeStep); /** * \brief Makes a deep copy of the current object */ virtual itk::LightObject::Pointer InternalClone () const; itkGetConstMacro(FirstTimePoint, TimePointType); itkSetMacro(FirstTimePoint, TimePointType); itkGetConstMacro(StepDuration, TimePointType); itkSetMacro(StepDuration, TimePointType); // void SetGeometryForTimeStep(TimeStepType timeStep, BaseGeometry& geometry); void ClearAllGeometries (); // void AddGeometry(BaseGeometry geometry); void ReserveSpaceForGeometries (TimeStepType numberOfGeometries); /** * \brief Initializes the TimeGeometry with equally time Step geometries * * Saves a copy for each time step. */ - void Initialize (Geometry3D* geometry, TimeStepType timeSteps); + void Initialize (BaseGeometry* geometry, TimeStepType timeSteps); /** - * \brief Initialize the TimeGeometry with empty Geometry3D + * \brief Initialize the TimeGeometry with empty BaseGeometry */ void Initialize (TimeStepType timeSteps); virtual void PrintSelf(std::ostream& os, itk::Indent indent) const; protected: virtual ~ProportionalTimeGeometry(); - std::vector m_GeometryVector; + std::vector m_GeometryVector; TimePointType m_FirstTimePoint; TimePointType m_StepDuration; }; // end class ProportialTimeGeometry } // end namespace MITK #endif // ProportionalTimeGeometry_h diff --git a/Core/Code/DataManagement/mitkSlicedData.cpp b/Core/Code/DataManagement/mitkSlicedData.cpp index 73548a368a..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(Geometry3D* aGeometry3D) +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 4530570c31..64791b7883 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(Geometry3D* aGeometry3D); + 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 e63b6ea31a..6a244be06d 100644 --- a/Core/Code/DataManagement/mitkSlicedGeometry3D.cpp +++ b/Core/Code/DataManagement/mitkSlicedGeometry3D.cpp @@ -1,1055 +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_EvenlySpaced( true ), m_Slices( 0 ), m_ReferenceGeometry( NULL ), m_SliceNavigationController( NULL ) { - m_DirectionVector.Fill(0); + m_DirectionVector.Fill(0); this->InitializeSlicedGeometry( m_Slices ); } mitk::SlicedGeometry3D::SlicedGeometry3D(const SlicedGeometry3D& other) -: Superclass(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); + 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::SlicedGeometry3D::GetGeometry2D( int s ) const +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 * m_Spacing[2]; + 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 + mitk::SlicedGeometry3D::GetBoundingBox() const { - assert(m_BoundingBox.IsNotNull()); - return m_BoundingBox.GetPointer(); + 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 ) + 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::SlicedGeometry3D::InitializeEvenlySpaced( + 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::SlicedGeometry3D::InitializeEvenlySpaced( + 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); - m_IndexToWorldTransform = const_cast< AffineTransform3D * >( - geometry2D->GetIndexToWorldTransform() ); + this->SetIndexToWorldTransform( const_cast< AffineTransform3D * >( + geometry2D->GetIndexToWorldTransform() )); } else { directionVector *= -1.0; - m_IndexToWorldTransform = AffineTransform3D::New(); - m_IndexToWorldTransform->SetMatrix( + this->SetIndexToWorldTransform( AffineTransform3D::New()); + this->GetIndexToWorldTransform()->SetMatrix( geometry2D->GetIndexToWorldTransform()->GetMatrix() ); AffineTransform3D::OutputVectorType scaleVector; FillVector3D(scaleVector, 1.0, 1.0, -1.0); - m_IndexToWorldTransform->Scale(scaleVector, true); - m_IndexToWorldTransform->SetOffset( + 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 ); - // Ensure that spacing differs from m_Spacing to make SetSpacing change the - // matrix. - m_Spacing[2] = zSpacing - 1; - this->SetDirectionVector( directionVector ); this->SetBounds( bounds ); this->SetGeometry2D( geometry2D, 0 ); - this->SetSpacing( spacing ); + this->SetSpacing( spacing ,true); this->SetEvenlySpaced(); this->SetTimeBounds( geometry2D->GetTimeBounds() ); - assert(m_IndexToWorldTransform.GetPointer() - != geometry2D->GetIndexToWorldTransform()); // (**) see above. + assert(this->GetIndexToWorldTransform() + != geometry2D->GetIndexToWorldTransform()); // (**) see above. this->SetFrameOfReferenceID( geometry2D->GetFrameOfReferenceID() ); this->SetImageGeometry( geometry2D->GetImageGeometry() ); geometry2D->UnRegister(); } - void -mitk::SlicedGeometry3D::InitializePlanes( - const mitk::Geometry3D *geometry3D, + mitk::SlicedGeometry3D::InitializePlanes( + const mitk::BaseGeometry *geometry3D, mitk::PlaneGeometry::PlaneOrientation planeorientation, bool top, bool frontside, bool rotated ) { - m_ReferenceGeometry = const_cast< Geometry3D * >( geometry3D ); + 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"); + 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( 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 ) + 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( 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 + 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 ); + // 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 + 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 ) + mitk::SlicedGeometry3D::SetImageGeometry( const bool isAnImageGeometry ) { Superclass::SetImageGeometry( isAnImageGeometry ); - mitk::Geometry3D* geometry; + 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::SlicedGeometry3D::ChangeImageGeometryConsideringOriginOffset( const bool isAnImageGeometry ) { - mitk::Geometry3D* geometry; + 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 + mitk::SlicedGeometry3D::IsValidSlice( int s ) const { return ((s >= 0) && (s < (int)m_Slices)); } void -mitk::SlicedGeometry3D::SetReferenceGeometry( Geometry3D *referenceGeometry ) + 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::SetSpacing( const mitk::Vector3D &aSpacing ) + mitk::SlicedGeometry3D::PreSetSpacing( 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; } } - Superclass::SetSpacing(aSpacing); + _SetSpacing(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(), &m_Spacing ); + 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 ) + mitk::SlicedGeometry3D + ::SetSliceNavigationController( SliceNavigationController *snc ) { m_SliceNavigationController = snc; } - mitk::SliceNavigationController * -mitk::SlicedGeometry3D::GetSliceNavigationController() + mitk::SlicedGeometry3D::GetSliceNavigationController() { return m_SliceNavigationController; } void -mitk::SlicedGeometry3D::SetEvenlySpaced(bool on) + mitk::SlicedGeometry3D::SetEvenlySpaced(bool on) { if(m_EvenlySpaced!=on) { m_EvenlySpaced=on; this->Modified(); } } - void -mitk::SlicedGeometry3D -::SetDirectionVector( const mitk::Vector3D& directionVector ) + mitk::SlicedGeometry3D + ::SetDirectionVector( const mitk::Vector3D& directionVector ) { - Vector3D newDir = directionVector; - newDir.Normalize(); - if ( newDir != m_DirectionVector ) + Vector3D newDir = directionVector; + newDir.Normalize(); + if ( newDir != m_DirectionVector ) { - m_DirectionVector = newDir; + m_DirectionVector = newDir; this->Modified(); } } - void -mitk::SlicedGeometry3D::SetTimeBounds( const mitk::TimeBounds& timebounds ) + mitk::SlicedGeometry3D::PostSetTimeBounds( const mitk::TimeBounds& timebounds ) { - Superclass::SetTimeBounds( timebounds ); - unsigned int s; for ( s = 0; s < m_Slices; ++s ) { if(m_Geometry2Ds[s].IsNotNull()) { m_Geometry2Ds[s]->SetTimeBounds( timebounds ); } } - m_TimeBounds = timebounds; } - itk::LightObject::Pointer -mitk::SlicedGeometry3D::InternalClone() const + 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 + 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) + 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(); } - Geometry3D::ExecuteOperation( ¢eredRotation ); + 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(); - iter != m_Geometry2Ds.end(); - ++iter) - { - // Test for empty slices, which can happen if evenly spaced geometry - if ((*iter).IsNotNull()) - { - (*iter)->ExecuteOperation(operation); - } - } + // Reach through to all slices in my container + 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 ); - Geometry3D::ExecuteOperation( rotOp); + BaseGeometry::ExecuteOperation( rotOp); } - } } else { // Reach through to all slices - for (std::vector::iterator iter = m_Geometry2Ds.begin(); - iter != m_Geometry2Ds.end(); - ++iter) + 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 + // 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; + 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()); + // 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(); + // 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 ); + // 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 - ); + 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(); - } + // 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) - Geometry3D::ExecuteOperation( ¢eredRotation ); + 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) - Geometry3D::ExecuteOperation( &op ); + 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(); - iter != m_Geometry2Ds.end(); - ++iter) + 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; - } + 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(); } - Geometry3D::ExecuteOperation(restorePlaneOp); + BaseGeometry::ExecuteOperation(restorePlaneOp); } } else { // Reach through to all slices - for (std::vector::iterator iter = m_Geometry2Ds.begin(); - iter != m_Geometry2Ds.end(); - ++iter) + 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() ); - Geometry3D::ExecuteOperation( applyMatrixOp ); + BaseGeometry::ExecuteOperation( applyMatrixOp ); break; } this->Modified(); } - diff --git a/Core/Code/DataManagement/mitkSlicedGeometry3D.h b/Core/Code/DataManagement/mitkSlicedGeometry3D.h index 34c4810e80..584c36d641 100644 --- a/Core/Code/DataManagement/mitkSlicedGeometry3D.h +++ b/Core/Code/DataManagement/mitkSlicedGeometry3D.h @@ -1,325 +1,314 @@ /*=================================================================== 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 "mitkGeometry3D.h" +#include "mitkBaseGeometry.h" #include "mitkPlaneGeometry.h" namespace mitk { + class SliceNavigationController; + class NavigationController; -class SliceNavigationController; -class NavigationController; - -/** \brief Describes the geometry of a data object consisting of slices. + /** \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::Geometry3D -{ -public: - mitkClassMacro(SlicedGeometry3D, Geometry3D); + class MITK_CORE_EXPORT SlicedGeometry3D : public mitk::BaseGeometry + { + public: + mitkClassMacro(SlicedGeometry3D, BaseGeometry); /** Method for creation through the object factory. */ itkFactorylessNewMacro(Self) itkCloneMacro(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 void SetTimeBounds( const mitk::TimeBounds& timebounds ); - - 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( Geometry3D *referenceGeometry ); - - /** - * \brief Set the spacing (m_Spacing), in direction of the plane normal. - * - * INTERNAL METHOD. - */ - virtual void SetSpacing( const mitk::Vector3D &aSpacing ); + 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::Geometry3D *geometry3D, + 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: + protected: SlicedGeometry3D(); SlicedGeometry3D(const SlicedGeometry3D& other); virtual ~SlicedGeometry3D(); + virtual void PreSetBounds(const BoundsArrayType& bounds){}; + + virtual void PostSetTimeBounds( const mitk::TimeBounds& timebounds ); + + /** + * \brief Set the spacing (m_Spacing), in direction of the plane normal. + * + * INTERNAL METHOD. + */ + virtual void PreSetSpacing( 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 Geometry3D *geometry ); + 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::Geometry3D *m_ReferenceGeometry; + 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/DataManagement/mitkSurface.cpp b/Core/Code/DataManagement/mitkSurface.cpp index 87d8b21755..27a0341c17 100644 --- a/Core/Code/DataManagement/mitkSurface.cpp +++ b/Core/Code/DataManagement/mitkSurface.cpp @@ -1,530 +1,530 @@ /*=================================================================== 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 "mitkSurface.h" #include "mitkInteractionConst.h" #include "mitkSurfaceOperation.h" #include #include static vtkPolyData* DeepCopy(vtkPolyData* other) { if (other == NULL) return NULL; vtkPolyData* copy = vtkPolyData::New(); copy->DeepCopy(other); return copy; } static void Delete(vtkPolyData* polyData) { if (polyData != NULL) polyData->Delete(); } static void Update(vtkPolyData* /*polyData*/) { // if (polyData != NULL) // polyData->Update(); //VTK6_TODO vtk pipeline } mitk::Surface::Surface() : m_CalculateBoundingBox(false) { this->InitializeEmpty(); } mitk::Surface::Surface(const mitk::Surface& other) : BaseData(other), m_LargestPossibleRegion(other.m_LargestPossibleRegion), m_RequestedRegion(other.m_RequestedRegion), m_CalculateBoundingBox(other.m_CalculateBoundingBox) { if(!other.m_PolyDatas.empty()) { m_PolyDatas.resize(other.m_PolyDatas.size()); std::transform(other.m_PolyDatas.begin(), other.m_PolyDatas.end(), m_PolyDatas.begin(), DeepCopy); } else { this->InitializeEmpty(); } } void mitk::Surface::Swap(mitk::Surface& other) { std::swap(m_PolyDatas, other.m_PolyDatas); std::swap(m_LargestPossibleRegion, other.m_LargestPossibleRegion); std::swap(m_RequestedRegion, other.m_RequestedRegion); std::swap(m_CalculateBoundingBox, other.m_CalculateBoundingBox); } mitk::Surface& mitk::Surface::operator=(Surface other) { this->Swap(other); return *this; } mitk::Surface::~Surface() { this->ClearData(); } void mitk::Surface::ClearData() { using ::Delete; std::for_each(m_PolyDatas.begin(), m_PolyDatas.end(), Delete); m_PolyDatas.clear(); Superclass::ClearData(); } const mitk::Surface::RegionType& mitk::Surface::GetLargestPossibleRegion() const { m_LargestPossibleRegion.SetIndex(3, 0); m_LargestPossibleRegion.SetSize(3, GetTimeGeometry()->CountTimeSteps()); return m_LargestPossibleRegion; } const mitk::Surface::RegionType& mitk::Surface::GetRequestedRegion() const { return m_RequestedRegion; } void mitk::Surface::InitializeEmpty() { if (!m_PolyDatas.empty()) this->ClearData(); Superclass::InitializeTimeGeometry(); m_PolyDatas.push_back(NULL); m_Initialized = true; } void mitk::Surface::SetVtkPolyData(vtkPolyData* polyData, unsigned int t) { this->Expand(t + 1); if (m_PolyDatas[t] != NULL) { if (m_PolyDatas[t] == polyData) return; m_PolyDatas[t]->Delete(); } m_PolyDatas[t] = polyData; if(polyData != NULL) polyData->Register(NULL); m_CalculateBoundingBox = true; this->Modified(); this->UpdateOutputInformation(); } bool mitk::Surface::IsEmptyTimeStep(unsigned int t) const { if(!IsInitialized()) return false; vtkPolyData* polyData = const_cast(this)->GetVtkPolyData(t); return polyData == NULL || ( polyData->GetNumberOfLines() == 0 && polyData->GetNumberOfPolys() == 0 && polyData->GetNumberOfStrips() == 0 && polyData->GetNumberOfVerts() == 0 ); } vtkPolyData* mitk::Surface::GetVtkPolyData(unsigned int t) { if (t < m_PolyDatas.size()) { if(m_PolyDatas[t] == NULL && this->GetSource().IsNotNull()) { RegionType requestedRegion; requestedRegion.SetIndex(3, t); requestedRegion.SetSize(3, 1); this->SetRequestedRegion(&requestedRegion); this->GetSource()->Update(); } return m_PolyDatas[t]; } return NULL; } void mitk::Surface::UpdateOutputInformation() { if (this->GetSource().IsNotNull()) this->GetSource()->UpdateOutputInformation(); if (m_CalculateBoundingBox == true && !m_PolyDatas.empty()) this->CalculateBoundingBox(); else this->GetTimeGeometry()->Update(); } void mitk::Surface::CalculateBoundingBox() { TimeGeometry* timeGeometry = this->GetTimeGeometry(); if (timeGeometry->CountTimeSteps() != m_PolyDatas.size()) mitkThrow() << "Number of geometry time steps is inconsistent with number of poly data pointers."; for (unsigned int i = 0; i < m_PolyDatas.size(); ++i) { vtkPolyData* polyData = m_PolyDatas[i]; double bounds[6] = {0}; if (polyData != NULL && polyData->GetNumberOfPoints() > 0) { // polyData->Update(); //VTK6_TODO vtk pipeline polyData->ComputeBounds(); polyData->GetBounds(bounds); } - Geometry3D::Pointer geometry = timeGeometry->GetGeometryForTimeStep(i); + BaseGeometry::Pointer geometry = timeGeometry->GetGeometryForTimeStep(i); if (geometry.IsNull()) mitkThrow() << "Time-sliced geometry is invalid (equals NULL)."; geometry->SetFloatBounds(bounds); } timeGeometry->Update(); m_CalculateBoundingBox = false; } void mitk::Surface::SetRequestedRegionToLargestPossibleRegion() { m_RequestedRegion = GetLargestPossibleRegion(); } bool mitk::Surface::RequestedRegionIsOutsideOfTheBufferedRegion() { RegionType::IndexValueType end = m_RequestedRegion.GetIndex(3) + m_RequestedRegion.GetSize(3); if(static_cast(m_PolyDatas.size()) < end) return true; for(RegionType::IndexValueType t = m_RequestedRegion.GetIndex(3); t < end; ++t) { if(m_PolyDatas[t] == NULL) return true; } return false; } bool mitk::Surface::VerifyRequestedRegion() { if(m_RequestedRegion.GetIndex(3) >= 0 && m_RequestedRegion.GetIndex(3) + m_RequestedRegion.GetSize(3) <= m_PolyDatas.size()) return true; return false; } void mitk::Surface::SetRequestedRegion(const itk::DataObject* data ) { const mitk::Surface *surface = dynamic_cast(data); if (surface != NULL) m_RequestedRegion = surface->GetRequestedRegion(); else mitkThrow() << "Data object used to get requested region is not a mitk::Surface."; } void mitk::Surface::SetRequestedRegion(Surface::RegionType* region) { if (region == NULL) mitkThrow() << "Requested region is invalid (equals NULL)"; m_RequestedRegion = *region; } void mitk::Surface::CopyInformation(const itk::DataObject* data) { Superclass::CopyInformation(data); const mitk::Surface* surface = dynamic_cast(data); if (surface == NULL) mitkThrow() << "Data object used to get largest possible region is not a mitk::Surface."; m_LargestPossibleRegion = surface->GetLargestPossibleRegion(); } void mitk::Surface::Update() { using ::Update; if (this->GetSource().IsNull()) std::for_each(m_PolyDatas.begin(), m_PolyDatas.end(), Update); Superclass::Update(); } void mitk::Surface::Expand(unsigned int timeSteps) { if (timeSteps > m_PolyDatas.size()) { Superclass::Expand(timeSteps); m_PolyDatas.resize(timeSteps); m_CalculateBoundingBox = true; } } void mitk::Surface::ExecuteOperation(Operation* operation) { switch (operation->GetOperationType()) { case OpSURFACECHANGED: { mitk::SurfaceOperation* surfaceOperation = dynamic_cast(operation); if(surfaceOperation == NULL) break; unsigned int timeStep = surfaceOperation->GetTimeStep(); if(m_PolyDatas[timeStep] != NULL) { vtkPolyData* updatedPolyData = surfaceOperation->GetVtkPolyData(); if(updatedPolyData != NULL) { this->SetVtkPolyData(updatedPolyData, timeStep); this->CalculateBoundingBox(); this->Modified(); } } break; } default: return; } } unsigned int mitk::Surface::GetSizeOfPolyDataSeries() const { return m_PolyDatas.size(); } void mitk::Surface::Graft(const DataObject* data) { const Surface* surface = dynamic_cast(data); if(surface == NULL) mitkThrow() << "Data object used to graft surface is not a mitk::Surface."; this->CopyInformation(data); m_PolyDatas.clear(); for (unsigned int i = 0; i < surface->GetSizeOfPolyDataSeries(); ++i) { m_PolyDatas.push_back(vtkPolyData::New()); m_PolyDatas.back()->DeepCopy(const_cast(surface)->GetVtkPolyData(i)); } } void mitk::Surface::PrintSelf(std::ostream& os, itk::Indent indent) const { Superclass::PrintSelf(os, indent); os << indent << "\nNumber PolyDatas: " << m_PolyDatas.size() << "\n"; unsigned int count = 0; for (std::vector::const_iterator it = m_PolyDatas.begin(); it != m_PolyDatas.end(); ++it) { os << "\n"; if(*it != NULL) { os << indent << "PolyData at time step " << count << ":\n"; os << indent << "Number of cells: " << (*it)->GetNumberOfCells() << "\n"; os << indent << "Number of points: " << (*it)->GetNumberOfPoints() << "\n\n"; os << indent << "VTKPolyData:\n"; (*it)->Print(os); } else { os << indent << "Empty PolyData at time step " << count << "\n"; } ++count; } } bool mitk::Equal( vtkPolyData* leftHandSide, vtkPolyData* rightHandSide, mitk::ScalarType eps, bool verbose ) { if(( leftHandSide == NULL ) || ( rightHandSide == NULL )) { MITK_ERROR << "mitk::Equal( vtkPolyData* leftHandSide, vtkPolyData* rightHandSide, mitk::ScalarType eps, bool verbose ) does not work for NULL pointer input."; return false; } return Equal( *leftHandSide, *rightHandSide, eps, verbose); } bool mitk::Equal( vtkPolyData& leftHandSide, vtkPolyData& rightHandSide, mitk::ScalarType eps, bool verbose ) { bool noDifferenceFound = true; if( ! mitk::Equal( leftHandSide.GetNumberOfCells(), rightHandSide.GetNumberOfCells(), eps, verbose ) ) { if(verbose) MITK_INFO << "[Equal( vtkPolyData*, vtkPolyData* )] Number of cells not equal"; noDifferenceFound = false; } if( ! mitk::Equal( leftHandSide.GetNumberOfVerts(), rightHandSide.GetNumberOfVerts(), eps, verbose )) { if(verbose) MITK_INFO << "[Equal( vtkPolyData*, vtkPolyData* )] Number of vertices not equal"; noDifferenceFound = false; } if( ! mitk::Equal( leftHandSide.GetNumberOfLines(), rightHandSide.GetNumberOfLines(), eps, verbose ) ) { if(verbose) MITK_INFO << "[Equal( vtkPolyData*, vtkPolyData* )] Number of lines not equal"; noDifferenceFound = false; } if( ! mitk::Equal( leftHandSide.GetNumberOfPolys(), rightHandSide.GetNumberOfPolys(), eps, verbose ) ) { if(verbose) MITK_INFO << "[Equal( vtkPolyData*, vtkPolyData* )] Number of polys not equal"; noDifferenceFound = false; } if( ! mitk::Equal( leftHandSide.GetNumberOfStrips(), rightHandSide.GetNumberOfStrips(), eps, verbose ) ) { if(verbose) MITK_INFO << "[Equal( vtkPolyData*, vtkPolyData* )] Number of strips not equal"; noDifferenceFound = false; } { unsigned int numberOfPointsRight = rightHandSide.GetPoints()->GetNumberOfPoints(); unsigned int numberOfPointsLeft = leftHandSide.GetPoints()->GetNumberOfPoints(); if(! mitk::Equal( numberOfPointsLeft, numberOfPointsRight, eps, verbose )) { if(verbose) MITK_INFO << "[Equal( vtkPolyData*, vtkPolyData* )] Number of points not equal"; noDifferenceFound = false; } else { for( unsigned int i( 0 ); i < numberOfPointsRight; i++ ) { bool pointFound = false; double pointOne[3]; rightHandSide.GetPoints()->GetPoint(i, pointOne); for( unsigned int j( 0 ); j < numberOfPointsLeft; j++ ) { double pointTwo[3]; leftHandSide.GetPoints()->GetPoint(j, pointTwo); double x = pointOne[0] - pointTwo[0]; double y = pointOne[1] - pointTwo[1]; double z = pointOne[2] - pointTwo[2]; double distance = x*x + y*y + z*z; if( distance < eps ) { pointFound = true; break; } } if( !pointFound ) { if(verbose) { MITK_INFO << "[Equal( vtkPolyData*, vtkPolyData* )] Right hand side point with id " << i << " and coordinates ( " << std::setprecision(12) << pointOne[0] << " ; " << pointOne[1] << " ; " << pointOne[2] << " ) could not be found in left hand side with epsilon " << eps << "."; } noDifferenceFound = false; break; } } } } return noDifferenceFound; } bool mitk::Equal( mitk::Surface* leftHandSide, mitk::Surface* rightHandSide, mitk::ScalarType eps, bool verbose ) { if(( leftHandSide == NULL ) || ( rightHandSide == NULL )) { MITK_ERROR << "mitk::Equal( mitk::Surface* leftHandSide, mitk::Surface* rightHandSide, mitk::ScalarType eps, bool verbose ) does not work with NULL pointer input."; return false; } return Equal( *leftHandSide, *rightHandSide, eps, verbose); } bool mitk::Equal( mitk::Surface& leftHandSide, mitk::Surface& rightHandSide, mitk::ScalarType eps, bool verbose ) { bool noDifferenceFound = true; if( ! mitk::Equal( leftHandSide.GetSizeOfPolyDataSeries(), rightHandSide.GetSizeOfPolyDataSeries(), eps, verbose ) ) { if(verbose) MITK_INFO << "[Equal( mitk::surface&, mitk::surface& )] Size of PolyData series not equal."; return false; } // No mitk::Equal for TimeGeometry implemented. //if( ! mitk::Equal( leftHandSide->GetTimeGeometry(), rightHandSide->GetTimeGeometry(), eps, verbose ) ) //{ // if(verbose) // MITK_INFO << "[Equal( mitk::surface&, mitk::surface& )] Time sliced geometries not equal"; // noDifferenceFound = false; //} for( unsigned int i( 0 ); i < rightHandSide.GetSizeOfPolyDataSeries(); i++ ) { if( ! mitk::Equal( *leftHandSide.GetVtkPolyData( i ), *rightHandSide.GetVtkPolyData( i ), eps, verbose ) ) { if(verbose) MITK_INFO << "[Equal( mitk::surface&, mitk::surface& )] Poly datas not equal."; noDifferenceFound = false; } } return noDifferenceFound; } diff --git a/Core/Code/DataManagement/mitkThinPlateSplineCurvedGeometry.cpp b/Core/Code/DataManagement/mitkThinPlateSplineCurvedGeometry.cpp index 43cdba71e9..562f2d6745 100644 --- a/Core/Code/DataManagement/mitkThinPlateSplineCurvedGeometry.cpp +++ b/Core/Code/DataManagement/mitkThinPlateSplineCurvedGeometry.cpp @@ -1,102 +1,100 @@ /*=================================================================== 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 "mitkThinPlateSplineCurvedGeometry.h" #include #include mitk::ThinPlateSplineCurvedGeometry::ThinPlateSplineCurvedGeometry() { m_InterpolatingAbstractTransform = m_ThinPlateSplineTransform = vtkThinPlateSplineTransform::New(); m_VtkTargetLandmarks = vtkPoints::New(); m_VtkProjectedLandmarks = vtkPoints::New(); m_ThinPlateSplineTransform->SetInverseIterations(5000); } mitk::ThinPlateSplineCurvedGeometry::ThinPlateSplineCurvedGeometry(const ThinPlateSplineCurvedGeometry& other ) : Superclass(other) { this->SetSigma(other.GetSigma()); } mitk::ThinPlateSplineCurvedGeometry::~ThinPlateSplineCurvedGeometry() { // don't need to delete m_ThinPlateSplineTransform, because it is // the same as m_InterpolatingAbstractTransform, which will be deleted // by the superclass. if(m_VtkTargetLandmarks!=NULL) m_VtkTargetLandmarks->Delete(); if(m_VtkProjectedLandmarks!=NULL) m_VtkProjectedLandmarks->Delete(); } bool mitk::ThinPlateSplineCurvedGeometry::IsValid() const { return m_TargetLandmarks.IsNotNull() && (m_TargetLandmarks->Size() >= 3) && m_LandmarkProjector.IsNotNull(); } void mitk::ThinPlateSplineCurvedGeometry::SetSigma(double sigma) { m_ThinPlateSplineTransform->SetSigma(sigma); } double mitk::ThinPlateSplineCurvedGeometry::GetSigma() const { return m_ThinPlateSplineTransform->GetSigma(); - } void mitk::ThinPlateSplineCurvedGeometry::ComputeGeometry() { Superclass::ComputeGeometry(); const mitk::PointSet::DataType::PointsContainer *finalTargetLandmarks, *projectedTargetLandmarks; finalTargetLandmarks = m_LandmarkProjector->GetFinalTargetLandmarks(); projectedTargetLandmarks = m_LandmarkProjector->GetProjectedLandmarks(); mitk::PointSet::DataType::PointsContainer::ConstIterator targetIt, projectedIt; targetIt = finalTargetLandmarks->Begin(); projectedIt = projectedTargetLandmarks->Begin(); //initialize Thin-Plate-Spline m_VtkTargetLandmarks->Reset(); m_VtkProjectedLandmarks->Reset(); vtkIdType id; int size=finalTargetLandmarks->Size(); for(id=0; id < size; ++id, ++targetIt, ++projectedIt) { const mitk::PointSet::PointType& target = targetIt->Value(); m_VtkTargetLandmarks->InsertPoint(id, target[0], target[1], target[2]); const mitk::PointSet::PointType& projected = projectedIt->Value(); m_VtkProjectedLandmarks->InsertPoint(id, projected[0], projected[1], projected[2]); } m_VtkTargetLandmarks->Modified(); m_VtkProjectedLandmarks->Modified(); m_ThinPlateSplineTransform->SetSourceLandmarks(m_VtkProjectedLandmarks); m_ThinPlateSplineTransform->SetTargetLandmarks(m_VtkTargetLandmarks); } itk::LightObject::Pointer mitk::ThinPlateSplineCurvedGeometry::InternalClone() const { - mitk::Geometry3D::Pointer newGeometry = new Self(*this); + mitk::BaseGeometry::Pointer newGeometry = new Self(*this); newGeometry->UnRegister(); return newGeometry.GetPointer(); } diff --git a/Core/Code/DataManagement/mitkThinPlateSplineCurvedGeometry.h b/Core/Code/DataManagement/mitkThinPlateSplineCurvedGeometry.h index d7fed98109..98ab9b8489 100644 --- a/Core/Code/DataManagement/mitkThinPlateSplineCurvedGeometry.h +++ b/Core/Code/DataManagement/mitkThinPlateSplineCurvedGeometry.h @@ -1,69 +1,65 @@ /*=================================================================== 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 MITKTHINPLATESPLINECURVEDGEOMETRY_H_HEADER_INCLUDED_C1C68A2C #define MITKTHINPLATESPLINECURVEDGEOMETRY_H_HEADER_INCLUDED_C1C68A2C #include "mitkLandmarkProjectorBasedCurvedGeometry.h" class vtkPoints; class vtkThinPlateSplineTransform; namespace mitk { - -//##Documentation -//## @brief Thin-plate-spline-based landmark-based curved geometry -//## -//## @ingroup Geometry -class MITK_CORE_EXPORT ThinPlateSplineCurvedGeometry : public LandmarkProjectorBasedCurvedGeometry -{ -public: + //##Documentation + //## @brief Thin-plate-spline-based landmark-based curved geometry + //## + //## @ingroup Geometry + class MITK_CORE_EXPORT ThinPlateSplineCurvedGeometry : public LandmarkProjectorBasedCurvedGeometry + { + public: mitkClassMacro(ThinPlateSplineCurvedGeometry, LandmarkProjectorBasedCurvedGeometry); itkFactorylessNewMacro(Self) itkCloneMacro(Self) virtual void ComputeGeometry(); virtual itk::LightObject::Pointer InternalClone() const; vtkThinPlateSplineTransform* GetThinPlateSplineTransform() const { return m_ThinPlateSplineTransform; } virtual void SetSigma(double sigma); virtual double GetSigma() const; virtual bool IsValid() const; -protected: + protected: ThinPlateSplineCurvedGeometry(); ThinPlateSplineCurvedGeometry(const ThinPlateSplineCurvedGeometry& other ); virtual ~ThinPlateSplineCurvedGeometry(); vtkThinPlateSplineTransform* m_ThinPlateSplineTransform; vtkPoints* m_VtkTargetLandmarks; vtkPoints* m_VtkProjectedLandmarks; - -}; - + }; } // namespace mitk #endif /* MITKTHINPLATESPLINECURVEDGEOMETRY_H_HEADER_INCLUDED_C1C68A2C */ diff --git a/Core/Code/DataManagement/mitkTimeGeometry.h b/Core/Code/DataManagement/mitkTimeGeometry.h index e95a584fd9..d96f6321f0 100644 --- a/Core/Code/DataManagement/mitkTimeGeometry.h +++ b/Core/Code/DataManagement/mitkTimeGeometry.h @@ -1,288 +1,288 @@ /*=================================================================== 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 TimeGeometry_h #define TimeGeometry_h //ITK #include //MITK #include #include #include "mitkOperationActor.h" -#include +#include namespace mitk { /** * \deprecatedSince{2013_09} GlobalInteraction is deprecated. It is replaced by mitk::Dispatcher. * Please use the new implementation described in \see DataInteractionPage . */ typedef mitk::ScalarType TimePointType; typedef std::size_t TimeStepType; /** * \brief Manages the geometries of a data object for each time step * * This class is an abstract class. The concrete implementation * depends on the way the different time steps are managed. * * The time is defined either by a time step or a time point. Time steps * are non-negativ integers starting from 0. A time point is is a ScalarType value * which gives the passed time since start in ms. Be aware that the starting * point is not fixed so it is possible that the same time point defines two * different time depending on the start time of the used time geometry. * * \addtogroup geometry */ class MITK_CORE_EXPORT TimeGeometry : public itk::Object, public OperationActor { protected: TimeGeometry(); virtual ~TimeGeometry(); /** * \brief Contains a bounding box which includes all time steps */ BoundingBox::Pointer m_BoundingBox; /** * \brief Makes a deep copy of the current object */ virtual LightObject::Pointer InternalClone() const; public: mitkClassMacro(TimeGeometry, itk::Object) itkCloneMacro(Self) itkCreateAnotherMacro(Self) /** * \brief Returns the number of time steps. * * Returns the number of time steps for which * geometries are saved. The number of time steps * is also the upper bound of the time steps. The * minimum time steps is always 0. */ virtual TimeStepType CountTimeSteps() const = 0; /** * \brief Returns the first time point for which the object is valid. * * Returns the first valid time point for this geometry. If only one * time steps available it usually goes from -max to +max. The time point * is given in ms. */ virtual TimePointType GetMinimumTimePoint () const = 0; /** * \brief Returns the last time point for which the object is valid * * Gives the last time point for which a valid geometrie is saved in * this time geometry. The time point is given in ms. */ virtual TimePointType GetMaximumTimePoint () const = 0; /** * \brief Get the time bounds (in ms) */ virtual TimeBounds GetTimeBounds( ) const = 0; /** * \brief Tests if a given time point is covered by this object * * Returns true if a geometry can be returned for the given time * point and falls if not. The time point must be given in ms. */ virtual bool IsValidTimePoint (TimePointType timePoint) const = 0; /** * \brief Test for the given time step if a geometry is availible * * Returns true if a geometry is defined for the given time step. * Otherwise false is returned. * The time step is defined as positiv number. */ virtual bool IsValidTimeStep (TimeStepType timeStep) const = 0; /** * \brief Converts a time step to a time point * * Converts a time step to a time point in a way that * the new time point indicates the same geometry as the time step. * If the original time steps does not point to a valid geometry, * a time point is calculated that also does not point to a valid * geometry, but no exception is raised. */ virtual TimePointType TimeStepToTimePoint (TimeStepType timeStep) const = 0; /** * \brief Converts a time point to the corresponding time step * * Converts a time point to a time step in a way that * the new time step indicates the same geometry as the time point. * If a negativ invalid time point is given always time step 0 is * returned. If an positiv invalid time step is given an invalid * time step will be returned. */ virtual TimeStepType TimePointToTimeStep (TimePointType timePoint) const = 0; /** * \brief Returns the geometry of a specific time point * * Returns the geometry which defines the given time point. If * the given time point is invalid an null-pointer is returned. * * The pointer to the returned geometry may point to the saved * geometry but this is not necessarily the case. So a change to * the returned geometry may or may not afflict the geometry for the * time point or all time points depending on the used implementation * of TimeGeometry. */ - virtual Geometry3D::Pointer GetGeometryForTimePoint ( TimePointType timePoint) const = 0; + virtual BaseGeometry::Pointer GetGeometryForTimePoint ( TimePointType timePoint) const = 0; /** * \brief Returns the geometry which corresponds to the given time step * * Returns the geometry which defines the given time step. If * the given time step is invalid an null-pointer is returned. * * The pointer to the returned geometry may point to the saved * geometry but this is not necessarily the case. So a change to * the returned geometry may or may not afflict the geometry for the * time step or all time steps depending on the used implementation * of TimeGeometry. */ - virtual Geometry3D::Pointer GetGeometryForTimeStep ( TimeStepType timeStep) const = 0; + virtual BaseGeometry::Pointer GetGeometryForTimeStep ( TimeStepType timeStep) const = 0; /** * \brief Returns a clone of the geometry of a specific time point * * If an invalid time step is given (e.g. no geometry is defined for this time step) * a null-pointer will be returned. */ - virtual Geometry3D::Pointer GetGeometryCloneForTimeStep( TimeStepType timeStep) const = 0; + virtual BaseGeometry::Pointer GetGeometryCloneForTimeStep( TimeStepType timeStep) const = 0; /** * \brief Sets the geometry for a given time step * * Sets the geometry for the given time steps. This may also afflects other * time steps, depending on the implementation of TimeGeometry. */ - virtual void SetTimeStepGeometry(Geometry3D* geometry, TimeStepType timeStep) = 0; + virtual void SetTimeStepGeometry(BaseGeometry* geometry, TimeStepType timeStep) = 0; /** * \brief Expands to the given number of time steps * * Expands to the given number of time steps. Each new created time * step is filled with an empty geometry. * Shrinking is not supported! */ virtual void Expand(TimeStepType size) = 0; /** * \brief Tests if all necessary informations are set and the object is valid */ virtual bool IsValid () const = 0; /** * \brief Get the position of the corner number \a id (in world coordinates) * * See SetImageGeometry for how a corner is defined on images. */ Point3D GetCornerPointInWorld(int id) const; /** * \brief Get the position of a corner (in world coordinates) * * See SetImageGeometry for how a corner is defined on images. */ Point3D GetCornerPointInWorld(bool xFront=true, bool yFront=true, bool zFront=true) const; /** * \brief Get the center of the bounding-box in mm */ Point3D GetCenterInWorld() const; /** * \brief Get the squared length of the diagonal of the bounding-box in mm */ double GetDiagonalLength2InWorld() const; /** * \brief Get the length of the diagonal of the bounding-box in mm */ double GetDiagonalLengthInWorld() const; /** * \brief Test whether the point \a p (world coordinates in mm) is inside the bounding box */ bool IsWorldPointInside(const mitk::Point3D& p) const; /** * \brief Updates the bounding box to cover the area used in all time steps * * The bounding box is updated by this method. The new bounding box * covers an area which includes all bounding boxes during * all times steps. */ void UpdateBoundingBox(); /** * \brief Returns a bounding box that covers all time steps */ BoundingBox* GetBoundingBoxInWorld() const { return m_BoundingBox; } /** * \brief Returns the world bounds of the object that cover all time steps */ BoundingBox::BoundsArrayType GetBoundsInWorld() const { return m_BoundingBox->GetBounds(); } /** * \brief Returns the Extend of the bounding in the given direction */ ScalarType GetExtentInWorld (unsigned int direction) const; /** * \brief Initializes the TimeGeometry */ virtual void Initialize(); /** * \brief Updates the geometry */ void Update(); /** * \brief Updates everything except the Bounding box * * This class should be overwritten by child classes. * The method is called when Update() is required. */ virtual void UpdateWithoutBoundingBox() {}; /** * \brief Executes the given operation on all time steps */ virtual void ExecuteOperation(Operation *op); virtual void PrintSelf(std::ostream& os, itk::Indent indent) const; }; // end class TimeGeometry } // end namespace MITK #endif // TimeGeometry_h diff --git a/Core/Code/IO/mitkSurfaceVtkWriter.txx b/Core/Code/IO/mitkSurfaceVtkWriter.txx index 9fc7fe2c25..ff0fa31722 100644 --- a/Core/Code/IO/mitkSurfaceVtkWriter.txx +++ b/Core/Code/IO/mitkSurfaceVtkWriter.txx @@ -1,174 +1,173 @@ /*=================================================================== 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 "mitkSurfaceVtkWriter.h" #include #include #include #include #include #include #include #include #include template mitk::SurfaceVtkWriter::SurfaceVtkWriter() : m_WriterWriteHasReturnValue( false ) { this->SetNumberOfRequiredInputs( 1 ); m_VtkWriter = vtkSmartPointer::New(); //enable to write ascii-formatted-file //m_VtkWriter->SetFileTypeToASCII(); SetDefaultExtension(); // and information about the Writer's Write() method } template mitk::SurfaceVtkWriter::~SurfaceVtkWriter() { } template void mitk::SurfaceVtkWriter::SetDefaultExtension() { m_Extension = ".vtk"; } template void mitk::SurfaceVtkWriter::ExecuteWrite( VtkWriterType* vtkWriter ) { if ( vtkWriter->Write() == 0 || vtkWriter->GetErrorCode() != 0 ) { itkExceptionMacro(<<"Error during surface writing: " << vtkErrorCode::GetStringFromErrorCode(vtkWriter->GetErrorCode()) ); } } template void mitk::SurfaceVtkWriter::GenerateData() { if ( m_FileName == "" ) { itkWarningMacro( << "Sorry, filename has not been set!" ); return ; } mitk::Surface::Pointer input = const_cast(this->GetInput()); vtkSmartPointer transformPolyData = vtkSmartPointer::New(); vtkPolyData * polyData; - Geometry3D* geometry; + BaseGeometry* geometry; unsigned int t, timesteps = input->GetTimeGeometry()->CountTimeSteps(); for(t = 0; t < timesteps; ++t) { // surfaces do not have to exist in all timeteps; therefor, only write valid surfaces if( input->GetVtkPolyData(t) == NULL ) continue; std::ostringstream filename; filename.imbue(::std::locale::classic()); geometry = input->GetGeometry(t); if ( timesteps > 1 ) { if(input->GetTimeGeometry()->IsValidTimeStep(t)) { const TimeBounds& timebounds = geometry->GetTimeBounds(); filename << m_FileName.c_str() << "_S" << std::setprecision(0) << timebounds[0] << "_E" << std::setprecision(0) << timebounds[1] << "_T" << t << m_Extension; } else { itkWarningMacro(<<"Error on write: TimeGeometry invalid of surface " << filename.str() << "."); filename << m_FileName.c_str() << "_T" << t << m_Extension; } m_VtkWriter->SetFileName(filename.str().c_str()); } else m_VtkWriter->SetFileName(m_FileName.c_str()); - geometry->TransferItkToVtkTransform(); transformPolyData->SetInputData(input->GetVtkPolyData(t)); transformPolyData->SetTransform(geometry->GetVtkTransform()); transformPolyData->UpdateWholeExtent(); polyData = transformPolyData->GetOutput(); m_VtkWriter->SetInputData(polyData); ExecuteWrite( m_VtkWriter ); } m_MimeType = "application/MITK.Surface"; } template void mitk::SurfaceVtkWriter::SetInput( mitk::Surface* surface ) { this->ProcessObject::SetNthInput( 0, surface ); } template const mitk::Surface* mitk::SurfaceVtkWriter::GetInput() { if ( this->GetNumberOfInputs() < 1 ) { return NULL; } else { return static_cast< const Surface * >( this->ProcessObject::GetInput( 0 ) ); } } template bool mitk::SurfaceVtkWriter::CanWriteDataType( DataNode* input ) { if ( input ) { BaseData* data = input->GetData(); if ( data ) { Surface::Pointer surface = dynamic_cast( data ); if( surface.IsNotNull() ) { SetDefaultExtension(); return true; } } } return false; } template void mitk::SurfaceVtkWriter::SetInput( DataNode* input ) { if( input && CanWriteDataType( input ) ) SetInput( dynamic_cast( input->GetData() ) ); } template std::string mitk::SurfaceVtkWriter::GetWritenMIMEType() { return m_MimeType; } template std::string mitk::SurfaceVtkWriter::GetFileExtension() { return m_Extension; } diff --git a/Core/Code/Interactions/mitkAffineInteractor.cpp b/Core/Code/Interactions/mitkAffineInteractor.cpp index f15650f790..24a9f30704 100755 --- a/Core/Code/Interactions/mitkAffineInteractor.cpp +++ b/Core/Code/Interactions/mitkAffineInteractor.cpp @@ -1,382 +1,382 @@ /*=================================================================== 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 "mitkAffineInteractor.h" #include "mitkInteractionConst.h" #include "mitkDataNode.h" #include "mitkGeometry3D.h" #include "mitkRotationOperation.h" #include "mitkPointOperation.h" #include "mitkPositionEvent.h" #include "mitkStateEvent.h" #include "mitkOperationEvent.h" #include "mitkUndoController.h" #include "mitkDisplayPositionEvent.h" #include "vtkTransform.h" #include "mitkVtkPropRenderer.h" #include "mitkProperties.h" #include #include #include "mitkAction.h" //#include "mitkBoundingObject.h" #include "mitkRenderingManager.h" #include #include #include #include "mitkGlobalInteraction.h" #include "mitkFocusManager.h" #include "mitkEventMapper.h" #include "vtkProp3D.h" #include "mitkVtkInteractorCameraController.h" #include #include "vtkRenderer.h" #include "vtkCamera.h" #include #include mitk::AffineInteractor::AffineInteractor(const char * type, DataNode* dataNode) : Interactor(type, dataNode) { } bool mitk::AffineInteractor::ExecuteAction(Action* action, mitk::StateEvent const* stateEvent) { bool ok = false; TimeGeometry* inputTimeGeometry = GetData()->GetTimeGeometry(); if (inputTimeGeometry == NULL) return false; - Geometry3D* geometry = inputTimeGeometry->GetGeometryForTimeStep(m_TimeStep); + BaseGeometry* geometry = inputTimeGeometry->GetGeometryForTimeStep(m_TimeStep); mitk::DisplayPositionEvent const *event = dynamic_cast (stateEvent->GetEvent()); switch (action->GetActionId()) { case AcCHECKELEMENT: { mitk::Point3D worldPoint = event->GetWorldPosition(); /* now we have a worldpoint. check if it is inside our object and select/deselect it accordingly */ mitk::BoolProperty::Pointer selected; mitk::ColorProperty::Pointer color; std::auto_ptr newStateEvent; selected = dynamic_cast(m_DataNode->GetProperty("selected")); if ( selected.IsNull() ) { selected = mitk::BoolProperty::New(); m_DataNode->GetPropertyList()->SetProperty("selected", selected); } color = dynamic_cast(m_DataNode->GetProperty("color")); if ( color.IsNull() ) { color = mitk::ColorProperty::New(); m_DataNode->GetPropertyList()->SetProperty("color", color); } if (this->CheckSelected(worldPoint, m_TimeStep)) { newStateEvent.reset(new mitk::StateEvent(EIDYES, stateEvent->GetEvent())); selected->SetValue(true); color->SetColor(1.0, 1.0, 0.0); } else { newStateEvent.reset(new mitk::StateEvent(EIDNO, stateEvent->GetEvent())); selected = mitk::BoolProperty::New(false); color->SetColor(0.0, 0.0, 1.0); /* mitk::BoundingObject* b = dynamic_cast(m_DataNode->GetData()); if(b != NULL) { color = (b->GetPositive())? mitk::ColorProperty::New(0.0, 0.0, 1.0) : mitk::ColorProperty::New(1.0, 0.0, 0.0); // if deselected, a boundingobject is colored according to its positive/negative state } else color = mitk::ColorProperty::New(1.0, 1.0, 1.0); // if deselcted and no bounding object, color is white */ } /* write new state (selected/not selected) to the property */ this->HandleEvent( newStateEvent.get() ); ok = true; break; } case AcADD: { mitk::Point3D worldPoint = event->GetWorldPosition(); std::auto_ptr newStateEvent; if (this->CheckSelected(worldPoint, m_TimeStep)) { newStateEvent.reset(new mitk::StateEvent(EIDYES, event)); m_DataNode->GetPropertyList()->SetProperty("selected", mitk::BoolProperty::New(true)); // TODO: Generate an Select Operation and send it to the undo controller ? } else // if not selected, do nothing (don't deselect) { newStateEvent.reset(new mitk::StateEvent(EIDNO, event)); } //call HandleEvent to leave the guard-state this->HandleEvent( newStateEvent.get() ); ok = true; break; } case AcTRANSLATESTART: case AcROTATESTART: case AcSCALESTART: { m_LastMousePosition = event->GetWorldPosition(); ok = true; break; } case AcTRANSLATE: { mitk::Point3D newPosition; newPosition = event->GetWorldPosition(); newPosition -= m_LastMousePosition.GetVectorFromOrigin(); // compute difference between actual and last mouse position m_LastMousePosition = event->GetWorldPosition(); // save current mouse position as last position /* create operation with position difference */ mitk::PointOperation* doOp = new mitk::PointOperation(OpMOVE, newPosition, 0); // Index is not used here if (m_UndoEnabled) //write to UndoMechanism { mitk::Point3D oldPosition=geometry->GetCornerPoint(0); PointOperation* undoOp = new mitk::PointOperation(OpMOVE, oldPosition, 0); OperationEvent *operationEvent = new OperationEvent(geometry, doOp, undoOp); m_UndoController->SetOperationEvent(operationEvent); } /* execute the Operation */ geometry->ExecuteOperation(doOp); if (!m_UndoEnabled) delete doOp; ok = true; break; } case AcTRANSLATEEND: { m_UndoController->SetOperationEvent(new UndoStackItem("Move object")); m_DataNode->InvokeEvent(TranslateEvent()); break; } case AcROTATE: { mitk::Point3D p = event->GetWorldPosition(); mitk::Vector3D newPosition = p.GetVectorFromOrigin(); mitk::Point3D dataPosition = geometry->GetCenter(); newPosition = newPosition - dataPosition.GetVectorFromOrigin(); // calculate vector from center of the data object to the current mouse position mitk::Vector3D startPosition = m_LastMousePosition.GetVectorFromOrigin() - dataPosition.GetVectorFromOrigin(); // calculate vector from center of the data object to the last mouse position /* calculate rotation axis (by calculating the cross produkt of the vectors) */ mitk::Vector3D rotationaxis; rotationaxis[0] = startPosition[1] * newPosition[2] - startPosition[2] * newPosition[1]; rotationaxis[1] = startPosition[2] * newPosition[0] - startPosition[0] * newPosition[2]; rotationaxis[2] = startPosition[0] * newPosition[1] - startPosition[1] * newPosition[0]; /* calculate rotation angle in degrees */ mitk::ScalarType angle = atan2((mitk::ScalarType)rotationaxis.GetNorm(), (mitk::ScalarType) (newPosition * startPosition)) * (180/vnl_math::pi); m_LastMousePosition = p; // save current mouse position as last mouse position /* create operation with center of rotation, angle and axis and send it to the geometry and Undo controller */ mitk::RotationOperation* doOp = new mitk::RotationOperation(OpROTATE, dataPosition, rotationaxis, angle); if (m_UndoEnabled) //write to UndoMechanism { RotationOperation* undoOp = new mitk::RotationOperation(OpROTATE, dataPosition, rotationaxis, -angle); OperationEvent *operationEvent = new OperationEvent(geometry, doOp, undoOp); m_UndoController->SetOperationEvent(operationEvent); } /* execute the Operation */ geometry->ExecuteOperation(doOp); if(!m_UndoEnabled) delete doOp; ok = true; break; } case AcROTATEEND: { m_UndoController->SetOperationEvent(new UndoStackItem("Rotate object")); m_DataNode->InvokeEvent(RotateEvent()); break; } case AcSCALE: { mitk::Point3D p = event->GetWorldPosition(); mitk::Vector3D v = p - m_LastMousePosition; /* calculate scale changes */ mitk::Point3D newScale; newScale[0] = (geometry->GetAxisVector(0) * v) / geometry->GetExtentInMM(0); // Scalarprodukt of normalized Axis newScale[1] = (geometry->GetAxisVector(1) * v) / geometry->GetExtentInMM(1); // and direction vector of mouse movement newScale[2] = (geometry->GetAxisVector(2) * v) / geometry->GetExtentInMM(2); // is the length of the movement vectors // projection onto the axis /* convert movement to local object coordinate system and mirror it to the positive quadrant */ Vector3D start; Vector3D end; mitk::ScalarType convert[3]; itk2vtk(m_LastMousePosition, convert); geometry->GetVtkTransform()->GetInverse()->TransformPoint(convert, convert); // transform start point to local object coordinates start[0] = fabs(convert[0]); start[1] = fabs(convert[1]); start[2] = fabs(convert[2]); // mirror it to the positive quadrant itk2vtk(p, convert); geometry->GetVtkTransform()->GetInverse()->TransformPoint(convert, convert); // transform end point to local object coordinates end[0] = fabs(convert[0]); end[1] = fabs(convert[1]); end[2] = fabs(convert[2]); // mirror it to the positive quadrant /* check if mouse movement is towards or away from the objects axes and adjust scale factors accordingly */ Vector3D vLocal = start - end; newScale[0] = (vLocal[0] > 0.0) ? -fabs(newScale[0]) : +fabs(newScale[0]); newScale[1] = (vLocal[1] > 0.0) ? -fabs(newScale[1]) : +fabs(newScale[1]); newScale[2] = (vLocal[2] > 0.0) ? -fabs(newScale[2]) : +fabs(newScale[2]); m_LastMousePosition = p; // update lastPosition for next mouse move /* generate Operation and send it to the receiving geometry */ PointOperation* doOp = new mitk::PointOperation(OpSCALE, newScale, 0); // Index is not used here if (m_UndoEnabled) //write to UndoMechanism { mitk::Point3D oldScaleData; oldScaleData[0] = -newScale[0]; oldScaleData[1] = -newScale[1]; oldScaleData[2] = -newScale[2]; PointOperation* undoOp = new mitk::PointOperation(OpSCALE, oldScaleData, 0); OperationEvent *operationEvent = new OperationEvent(geometry, doOp, undoOp); m_UndoController->SetOperationEvent(operationEvent); } /* execute the Operation */ geometry->ExecuteOperation(doOp); if(!m_UndoEnabled) delete doOp; /* Update Volume Property with new value */ /* mitk::BoundingObject* b = dynamic_cast(m_DataNode->GetData()); if (b != NULL) { m_DataNode->GetPropertyList()->SetProperty("volume", FloatProperty::New(b->GetVolume())); //MITK_INFO << "Volume of Boundingobject is " << b->GetVolume()/1000.0 << " ml" << std::endl; } */ ok = true; break; } case AcSCALEEND: { m_UndoController->SetOperationEvent(new UndoStackItem("Scale object")); m_DataNode->InvokeEvent(ScaleEvent()); break; } default: ok = Superclass::ExecuteAction(action, stateEvent);//, objectEventId, groupEventId); } mitk::RenderingManager::GetInstance()->RequestUpdateAll(); return ok; } bool mitk::AffineInteractor::CheckSelected(const mitk::Point3D& worldPoint, int timestep ) { bool selected = false; if (m_DataNode->GetBoolProperty("selected", selected) == false) // if property does not exist m_DataNode->SetProperty("selected", mitk::BoolProperty::New(false)); // create it // check if mouseclick has hit the object /* mitk::BoundingObject::Pointer boundingObject = dynamic_cast(m_DataNode->GetData()); if(boundingObject.IsNotNull()) // if it is a bounding object, use its inside function for exact hit calculation { selected = boundingObject->IsInside(worldPoint); // check if point is inside the object } else // use the data objects bounding box to determine if hit */ { GetData()->GetTimeGeometry()->Update(); - const Geometry3D* geometry = GetData()->GetGeometry( timestep ); + const BaseGeometry* geometry = GetData()->GetGeometry( timestep ); selected = geometry->IsInside(worldPoint); } return selected; } bool mitk::AffineInteractor::ConvertDisplayEventToWorldPosition(mitk::DisplayPositionEvent const* displayEvent, mitk::Point3D& worldPoint) { mitk::Point2D displayPoint = displayEvent->GetDisplayPosition(); /* Copied from vtk Sphere widget */ double focalPoint[4], position[4]; double z; FocusManager::FocusElement* fe = mitk::GlobalInteraction::GetInstance()->GetFocus(); mitk::VtkPropRenderer* glRenderer = dynamic_cast( fe ); if ( glRenderer == NULL ) { return false; } vtkRenderer *renderer = glRenderer->GetVtkRenderer(); vtkCamera *camera = renderer->GetActiveCamera(); if ( !camera ) { return false; } // Compute the two points defining the motion vector camera->GetFocalPoint(focalPoint); //this->ComputeWorldToDisplay(focalPoint[0], focalPoint[1], focalPoint[2], focalPoint); renderer->SetWorldPoint(focalPoint[0], focalPoint[1], focalPoint[2], 1.0); renderer->WorldToDisplay(); renderer->GetDisplayPoint(focalPoint); z = focalPoint[2]; // this->ComputeDisplayToWorld(displayPoint.x, displayPoint.y, z, position); renderer->SetDisplayPoint(displayPoint[0], displayPoint[1], z); renderer->DisplayToWorld(); renderer->GetWorldPoint(position); if (position[3]) { worldPoint[0] = position[0] / position[3]; worldPoint[1] = position[1] / position[3]; worldPoint[2] = position[2] / position[3]; position[3] = 1.0; } else { worldPoint[0] = position[0]; worldPoint[1] = position[1]; worldPoint[2] = position[2]; } return true; } float mitk::AffineInteractor::CanHandleEvent( StateEvent const* stateEvent ) const { float jd = 0.0f; if ( stateEvent->GetEvent()->GetSender()->GetMapperID() == mitk::BaseRenderer::Standard3D ) { MITK_DEBUG << "Sorry, mitkAffineInteractor does not support interaction in a 3D view at the moment."; return jd; } return Superclass::CanHandleEvent( stateEvent ); } 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/Interactions/mitkMoveBaseDataInteractor.cpp b/Core/Code/Interactions/mitkMoveBaseDataInteractor.cpp index b3fa20353f..bca46309b9 100644 --- a/Core/Code/Interactions/mitkMoveBaseDataInteractor.cpp +++ b/Core/Code/Interactions/mitkMoveBaseDataInteractor.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 "mitkMoveBaseDataInteractor.h" #include "mitkInteractionConst.h" #include #include "mitkDisplayPositionEvent.h" #include "mitkStateEvent.h" #include "mitkProperties.h" //for an temporary update #include "mitkRenderingManager.h" //## Default Constructor mitk::MoveBaseDataInteractor ::MoveBaseDataInteractor(const char * type, DataNode* dataNode) :Interactor(type, dataNode) { //define the colors for selected/deselected state m_DataNode->AddProperty( "MovingInteractor.SelectedColor", ColorProperty::New(0.0,1.0,0.0) ); m_DataNode->AddProperty( "MovingInteractor.DeselectedColor", ColorProperty::New(0.0,0.0,1.0) ); //save the previous color of the node, in order to restore it after the interactor is destroyed mitk::ColorProperty::Pointer priorColor = dynamic_cast(m_DataNode->GetProperty("color")); if ( priorColor.IsNotNull() ) { mitk::ColorProperty::Pointer tmpCopyOfPriorColor = mitk::ColorProperty::New(); tmpCopyOfPriorColor->SetColor( priorColor->GetColor() ); m_DataNode->AddProperty( "MovingInteractor.PriorColor", tmpCopyOfPriorColor ); } } mitk::MoveBaseDataInteractor::~MoveBaseDataInteractor() { mitk::ColorProperty::Pointer color = dynamic_cast(m_DataNode->GetProperty("MovingInteractor.PriorColor")); if ( color.IsNotNull() ) { m_DataNode->GetPropertyList()->SetProperty("color", color); } m_DataNode->GetPropertyList()->DeleteProperty("MovingInteractor.SelectedColor"); m_DataNode->GetPropertyList()->DeleteProperty("MovingInteractor.DeselectedColor"); m_DataNode->GetPropertyList()->DeleteProperty("MovingInteractor.PriorColor"); //update rendering mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } bool mitk::MoveBaseDataInteractor::ExecuteAction( Action* action, mitk::StateEvent const* stateEvent ) { bool ok = false; /*Each case must watch the type of the event!*/ switch (action->GetActionId()) { case AcDONOTHING: ok = true; break; case AcCHECKELEMENT: /* * picking: Answer the question if the given position within stateEvent is close enough to select an object * send yes if close enough and no if not picked */ { mitk::DisplayPositionEvent const *posEvent = dynamic_cast (stateEvent->GetEvent()); if (posEvent == NULL) { MITK_WARN<<"Wrong usage of mitkMoveBaseDataInteractor! Aborting interaction!\n"; return false; } mitk::Point3D worldPoint = posEvent->GetWorldPosition(); /* now we have a worldpoint. check if it is inside our object and select/deselect it accordingly */ std::auto_ptr newStateEvent; - const Geometry3D* geometry = GetData()->GetUpdatedTimeGeometry()->GetGeometryForTimeStep( m_TimeStep ); + const BaseGeometry* geometry = GetData()->GetUpdatedTimeGeometry()->GetGeometryForTimeStep( m_TimeStep ); if (geometry->IsInside(worldPoint)) newStateEvent.reset(new mitk::StateEvent(EIDYES, stateEvent->GetEvent())); else newStateEvent.reset(new mitk::StateEvent(EIDNO, stateEvent->GetEvent())); /* write new state (selected/not selected) to the property */ this->HandleEvent( newStateEvent.get() ); ok = true; break; } case AcSELECT: // select the data { mitk::BoolProperty::Pointer selected = dynamic_cast(m_DataNode->GetProperty("selected")); if ( selected.IsNull() ) { selected = mitk::BoolProperty::New(); m_DataNode->GetPropertyList()->SetProperty("selected", selected); } mitk::ColorProperty::Pointer selectedColor = dynamic_cast(m_DataNode->GetProperty("MovingInteractor.SelectedColor")); if ( selectedColor.IsNotNull() ) { m_DataNode->GetPropertyList()->SetProperty("color", selectedColor); } selected->SetValue(true); //update rendering mitk::RenderingManager::GetInstance()->RequestUpdateAll(); ok = true; break; } case AcDESELECT: //deselect the data { mitk::BoolProperty::Pointer selected = dynamic_cast(m_DataNode->GetProperty("selected")); if ( selected.IsNull() ) { selected = mitk::BoolProperty::New(); m_DataNode->GetPropertyList()->SetProperty("selected", selected); } mitk::ColorProperty::Pointer deselectedColor = dynamic_cast(m_DataNode->GetProperty("MovingInteractor.DeselectedColor")); if ( deselectedColor.IsNotNull() ) { m_DataNode->GetPropertyList()->SetProperty("color", deselectedColor); } selected = mitk::BoolProperty::New(false); //update rendering mitk::RenderingManager::GetInstance()->RequestUpdateAll(); ok = true; break; } case AcMOVE: { //modify Geometry from data as given in parameters or in event mitk::IntProperty* xP = dynamic_cast(action->GetProperty("DIRECTION_X")); mitk::IntProperty* yP = dynamic_cast(action->GetProperty("DIRECTION_Y")); mitk::IntProperty* zP = dynamic_cast(action->GetProperty("DIRECTION_Z")); if (xP == NULL || yP == NULL || zP == NULL) { MITK_WARN<<"No properties returned\n!"; return false; } mitk::Vector3D movementVector; movementVector.SetElement(0, (float) xP->GetValue()); movementVector.SetElement(1, (float) yP->GetValue()); movementVector.SetElement(2, (float) zP->GetValue()); - Geometry3D* geometry = m_DataNode->GetData()->GetUpdatedTimeGeometry()->GetGeometryForTimeStep( m_TimeStep ); + BaseGeometry* geometry = m_DataNode->GetData()->GetUpdatedTimeGeometry()->GetGeometryForTimeStep( m_TimeStep ); geometry->Translate(movementVector); // indicate modification of data tree node m_DataNode->Modified(); //update rendering mitk::RenderingManager::GetInstance()->RequestUpdateAll(); ok = true; break; } default: return Superclass::ExecuteAction( action, stateEvent ); } return ok; } /** \example mitkMoveBaseDataInteractor.cpp * This is an example of how to implement a new Interactor. * See more details about this example in tutorial Step10. */ diff --git a/Core/Code/Rendering/mitkBaseRenderer.cpp b/Core/Code/Rendering/mitkBaseRenderer.cpp index f2418cb26f..f5093e6188 100644 --- a/Core/Code/Rendering/mitkBaseRenderer.cpp +++ b/Core/Code/Rendering/mitkBaseRenderer.cpp @@ -1,876 +1,876 @@ /*=================================================================== 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,RenderingMode::Type renderingMode) : 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_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( renderingMode == RenderingMode::DepthPeeling ) { m_VtkRenderer->SetUseDepthPeeling(1); m_VtkRenderer->SetMaximumNumberOfPeels(8); m_VtkRenderer->SetOcclusionRatio(0.0); } 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(const Point2D& mousePosition) const { Point2D p_mm; Point3D position; if (m_MapperID == 1) { GetDisplayGeometry()->DisplayToWorld(mousePosition, p_mm); GetDisplayGeometry()->Map(p_mm, position); } else if (m_MapperID == 2) { 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); } } 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; - Geometry3D* geometry3d; + BaseGeometry* geometry3d; geometry3d = m_WorldTimeGeometry->GetGeometryForTimeStep(m_TimeStep); SetWorldGeometry3D(geometry3d); } } -void mitk::BaseRenderer::SetWorldGeometry3D(mitk::Geometry3D* geometry) +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::Geometry3D* geometry) +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); } diff --git a/Core/Code/Rendering/mitkBaseRenderer.h b/Core/Code/Rendering/mitkBaseRenderer.h index df60f34502..697e750a99 100644 --- a/Core/Code/Rendering/mitkBaseRenderer.h +++ b/Core/Code/Rendering/mitkBaseRenderer.h @@ -1,646 +1,646 @@ /*=================================================================== 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: /** \brief This rendering mode enumeration is specified at various constructors * of the Renderer and RenderWindow classes, which autoconfigures the * respective VTK objects. This has to be done at construction time because later * configuring turns out to be not working on most platforms. */ struct RenderingMode { enum Type { Standard = 0, // no multi-sampling, no depth-peeling MultiSampling, // multi-sampling (antialiasing), no depth-peeling DepthPeeling // no multi-sampling, depth-peeling is on (order-independant transparency) }; }; 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,RenderingMode::Type mode = RenderingMode::Standard); //##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(Geometry3D* geometry); + 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, Geometry3D) - itkGetObjectMacro(WorldGeometry, Geometry3D) + 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, Geometry3D) + 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) 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(const Point2D& mousePosition) const; /** * \deprecatedSince{2014_03} Please use Map2DRendererPositionTo3DWorldPosition(const Point2D& mousePosition) const */ DEPRECATED(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(Geometry3D* geometry); + 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 - Geometry3D::Pointer m_WorldGeometry; + 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. - Geometry3D::Pointer m_CurrentWorldGeometry; + 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; 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 */ \ No newline at end of file diff --git a/Core/Code/Rendering/mitkGeometry2DDataMapper2D.cpp b/Core/Code/Rendering/mitkGeometry2DDataMapper2D.cpp index 73db2784a6..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 Geometry3D *referenceGeometry = + 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 12496469ea..421c1c05dd 100644 --- a/Core/Code/Rendering/mitkGeometry2DDataMapper2D.h +++ b/Core/Code/Rendering/mitkGeometry2DDataMapper2D.h @@ -1,123 +1,123 @@ /*=================================================================== 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); itkFactorylessNewMacro(Self) itkCloneMacro(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 fa355c8e25..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() ) { - Geometry3D *referenceGeometry = + 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 d556213a74..d71ac0a672 100644 --- a/Core/Code/Rendering/mitkGeometry2DDataVtkMapper3D.h +++ b/Core/Code/Rendering/mitkGeometry2DDataVtkMapper3D.h @@ -1,212 +1,212 @@ /*=================================================================== 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); itkFactorylessNewMacro(Self) itkCloneMacro(Self) /** * 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 1ad19b7fe9..e0a8e9a41e 100644 --- a/Core/Code/Rendering/mitkImageVtkMapper2D.cpp +++ b/Core/Code/Rendering/mitkImageVtkMapper2D.cpp @@ -1,1073 +1,1073 @@ /*=================================================================== 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::LOOKUPTABLE_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::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. Using LOOKUPTABLE_LEVELWINDOW_COLOR instead."; this->ApplyLookuptable( renderer ); this->ApplyLevelWindow( renderer ); 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); // Set default grayscale look-up table mitk::LookupTable::Pointer mitkLut = mitk::LookupTable::New(); mitkLut->SetType(mitk::LookupTable::GRAYSCALE); mitk::LookupTableProperty::Pointer mitkLutProp = mitk::LookupTableProperty::New(); mitkLutProp->SetLookupTable(mitkLut); node->SetProperty("LookupTable", mitkLutProp); 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 { // Set inverse grayscale look-up table mitkLut->SetType(mitk::LookupTable::INVERSE_GRAYSCALE); mitkLutProp->SetLookupTable(mitkLut); node->SetProperty("LookupTable", mitkLutProp); } // Otherwise do nothing - the default grayscale look-up table has already been set } 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(); mitk::LookupTable::Pointer mitkLUT = mitk::LookupTable::New(); //built a default lookuptable mitkLUT->SetType(mitk::LookupTable::GRAYSCALE); m_DefaultLookupTable = mitkLUT->GetVtkLookupTable(); mitkLUT->SetType(mitk::LookupTable::LEGACY_BINARY); m_BinaryLookupTable = mitkLUT->GetVtkLookupTable(); mitkLUT->SetType(mitk::LookupTable::LEGACY_RAINBOW_COLOR); m_ColorLookupTable = mitkLUT->GetVtkLookupTable(); //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 1409106e72..8ba9e8b2a7 100644 --- a/Core/Code/Rendering/mitkImageVtkMapper2D.h +++ b/Core/Code/Rendering/mitkImageVtkMapper2D.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 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. */ itkFactorylessNewMacro(Self) itkCloneMacro(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/mitkPointSetVtkMapper2D.cpp b/Core/Code/Rendering/mitkPointSetVtkMapper2D.cpp index 2f40beea48..f3022acda9 100644 --- a/Core/Code/Rendering/mitkPointSetVtkMapper2D.cpp +++ b/Core/Code/Rendering/mitkPointSetVtkMapper2D.cpp @@ -1,716 +1,716 @@ /*=================================================================== 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 "mitkPointSetVtkMapper2D.h" //mitk includes #include "mitkDataNode.h" #include "mitkProperties.h" #include "mitkVtkPropRenderer.h" #include "mitkPointSet.h" //vtk includes #include #include #include #include #include #include #include #include #include #include #include #include #include #include // constructor LocalStorage mitk::PointSetVtkMapper2D::LocalStorage::LocalStorage() { // points m_UnselectedPoints = vtkSmartPointer::New(); m_SelectedPoints = vtkSmartPointer::New(); m_ContourPoints = vtkSmartPointer::New(); // scales m_UnselectedScales = vtkSmartPointer::New(); m_SelectedScales = vtkSmartPointer::New(); // distances m_DistancesBetweenPoints = vtkSmartPointer::New(); // lines m_ContourLines = vtkSmartPointer::New(); // glyph source (provides the different shapes) m_UnselectedGlyphSource2D = vtkSmartPointer::New(); m_SelectedGlyphSource2D = vtkSmartPointer::New(); // glyphs m_UnselectedGlyph3D = vtkSmartPointer::New(); m_SelectedGlyph3D = vtkSmartPointer::New(); // polydata m_VtkUnselectedPointListPolyData = vtkSmartPointer::New(); m_VtkSelectedPointListPolyData = vtkSmartPointer ::New(); m_VtkContourPolyData = vtkSmartPointer::New(); // actors m_UnselectedActor = vtkSmartPointer ::New(); m_SelectedActor = vtkSmartPointer ::New(); m_ContourActor = vtkSmartPointer ::New(); // mappers m_VtkUnselectedPolyDataMapper = vtkSmartPointer::New(); m_VtkSelectedPolyDataMapper = vtkSmartPointer::New(); m_VtkContourPolyDataMapper = vtkSmartPointer::New(); // propassembly m_PropAssembly = vtkSmartPointer ::New(); } // destructor LocalStorage mitk::PointSetVtkMapper2D::LocalStorage::~LocalStorage() { } // input for this mapper ( = point set) const mitk::PointSet* mitk::PointSetVtkMapper2D::GetInput() const { return static_cast ( GetDataNode()->GetData() ); } // constructor PointSetVtkMapper2D mitk::PointSetVtkMapper2D::PointSetVtkMapper2D() : m_ShowContour(false), m_CloseContour(false), m_ShowPoints(true), m_ShowDistances(false), m_DistancesDecimalDigits(1), m_ShowAngles(false), m_ShowDistantLines(false), m_LineWidth(1), m_PointLineWidth(1), m_Point2DSize(6), m_IDShapeProperty(mitk::PointSetShapeProperty::CROSS), m_FillShape(false), m_DistanceToPlane(4.0f) { } // destructor mitk::PointSetVtkMapper2D::~PointSetVtkMapper2D() { } // reset mapper so that nothing is displayed e.g. toggle visiblity of the propassembly void mitk::PointSetVtkMapper2D::ResetMapper( BaseRenderer* renderer ) { LocalStorage *ls = m_LSH.GetLocalStorage(renderer); ls->m_PropAssembly->VisibilityOff(); } // returns propassembly vtkProp* mitk::PointSetVtkMapper2D::GetVtkProp(mitk::BaseRenderer * renderer) { LocalStorage *ls = m_LSH.GetLocalStorage(renderer); return ls->m_PropAssembly; } static bool makePerpendicularVector2D(const mitk::Vector2D& in, mitk::Vector2D& out) { // The dot product of orthogonal vectors is zero. // In two dimensions the slopes of perpendicular lines are negative reciprocals. if((fabs(in[0])>0) && ( (fabs(in[0])>fabs(in[1])) || (in[1] == 0) ) ) { // negative reciprocal out[0]=-in[1]/in[0]; out[1]=1; out.Normalize(); return true; } else if(fabs(in[1])>0) { out[0]=1; // negative reciprocal out[1]=-in[0]/in[1]; out.Normalize(); return true; } else return false; } void mitk::PointSetVtkMapper2D::CreateVTKRenderObjects(mitk::BaseRenderer* renderer) { LocalStorage *ls = m_LSH.GetLocalStorage(renderer); unsigned i = 0; // The vtk text actors need to be removed manually from the propassembly // since the same vtk text actors are not overwriten within this function, // but new actors are added to the propassembly each time this function is executed. // Thus, the actors from the last call must be removed in the beginning. for(i=0; i< ls->m_VtkTextLabelActors.size(); i++) { if(ls->m_PropAssembly->GetParts()->IsItemPresent(ls->m_VtkTextLabelActors.at(i))) ls->m_PropAssembly->RemovePart(ls->m_VtkTextLabelActors.at(i)); } for(i=0; i< ls->m_VtkTextDistanceActors.size(); i++) { if(ls->m_PropAssembly->GetParts()->IsItemPresent(ls->m_VtkTextDistanceActors.at(i))) ls->m_PropAssembly->RemovePart(ls->m_VtkTextDistanceActors.at(i)); } for(i=0; i< ls->m_VtkTextAngleActors.size(); i++) { if(ls->m_PropAssembly->GetParts()->IsItemPresent(ls->m_VtkTextAngleActors.at(i))) ls->m_PropAssembly->RemovePart(ls->m_VtkTextAngleActors.at(i)); } // initialize polydata here, otherwise we have update problems when // executing this function again ls->m_VtkUnselectedPointListPolyData = vtkSmartPointer::New(); ls->m_VtkSelectedPointListPolyData = vtkSmartPointer ::New(); ls->m_VtkContourPolyData = vtkSmartPointer::New(); // get input point set and update the PointSet mitk::PointSet::Pointer input = const_cast(this->GetInput()); // only update the input data, if the property tells us to bool update = true; this->GetDataNode()->GetBoolProperty("updateDataOnRender", update); if (update == true) input->Update(); int timestep = this->GetTimestep(); mitk::PointSet::DataType::Pointer itkPointSet = input->GetPointSet( timestep ); if ( itkPointSet.GetPointer() == NULL) { ls->m_PropAssembly->VisibilityOff(); return; } //iterator for point set mitk::PointSet::PointsContainer::Iterator pointsIter = itkPointSet->GetPoints()->Begin(); // PointDataContainer has additional information to each point, e.g. whether // it is selected or not mitk::PointSet::PointDataContainer::Iterator pointDataIter; pointDataIter = itkPointSet->GetPointData()->Begin(); //check if the list for the PointDataContainer is the same size as the PointsContainer. //If not, then the points were inserted manually and can not be visualized according to the PointData (selected/unselected) bool pointDataBroken = (itkPointSet->GetPointData()->Size() != itkPointSet->GetPoints()->Size()); if( itkPointSet->GetPointData()->size() == 0 || pointDataBroken) { ls->m_PropAssembly->VisibilityOff(); return; } ls->m_PropAssembly->VisibilityOn(); // empty point sets, cellarrays, scalars ls->m_UnselectedPoints->Reset(); ls->m_SelectedPoints->Reset(); ls->m_ContourPoints->Reset(); ls->m_ContourLines->Reset(); ls->m_UnselectedScales->Reset(); ls->m_SelectedScales->Reset(); ls->m_DistancesBetweenPoints->Reset(); ls->m_VtkTextLabelActors.clear(); ls->m_VtkTextDistanceActors.clear(); ls->m_VtkTextAngleActors.clear(); ls->m_UnselectedScales->SetNumberOfComponents(3); ls->m_SelectedScales->SetNumberOfComponents(3); int NumberContourPoints = 0; bool pointsOnSameSideOfPlane = false; const int text2dDistance = 10; // initialize points with a random start value // current point in point set itk::Point point = pointsIter->Value(); mitk::Point3D p = point; // currently visited point mitk::Point3D lastP = point; // last visited point (predecessor in point set of "point") mitk::Vector3D vec; // p - lastP mitk::Vector3D lastVec; // lastP - point before lastP vec.Fill(0); lastVec.Fill(0); mitk::Point3D projected_p = point; // p projected on viewplane mitk::Point2D pt2d; pt2d[0] = point[0]; // projected_p in display coordinates pt2d[1] = point[1]; mitk::Point2D lastPt2d = pt2d; // last projected_p in display coordinates (predecessor in point set of "pt2d") mitk::Point2D preLastPt2d = pt2d ; // projected_p in display coordinates before lastPt2 mitk::DisplayGeometry::Pointer displayGeometry = renderer->GetDisplayGeometry(); - const mitk::Geometry2D* geo2D = renderer->GetCurrentWorldGeometry2D(); + const mitk::PlaneGeometry* geo2D = renderer->GetCurrentWorldGeometry2D(); vtkLinearTransform* dataNodeTransform = input->GetGeometry()->GetVtkTransform(); int count = 0; for (pointsIter=itkPointSet->GetPoints()->Begin(); pointsIter!=itkPointSet->GetPoints()->End(); pointsIter++) { lastP = p; // valid for number of points count > 0 preLastPt2d = lastPt2d; // valid only for count > 1 lastPt2d = pt2d; // valid for number of points count > 0 lastVec = vec; // valid only for counter > 1 // get current point in point set point = pointsIter->Value(); // transform point { float vtkp[3]; itk2vtk(point, vtkp); dataNodeTransform->TransformPoint(vtkp, vtkp); vtk2itk(vtkp,point); } p[0] = point[0]; p[1] = point[1]; p[2] = point[2]; displayGeometry->Project(p, projected_p); displayGeometry->Map(projected_p, pt2d); displayGeometry->WorldToDisplay(pt2d, pt2d); vec = p-lastP; // valid only for counter > 0 // compute distance to current plane float diff = geo2D->Distance(point); diff = diff * diff; // draw markers on slices a certain distance away from the points true location according to the tolerance threshold (m_DistanceToPlane) if(diff < m_DistanceToPlane) { // is point selected or not? if (pointDataIter->Value().selected) { ls->m_SelectedPoints->InsertNextPoint(point[0],point[1],point[2]); // point is scaled according to its distance to the plane ls->m_SelectedScales->InsertNextTuple3(m_Point2DSize - (2*diff),0,0); } else { ls->m_UnselectedPoints->InsertNextPoint(point[0],point[1],point[2]); // point is scaled according to its distance to the plane ls->m_UnselectedScales->InsertNextTuple3(m_Point2DSize - (2*diff),0,0); } //---- LABEL -----// // paint label for each point 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) { std::stringstream ss; ss << pointsIter->Index(); l.append(ss.str()); } ls->m_VtkTextActor = vtkSmartPointer::New(); ls->m_VtkTextActor->SetPosition(pt2d[0] + text2dDistance, pt2d[1] + text2dDistance); ls->m_VtkTextActor->SetInput(l.c_str()); ls->m_VtkTextActor->GetTextProperty()->SetOpacity( 100 ); float unselectedColor[4]; //check if there is a color property GetDataNode()->GetColor(unselectedColor); if (unselectedColor != NULL) ls->m_VtkTextActor->GetTextProperty()->SetColor(unselectedColor[0], unselectedColor[1], unselectedColor[2]); else ls->m_VtkTextActor->GetTextProperty()->SetColor(0.0f, 1.0f, 0.0f); ls->m_VtkTextLabelActors.push_back(ls->m_VtkTextActor); } } // draw contour, distance text and angle text in render window // lines between points, which intersect the current plane, are drawn if( m_ShowContour && count > 0 ) { ScalarType distance = displayGeometry->GetWorldGeometry()->SignedDistance(point); ScalarType lastDistance = displayGeometry->GetWorldGeometry()->SignedDistance(lastP); pointsOnSameSideOfPlane = (distance * lastDistance) > 0.5; // Points must be on different side of plane in order to draw a contour. // If "show distant lines" is enabled this condition is disregarded. if ( !pointsOnSameSideOfPlane || m_ShowDistantLines) { vtkSmartPointer line = vtkSmartPointer::New(); ls->m_ContourPoints->InsertNextPoint(lastP[0],lastP[1],lastP[2]); line->GetPointIds()->SetId(0, NumberContourPoints); NumberContourPoints++; ls->m_ContourPoints->InsertNextPoint(point[0], point[1], point[2]); line->GetPointIds()->SetId(1, NumberContourPoints); NumberContourPoints++; ls->m_ContourLines->InsertNextCell(line); if(m_ShowDistances) // calculate and print distance between adjacent points { float distancePoints = point.EuclideanDistanceTo(lastP); std::stringstream buffer; buffer<m_VtkTextActor = vtkSmartPointer::New(); ls->m_VtkTextActor->SetPosition(pos2d[0],pos2d[1]); ls->m_VtkTextActor->SetInput(buffer.str().c_str()); ls->m_VtkTextActor->GetTextProperty()->SetColor(0.0, 1.0, 0.0); ls->m_VtkTextDistanceActors.push_back(ls->m_VtkTextActor); } if(m_ShowAngles && count > 1) // calculate and print angle between connected lines { std::stringstream buffer; //(char) 176 is the degree sign buffer << angle(vec.GetVnlVector(), -lastVec.GetVnlVector())*180/vnl_math::pi << (char)176; //compute desired display position of text Vector2D vec2d = pt2d-lastPt2d; // first arm enclosing the angle vec2d.Normalize(); Vector2D lastVec2d = lastPt2d-preLastPt2d; // second arm enclosing the angle lastVec2d.Normalize(); vec2d=vec2d-lastVec2d; // vector connecting both arms vec2d.Normalize(); // middle between two vectors that enclose the angle Vector2D pos2d = lastPt2d.GetVectorFromOrigin() + vec2d * text2dDistance * text2dDistance; ls->m_VtkTextActor = vtkSmartPointer::New(); ls->m_VtkTextActor->SetPosition(pos2d[0],pos2d[1]); ls->m_VtkTextActor->SetInput(buffer.str().c_str()); ls->m_VtkTextActor->GetTextProperty()->SetColor(0.0, 1.0, 0.0); ls->m_VtkTextAngleActors.push_back(ls->m_VtkTextActor); } } } if(pointDataIter != itkPointSet->GetPointData()->End()) { pointDataIter++; count++; } } // add each single text actor to the assembly for(i=0; i< ls->m_VtkTextLabelActors.size(); i++) { ls->m_PropAssembly->AddPart(ls->m_VtkTextLabelActors.at(i)); } for(i=0; i< ls->m_VtkTextDistanceActors.size(); i++) { ls->m_PropAssembly->AddPart(ls->m_VtkTextDistanceActors.at(i)); } for(i=0; i< ls->m_VtkTextAngleActors.size(); i++) { ls->m_PropAssembly->AddPart(ls->m_VtkTextAngleActors.at(i)); } //---- CONTOUR -----// //create lines between the points which intersect the plane if (m_ShowContour) { // draw line between first and last point which is rendered if(m_CloseContour && NumberContourPoints > 1){ vtkSmartPointer closingLine = vtkSmartPointer::New(); closingLine->GetPointIds()->SetId(0, 0); // index of first point closingLine->GetPointIds()->SetId(1, NumberContourPoints-1); // index of last point ls->m_ContourLines->InsertNextCell(closingLine); } ls->m_VtkContourPolyData->SetPoints(ls->m_ContourPoints); ls->m_VtkContourPolyData->SetLines(ls->m_ContourLines); ls->m_VtkContourPolyDataMapper->SetInputData(ls->m_VtkContourPolyData); ls->m_ContourActor->SetMapper(ls->m_VtkContourPolyDataMapper); ls->m_ContourActor->GetProperty()->SetLineWidth(m_LineWidth); ls->m_PropAssembly->AddPart(ls->m_ContourActor); } // the point set must be transformed in order to obtain the appropriate glyph orientation // according to the current view vtkSmartPointer transform = vtkSmartPointer::New(); vtkSmartPointer a,b = vtkSmartPointer::New(); a = geo2D->GetVtkTransform()->GetMatrix(); b->DeepCopy( a ); // delete transformation from matrix, only take orientation b->SetElement(3,3,1); b->SetElement(2,3,0); b->SetElement(1,3,0); b->SetElement(0,3,0); b->SetElement(3,2,0); b->SetElement(3,1,0); b->SetElement(3,0,0); transform->SetMatrix( b ); //---- UNSELECTED POINTS -----// // apply properties to glyph ls->m_UnselectedGlyphSource2D->SetGlyphType(m_IDShapeProperty); if(m_FillShape) ls->m_UnselectedGlyphSource2D->FilledOn(); else ls->m_UnselectedGlyphSource2D->FilledOff(); // apply transform vtkSmartPointer transformFilterU = vtkSmartPointer::New(); transformFilterU->SetInputConnection(ls->m_UnselectedGlyphSource2D->GetOutputPort()); transformFilterU->SetTransform(transform); ls->m_VtkUnselectedPointListPolyData->SetPoints(ls->m_UnselectedPoints); ls->m_VtkUnselectedPointListPolyData->GetPointData()->SetVectors(ls->m_UnselectedScales); // apply transform of current plane to glyphs ls->m_UnselectedGlyph3D->SetSourceConnection(transformFilterU->GetOutputPort()); ls->m_UnselectedGlyph3D->SetInputData(ls->m_VtkUnselectedPointListPolyData); ls->m_UnselectedGlyph3D->SetScaleModeToScaleByVector(); ls->m_UnselectedGlyph3D->SetVectorModeToUseVector(); ls->m_VtkUnselectedPolyDataMapper->SetInputConnection(ls->m_UnselectedGlyph3D->GetOutputPort()); ls->m_UnselectedActor->SetMapper(ls->m_VtkUnselectedPolyDataMapper); ls->m_UnselectedActor->GetProperty()->SetLineWidth(m_PointLineWidth); ls->m_PropAssembly->AddPart(ls->m_UnselectedActor); //---- SELECTED POINTS -----// ls->m_SelectedGlyphSource2D->SetGlyphTypeToDiamond(); ls->m_SelectedGlyphSource2D->CrossOn(); ls->m_SelectedGlyphSource2D->FilledOff(); // apply transform vtkSmartPointer transformFilterS = vtkSmartPointer::New(); transformFilterS->SetInputConnection(ls->m_SelectedGlyphSource2D->GetOutputPort()); transformFilterS->SetTransform(transform); ls->m_VtkSelectedPointListPolyData->SetPoints(ls->m_SelectedPoints); ls->m_VtkSelectedPointListPolyData->GetPointData()->SetVectors(ls->m_SelectedScales); // apply transform of current plane to glyphs ls->m_SelectedGlyph3D->SetSourceConnection(transformFilterS->GetOutputPort()); ls->m_SelectedGlyph3D->SetInputData(ls->m_VtkSelectedPointListPolyData); ls->m_SelectedGlyph3D->SetScaleModeToScaleByVector(); ls->m_SelectedGlyph3D->SetVectorModeToUseVector(); ls->m_VtkSelectedPolyDataMapper->SetInputConnection(ls->m_SelectedGlyph3D->GetOutputPort()); ls->m_SelectedActor->SetMapper(ls->m_VtkSelectedPolyDataMapper); ls->m_SelectedActor->GetProperty()->SetLineWidth(m_PointLineWidth); ls->m_PropAssembly->AddPart(ls->m_SelectedActor); } void mitk::PointSetVtkMapper2D::GenerateDataForRenderer( mitk::BaseRenderer *renderer ) { const mitk::DataNode* node = GetDataNode(); if( node == NULL ) return; LocalStorage *ls = m_LSH.GetLocalStorage(renderer); // check whether the input data has been changed bool needGenerateData = ls->IsGenerateDataRequired( renderer, this, GetDataNode() ); // toggle visibility bool visible = true; node->GetVisibility(visible, renderer, "visible"); if(!visible) { ls->m_UnselectedActor->VisibilityOff(); ls->m_SelectedActor->VisibilityOff(); ls->m_ContourActor->VisibilityOff(); ls->m_PropAssembly->VisibilityOff(); return; }else{ ls->m_PropAssembly->VisibilityOn(); } node->GetBoolProperty("show contour", m_ShowContour, renderer); node->GetBoolProperty("close contour", m_CloseContour, renderer); node->GetBoolProperty("show points", m_ShowPoints, renderer); node->GetBoolProperty("show distances", m_ShowDistances, renderer); node->GetIntProperty("distance decimal digits", m_DistancesDecimalDigits, renderer); node->GetBoolProperty("show angles", m_ShowAngles, renderer); node->GetBoolProperty("show distant lines", m_ShowDistantLines, renderer); node->GetIntProperty("line width", m_LineWidth, renderer); node->GetIntProperty("point line width", m_PointLineWidth, renderer); node->GetIntProperty("point 2D size", m_Point2DSize, renderer); node->GetBoolProperty("Pointset.2D.fill shape", m_FillShape, renderer); node->GetFloatProperty("Pointset.2D.distance to plane", m_DistanceToPlane, renderer ); mitk::PointSetShapeProperty::Pointer shape = dynamic_cast(this->GetDataNode()->GetProperty( "Pointset.2D.shape", renderer )); if(shape.IsNotNull()) { m_IDShapeProperty = shape->GetPointSetShape(); } //check for color props and use it for rendering of selected/unselected points and contour //due to different params in VTK (double/float) we have to convert float unselectedColor[4]; double selectedColor[4]={1.0f,0.0f,0.0f,1.0f}; //red double contourColor[4]={1.0f,0.0f,0.0f,1.0f}; //red float opacity = 1.0; GetDataNode()->GetOpacity(opacity, renderer); // apply color and opacity if(m_ShowPoints) { ls->m_UnselectedActor->VisibilityOn(); ls->m_SelectedActor->VisibilityOn(); //check if there is a color property GetDataNode()->GetColor(unselectedColor); //get selected color property if (dynamic_cast(this->GetDataNode()->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; // alpha value } else if (dynamic_cast(this->GetDataNode()->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; // alpha value } ls->m_SelectedActor->GetProperty()->SetColor(selectedColor); ls->m_SelectedActor->GetProperty()->SetOpacity(opacity); ls->m_UnselectedActor->GetProperty()->SetColor(unselectedColor[0],unselectedColor[1],unselectedColor[2]); ls->m_UnselectedActor->GetProperty()->SetOpacity(opacity); } else { ls->m_UnselectedActor->VisibilityOff(); ls-> m_SelectedActor->VisibilityOff(); } if (m_ShowContour) { ls->m_ContourActor->VisibilityOn(); //get contour color property if (dynamic_cast(this->GetDataNode()->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(this->GetDataNode()->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; } ls->m_ContourActor->GetProperty()->SetColor(contourColor); ls->m_ContourActor->GetProperty()->SetOpacity(opacity); } else { ls->m_ContourActor->VisibilityOff(); } if(needGenerateData) { // create new vtk render objects (e.g. a circle for a point) this->CreateVTKRenderObjects(renderer); } } void mitk::PointSetVtkMapper2D::SetDefaultProperties(mitk::DataNode* node, mitk::BaseRenderer* renderer, bool overwrite) { node->AddProperty( "line width", mitk::IntProperty::New(2), renderer, overwrite ); node->AddProperty( "point line width", mitk::IntProperty::New(1), renderer, overwrite ); node->AddProperty( "point 2D size", mitk::IntProperty::New(6), renderer, overwrite ); node->AddProperty( "show contour", mitk::BoolProperty::New(false), renderer, overwrite ); node->AddProperty( "close contour", mitk::BoolProperty::New(false), renderer, overwrite ); node->AddProperty( "show points", mitk::BoolProperty::New(true), renderer, overwrite ); node->AddProperty( "show distances", mitk::BoolProperty::New(false), renderer, overwrite ); node->AddProperty( "distance decimal digits", mitk::IntProperty::New(2), renderer, overwrite ); node->AddProperty( "show angles", mitk::BoolProperty::New(false), renderer, overwrite ); node->AddProperty( "show distant lines", mitk::BoolProperty::New(false), renderer, overwrite ); node->AddProperty( "layer", mitk::IntProperty::New(1), renderer, overwrite ); node->AddProperty( "Pointset.2D.fill shape", mitk::BoolProperty::New(false), renderer, overwrite); // fill or do not fill the glyph shape mitk::PointSetShapeProperty::Pointer pointsetShapeProperty = mitk::PointSetShapeProperty::New(); node->AddProperty( "Pointset.2D.shape", pointsetShapeProperty, renderer, overwrite); node->AddProperty( "Pointset.2D.distance to plane", mitk::FloatProperty::New(4.0f), renderer, overwrite ); //show the point at a certain distance above/below the 2D imaging plane. Superclass::SetDefaultProperties(node, renderer, overwrite); } diff --git a/Core/Code/Rendering/mitkSurfaceGLMapper2D.cpp b/Core/Code/Rendering/mitkSurfaceGLMapper2D.cpp index de23b26633..0dabdf6ed9 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; 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 77b7766371..310409b441 100644 --- a/Core/Code/Rendering/mitkSurfaceGLMapper2D.h +++ b/Core/Code/Rendering/mitkSurfaceGLMapper2D.h @@ -1,156 +1,156 @@ /*=================================================================== 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); itkFactorylessNewMacro(Self) itkCloneMacro(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/Rendering/mitkVolumeDataVtkMapper3D.cpp b/Core/Code/Rendering/mitkVolumeDataVtkMapper3D.cpp index 52f8065424..1ad7e45e0a 100644 --- a/Core/Code/Rendering/mitkVolumeDataVtkMapper3D.cpp +++ b/Core/Code/Rendering/mitkVolumeDataVtkMapper3D.cpp @@ -1,706 +1,706 @@ /*=================================================================== 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 "mitkVolumeDataVtkMapper3D.h" #include "mitkDataNode.h" #include "mitkProperties.h" #include "mitkLevelWindow.h" #include "mitkColorProperty.h" #include "mitkLevelWindowProperty.h" #include "mitkLookupTableProperty.h" #include "mitkTransferFunctionProperty.h" #include "mitkTransferFunctionInitializer.h" #include "mitkColorProperty.h" #include "mitkVtkPropRenderer.h" #include "mitkRenderingManager.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 "mitkVtkVolumeRenderingProperty.h" #include const mitk::Image* mitk::VolumeDataVtkMapper3D::GetInput() { return static_cast ( GetDataNode()->GetData() ); } mitk::VolumeDataVtkMapper3D::VolumeDataVtkMapper3D() : m_Mask( NULL ) { m_PlaneSet = false; m_ClippingPlane = vtkPlane::New(); m_PlaneWidget = vtkImplicitPlaneWidget::New(); /* m_T2DMapper = vtkVolumeTextureMapper2D::New(); m_T2DMapper->SetMaximumNumberOfPlanes( 100 ); */ m_HiResMapper = vtkVolumeRayCastMapper::New(); m_HiResMapper->SetSampleDistance(1.0); // 4 rays for every pixel m_HiResMapper->IntermixIntersectingGeometryOn(); m_HiResMapper->SetNumberOfThreads( itk::MultiThreader::GetGlobalDefaultNumberOfThreads() ); /* vtkVolumeRayCastCompositeFunction* compositeFunction = vtkVolumeRayCastCompositeFunction::New(); compositeFunction->SetCompositeMethodToClassifyFirst(); m_HiResMapper->SetVolumeRayCastFunction(compositeFunction); compositeFunction->Delete(); vtkVolumeRayCastMIPFunction* mipFunction = vtkVolumeRayCastMIPFunction::New(); m_HiResMapper->SetVolumeRayCastFunction(mipFunction); mipFunction->Delete(); */ vtkFiniteDifferenceGradientEstimator* gradientEstimator = vtkFiniteDifferenceGradientEstimator::New(); m_HiResMapper->SetGradientEstimator(gradientEstimator); gradientEstimator->Delete(); m_VolumePropertyLow = vtkVolumeProperty::New(); m_VolumePropertyMed = vtkVolumeProperty::New(); m_VolumePropertyHigh = vtkVolumeProperty::New(); m_VolumeLOD = vtkLODProp3D::New(); m_VolumeLOD->VisibilityOff(); m_HiResID = m_VolumeLOD->AddLOD(m_HiResMapper,m_VolumePropertyHigh,0.0); // RayCast // m_LowResID = m_VolumeLOD->AddLOD(m_T2DMapper,m_VolumePropertyLow,0.0); // TextureMapper2D m_MedResID = m_VolumeLOD->AddLOD(m_HiResMapper,m_VolumePropertyMed,0.0); // RayCast m_Resampler = vtkImageResample::New(); m_Resampler->SetAxisMagnificationFactor(0,0.25); m_Resampler->SetAxisMagnificationFactor(1,0.25); m_Resampler->SetAxisMagnificationFactor(2,0.25); // For abort rendering mechanism m_VolumeLOD->AutomaticLODSelectionOff(); m_BoundingBox = vtkCubeSource::New(); m_BoundingBox->SetXLength( 0.0 ); m_BoundingBox->SetYLength( 0.0 ); m_BoundingBox->SetZLength( 0.0 ); m_BoundingBoxMapper = vtkPolyDataMapper::New(); m_BoundingBoxMapper->SetInputConnection( m_BoundingBox->GetOutputPort() ); m_BoundingBoxActor = vtkActor::New(); m_BoundingBoxActor->SetMapper( m_BoundingBoxMapper ); m_BoundingBoxActor->GetProperty()->SetColor( 1.0, 1.0, 1.0 ); m_BoundingBoxActor->GetProperty()->SetRepresentationToWireframe(); // BoundingBox rendering is not working due to problem with assembly // transformation; see bug #454 // If commenting in the following, do not forget to comment in the // m_Prop3DAssembly->Delete() line in the destructor. //m_Prop3DAssembly = vtkAssembly::New(); //m_Prop3DAssembly->AddPart( m_VolumeLOD ); //m_Prop3DAssembly->AddPart( m_BoundingBoxActor ); //m_Prop3D = m_Prop3DAssembly; m_ImageCast = vtkImageShiftScale::New(); m_ImageCast->SetOutputScalarTypeToUnsignedShort(); m_ImageCast->ClampOverflowOn(); m_UnitSpacingImageFilter = vtkImageChangeInformation::New(); m_UnitSpacingImageFilter->SetInputConnection(m_ImageCast->GetOutputPort()); m_UnitSpacingImageFilter->SetOutputSpacing( 1.0, 1.0, 1.0 ); m_ImageMaskFilter = vtkImageMask::New(); m_ImageMaskFilter->SetMaskedOutputValue(0xffff); this->m_Resampler->SetInputConnection( this->m_UnitSpacingImageFilter->GetOutputPort() ); this->m_HiResMapper->SetInputConnection( this->m_UnitSpacingImageFilter->GetOutputPort() ); // m_T2DMapper->SetInput(m_Resampler->GetOutput()); this->CreateDefaultTransferFunctions(); } vtkProp *mitk::VolumeDataVtkMapper3D::GetVtkProp(mitk::BaseRenderer * /*renderer*/) { return m_VolumeLOD; } mitk::VolumeDataVtkMapper3D::~VolumeDataVtkMapper3D() { m_UnitSpacingImageFilter->Delete(); m_ImageCast->Delete(); // m_T2DMapper->Delete(); m_HiResMapper->Delete(); m_Resampler->Delete(); m_VolumePropertyLow->Delete(); m_VolumePropertyMed->Delete(); m_VolumePropertyHigh->Delete(); m_VolumeLOD->Delete(); m_ClippingPlane->Delete(); m_PlaneWidget->Delete(); // m_Prop3DAssembly->Delete(); m_BoundingBox->Delete(); m_BoundingBoxMapper->Delete(); m_BoundingBoxActor->Delete(); m_ImageMaskFilter->Delete(); m_DefaultColorTransferFunction->Delete(); m_DefaultOpacityTransferFunction->Delete(); m_DefaultGradientTransferFunction->Delete(); if (m_Mask) { m_Mask->Delete(); } } void mitk::VolumeDataVtkMapper3D::GenerateDataForRenderer( mitk::BaseRenderer *renderer ) { SetVtkMapperImmediateModeRendering(m_BoundingBoxMapper); mitk::Image *input = const_cast< mitk::Image * >( this->GetInput() ); if ( !input || !input->IsInitialized() ) return; vtkRenderWindow* renderWindow = renderer->GetRenderWindow(); bool volumeRenderingEnabled = true; bool visible = true; GetDataNode()->GetVisibility(visible, renderer, "visible"); if ( !visible || this->GetDataNode() == NULL || dynamic_cast(GetDataNode()->GetProperty("volumerendering",renderer))==NULL || dynamic_cast(GetDataNode()->GetProperty("volumerendering",renderer))->GetValue() == false ) { volumeRenderingEnabled = false; // Check if a bounding box should be displayed around the dataset // (even if volume rendering is disabled) bool hasBoundingBox = false; this->GetDataNode()->GetBoolProperty( "bounding box", hasBoundingBox ); if ( !hasBoundingBox ) { m_BoundingBoxActor->VisibilityOff(); } else { m_BoundingBoxActor->VisibilityOn(); const BoundingBox::BoundsArrayType &bounds = input->GetTimeGeometry()->GetBoundsInWorld(); m_BoundingBox->SetBounds( bounds[0], bounds[1], bounds[2], bounds[3], bounds[4], bounds[5] ); ColorProperty *colorProperty; if ( this->GetDataNode()->GetProperty( colorProperty, "color" ) ) { const mitk::Color &color = colorProperty->GetColor(); m_BoundingBoxActor->GetProperty()->SetColor( color[0], color[1], color[2] ); } else { m_BoundingBoxActor->GetProperty()->SetColor( 1.0, 1.0, 1.0 ); } } } // Don't do anything if VR is disabled if ( !volumeRenderingEnabled ) { m_VolumeLOD->VisibilityOff(); return; } else { mitk::VtkVolumeRenderingProperty* vrp=dynamic_cast(GetDataNode()->GetProperty("volumerendering configuration",renderer)); if(vrp) { int renderingValue = vrp->GetValueAsId(); switch(renderingValue) { case VTK_VOLUME_RAY_CAST_MIP_FUNCTION: { vtkVolumeRayCastMIPFunction* mipFunction = vtkVolumeRayCastMIPFunction::New(); m_HiResMapper->SetVolumeRayCastFunction(mipFunction); mipFunction->Delete(); MITK_INFO <<"in switch" <SetCompositeMethodToClassifyFirst(); m_HiResMapper->SetVolumeRayCastFunction(compositeFunction); compositeFunction->Delete(); break; } default: MITK_ERROR <<"Warning: invalid volume rendering option. " << std::endl; } } m_VolumeLOD->VisibilityOn(); } this->SetPreferences(); /* switch ( mitk::RenderingManager::GetInstance()->GetNextLOD( renderer ) ) { case 0: m_VolumeLOD->SetSelectedLODID(m_MedResID); m_LowResID ); break; default: case 1: m_VolumeLOD->SetSelectedLODID( m_HiResID ); break; } */ m_VolumeLOD->SetSelectedLODID( m_HiResID ); assert(input->GetTimeGeometry()); - const Geometry3D* worldgeometry = renderer->GetCurrentWorldGeometry(); + const BaseGeometry* worldgeometry = renderer->GetCurrentWorldGeometry(); if(worldgeometry==NULL) { GetDataNode()->SetProperty("volumerendering",mitk::BoolProperty::New(false)); return; } vtkImageData *inputData = input->GetVtkImageData( this->GetTimestep() ); if(inputData==NULL) return; m_ImageCast->SetInputData( inputData ); //If mask exists, process mask before resampling. if (this->m_Mask) { this->m_UnitSpacingImageFilter->Update(); this->m_ImageMaskFilter->SetImageInputData(this->m_UnitSpacingImageFilter->GetOutput()); this->m_Resampler->SetInputConnection(this->m_ImageMaskFilter->GetOutputPort()); this->m_HiResMapper->SetInputConnection(this->m_ImageMaskFilter->GetOutputPort()); } else { this->m_Resampler->SetInputConnection(this->m_UnitSpacingImageFilter->GetOutputPort()); this->m_HiResMapper->SetInputConnection(this->m_UnitSpacingImageFilter->GetOutputPort()); } this->UpdateTransferFunctions( renderer ); vtkRenderWindowInteractor *interactor = renderWindow->GetInteractor(); float frameRate; if( this->GetDataNode()->GetFloatProperty( "framerate", frameRate ) && frameRate > 0 && frameRate <= 60) { interactor->SetDesiredUpdateRate( frameRate ); interactor->SetStillUpdateRate( frameRate ); } else if( frameRate > 60 ) { this->GetDataNode()->SetProperty( "framerate",mitk::FloatProperty::New(60)); interactor->SetDesiredUpdateRate( 60 ); interactor->SetStillUpdateRate( 60 ); } else { this->GetDataNode()->SetProperty( "framerate",mitk::FloatProperty::New(0.00001)); interactor->SetDesiredUpdateRate( 0.00001 ); interactor->SetStillUpdateRate( 0.00001 ); } if ( m_RenderWindowInitialized.find( renderWindow ) == m_RenderWindowInitialized.end() ) { m_RenderWindowInitialized.insert( renderWindow ); // mitk::RenderingManager::GetInstance()->SetNextLOD( 0, renderer ); mitk::RenderingManager::GetInstance()->SetShading( true, 0 ); mitk::RenderingManager::GetInstance()->SetShading( true, 1 ); //mitk::RenderingManager::GetInstance()->SetShading( true, 2 ); mitk::RenderingManager::GetInstance()->SetShadingValues( m_VolumePropertyHigh->GetAmbient(), m_VolumePropertyHigh->GetDiffuse(), m_VolumePropertyHigh->GetSpecular(), m_VolumePropertyHigh->GetSpecularPower()); mitk::RenderingManager::GetInstance()->SetClippingPlaneStatus(false); } this->SetClippingPlane( interactor ); } void mitk::VolumeDataVtkMapper3D::CreateDefaultTransferFunctions() { m_DefaultOpacityTransferFunction = vtkPiecewiseFunction::New(); m_DefaultOpacityTransferFunction->AddPoint( 0.0, 0.0 ); m_DefaultOpacityTransferFunction->AddPoint( 255.0, 0.8 ); m_DefaultOpacityTransferFunction->ClampingOn(); m_DefaultGradientTransferFunction = vtkPiecewiseFunction::New(); m_DefaultGradientTransferFunction->AddPoint( 0.0, 0.0 ); m_DefaultGradientTransferFunction->AddPoint( 255.0, 0.8 ); m_DefaultGradientTransferFunction->ClampingOn(); m_DefaultColorTransferFunction = vtkColorTransferFunction::New(); m_DefaultColorTransferFunction->AddRGBPoint( 0.0, 0.0, 0.0, 0.0 ); m_DefaultColorTransferFunction->AddRGBPoint( 127.5, 1, 1, 0.0 ); m_DefaultColorTransferFunction->AddRGBPoint( 255.0, 0.8, 0.2, 0 ); m_DefaultColorTransferFunction->ClampingOn(); } void mitk::VolumeDataVtkMapper3D::UpdateTransferFunctions( mitk::BaseRenderer *renderer ) { vtkSmartPointer opacityTransferFunction; vtkSmartPointer gradientTransferFunction; vtkSmartPointer colorTransferFunction; mitk::LookupTableProperty::Pointer lookupTableProp; lookupTableProp = dynamic_cast(this->GetDataNode()->GetProperty("LookupTable")); mitk::TransferFunctionProperty::Pointer transferFunctionProp = dynamic_cast(this->GetDataNode()->GetProperty("TransferFunction")); if ( transferFunctionProp.IsNotNull() ) { opacityTransferFunction = transferFunctionProp->GetValue()->GetScalarOpacityFunction(); gradientTransferFunction = transferFunctionProp->GetValue()->GetGradientOpacityFunction(); colorTransferFunction = transferFunctionProp->GetValue()->GetColorTransferFunction(); } else if (lookupTableProp.IsNotNull() ) { opacityTransferFunction = lookupTableProp->GetLookupTable()->CreateOpacityTransferFunction(); opacityTransferFunction->ClampingOn(); gradientTransferFunction = lookupTableProp->GetLookupTable()->CreateGradientTransferFunction(); gradientTransferFunction->ClampingOn(); colorTransferFunction = lookupTableProp->GetLookupTable()->CreateColorTransferFunction(); colorTransferFunction->ClampingOn(); } else { opacityTransferFunction = m_DefaultOpacityTransferFunction; gradientTransferFunction = m_DefaultGradientTransferFunction; colorTransferFunction = m_DefaultColorTransferFunction; float rgb[3]={1.0f,1.0f,1.0f}; // check for color prop and use it for rendering if it exists if(GetDataNode()->GetColor(rgb, renderer, "color")) { colorTransferFunction->AddRGBPoint( 0.0, 0.0, 0.0, 0.0 ); colorTransferFunction->AddRGBPoint( 127.5, rgb[0], rgb[1], rgb[2] ); colorTransferFunction->AddRGBPoint( 255.0, rgb[0], rgb[1], rgb[2] ); } } if (this->m_Mask) { opacityTransferFunction->AddPoint(0xffff, 0.0); } m_VolumePropertyLow->SetColor( colorTransferFunction ); m_VolumePropertyLow->SetScalarOpacity( opacityTransferFunction ); m_VolumePropertyLow->SetGradientOpacity( gradientTransferFunction ); m_VolumePropertyLow->SetInterpolationTypeToNearest(); m_VolumePropertyMed->SetColor( colorTransferFunction ); m_VolumePropertyMed->SetScalarOpacity( opacityTransferFunction ); m_VolumePropertyMed->SetGradientOpacity( gradientTransferFunction ); m_VolumePropertyMed->SetInterpolationTypeToNearest(); m_VolumePropertyHigh->SetColor( colorTransferFunction ); m_VolumePropertyHigh->SetScalarOpacity( opacityTransferFunction ); m_VolumePropertyHigh->SetGradientOpacity( gradientTransferFunction ); m_VolumePropertyHigh->SetInterpolationTypeToLinear(); } /* Shading enabled / disabled */ void mitk::VolumeDataVtkMapper3D::SetPreferences() { //LOD 0 /*if(mitk::RenderingManager::GetInstance()->GetShading(0)) { m_VolumePropertyLow->ShadeOn(); m_VolumePropertyLow->SetAmbient(mitk::RenderingManager::GetInstance()->GetShadingValues()[0]); m_VolumePropertyLow->SetDiffuse(mitk::RenderingManager::GetInstance()->GetShadingValues()[1]); m_VolumePropertyLow->SetSpecular(mitk::RenderingManager::GetInstance()->GetShadingValues()[2]); m_VolumePropertyLow->SetSpecularPower(mitk::RenderingManager::GetInstance()->GetShadingValues()[3]); } else*/ { m_VolumePropertyLow->ShadeOff(); } //LOD 1 /*if(mitk::RenderingManager::GetInstance()->GetShading(1)) { m_VolumePropertyMed->ShadeOn(); m_VolumePropertyMed->SetAmbient(mitk::RenderingManager::GetInstance()->GetShadingValues()[0]); m_VolumePropertyMed->SetDiffuse(mitk::RenderingManager::GetInstance()->GetShadingValues()[1]); m_VolumePropertyMed->SetSpecular(mitk::RenderingManager::GetInstance()->GetShadingValues()[2]); m_VolumePropertyMed->SetSpecularPower(mitk::RenderingManager::GetInstance()->GetShadingValues()[3]); } else*/ { m_VolumePropertyMed->ShadeOff(); } //LOD 2 /* if(mitk::RenderingManager::GetInstance()->GetShading(2)) { m_VolumePropertyHigh->ShadeOn(); //Shading Properties m_VolumePropertyHigh->SetAmbient(mitk::RenderingManager::GetInstance()->GetShadingValues()[0]); m_VolumePropertyHigh->SetDiffuse(mitk::RenderingManager::GetInstance()->GetShadingValues()[1]); m_VolumePropertyHigh->SetSpecular(mitk::RenderingManager::GetInstance()->GetShadingValues()[2]); m_VolumePropertyHigh->SetSpecularPower(mitk::RenderingManager::GetInstance()->GetShadingValues()[3]); } else { m_VolumePropertyHigh->ShadeOff(); } */ } /* Adds A Clipping Plane to the Mapper */ void mitk::VolumeDataVtkMapper3D::SetClippingPlane(vtkRenderWindowInteractor* interactor) { if(mitk::RenderingManager::GetInstance()->GetClippingPlaneStatus()) //if clipping plane is enabled { if(!m_PlaneSet) { m_PlaneWidget->SetInteractor(interactor); m_PlaneWidget->SetPlaceFactor(1.0); m_PlaneWidget->SetInputData(m_UnitSpacingImageFilter->GetOutput()); m_PlaneWidget->OutlineTranslationOff(); //disables scaling of the bounding box m_PlaneWidget->ScaleEnabledOff(); //disables scaling of the bounding box m_PlaneWidget->DrawPlaneOff(); //clipping plane is transparent mitk::Image* input = const_cast(this->GetInput()); /*places the widget within the specified bounds*/ m_PlaneWidget->PlaceWidget( input->GetGeometry()->GetOrigin()[0],(input->GetGeometry()->GetOrigin()[0])+(input->GetDimension(0))*(input->GetVtkImageData()->GetSpacing()[0]), input->GetGeometry()->GetOrigin()[1],(input->GetGeometry()->GetOrigin()[1])+(input->GetDimension(1))*(input->GetVtkImageData()->GetSpacing()[1]), input->GetGeometry()->GetOrigin()[2],(input->GetGeometry()->GetOrigin()[2])+(input->GetDimension(2))*(input->GetVtkImageData()->GetSpacing()[2])); // m_T2DMapper->AddClippingPlane(m_ClippingPlane); m_HiResMapper->AddClippingPlane(m_ClippingPlane); } m_PlaneWidget->GetPlane(m_ClippingPlane); m_PlaneSet = true; } else //if clippingplane is disabled { if(m_PlaneSet) //if plane exists { DelClippingPlane(); } } } /* Removes the clipping plane */ void mitk::VolumeDataVtkMapper3D::DelClippingPlane() { // m_T2DMapper->RemoveAllClippingPlanes(); m_HiResMapper->RemoveAllClippingPlanes(); m_PlaneSet = false; } void mitk::VolumeDataVtkMapper3D::SetDefaultProperties(mitk::DataNode* node, mitk::BaseRenderer* renderer, bool overwrite) { node->AddProperty( "volumerendering", mitk::BoolProperty::New( false ), renderer, overwrite ); node->AddProperty( "volumerendering configuration", mitk::VtkVolumeRenderingProperty::New( 1 ), renderer, overwrite ); node->AddProperty( "binary", mitk::BoolProperty::New( false ), renderer, overwrite ); mitk::Image::Pointer image = dynamic_cast(node->GetData()); if(image.IsNotNull() && image->IsInitialized()) { if((overwrite) || (node->GetProperty("levelwindow", renderer)==NULL)) { mitk::LevelWindowProperty::Pointer levWinProp = mitk::LevelWindowProperty::New(); mitk::LevelWindow levelwindow; levelwindow.SetAuto( image ); levWinProp->SetLevelWindow( levelwindow ); node->SetProperty( "levelwindow", levWinProp, renderer ); } //This mapper used to set a default lut "LookupTable" for images. However, this will //overwrite the default lut of the 2D image mapper. Thus, this property here is renamed. /* if((overwrite) || (node->GetProperty("Volume.LookupTable", renderer)==NULL)) { // add a default rainbow lookup table for color mapping mitk::LookupTable::Pointer mitkLut = mitk::LookupTable::New(); vtkLookupTable* vtkLut = mitkLut->GetVtkLookupTable(); vtkLut->SetHueRange(0.6667, 0.0); vtkLut->SetTableRange(0.0, 20.0); vtkLut->Build(); mitk::LookupTableProperty::Pointer mitkLutProp = mitk::LookupTableProperty::New(); mitkLutProp->SetLookupTable(mitkLut); node->SetProperty( "Volume.LookupTable", mitkLutProp ); }*/ if((overwrite) || (node->GetProperty("TransferFunction", renderer)==NULL)) { // add a default transfer function mitk::TransferFunction::Pointer tf = mitk::TransferFunction::New(); mitk::TransferFunctionInitializer::Pointer tfInit = mitk::TransferFunctionInitializer::New(tf); tfInit->SetTransferFunctionMode(0); node->SetProperty ( "TransferFunction", mitk::TransferFunctionProperty::New ( tf.GetPointer() ) ); } } Superclass::SetDefaultProperties(node, renderer, overwrite); } bool mitk::VolumeDataVtkMapper3D::IsLODEnabled( mitk::BaseRenderer * /*renderer*/ ) const { return false; // Volume mapper is LOD enabled if volumerendering is enabled /* return dynamic_cast(GetDataNode()->GetProperty("volumerendering",renderer)) != NULL && dynamic_cast(GetDataNode()->GetProperty("volumerendering",renderer))->GetValue() == true; */ } void mitk::VolumeDataVtkMapper3D::EnableMask() { if (!this->m_Mask) { const Image *orig_image = this->GetInput(); unsigned int *dimensions = orig_image->GetDimensions(); this->m_Mask = vtkImageData::New(); this->m_Mask->SetDimensions(dimensions[0], dimensions[1], dimensions[2]); this->m_Mask->AllocateScalars(VTK_UNSIGNED_CHAR,1); unsigned char *mask_data = static_cast(this->m_Mask->GetScalarPointer()); unsigned int size = dimensions[0] * dimensions[1] * dimensions[2]; for (unsigned int i = 0u; i < size; ++i) { *mask_data++ = 1u; } this->m_ImageMaskFilter->SetMaskInputData(this->m_Mask); this->m_ImageMaskFilter->Modified(); } } void mitk::VolumeDataVtkMapper3D::DisableMask() { if (this->m_Mask) { this->m_Mask->Delete(); this->m_Mask = 0; } } mitk::Image::Pointer mitk::VolumeDataVtkMapper3D::GetMask() { if (this->m_Mask) { Image::Pointer mask = Image::New(); mask->Initialize(this->m_Mask); mask->SetImportVolume(this->m_Mask->GetScalarPointer(), 0, 0, Image::ReferenceMemory); mask->SetGeometry(this->GetInput()->GetGeometry()); return mask; } return 0; } void mitk::VolumeDataVtkMapper3D::UpdateMask() { if (this->m_Mask) { this->m_ImageMaskFilter->Modified(); } } bool mitk::VolumeDataVtkMapper3D::SetMask(const mitk::Image* mask) { if (this->m_Mask) { if ( (mask->GetPixelType().GetComponentType() == itk::ImageIOBase::UCHAR) &&(mask->GetPixelType().GetPixelType() == itk::ImageIOBase::SCALAR )) { Image *img = const_cast(mask); this->m_Mask->DeepCopy(img->GetVtkImageData()); this->m_ImageMaskFilter->Modified(); return true; } } return false; } diff --git a/Core/Code/Testing/DICOMTesting/mitkTestDICOMLoading.cpp b/Core/Code/Testing/DICOMTesting/mitkTestDICOMLoading.cpp index df595732e0..bd9256db26 100644 --- a/Core/Code/Testing/DICOMTesting/mitkTestDICOMLoading.cpp +++ b/Core/Code/Testing/DICOMTesting/mitkTestDICOMLoading.cpp @@ -1,449 +1,449 @@ /*=================================================================== 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 MBILOG_ENABLE_DEBUG #include #include "mitkTestDICOMLoading.h" #include mitk::TestDICOMLoading::TestDICOMLoading() :m_PreviousCLocale(NULL) { } void mitk::TestDICOMLoading::SetDefaultLocale() { // remember old locale only once if (m_PreviousCLocale == NULL) { m_PreviousCLocale = setlocale(LC_NUMERIC, NULL); // set to "C" setlocale(LC_NUMERIC, "C"); m_PreviousCppLocale = std::cin.getloc(); std::locale l( "C" ); std::cin.imbue(l); std::cout.imbue(l); } } void mitk::TestDICOMLoading::ResetUserLocale() { if (m_PreviousCLocale) { setlocale(LC_NUMERIC, m_PreviousCLocale); std::cin.imbue(m_PreviousCppLocale); std::cout.imbue(m_PreviousCppLocale); m_PreviousCLocale = NULL; } } mitk::TestDICOMLoading::ImageList mitk::TestDICOMLoading::LoadFiles( const StringContainer& files, Image::Pointer preLoadedVolume ) { for (StringContainer::const_iterator iter = files.begin(); iter != files.end(); ++iter) { MITK_DEBUG << "File " << *iter; } ImageList result; DicomSeriesReader::FileNamesGrouping seriesInFiles = DicomSeriesReader::GetSeries( files, true ); // TODO sort series UIDs, implementation of map iterator might differ on different platforms (or verify this is a standard topic??) for (DicomSeriesReader::FileNamesGrouping::const_iterator seriesIter = seriesInFiles.begin(); seriesIter != seriesInFiles.end(); ++seriesIter) { StringContainer files = seriesIter->second.GetFilenames(); DataNode::Pointer node = DicomSeriesReader::LoadDicomSeries( files, true, true, true, 0, preLoadedVolume ); // true, true, true ist just a copy of the default values if (node.IsNotNull()) { Image::Pointer image = dynamic_cast( node->GetData() ); result.push_back( image ); } else { } } return result; } std::string mitk::TestDICOMLoading::ComponentTypeToString(int type) { if (type == itk::ImageIOBase::UCHAR) return "UCHAR"; else if (type == itk::ImageIOBase::CHAR) return "CHAR"; else if (type == itk::ImageIOBase::USHORT) return "USHORT"; else if (type == itk::ImageIOBase::SHORT) return "SHORT"; else if (type == itk::ImageIOBase::UINT) return "UINT"; else if (type == itk::ImageIOBase::INT) return "INT"; else if (type == itk::ImageIOBase::ULONG) return "ULONG"; else if (type == itk::ImageIOBase::LONG) return "LONG"; else if (type == itk::ImageIOBase::FLOAT) return "FLOAT"; else if (type == itk::ImageIOBase::DOUBLE) return "DOUBLE"; else return "UNKNOWN"; } // add a line to stringstream result (see DumpImageInformation #define DumpLine(field, data) DumpILine(0, field, data) // add an indented(!) line to stringstream result (see DumpImageInformation #define DumpILine(indent, field, data) \ { \ std::string DumpLine_INDENT; DumpLine_INDENT.resize(indent, ' ' ); \ result << DumpLine_INDENT << field << ": " << data << "\n"; \ } std::string mitk::TestDICOMLoading::DumpImageInformation( const Image* image ) { std::stringstream result; if (image == NULL) return result.str(); SetDefaultLocale(); // basic image data DumpLine( "Pixeltype", ComponentTypeToString(image->GetPixelType().GetComponentType()) ); DumpLine( "BitsPerPixel", image->GetPixelType().GetBpe() ); DumpLine( "Dimension", image->GetDimension() ); result << "Dimensions: "; for (unsigned int dim = 0; dim < image->GetDimension(); ++dim) result << image->GetDimension(dim) << " "; result << "\n"; // geometry data result << "Geometry: \n"; - Geometry3D* geometry = image->GetGeometry(); + BaseGeometry* geometry = image->GetGeometry(); if (geometry) { AffineTransform3D* transform = geometry->GetIndexToWorldTransform(); if (transform) { result << " " << "Matrix: "; const AffineTransform3D::MatrixType& matrix = transform->GetMatrix(); for (unsigned int i = 0; i < 3; ++i) for (unsigned int j = 0; j < 3; ++j) result << matrix[i][j] << " "; result << "\n"; result << " " << "Offset: "; const AffineTransform3D::OutputVectorType& offset = transform->GetOffset(); for (unsigned int i = 0; i < 3; ++i) result << offset[i] << " "; result << "\n"; result << " " << "Center: "; const AffineTransform3D::InputPointType& center = transform->GetCenter(); for (unsigned int i = 0; i < 3; ++i) result << center[i] << " "; result << "\n"; result << " " << "Translation: "; const AffineTransform3D::OutputVectorType& translation = transform->GetTranslation(); for (unsigned int i = 0; i < 3; ++i) result << translation[i] << " "; result << "\n"; result << " " << "Scale: "; const double* scale = transform->GetScale(); for (unsigned int i = 0; i < 3; ++i) result << scale[i] << " "; result << "\n"; result << " " << "Origin: "; const Point3D& origin = geometry->GetOrigin(); for (unsigned int i = 0; i < 3; ++i) result << origin[i] << " "; result << "\n"; result << " " << "Spacing: "; const Vector3D& spacing = geometry->GetSpacing(); for (unsigned int i = 0; i < 3; ++i) result << spacing[i] << " "; result << "\n"; result << " " << "TimeBounds: "; const TimeBounds timeBounds = geometry->GetTimeBounds(); for (unsigned int i = 0; i < 2; ++i) result << timeBounds[i] << " "; result << "\n"; } } ResetUserLocale(); return result.str(); } std::string mitk::TestDICOMLoading::trim(const std::string& pString, const std::string& pWhitespace) { const size_t beginStr = pString.find_first_not_of(pWhitespace); if (beginStr == std::string::npos) { // no content return ""; } const size_t endStr = pString.find_last_not_of(pWhitespace); const size_t range = endStr - beginStr + 1; return pString.substr(beginStr, range); } std::string mitk::TestDICOMLoading::reduce(const std::string& pString, const std::string& pFill, const std::string& pWhitespace) { // trim first std::string result(trim(pString, pWhitespace)); // replace sub ranges size_t beginSpace = result.find_first_of(pWhitespace); while (beginSpace != std::string::npos) { const size_t endSpace = result.find_first_not_of(pWhitespace, beginSpace); const size_t range = endSpace - beginSpace; result.replace(beginSpace, range, pFill); const size_t newStart = beginSpace + pFill.length(); beginSpace = result.find_first_of(pWhitespace, newStart); } return result; } bool mitk::TestDICOMLoading::CompareSpacedValueFields( const std::string& reference, const std::string& test, double /*eps*/ ) { bool result(true); // tokenize string, compare each token, if possible by float comparison std::stringstream referenceStream(reduce(reference)); std::stringstream testStream(reduce(test)); std::string refToken; std::string testToken; while ( std::getline( referenceStream, refToken, ' ' ) && std::getline ( testStream, testToken, ' ' ) ) { float refNumber; float testNumber; if ( this->StringToNumber(refToken, refNumber) ) { if ( this->StringToNumber(testToken, testNumber) ) { // print-out compared tokens if DEBUG output allowed MITK_DEBUG << "Reference Token '" << refToken << "'" << " value " << refNumber << ", test Token '" << testToken << "'" << " value " << testNumber; bool old_result = result; result &= ( fabs(refNumber - testNumber) < 0.0001 /*mitk::eps*/ ); // log the token/number which causes the test to fail if( old_result != result) { MITK_ERROR << std::setprecision(16) << "Reference Token '" << refToken << "'" << " value " << refNumber << ", test Token '" << testToken << "'" << " value " << testNumber; MITK_ERROR << "[FALSE] - difference: " << std::setprecision(16) << fabs(refNumber - testNumber) << " EPS: " << 0.0001;// mitk::eps; } } else { MITK_ERROR << refNumber << " cannot be compared to '" << testToken << "'"; } } else { MITK_DEBUG << "Token '" << refToken << "'" << " handled as string"; result &= refToken == testToken; } } if ( std::getline( referenceStream, refToken, ' ' ) ) { MITK_ERROR << "Reference string still had values when test string was already parsed: ref '" << reference << "', test '" << test << "'"; result = false; } else if ( std::getline( testStream, testToken, ' ' ) ) { MITK_ERROR << "Test string still had values when reference string was already parsed: ref '" << reference << "', test '" << test << "'"; result = false; } return result; } bool mitk::TestDICOMLoading::CompareImageInformationDumps( const std::string& referenceDump, const std::string& testDump ) { KeyValueMap reference = ParseDump(referenceDump); KeyValueMap test = ParseDump(testDump); bool testResult(true); // verify all expected values for (KeyValueMap::const_iterator refIter = reference.begin(); refIter != reference.end(); ++refIter) { const std::string& refKey = refIter->first; const std::string& refValue = refIter->second; if ( test.find(refKey) != test.end() ) { const std::string& testValue = test[refKey]; bool thisTestResult = CompareSpacedValueFields( refValue, testValue ); testResult &= thisTestResult; MITK_DEBUG << refKey << ": '" << refValue << "' == '" << testValue << "' ? " << (thisTestResult?"YES":"NO"); } else { MITK_ERROR << "Reference dump contains a key'" << refKey << "' (value '" << refValue << "')." ; MITK_ERROR << "This key is expected to be generated for tests (but was not). Most probably you need to update your test data."; return false; } } // now check test dump does not contain any additional keys for (KeyValueMap::const_iterator testIter = test.begin(); testIter != test.end(); ++testIter) { const std::string& key = testIter->first; const std::string& value = testIter->second; if ( reference.find(key) == reference.end() ) { MITK_ERROR << "Test dump contains an unexpected key'" << key << "' (value '" << value << "')." ; MITK_ERROR << "This key is not expected. Most probably you need to update your test data."; return false; } } return testResult; } mitk::TestDICOMLoading::KeyValueMap mitk::TestDICOMLoading::ParseDump( const std::string& dump ) { KeyValueMap parsedResult; std::string shredder(dump); std::stack surroundingKeys; std::stack expectedIndents; expectedIndents.push(0); while (true) { std::string::size_type newLinePos = shredder.find( '\n' ); if (newLinePos == std::string::npos || newLinePos == 0) break; std::string line = shredder.substr( 0, newLinePos ); shredder = shredder.erase( 0, newLinePos+1 ); std::string::size_type keyPosition = line.find_first_not_of( ' ' ); std::string::size_type colonPosition = line.find( ':' ); std::string key = line.substr(keyPosition, colonPosition - keyPosition); std::string::size_type firstSpacePosition = key.find_first_of(" "); if (firstSpacePosition != std::string::npos) { key.erase(firstSpacePosition); } if ( keyPosition > expectedIndents.top() ) { // more indent than before expectedIndents.push(keyPosition); } else if (keyPosition == expectedIndents.top() ) { if (!surroundingKeys.empty()) { surroundingKeys.pop(); // last of same length } } else { // less indent than before do expectedIndents.pop(); while (expectedIndents.top() != keyPosition); // unwind until current indent is found } if (!surroundingKeys.empty()) { key = surroundingKeys.top() + "." + key; // construct current key name } surroundingKeys.push(key); // this is the new embracing key std::string value = line.substr(colonPosition+1); MITK_DEBUG << " Key: '" << key << "' value '" << value << "'" ; parsedResult[key] = value; // store parsing result } return parsedResult; } diff --git a/Core/Code/Testing/files.cmake b/Core/Code/Testing/files.cmake index 5fa971e04d..f8f8eccce9 100644 --- a/Core/Code/Testing/files.cmake +++ b/Core/Code/Testing/files.cmake @@ -1,199 +1,200 @@ # tests with no extra command line parameter set(MODULE_TESTS # IMPORTANT: If you plan to deactivate / comment out a test please write a bug number to the commented out line of code. # # Example: #mitkMyTest #this test is commented out because of bug 12345 # # It is important that the bug is open and that the test will be activated again before the bug is closed. This assures that # no test is forgotten after it was commented out. If there is no bug for your current problem, please add a new one and # mark it as critical. ################## DISABLED TESTS ################################################# #mitkAbstractTransformGeometryTest.cpp #seems as tested class mitkExternAbstractTransformGeometry doesnt exist any more #mitkStateMachineContainerTest.cpp #rewrite test, indirect since no longer exported Bug 14529 #mitkRegistrationBaseTest.cpp #tested class mitkRegistrationBase doesn't exist any more #mitkSegmentationInterpolationTest.cpp #file doesn't exist! #mitkPipelineSmartPointerCorrectnessTest.cpp #file doesn't exist! #mitkITKThreadingTest.cpp #test outdated because itk::Semaphore was removed from ITK #mitkAbstractTransformPlaneGeometryTest.cpp #mitkVtkAbstractTransformPlaneGeometry doesn't exist any more #mitkTestUtilSharedLibrary.cpp #Linker problem with this test... #mitkTextOverlay2DSymbolsRenderingTest.cpp #Implementation of the tested feature is not finished yet. Ask Christoph or see bug 15104 for details. ################# RUNNING TESTS ################################################### mitkAccessByItkTest.cpp mitkCoreObjectFactoryTest.cpp mitkMaterialTest.cpp mitkActionTest.cpp mitkDispatcherTest.cpp mitkEnumerationPropertyTest.cpp mitkEventTest.cpp mitkFocusManagerTest.cpp mitkGenericPropertyTest.cpp mitkGeometry2DTest.cpp mitkGeometry3DTest.cpp mitkGeometry3DEqualTest.cpp mitkGeometryDataToSurfaceFilterTest.cpp mitkGlobalInteractionTest.cpp mitkImageEqualTest.cpp mitkImageDataItemTest.cpp mitkImageGeneratorTest.cpp mitkIOUtilTest.cpp mitkBaseDataTest.cpp mitkImportItkImageTest.cpp mitkGrabItkImageMemoryTest.cpp mitkInstantiateAccessFunctionTest.cpp mitkInteractorTest.cpp mitkLevelWindowTest.cpp mitkMessageTest.cpp mitkPixelTypeTest.cpp mitkPlaneGeometryTest.cpp mitkPointSetEqualTest.cpp mitkPointSetFileIOTest.cpp mitkPointSetTest.cpp mitkPointSetWriterTest.cpp mitkPointSetReaderTest.cpp mitkPointSetInteractorTest.cpp mitkPropertyTest.cpp mitkPropertyListTest.cpp mitkSlicedGeometry3DTest.cpp mitkSliceNavigationControllerTest.cpp mitkStateMachineTest.cpp mitkStateTest.cpp mitkSurfaceTest.cpp mitkSurfaceEqualTest.cpp mitkSurfaceToSurfaceFilterTest.cpp mitkTimeGeometryTest.cpp mitkTransitionTest.cpp mitkUndoControllerTest.cpp mitkVtkWidgetRenderingTest.cpp mitkVerboseLimitedLinearUndoTest.cpp mitkWeakPointerTest.cpp mitkTransferFunctionTest.cpp mitkStepperTest.cpp mitkRenderingManagerTest.cpp vtkMitkThickSlicesFilterTest.cpp mitkNodePredicateSourceTest.cpp mitkVectorTest.cpp mitkClippedSurfaceBoundsCalculatorTest.cpp mitkExceptionTest.cpp mitkExtractSliceFilterTest.cpp mitkLogTest.cpp mitkImageDimensionConverterTest.cpp mitkLoggingAdapterTest.cpp mitkUIDGeneratorTest.cpp mitkShaderRepositoryTest.cpp mitkPlanePositionManagerTest.cpp mitkAffineTransformBaseTest.cpp mitkPropertyAliasesTest.cpp mitkPropertyDescriptionsTest.cpp mitkPropertyExtensionsTest.cpp mitkPropertyFiltersTest.cpp mitkTinyXMLTest.cpp mitkRawImageFileReaderTest.cpp mitkInteractionEventTest.cpp mitkLookupTableTest.cpp mitkSTLFileReaderTest.cpp mitkSurfaceToImageFilterTest.cpp + mitkBaseGeometryTest.cpp mitkImageToSurfaceFilterTest.cpp mitkEqualTest.cpp ) if(MITK_ENABLE_RENDERING_TESTING) #since mitkInteractionTestHelper is currently creating a vtkRenderWindow set(MODULE_TESTS ${MODULE_TESTS} mitkPointSetDataInteractorTest.cpp ) endif() # test with image filename as an extra command line parameter set(MODULE_IMAGE_TESTS mitkImageTimeSelectorTest.cpp #only runs on images mitkImageAccessorTest.cpp #only runs on images mitkDataNodeFactoryTest.cpp #runs on all types of data ) set(MODULE_SURFACE_TESTS mitkSurfaceVtkWriterTest.cpp #only runs on surfaces mitkDataNodeFactoryTest.cpp #runs on all types of data ) # list of images for which the tests are run set(MODULE_TESTIMAGES US4DCyl.nrrd Pic3D.nrrd Pic2DplusT.nrrd BallBinary30x30x30.nrrd Png2D-bw.png ) set(MODULE_TESTSURFACES binary.stl ball.stl ) set(MODULE_CUSTOM_TESTS mitkDataStorageTest.cpp mitkDataNodeTest.cpp mitkDicomSeriesReaderTest.cpp mitkDICOMLocaleTest.cpp mitkEventMapperTest.cpp mitkEventConfigTest.cpp mitkNodeDependentPointSetInteractorTest.cpp mitkStateMachineFactoryTest.cpp mitkPointSetLocaleTest.cpp mitkImageTest.cpp mitkImageWriterTest.cpp mitkImageVtkMapper2DTest.cpp mitkImageVtkMapper2DLevelWindowTest.cpp mitkImageVtkMapper2DOpacityTest.cpp mitkImageVtkMapper2DResliceInterpolationPropertyTest.cpp mitkImageVtkMapper2DColorTest.cpp mitkImageVtkMapper2DSwivelTest.cpp mitkImageVtkMapper2DTransferFunctionTest.cpp mitkImageVtkMapper2DLookupTableTest.cpp mitkSurfaceVtkMapper3DTest mitkSurfaceVtkMapper3DTexturedSphereTest.cpp mitkSurfaceGLMapper2DColorTest.cpp mitkSurfaceGLMapper2DOpacityTest.cpp mitkVolumeCalculatorTest.cpp mitkLevelWindowManagerTest.cpp mitkPointSetVtkMapper2DTest.cpp mitkPointSetVtkMapper2DImageTest.cpp mitkPointSetVtkMapper2DGlyphTypeTest.cpp mitkPointSetVtkMapper2DTransformedPointsTest.cpp mitkLabelOverlay3DRendering2DTest.cpp mitkLabelOverlay3DRendering3DTest.cpp mitkTextOverlay2DRenderingTest.cpp mitkTextOverlay2DLayouterRenderingTest.cpp mitkTextOverlay3DRendering2DTest.cpp mitkTextOverlay3DRendering3DTest.cpp mitkTextOverlay3DColorRenderingTest.cpp mitkVTKRenderWindowSizeTest.cpp mitkMultiComponentImageDataComparisonFilterTest.cpp mitkImageToItkTest.cpp mitkImageSliceSelectorTest.cpp mitkSurfaceDepthPeelingTest.cpp ) set(MODULE_RESOURCE_FILES Interactions/AddAndRemovePoints.xml Interactions/globalConfig.xml Interactions/StatemachineTest.xml Interactions/StatemachineConfigTest.xml ) # Create an artificial module initializing class for # the usServiceListenerTest.cpp usFunctionGenerateExecutableInit(testdriver_init_file IDENTIFIER ${MODULE_NAME}TestDriver ) # Embed the resources set(testdriver_resources ) usFunctionEmbedResources(testdriver_resources EXECUTABLE_NAME ${MODULE_NAME}TestDriver ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/Resources FILES ${MODULE_RESOURCE_FILES} ) set(TEST_CPP_FILES ${testdriver_init_file} ${testdriver_resources}) diff --git a/Core/Code/Testing/mitkBaseDataTest.cpp b/Core/Code/Testing/mitkBaseDataTest.cpp index 373df3947c..c0a8a3f3a9 100644 --- a/Core/Code/Testing/mitkBaseDataTest.cpp +++ b/Core/Code/Testing/mitkBaseDataTest.cpp @@ -1,120 +1,121 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkBaseDataTestImplementation.h" #include "mitkStringProperty.h" #include "mitkTestingMacros.h" #include #include #include "itkImage.h" int mitkBaseDataTest(int /*argc*/, char* /*argv*/[]) { MITK_TEST_BEGIN("BaseData") //Create a BaseData implementation MITK_INFO << "Creating a base data instance..."; mitk::BaseDataTestImplementation::Pointer baseDataImpl = mitk::BaseDataTestImplementation::New(); MITK_TEST_CONDITION_REQUIRED(baseDataImpl.IsNotNull(),"Testing instantiation"); MITK_TEST_CONDITION(baseDataImpl->IsInitialized(), "BaseDataTestImplementation is initialized"); MITK_TEST_CONDITION(baseDataImpl->IsEmpty(), "BaseDataTestImplementation is initialized and empty"); mitk::BaseDataTestImplementation::Pointer cloneBaseData = baseDataImpl->Clone(); MITK_TEST_CONDITION_REQUIRED(cloneBaseData.IsNotNull(),"Testing instantiation of base data clone"); MITK_TEST_CONDITION(cloneBaseData->IsInitialized(), "Clone of BaseDataTestImplementation is initialized"); MITK_TEST_CONDITION(cloneBaseData->IsEmpty(), "Clone of BaseDataTestImplementation is initialized and empty"); MITK_INFO << "Testing setter and getter for geometries..."; //test method GetTimeGeometry() MITK_TEST_CONDITION(baseDataImpl->GetTimeGeometry(), "Testing creation of TimeGeometry"); mitk::TimeGeometry* geo = NULL; baseDataImpl->SetTimeGeometry(geo); MITK_TEST_CONDITION(baseDataImpl->GetTimeGeometry() == NULL, "Reset Geometry"); mitk::ProportionalTimeGeometry::Pointer geo2 = mitk::ProportionalTimeGeometry::New(); baseDataImpl->SetTimeGeometry(geo2); geo2->Initialize(2); MITK_TEST_CONDITION(baseDataImpl->GetTimeGeometry() == geo2.GetPointer(), "Correct Reinit of TimeGeometry"); //test method GetGeometry(int timeStep) MITK_TEST_CONDITION(baseDataImpl->GetGeometry(1) != NULL, "... and single Geometries"); //test method Expand(unsigned int timeSteps) baseDataImpl->Expand(5); MITK_TEST_CONDITION(baseDataImpl->GetTimeSteps() == 5, "Expand the geometry to further time slices!"); //test method GetUpdatedGeometry(int timeStep); - mitk::Geometry3D::Pointer geo3 = mitk::Geometry3D::New(); + mitk::Geometry3D::Pointer geometry3D = mitk::Geometry3D::New(); + mitk::BaseGeometry::Pointer geo3 = dynamic_cast(geometry3D.GetPointer()); mitk::ProportionalTimeGeometry::Pointer timeGeometry = dynamic_cast(baseDataImpl->GetTimeGeometry()); if (timeGeometry.IsNotNull() ) { timeGeometry->SetTimeStepGeometry(geo3,1); } MITK_TEST_CONDITION(baseDataImpl->GetUpdatedGeometry(1) == geo3, "Set Geometry for time step 1"); MITK_TEST_CONDITION(baseDataImpl->GetMTime()!= 0, "Check if modified time is set"); baseDataImpl->SetClonedGeometry(geo3, 1); mitk::ScalarType x[3]; x[0] = 2; x[1] = 4; x[2] = 6; mitk::Point3D p3d(x); baseDataImpl->SetOrigin(p3d); geo3->SetOrigin(p3d); MITK_TEST_CONDITION(baseDataImpl->GetGeometry(1)->GetOrigin() == geo3->GetOrigin(), "Testing Origin set"); cloneBaseData = baseDataImpl->Clone(); MITK_TEST_CONDITION(cloneBaseData->GetGeometry(1)->GetOrigin() == geo3->GetOrigin(), "Testing origin set in clone!"); MITK_TEST_CONDITION(!baseDataImpl->IsEmptyTimeStep(1), "Is not empty before clear()!"); baseDataImpl->Clear(); MITK_TEST_CONDITION(baseDataImpl->IsEmptyTimeStep(1), "...but afterwards!"); //test method Set-/GetProperty() baseDataImpl->SetProperty("property38", mitk::StringProperty::New("testproperty")); //baseDataImpl->SetProperty("visibility", mitk::BoolProperty::New()); MITK_TEST_CONDITION(baseDataImpl->GetProperty("property38")->GetValueAsString() == "testproperty","Check if base property is set correctly!"); cloneBaseData = baseDataImpl->Clone(); MITK_TEST_CONDITION(cloneBaseData->GetProperty("property38")->GetValueAsString() == "testproperty", "Testing origin set in clone!"); //test method Set-/GetPropertyList mitk::PropertyList::Pointer propertyList = mitk::PropertyList::New(); propertyList->SetFloatProperty("floatProperty1", 123.45); propertyList->SetBoolProperty("visibility",true); propertyList->SetStringProperty("nameXY","propertyName"); baseDataImpl->SetPropertyList(propertyList); bool value = false; MITK_TEST_CONDITION(baseDataImpl->GetPropertyList() == propertyList, "Check if base property list is set correctly!"); MITK_TEST_CONDITION(baseDataImpl->GetPropertyList()->GetBoolProperty("visibility", value) == true, "Check if base property is set correctly in the property list!"); //test method UpdateOutputInformation() baseDataImpl->UpdateOutputInformation(); MITK_TEST_CONDITION(baseDataImpl->GetUpdatedTimeGeometry() == geo2, "TimeGeometry update!"); //Test method CopyInformation() mitk::BaseDataTestImplementation::Pointer newBaseData = mitk::BaseDataTestImplementation::New(); newBaseData->CopyInformation(baseDataImpl); MITK_TEST_CONDITION_REQUIRED( newBaseData->GetTimeGeometry()->CountTimeSteps() == 5, "Check copying of of Basedata Data Object!"); MITK_TEST_END() } diff --git a/Core/Code/Testing/mitkBaseGeometryTest.cpp b/Core/Code/Testing/mitkBaseGeometryTest.cpp new file mode 100644 index 0000000000..6067b39a07 --- /dev/null +++ b/Core/Code/Testing/mitkBaseGeometryTest.cpp @@ -0,0 +1,1227 @@ +/*=================================================================== + +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 + +#include +#include +#include +#include "mitkoperationactor.h" + +#include +#include "mitkvector.h" +#include +#include +#include "itkScalableAffineTransform.h" +#include +#include +#include + +#include "mitkRotationOperation.h" +#include "mitkInteractionConst.h" +#include +#include + +class vtkMatrix4x4; +class vtkMatrixToLinearTransform; +class vtkLinearTransform; + +typedef itk::BoundingBox BoundingBox; +typedef itk::BoundingBox BoundingBoxType; +typedef BoundingBoxType::BoundsArrayType BoundsArrayType; +typedef BoundingBoxType::Pointer BoundingBoxPointer; + +// Dummy instance of abstract base class +class DummyTestClass : public mitk::BaseGeometry +{ +public: + DummyTestClass(){}; + DummyTestClass(const DummyTestClass& other) : BaseGeometry(other){}; + ~DummyTestClass(){}; + + mitkClassMacro(DummyTestClass, mitk::BaseGeometry); + itkNewMacro(Self); + mitkNewMacro1Param(Self,Self); + + itk::LightObject::Pointer DummyTestClass::InternalClone() const + { + Self::Pointer newGeometry = new Self(*this); + newGeometry->UnRegister(); + return newGeometry.GetPointer(); + } + + virtual void PrintSelf(std::ostream& os, itk::Indent indent) const{}; + +}; + +class mitkBaseGeometryTestSuite : public mitk::TestFixture +{ + // List of Tests + CPPUNIT_TEST_SUITE(mitkBaseGeometryTestSuite); + + //Constructor + MITK_TEST(TestConstructors); + MITK_TEST(TestInitialize); + + //Set + MITK_TEST(TestSetOrigin); + MITK_TEST(TestSetBounds); + MITK_TEST(TestSetFloatBounds); + MITK_TEST(TestSetFloatBoundsDouble); + MITK_TEST(TestSetFrameOfReferenceID); + MITK_TEST(TestSetIndexToWorldTransform); + MITK_TEST(TestSetSpacing); + MITK_TEST(TestTransferItkToVtkTransform); + MITK_TEST(TestSetIndexToWorldTransformByVtkMatrix); + MITK_TEST(TestSetIdentity); + MITK_TEST(TestSetImageGeometry); + //Equal + MITK_TEST(Equal_CloneAndOriginal_ReturnsTrue); + MITK_TEST(Equal_DifferentOrigin_ReturnsFalse); + MITK_TEST(Equal_DifferentIndexToWorldTransform_ReturnsFalse); + MITK_TEST(Equal_DifferentSpacing_ReturnsFalse); + MITK_TEST(Equal_InputIsNull_ReturnsFalse); + MITK_TEST(Equal_DifferentBoundingBox_ReturnsFalse); + //other Functions + MITK_TEST(TestComposeTransform); + MITK_TEST(TestComposeVtkMatrix); + MITK_TEST(TestTranslate); + MITK_TEST(TestIndexToWorld); + MITK_TEST(TestExecuteOperation); + MITK_TEST(TestCalculateBoundingBoxRelToTransform); + MITK_TEST(TestSetTimeBounds); + MITK_TEST(TestIs2DConvertable); + MITK_TEST(TestGetCornerPoint); + MITK_TEST(TestExtentInMM); + MITK_TEST(TestGetAxisVector); + MITK_TEST(TestGetCenter); + MITK_TEST(TestGetDiagonalLength); + MITK_TEST(TestGetExtent); + MITK_TEST(TestIsInside); + MITK_TEST(TestGetMatrixColumn); + + CPPUNIT_TEST_SUITE_END(); + + // Used Variables +private: + mitk::Point3D aPoint; + float aFloatSpacing[3]; + mitk::Vector3D aSpacing; + mitk::AffineTransform3D::Pointer aTransform; + BoundingBoxPointer aBoundingBox; + mitk::AffineTransform3D::MatrixType aMatrix; + + mitk::Point3D anotherPoint; + mitk::Vector3D anotherSpacing; + BoundingBoxPointer anotherBoundingBox; + BoundingBoxPointer aThirdBoundingBox; + mitk::AffineTransform3D::Pointer anotherTransform; + mitk::AffineTransform3D::Pointer aThirdTransform; + mitk::AffineTransform3D::MatrixType anotherMatrix; + mitk::AffineTransform3D::MatrixType aThirdMatrix; + + DummyTestClass::Pointer aDummyGeometry; + DummyTestClass::Pointer anotherDummyGeometry; + +public: + + // Set up for variables + void setUp() + { + mitk::FillVector3D(aFloatSpacing, 1,1,1); + mitk::FillVector3D(aSpacing, 1,1,1); + mitk::FillVector3D(aPoint, 0,0,0); + + //Transform + aTransform = mitk::AffineTransform3D::New(); + aTransform->SetIdentity(); + + aMatrix.SetIdentity(); + + anotherTransform = mitk::AffineTransform3D::New(); + + anotherMatrix.SetIdentity(); + anotherMatrix(1,1) = 2; + anotherTransform->SetMatrix( anotherMatrix ); + + aThirdTransform = mitk::AffineTransform3D::New(); + + aThirdMatrix.SetIdentity(); + aThirdMatrix(1,1) = 7; + aThirdTransform->SetMatrix( aThirdMatrix ); + + //Bounding Box + float bounds[6] = {0,1,0,1,0,1}; + mitk::BoundingBox::BoundsArrayType b; + const float *input = bounds; + int j=0; + for(mitk::BoundingBox::BoundsArrayType::Iterator it = b.Begin(); j < 6 ;++j) *it++ = (mitk::ScalarType)*input++; + + aBoundingBox = BoundingBoxType::New(); + + BoundingBoxType::PointsContainer::Pointer pointscontainer = BoundingBoxType::PointsContainer::New(); + BoundingBoxType::PointType p; + BoundingBoxType::PointIdentifier pointid; + for(pointid=0; pointid<2;++pointid) + { + unsigned int i; + for(i=0; i<3; ++i) + { + p[i] = bounds[2*i+pointid]; + } + pointscontainer->InsertElement(pointid, p); + } + + aBoundingBox->SetPoints(pointscontainer); + aBoundingBox->ComputeBoundingBox(); + + anotherBoundingBox = BoundingBoxType::New(); + p[0]=11; + p[1]=12; + p[2]=13; + pointscontainer->InsertElement(1, p); + anotherBoundingBox->SetPoints(pointscontainer); + anotherBoundingBox->ComputeBoundingBox(); + + aThirdBoundingBox = BoundingBoxType::New(); + p[0]=22; + p[1]=23; + p[2]=24; + pointscontainer->InsertElement(1, p); + aThirdBoundingBox->SetPoints(pointscontainer); + aThirdBoundingBox->ComputeBoundingBox(); + + mitk::FillVector3D(anotherPoint, 2,3,4); + mitk::FillVector3D(anotherSpacing, 5,6.5,7); + + aDummyGeometry = DummyTestClass::New(); + aDummyGeometry->Initialize(); + anotherDummyGeometry = aDummyGeometry->Clone(); + } + + void tearDown() + { + aDummyGeometry = NULL; + anotherDummyGeometry = NULL; + } + + // Test functions + + void TestSetOrigin() + { + DummyTestClass::Pointer dummy = DummyTestClass::New(); + dummy->SetOrigin(anotherPoint); + CPPUNIT_ASSERT(anotherPoint==dummy->GetOrigin()); + + //undo changes, new and changed object need to be the same! + dummy->SetOrigin(aPoint); + DummyTestClass::Pointer newDummy = DummyTestClass::New(); + CPPUNIT_ASSERT(mitk::Equal(dummy,newDummy,mitk::eps,true)); + } + + void TestSetImageGeometry() + { + DummyTestClass::Pointer dummy = DummyTestClass::New(); + dummy->SetImageGeometry(true); + CPPUNIT_ASSERT(dummy->GetImageGeometry()); + + //undo changes, new and changed object need to be the same! + dummy->SetImageGeometry(false); + CPPUNIT_ASSERT(dummy->GetImageGeometry()==false); + DummyTestClass::Pointer newDummy = DummyTestClass::New(); + CPPUNIT_ASSERT(mitk::Equal(dummy,newDummy,mitk::eps,true)); + } + + void TestSetFloatBounds(){ + float bounds[6] = {0,11,0,12,0,13}; + DummyTestClass::Pointer dummy = DummyTestClass::New(); + dummy->SetFloatBounds(bounds); + CPPUNIT_ASSERT(mitk::Equal( dummy->GetBoundingBox(), anotherBoundingBox, mitk::eps, true)); + + //Wrong bounds, test needs to fail + bounds[1]=7; + dummy->SetFloatBounds(bounds); + CPPUNIT_ASSERT((mitk::Equal( dummy->GetBoundingBox(), anotherBoundingBox, mitk::eps, false))==false); + + //undo changes, new and changed object need to be the same! + float originalBounds[6] = {0,1,0,1,0,1}; + dummy->SetFloatBounds(originalBounds); + DummyTestClass::Pointer newDummy = DummyTestClass::New(); + CPPUNIT_ASSERT(mitk::Equal(dummy,newDummy,mitk::eps,true)); + } + + void TestSetBounds(){ + DummyTestClass::Pointer dummy = DummyTestClass::New(); + dummy->SetBounds(anotherBoundingBox->GetBounds()); + CPPUNIT_ASSERT(mitk::Equal( dummy->GetBoundingBox(), anotherBoundingBox, mitk::eps, true)); + + //Test needs to fail now + dummy->SetBounds(aThirdBoundingBox->GetBounds()); + CPPUNIT_ASSERT(mitk::Equal( dummy->GetBoundingBox(), anotherBoundingBox, mitk::eps, false)==false); + + //undo changes, new and changed object need to be the same! + dummy->SetBounds(aBoundingBox->GetBounds()); + DummyTestClass::Pointer newDummy = DummyTestClass::New(); + CPPUNIT_ASSERT(mitk::Equal(dummy,newDummy,mitk::eps,true)); + } + + void TestSetFloatBoundsDouble(){ + double bounds[6] = {0,11,0,12,0,13}; + DummyTestClass::Pointer dummy = DummyTestClass::New(); + dummy->SetFloatBounds(bounds); + CPPUNIT_ASSERT(mitk::Equal( dummy->GetBoundingBox(), anotherBoundingBox, mitk::eps, true)); + + //Test needs to fail now + bounds[3]=7; + dummy->SetFloatBounds(bounds); + CPPUNIT_ASSERT(mitk::Equal( dummy->GetBoundingBox(), anotherBoundingBox, mitk::eps, false)==false); + + //undo changes, new and changed object need to be the same! + double originalBounds[6] = {0,1,0,1,0,1}; + dummy->SetFloatBounds(originalBounds); + DummyTestClass::Pointer newDummy = DummyTestClass::New(); + CPPUNIT_ASSERT(mitk::Equal(dummy,newDummy,mitk::eps,true)); + } + + void TestSetFrameOfReferenceID() + { + DummyTestClass::Pointer dummy = DummyTestClass::New(); + dummy->SetFrameOfReferenceID(5); + CPPUNIT_ASSERT(dummy->GetFrameOfReferenceID()==5); + + //undo changes, new and changed object need to be the same! + dummy->SetFrameOfReferenceID(0); + DummyTestClass::Pointer newDummy = DummyTestClass::New(); + CPPUNIT_ASSERT(mitk::Equal(dummy,newDummy,mitk::eps,true)); + } + + void TestSetIndexToWorldTransform() + { + DummyTestClass::Pointer dummy = DummyTestClass::New(); + dummy->SetIndexToWorldTransform(anotherTransform); + CPPUNIT_ASSERT(mitk::Equal(anotherTransform,dummy->GetIndexToWorldTransform(),mitk::eps,true)); + + //Test needs to fail now + dummy->SetIndexToWorldTransform(aThirdTransform); + CPPUNIT_ASSERT(mitk::Equal(anotherTransform,dummy->GetIndexToWorldTransform(),mitk::eps,false)==false); + + //undo changes, new and changed object need to be the same! + dummy->SetIndexToWorldTransform(aTransform); + DummyTestClass::Pointer newDummy = DummyTestClass::New(); + CPPUNIT_ASSERT(mitk::Equal(dummy,newDummy,mitk::eps,true)); + } + + void TestSetIndexToWorldTransformByVtkMatrix() + { + vtkMatrix4x4* vtkmatrix; + vtkmatrix = vtkMatrix4x4::New(); + vtkmatrix->Identity(); + vtkmatrix->SetElement(1,1,2); + DummyTestClass::Pointer dummy = DummyTestClass::New(); + dummy->SetIndexToWorldTransformByVtkMatrix(vtkmatrix); + CPPUNIT_ASSERT(mitk::Equal(anotherTransform,dummy->GetIndexToWorldTransform(),mitk::eps,true)); + + //test needs to fail now + vtkmatrix->SetElement(1,1,7); + dummy->SetIndexToWorldTransformByVtkMatrix(vtkmatrix); + CPPUNIT_ASSERT(mitk::Equal(anotherTransform,dummy->GetIndexToWorldTransform(),mitk::eps,false)==false); + + //undo changes, new and changed object need to be the same! + vtkmatrix->SetElement(1,1,1); + dummy->SetIndexToWorldTransformByVtkMatrix(vtkmatrix); + DummyTestClass::Pointer newDummy = DummyTestClass::New(); + CPPUNIT_ASSERT(mitk::Equal(dummy,newDummy,mitk::eps,true)); + } + + void TestSetIdentity() + { + DummyTestClass::Pointer dummy = DummyTestClass::New(); + //Change IndextoWorldTransform and Origin + dummy->SetIndexToWorldTransform(anotherTransform); + dummy->SetOrigin(anotherPoint); + + //Set Identity should reset ITWT and Origin + dummy->SetIdentity(); + + CPPUNIT_ASSERT(mitk::Equal(aTransform,dummy->GetIndexToWorldTransform(),mitk::eps,true)); + CPPUNIT_ASSERT(aPoint==dummy->GetOrigin()); + CPPUNIT_ASSERT(aSpacing==dummy->GetSpacing()); + + //new and changed object need to be the same! + DummyTestClass::Pointer newDummy = DummyTestClass::New(); + CPPUNIT_ASSERT(mitk::Equal(dummy,newDummy,mitk::eps,true)); + } + + void TestSetSpacing() + { + DummyTestClass::Pointer dummy = DummyTestClass::New(); + dummy->SetSpacing(anotherSpacing); + CPPUNIT_ASSERT(anotherSpacing==dummy->GetSpacing()); + + //undo changes, new and changed object need to be the same! + dummy->SetSpacing(aSpacing); + DummyTestClass::Pointer newDummy = DummyTestClass::New(); + CPPUNIT_ASSERT(mitk::Equal(dummy,newDummy,mitk::eps,true)); + } + + void TestTransferItkToVtkTransform() + { + DummyTestClass::Pointer dummy = DummyTestClass::New(); + dummy->SetIndexToWorldTransform(anotherTransform); //calls TransferItkToVtkTransform + mitk::AffineTransform3D::Pointer dummyTransform = dummy->GetIndexToWorldTransform(); + CPPUNIT_ASSERT(mitk::MatrixEqualElementWise( anotherMatrix, dummyTransform->GetMatrix() )); + } + + void TestConstructors() + { + //test standard constructor + DummyTestClass::Pointer dummy1 = DummyTestClass::New(); + bool test = dummy1->IsValid(); + CPPUNIT_ASSERT(test == true); + CPPUNIT_ASSERT(dummy1->GetFrameOfReferenceID() == 0); + CPPUNIT_ASSERT(dummy1->GetIndexToWorldTransformLastModified() == 0); + + CPPUNIT_ASSERT(dummy1->GetSpacing() == aSpacing); + CPPUNIT_ASSERT(dummy1->GetOrigin()==aPoint); + + CPPUNIT_ASSERT(dummy1->GetImageGeometry()==false); + + CPPUNIT_ASSERT(mitk::Equal( dummy1->GetIndexToWorldTransform(), aTransform, mitk::eps, true)); + + CPPUNIT_ASSERT(mitk::Equal( dummy1->GetBoundingBox(), aBoundingBox, mitk::eps, true)); + + DummyTestClass::Pointer dummy2 = DummyTestClass::New(); + dummy2->SetOrigin(anotherPoint); + float bounds[6] = {0,11,0,12,0,13}; + dummy2->SetFloatBounds(bounds); + dummy2->SetIndexToWorldTransform(anotherTransform); + dummy2->SetSpacing(anotherSpacing); + + DummyTestClass::Pointer dummy3; + dummy3 = DummyTestClass::New(*dummy2); + CPPUNIT_ASSERT(mitk::Equal(dummy3,dummy2,mitk::eps,true)); + } + + //Equal Tests + + void Equal_CloneAndOriginal_ReturnsTrue() + { + CPPUNIT_ASSERT( mitk::Equal(aDummyGeometry, anotherDummyGeometry, mitk::eps,true)); + } + + void Equal_DifferentOrigin_ReturnsFalse() + { + anotherDummyGeometry->SetOrigin(anotherPoint); + + CPPUNIT_ASSERT( mitk::Equal(aDummyGeometry, anotherDummyGeometry, mitk::eps,false)==false); + } + + void Equal_DifferentIndexToWorldTransform_ReturnsFalse() + { + anotherDummyGeometry->SetIndexToWorldTransform(anotherTransform); + + CPPUNIT_ASSERT( mitk::Equal(aDummyGeometry, anotherDummyGeometry, mitk::eps,false)==false); + } + + void Equal_DifferentSpacing_ReturnsFalse() + { + anotherDummyGeometry->SetSpacing(anotherSpacing); + + CPPUNIT_ASSERT( mitk::Equal(aDummyGeometry, anotherDummyGeometry, mitk::eps,false)==false); + } + + void Equal_InputIsNull_ReturnsFalse() + { + DummyTestClass::Pointer geometryNull = NULL; + CPPUNIT_ASSERT( mitk::Equal(geometryNull, anotherDummyGeometry, mitk::eps,false)==false); + } + + void Equal_DifferentBoundingBox_ReturnsFalse() + { + //create different bounds to make the comparison false + mitk::ScalarType bounds[ ] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0}; + anotherDummyGeometry->SetBounds(bounds); + + CPPUNIT_ASSERT( mitk::Equal(aDummyGeometry, anotherDummyGeometry, mitk::eps,false)==false); + } + + void TestComposeTransform(){ + //Create Transformations to set and compare + mitk::AffineTransform3D::Pointer transform1; + transform1 = mitk::AffineTransform3D::New(); + mitk::AffineTransform3D::MatrixType matrix1; + matrix1.SetIdentity(); + matrix1(1,1) = 2; + transform1->SetMatrix( matrix1 ); //Spacing = 2 + + mitk::AffineTransform3D::Pointer transform2; + transform2 = mitk::AffineTransform3D::New(); + mitk::AffineTransform3D::MatrixType matrix2; + matrix2.SetIdentity(); + matrix2(1,1) = 2; + transform2->SetMatrix( matrix2 ); //Spacing = 2 + + mitk::AffineTransform3D::Pointer transform3; + transform3 = mitk::AffineTransform3D::New(); + mitk::AffineTransform3D::MatrixType matrix3; + matrix3.SetIdentity(); + matrix3(1,1) = 4; + transform3->SetMatrix( matrix3 ); //Spacing = 4 + + mitk::AffineTransform3D::Pointer transform4; + transform4 = mitk::AffineTransform3D::New(); + mitk::AffineTransform3D::MatrixType matrix4; + matrix4.SetIdentity(); + matrix4(1,1) = 0.25; + transform4->SetMatrix( matrix4 ); //Spacing = 0.25 + + //Vector to compare spacing + mitk::Vector3D expectedSpacing; + expectedSpacing.Fill(1.0); + expectedSpacing[1] = 4; + + DummyTestClass::Pointer dummy = DummyTestClass::New(); + dummy->SetIndexToWorldTransform(transform1); //Spacing = 2 + dummy->Compose(transform2); //Spacing = 4 + CPPUNIT_ASSERT(mitk::Equal(dummy->GetSpacing(), expectedSpacing)); + CPPUNIT_ASSERT(mitk::Equal(transform3,dummy->GetIndexToWorldTransform(),mitk::eps,true)); // 4=4 + + //undo changes, new and changed object need to be the same! + dummy->Compose(transform4); //Spacing = 1 + DummyTestClass::Pointer newDummy = DummyTestClass::New(); + CPPUNIT_ASSERT(mitk::Equal(dummy,newDummy,mitk::eps,true)); // 1=1 + } + + void TestComposeVtkMatrix(){ + //Create Transformations to set and compare + mitk::AffineTransform3D::Pointer transform1; + transform1 = mitk::AffineTransform3D::New(); + mitk::AffineTransform3D::MatrixType matrix1; + matrix1.SetIdentity(); + matrix1(1,1) = 2; + transform1->SetMatrix( matrix1 ); //Spacing = 2 + + vtkMatrix4x4* vtkmatrix2; + vtkmatrix2 = vtkMatrix4x4::New(); + vtkmatrix2->Identity(); + vtkmatrix2->SetElement(1,1,2); //Spacing = 2 + + mitk::AffineTransform3D::Pointer transform3; + transform3 = mitk::AffineTransform3D::New(); + mitk::AffineTransform3D::MatrixType matrix3; + matrix3.SetIdentity(); + matrix3(1,1) = 4; + transform3->SetMatrix( matrix3 ); //Spacing = 4 + + vtkMatrix4x4* vtkmatrix4; + vtkmatrix4 = vtkMatrix4x4::New(); + vtkmatrix4->Identity(); + vtkmatrix4->SetElement(1,1,0.25); //Spacing = 0.25 + + //Vector to compare spacing + mitk::Vector3D expectedSpacing; + expectedSpacing.Fill(1.0); + expectedSpacing[1] = 4; + + DummyTestClass::Pointer dummy = DummyTestClass::New(); + dummy->SetIndexToWorldTransform(transform1); //Spacing = 2 + dummy->Compose(vtkmatrix2); //Spacing = 4 + + CPPUNIT_ASSERT(mitk::Equal(transform3,dummy->GetIndexToWorldTransform(),mitk::eps,true)); // 4=4 + CPPUNIT_ASSERT(mitk::Equal(dummy->GetSpacing(), expectedSpacing)); + + //undo changes, new and changed object need to be the same! + dummy->Compose(vtkmatrix4); //Spacing = 1 + DummyTestClass::Pointer newDummy = DummyTestClass::New(); + CPPUNIT_ASSERT(mitk::Equal(dummy,newDummy,mitk::eps,true)); // 1=1 + } + + void TestTranslate(){ + DummyTestClass::Pointer dummy = DummyTestClass::New(); + dummy->SetOrigin(anotherPoint); + CPPUNIT_ASSERT(anotherPoint==dummy->GetOrigin()); + + //use some random values for translation + mitk::Vector3D translationVector; + translationVector.SetElement(0, 17.5f); + translationVector.SetElement(1, -32.3f); + translationVector.SetElement(2, 4.0f); + + //compute ground truth + mitk::Point3D tmpResult = anotherPoint + translationVector; + dummy->Translate(translationVector); + CPPUNIT_ASSERT( mitk::Equal( dummy->GetOrigin(), tmpResult )); + + //undo changes + translationVector*=-1; + dummy->Translate(translationVector); + CPPUNIT_ASSERT( mitk::Equal( dummy->GetOrigin(), anotherPoint )); + + //undo changes, new and changed object need to be the same! + translationVector.SetElement(0, -1 * anotherPoint[0]); + translationVector.SetElement(1, -1 * anotherPoint[1]); + translationVector.SetElement(2, -1 * anotherPoint[2]); + dummy->Translate(translationVector); + + DummyTestClass::Pointer newDummy = DummyTestClass::New(); + CPPUNIT_ASSERT(mitk::Equal(dummy,newDummy,mitk::eps,true)); + } + + // a part of the test requires axis-parallel coordinates + int testIndexAndWorldConsistency(DummyTestClass* dummyGeometry) + { + //Testing consistency of index and world coordinate systems + mitk::Point3D origin = dummyGeometry->GetOrigin(); + mitk::Point3D dummyPoint; + + //Testing index->world->index conversion consistency + dummyGeometry->WorldToIndex(origin, dummyPoint); + dummyGeometry->IndexToWorld(dummyPoint, dummyPoint); + CPPUNIT_ASSERT(dummyPoint == origin); + + //Testing WorldToIndex(origin, mitk::Point3D)==(0,0,0) + mitk::Point3D globalOrigin; + mitk::FillVector3D(globalOrigin, 0,0,0); + + mitk::Point3D originContinuousIndex; + dummyGeometry->WorldToIndex(origin, originContinuousIndex); + CPPUNIT_ASSERT(originContinuousIndex == globalOrigin); + + //Testing WorldToIndex(origin, itk::Index)==(0,0,0) + itk::Index<3> itkindex; + dummyGeometry->WorldToIndex(origin, itkindex); + itk::Index<3> globalOriginIndex; + mitk::vtk2itk(globalOrigin, globalOriginIndex); + CPPUNIT_ASSERT(itkindex == globalOriginIndex); + + //Testing WorldToIndex(origin-0.5*spacing, itk::Index)==(0,0,0) + mitk::Vector3D halfSpacingStep = dummyGeometry->GetSpacing()*0.5; + mitk::Matrix3D rotation; + mitk::Point3D originOffCenter = origin-halfSpacingStep; + dummyGeometry->WorldToIndex(originOffCenter, itkindex); + CPPUNIT_ASSERT(itkindex == globalOriginIndex); + + //Testing WorldToIndex(origin+0.5*spacing-eps, itk::Index)==(0,0,0) + originOffCenter = origin+halfSpacingStep; + originOffCenter -= 0.0001; + dummyGeometry->WorldToIndex( originOffCenter, itkindex); + CPPUNIT_ASSERT(itkindex == globalOriginIndex); + + //Testing WorldToIndex(origin+0.5*spacing, itk::Index)==(1,1,1)"); + originOffCenter = origin+halfSpacingStep; + itk::Index<3> global111; + mitk::FillVector3D(global111, 1,1,1); + dummyGeometry->WorldToIndex( originOffCenter, itkindex); + CPPUNIT_ASSERT(itkindex == global111); + + //Testing WorldToIndex(GetCenter())==BoundingBox.GetCenter + mitk::Point3D center = dummyGeometry->GetCenter(); + mitk::Point3D centerContIndex; + dummyGeometry->WorldToIndex(center, centerContIndex); + mitk::BoundingBox::ConstPointer boundingBox = dummyGeometry->GetBoundingBox(); + mitk::BoundingBox::PointType centerBounds = boundingBox->GetCenter(); + CPPUNIT_ASSERT(mitk::Equal(centerContIndex,centerBounds)); + + //Testing GetCenter()==IndexToWorld(BoundingBox.GetCenter) + center = dummyGeometry->GetCenter(); + mitk::Point3D centerBoundsInWorldCoords; + dummyGeometry->IndexToWorld(centerBounds, centerBoundsInWorldCoords); + CPPUNIT_ASSERT(mitk::Equal(center,centerBoundsInWorldCoords)); + + //Test using random point, + //Testing consistency of index and world coordinate systems + mitk::Point3D point; + mitk::FillVector3D(point,3.5,-2,4.6); + + //Testing index->world->index conversion consistency + dummyGeometry->WorldToIndex(point, dummyPoint); + dummyGeometry->IndexToWorld(dummyPoint, dummyPoint); + CPPUNIT_ASSERT(dummyPoint == point); + + return EXIT_SUCCESS; + } + + int testIndexAndWorldConsistencyForVectors(DummyTestClass* dummyGeometry) + { + //Testing consistency of index and world coordinate systems for vectors + mitk::Vector3D xAxisMM = dummyGeometry->GetAxisVector(0); + mitk::Vector3D xAxisContinuousIndex; + + mitk::Point3D p, pIndex, origin; + origin = dummyGeometry->GetOrigin(); + p[0] = xAxisMM[0]+origin[0]; + p[1] = xAxisMM[1]+origin[1]; + p[2] = xAxisMM[2]+origin[2]; + + dummyGeometry->WorldToIndex(p,pIndex); + + dummyGeometry->WorldToIndex(xAxisMM,xAxisContinuousIndex); + CPPUNIT_ASSERT(xAxisContinuousIndex[0] == pIndex[0]); + CPPUNIT_ASSERT(xAxisContinuousIndex[1] == pIndex[1]); + CPPUNIT_ASSERT(xAxisContinuousIndex[2] == pIndex[2]); + + dummyGeometry->IndexToWorld(xAxisContinuousIndex,xAxisContinuousIndex); + + dummyGeometry->IndexToWorld(pIndex,p); + + CPPUNIT_ASSERT(xAxisContinuousIndex == xAxisMM); + CPPUNIT_ASSERT(xAxisContinuousIndex[0] == p[0]-origin[0]); + CPPUNIT_ASSERT(xAxisContinuousIndex[1] == p[1]-origin[1]); + CPPUNIT_ASSERT(xAxisContinuousIndex[2] == p[2]-origin[2]); + + //Test consictency for random vector + mitk::Vector3D vector; + mitk::FillVector3D(vector, 2.5,-3.2,8.1); + mitk::Vector3D vectorContinuousIndex; + + p[0] = vector[0]+origin[0]; + p[1] = vector[1]+origin[1]; + p[2] = vector[2]+origin[2]; + + dummyGeometry->WorldToIndex(p,pIndex); + + dummyGeometry->WorldToIndex(vector,vectorContinuousIndex); + CPPUNIT_ASSERT(vectorContinuousIndex[0] == pIndex[0]); + CPPUNIT_ASSERT(vectorContinuousIndex[1] == pIndex[1]); + CPPUNIT_ASSERT(vectorContinuousIndex[2] == pIndex[2]); + + dummyGeometry->IndexToWorld(vectorContinuousIndex,vectorContinuousIndex); + + dummyGeometry->IndexToWorld(pIndex,p); + + CPPUNIT_ASSERT(vectorContinuousIndex == vector); + CPPUNIT_ASSERT(vectorContinuousIndex[0] == p[0]-origin[0]); + CPPUNIT_ASSERT(vectorContinuousIndex[1] == p[1]-origin[1]); + CPPUNIT_ASSERT(vectorContinuousIndex[2] == p[2]-origin[2]); + + return EXIT_SUCCESS; + } + + int testIndexAndWorldConsistencyForIndex(DummyTestClass* dummyGeometry) + { + //Testing consistency of index and world coordinate systems + + // creating testing data + itk::Index<4> itkIndex4, itkIndex4b; + itk::Index<3> itkIndex3, itkIndex3b; + itk::Index<2> itkIndex2, itkIndex2b; + mitk::Index3D mitkIndex, mitkIndexb; + + itkIndex4[0] = itkIndex4[1] = itkIndex4[2] = itkIndex4[3] = 4; + itkIndex3[0] = itkIndex3[1] = itkIndex3[2] = 6; + itkIndex2[0] = itkIndex2[1] = 2; + mitkIndex[0] = mitkIndex[1] = mitkIndex[2] = 13; + + // check for constistency + mitk::Point3D point; + dummyGeometry->IndexToWorld(itkIndex2,point); + dummyGeometry->WorldToIndex(point,itkIndex2b); + + CPPUNIT_ASSERT( + ((itkIndex2b[0] == itkIndex2[0]) && + (itkIndex2b[1] == itkIndex2[1]))); + //Testing itk::index<2> for IndexToWorld/WorldToIndex consistency + + dummyGeometry->IndexToWorld(itkIndex3,point); + dummyGeometry->WorldToIndex(point,itkIndex3b); + + CPPUNIT_ASSERT( + ((itkIndex3b[0] == itkIndex3[0]) && + (itkIndex3b[1] == itkIndex3[1]) && + (itkIndex3b[2] == itkIndex3[2]))); + //Testing itk::index<3> for IndexToWorld/WorldToIndex consistency + + dummyGeometry->IndexToWorld(itkIndex4,point); + dummyGeometry->WorldToIndex(point,itkIndex4b); + + CPPUNIT_ASSERT( + ((itkIndex4b[0] == itkIndex4[0]) && + (itkIndex4b[1] == itkIndex4[1]) && + (itkIndex4b[2] == itkIndex4[2]) && + (itkIndex4b[3] == 0))); + //Testing itk::index<3> for IndexToWorld/WorldToIndex consistency + + dummyGeometry->IndexToWorld(mitkIndex,point); + dummyGeometry->WorldToIndex(point,mitkIndexb); + + CPPUNIT_ASSERT( + ((mitkIndexb[0] == mitkIndex[0]) && + (mitkIndexb[1] == mitkIndex[1]) && + (mitkIndexb[2] == mitkIndex[2]))); + //Testing mitk::Index for IndexToWorld/WorldToIndex consistency + + return EXIT_SUCCESS; + } + + void TestIndexToWorld(){ + DummyTestClass::Pointer dummy = DummyTestClass::New(); + + testIndexAndWorldConsistency(dummy); + testIndexAndWorldConsistencyForVectors(dummy); + testIndexAndWorldConsistencyForIndex(dummy); + + //Geometry must not have changed + DummyTestClass::Pointer newDummy = DummyTestClass::New(); + CPPUNIT_ASSERT(mitk::Equal(dummy,newDummy,mitk::eps,true)); + + //Test with other geometries + dummy->SetOrigin(anotherPoint); + testIndexAndWorldConsistency(dummy); + testIndexAndWorldConsistencyForVectors(dummy); + testIndexAndWorldConsistencyForIndex(dummy); + + dummy->SetIndexToWorldTransform(anotherTransform); + testIndexAndWorldConsistency(dummy); + testIndexAndWorldConsistencyForVectors(dummy); + testIndexAndWorldConsistencyForIndex(dummy); + + dummy->SetOrigin(anotherPoint); + testIndexAndWorldConsistency(dummy); + testIndexAndWorldConsistencyForVectors(dummy); + testIndexAndWorldConsistencyForIndex(dummy); + + dummy->SetSpacing(anotherSpacing); + testIndexAndWorldConsistency(dummy); + testIndexAndWorldConsistencyForVectors(dummy); + testIndexAndWorldConsistencyForIndex(dummy); + } + + void TestExecuteOperation(){ + DummyTestClass::Pointer dummy = DummyTestClass::New(); + + //Do same Operations with new Dummy and compare + DummyTestClass::Pointer newDummy = DummyTestClass::New(); + + //Test operation Nothing + mitk::Operation* opN = new mitk::Operation(mitk::OpNOTHING); + dummy->ExecuteOperation(opN); + CPPUNIT_ASSERT(mitk::Equal(dummy,newDummy,mitk::eps,true)); + + //Test operation Move + mitk::PointOperation* opP = new mitk::PointOperation(mitk::OpMOVE,anotherPoint); + dummy->ExecuteOperation(opP); + CPPUNIT_ASSERT(anotherPoint==dummy->GetOrigin()); + newDummy->SetOrigin(anotherPoint); + CPPUNIT_ASSERT(mitk::Equal(dummy,newDummy,mitk::eps,true)); + + //Test operation Scale, Scale sets spacing to scale+1 + mitk::Point3D spacing; + spacing[0]=anotherSpacing[0]-1.; + spacing[1]=anotherSpacing[1]-1.; + spacing[2]=anotherSpacing[2]-1.; + + mitk::PointOperation* opS = new mitk::PointOperation(mitk::OpSCALE,spacing); + dummy->ExecuteOperation(opS); + CPPUNIT_ASSERT(anotherSpacing==dummy->GetSpacing()); + newDummy->SetSpacing(anotherSpacing); + CPPUNIT_ASSERT(mitk::Equal(dummy,newDummy,mitk::eps,true)); + + //change Geometry to test more cases + dummy->SetIndexToWorldTransform(anotherTransform); + dummy->SetSpacing(anotherSpacing); + + //Testing a rotation of the geometry + double angle = 35.0; + mitk::Vector3D rotationVector; mitk::FillVector3D( rotationVector, 1, 0, 0 ); + mitk::Point3D center = dummy->GetCenter(); + mitk::RotationOperation* opR = new mitk::RotationOperation( mitk::OpROTATE, center, rotationVector, angle ); + dummy->ExecuteOperation(opR); + + mitk::Matrix3D rotation; + mitk::GetRotation(dummy, rotation); + mitk::Vector3D voxelStep=rotation*anotherSpacing; + mitk::Vector3D voxelStepIndex; + dummy->WorldToIndex(voxelStep, voxelStepIndex); + mitk::Vector3D expectedVoxelStepIndex; + expectedVoxelStepIndex.Fill(1); + CPPUNIT_ASSERT(mitk::Equal(voxelStepIndex,expectedVoxelStepIndex)); + + delete opR, opN, opS, opP; + } + + void TestCalculateBoundingBoxRelToTransform(){ + DummyTestClass::Pointer dummy = DummyTestClass::New(); + dummy->SetExtentInMM(0,15); + dummy->SetExtentInMM(1,20); + dummy->SetExtentInMM(2,8); + + mitk::BoundingBox::Pointer dummyBoundingBox = dummy->CalculateBoundingBoxRelativeToTransform(anotherTransform); + + mitk::BoundingBox::PointsContainer::Pointer pointscontainer=mitk::BoundingBox::PointsContainer::New(); + mitk::BoundingBox::PointIdentifier pointid=0; + unsigned char i; + mitk::AffineTransform3D::Pointer inverse = mitk::AffineTransform3D::New(); + anotherTransform->GetInverse(inverse); + for(i=0; i<8; ++i) + pointscontainer->InsertElement( pointid++, inverse->TransformPoint( dummy->GetCornerPoint(i) )); + mitk::BoundingBox::Pointer result = mitk::BoundingBox::New(); + result->SetPoints(pointscontainer); + result->ComputeBoundingBox(); + + CPPUNIT_ASSERT(mitk::Equal(result,dummyBoundingBox,mitk::eps,true)); + + //dummy still needs to be unchanged, except for extend + DummyTestClass::Pointer newDummy = DummyTestClass::New(); + newDummy->SetExtentInMM(0,15); + newDummy->SetExtentInMM(1,20); + newDummy->SetExtentInMM(2,8); + CPPUNIT_ASSERT(mitk::Equal(dummy,newDummy,mitk::eps,true)); + } + + void TestSetTimeBounds(){ + mitk::TimeBounds timeBounds; + timeBounds[0] = 1; + timeBounds[1] = 9; + + DummyTestClass::Pointer dummy = DummyTestClass::New(); + dummy->SetTimeBounds(timeBounds); + mitk::TimeBounds timeBounds2 = dummy->GetTimeBounds(); + + CPPUNIT_ASSERT(timeBounds[0]==timeBounds2[0]); + CPPUNIT_ASSERT(timeBounds[1]==timeBounds2[1]); + + //undo changes, new and changed object need to be the same! + timeBounds[0]=mitk::ScalarTypeNumericTraits::NonpositiveMin(); + timeBounds[1]=mitk::ScalarTypeNumericTraits::max(); + + DummyTestClass::Pointer newDummy = DummyTestClass::New(); + CPPUNIT_ASSERT(mitk::Equal(dummy,newDummy,mitk::eps,true)); + } + + void TestIs2DConvertable(){ + DummyTestClass::Pointer dummy = DummyTestClass::New(); + + //new initialized geometry is 2D convertable + CPPUNIT_ASSERT(dummy->Is2DConvertable()); + + //Wrong Spacing needs to fail + dummy->SetSpacing(anotherSpacing); + CPPUNIT_ASSERT(dummy->Is2DConvertable()==false); + //undo + dummy->SetSpacing(aSpacing); + CPPUNIT_ASSERT(dummy->Is2DConvertable()); + + //Wrong Origin needs to fail + dummy->SetOrigin(anotherPoint); + CPPUNIT_ASSERT(dummy->Is2DConvertable()==false); + //undo + dummy->SetOrigin(aPoint); + CPPUNIT_ASSERT(dummy->Is2DConvertable()); + + //third dimension must not be transformed + mitk::AffineTransform3D::Pointer dummyTransform = mitk::AffineTransform3D::New(); + mitk::AffineTransform3D::MatrixType dummyMatrix; + dummyMatrix.SetIdentity(); + dummyTransform->SetMatrix( dummyMatrix ); + dummy->SetIndexToWorldTransform(dummyTransform); + + //identity matrix is 2DConvertable + CPPUNIT_ASSERT(dummy->Is2DConvertable()); + + dummyMatrix(0,2) = 3; + dummyTransform->SetMatrix( dummyMatrix ); + CPPUNIT_ASSERT(dummy->Is2DConvertable()==false); + + dummyMatrix.SetIdentity(); + dummyMatrix(1,2) = 0.4; + dummyTransform->SetMatrix( dummyMatrix ); + CPPUNIT_ASSERT(dummy->Is2DConvertable()==false); + + dummyMatrix.SetIdentity(); + dummyMatrix(2,2) = 3; + dummyTransform->SetMatrix( dummyMatrix ); + CPPUNIT_ASSERT(dummy->Is2DConvertable()==false); + + dummyMatrix.SetIdentity(); + dummyMatrix(2,1) = 3; + dummyTransform->SetMatrix( dummyMatrix ); + CPPUNIT_ASSERT(dummy->Is2DConvertable()==false); + + dummyMatrix.SetIdentity(); + dummyMatrix(2,0) = 3; + dummyTransform->SetMatrix( dummyMatrix ); + CPPUNIT_ASSERT(dummy->Is2DConvertable()==false); + + //undo changes, new and changed object need to be the same! + dummyMatrix.SetIdentity(); + dummyTransform->SetMatrix( dummyMatrix ); + DummyTestClass::Pointer newDummy = DummyTestClass::New(); + CPPUNIT_ASSERT(mitk::Equal(dummy,newDummy,mitk::eps,true)); + } + + void TestGetCornerPoint(){ + DummyTestClass::Pointer dummy = DummyTestClass::New(); + dummy->SetIndexToWorldTransform(anotherTransform); + double bounds[6] = {0,11,0,12,0,13}; + dummy->SetFloatBounds(bounds); + + mitk::Point3D corner, refCorner; + + //Corner 0 + mitk::FillVector3D(refCorner,bounds[0],bounds[2],bounds[4]); + refCorner = anotherTransform->TransformPoint(refCorner); + corner=dummy->GetCornerPoint(0); + CPPUNIT_ASSERT(refCorner==corner); + corner=dummy->GetCornerPoint(true,true,true); + CPPUNIT_ASSERT(refCorner==corner); + + //Corner 1 + mitk::FillVector3D(refCorner,bounds[0],bounds[2],bounds[5]); + refCorner = anotherTransform->TransformPoint(refCorner); + corner=dummy->GetCornerPoint(1); + CPPUNIT_ASSERT(refCorner==corner); + corner=dummy->GetCornerPoint(true,true,false); + CPPUNIT_ASSERT(refCorner==corner); + + //Corner 2 + mitk::FillVector3D(refCorner,bounds[0],bounds[3],bounds[4]); + refCorner = anotherTransform->TransformPoint(refCorner); + corner=dummy->GetCornerPoint(2); + CPPUNIT_ASSERT(refCorner==corner); + corner=dummy->GetCornerPoint(true,false,true); + CPPUNIT_ASSERT(refCorner==corner); + + //Corner 3 + mitk::FillVector3D(refCorner,bounds[0],bounds[3],bounds[5]); + refCorner = anotherTransform->TransformPoint(refCorner); + corner=dummy->GetCornerPoint(3); + CPPUNIT_ASSERT(refCorner==corner); + corner=dummy->GetCornerPoint(true,false,false); + CPPUNIT_ASSERT(refCorner==corner); + + //Corner 4 + mitk::FillVector3D(refCorner,bounds[1],bounds[2],bounds[4]); + refCorner = anotherTransform->TransformPoint(refCorner); + corner=dummy->GetCornerPoint(4); + CPPUNIT_ASSERT(refCorner==corner); + corner=dummy->GetCornerPoint(false,true,true); + CPPUNIT_ASSERT(refCorner==corner); + + //Corner 5 + mitk::FillVector3D(refCorner,bounds[1],bounds[2],bounds[5]); + refCorner = anotherTransform->TransformPoint(refCorner); + corner=dummy->GetCornerPoint(5); + CPPUNIT_ASSERT(refCorner==corner); + corner=dummy->GetCornerPoint(false,true,false); + CPPUNIT_ASSERT(refCorner==corner); + + //Corner 6 + mitk::FillVector3D(refCorner,bounds[1],bounds[3],bounds[4]); + refCorner = anotherTransform->TransformPoint(refCorner); + corner=dummy->GetCornerPoint(6); + CPPUNIT_ASSERT(refCorner==corner); + corner=dummy->GetCornerPoint(false,false,true); + CPPUNIT_ASSERT(refCorner==corner); + + //Corner 7 + mitk::FillVector3D(refCorner,bounds[1],bounds[3],bounds[5]); + refCorner = anotherTransform->TransformPoint(refCorner); + corner=dummy->GetCornerPoint(7); + CPPUNIT_ASSERT(refCorner==corner); + corner=dummy->GetCornerPoint(false,false,false); + CPPUNIT_ASSERT(refCorner==corner); + + //Wrong Corner needs to fail + CPPUNIT_ASSERT_THROW(dummy->GetCornerPoint(-1),itk::ExceptionObject); + + //dummy geometry must not have changed! + DummyTestClass::Pointer newDummy = DummyTestClass::New(); + newDummy->SetIndexToWorldTransform(anotherTransform); + newDummy->SetFloatBounds(bounds); + CPPUNIT_ASSERT(mitk::Equal(dummy,newDummy,mitk::eps,true)); + } + + void TestExtentInMM() + { + DummyTestClass::Pointer dummy = DummyTestClass::New(); + dummy->SetExtentInMM(0,50); + CPPUNIT_ASSERT(50==dummy->GetExtentInMM(0)); + //Vnl Matrix has changed. The next line only works because the spacing is 1! + CPPUNIT_ASSERT(50==dummy->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(0).magnitude()); + + //Smaller extent than original + dummy->SetExtentInMM(0,5); + CPPUNIT_ASSERT(5==dummy->GetExtentInMM(0)); + CPPUNIT_ASSERT(5==dummy->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(0).magnitude()); + + dummy->SetExtentInMM(1,4); + CPPUNIT_ASSERT(4==dummy->GetExtentInMM(1)); + CPPUNIT_ASSERT(4==dummy->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(1).magnitude()); + + dummy->SetExtentInMM(2,2.5); + CPPUNIT_ASSERT(2.5==dummy->GetExtentInMM(2)); + CPPUNIT_ASSERT(2.5==dummy->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(2).magnitude()); + } + + void TestGetAxisVector(){ + DummyTestClass::Pointer dummy = DummyTestClass::New(); + dummy->SetIndexToWorldTransform(anotherTransform); + double bounds[6] = {0,11,0,12,0,13}; + dummy->SetFloatBounds(bounds); + + mitk::Vector3D vector; + mitk::FillVector3D(vector,bounds[1],0,0); + dummy->IndexToWorld(vector,vector); + CPPUNIT_ASSERT(dummy->GetAxisVector(0)==vector); + + mitk::FillVector3D(vector,0,bounds[3],0); + dummy->IndexToWorld(vector,vector); + CPPUNIT_ASSERT(dummy->GetAxisVector(1)==vector); + + mitk::FillVector3D(vector,0,0,bounds[5]); + dummy->IndexToWorld(vector,vector); + CPPUNIT_ASSERT(dummy->GetAxisVector(2)==vector); + } + + void TestGetCenter(){ + DummyTestClass::Pointer dummy = DummyTestClass::New(); + dummy->SetIndexToWorldTransform(anotherTransform); + double bounds[6] = {0,11,2,12,1,13}; + dummy->SetFloatBounds(bounds); + + mitk::Point3D refCenter; + for( int i=0;i<3;i++) + refCenter.SetElement(i,( bounds[2 * i] + bounds[2 * i + 1] ) / 2.0); + + dummy->IndexToWorld(refCenter,refCenter); + + CPPUNIT_ASSERT(dummy->GetCenter()==refCenter); + } + + void TestGetDiagonalLength(){ + DummyTestClass::Pointer dummy = DummyTestClass::New(); + double bounds[6] = {1,3,5,8,7.5,11.5}; + dummy->SetFloatBounds(bounds); + //3-1=2, 8-5=3, 11.5-7.5=4; 2^2+3^2+4^2 = 29 + double expectedLength = sqrt(29.); + + CPPUNIT_ASSERT(expectedLength==dummy->GetDiagonalLength()); + CPPUNIT_ASSERT(29==dummy->GetDiagonalLength2()); + + //dummy must not have changed + DummyTestClass::Pointer newDummy = DummyTestClass::New(); + newDummy->SetFloatBounds(bounds); + CPPUNIT_ASSERT(mitk::Equal(dummy,newDummy,mitk::eps,true)); + } + + void TestGetExtent(){ + DummyTestClass::Pointer dummy = DummyTestClass::New(); + double bounds[6] = {1,3,5,8,7.5,11.5}; + dummy->SetFloatBounds(bounds); + + CPPUNIT_ASSERT(2==dummy->GetExtent(0)); + CPPUNIT_ASSERT(3==dummy->GetExtent(1)); + CPPUNIT_ASSERT(4==dummy->GetExtent(2)); + + //dummy must not have changed + DummyTestClass::Pointer newDummy = DummyTestClass::New(); + newDummy->SetFloatBounds(bounds); + CPPUNIT_ASSERT(mitk::Equal(dummy,newDummy,mitk::eps,true)); + } + + void TestIsInside(){ + DummyTestClass::Pointer dummy = DummyTestClass::New(); + double bounds[6] = {1,3,5,8,7.5,11.5}; + dummy->SetFloatBounds(bounds); + + mitk::Point3D insidePoint; + mitk::Point3D outsidePoint; + + mitk::FillVector3D(insidePoint,2,6,7.6); + mitk::FillVector3D(outsidePoint,0,9,8.2); + + CPPUNIT_ASSERT(dummy->IsIndexInside(insidePoint)); + CPPUNIT_ASSERT(false==dummy->IsIndexInside(outsidePoint)); + + dummy->IndexToWorld(insidePoint,insidePoint); + dummy->IndexToWorld(outsidePoint,outsidePoint); + + CPPUNIT_ASSERT(dummy->IsInside(insidePoint)); + CPPUNIT_ASSERT(false==dummy->IsInside(outsidePoint)); + + //dummy must not have changed + DummyTestClass::Pointer newDummy = DummyTestClass::New(); + newDummy->SetFloatBounds(bounds); + CPPUNIT_ASSERT(mitk::Equal(dummy,newDummy,mitk::eps,true)); + } + + void TestInitialize() + { + //test standard constructor + DummyTestClass::Pointer dummy1 = DummyTestClass::New(); + + DummyTestClass::Pointer dummy2 = DummyTestClass::New(); + dummy2->SetOrigin(anotherPoint); + dummy2->SetBounds(anotherBoundingBox->GetBounds()); + mitk::TimeBounds timeBounds; + timeBounds[0] = 1; + timeBounds[1] = 9; + dummy2->SetTimeBounds(timeBounds); + dummy2->SetIndexToWorldTransform(anotherTransform); + dummy2->SetSpacing(anotherSpacing); + + dummy1->InitializeGeometry(dummy2); + + CPPUNIT_ASSERT(mitk::Equal(dummy1,dummy2,mitk::eps,true)); + + dummy1->Initialize(); + + DummyTestClass::Pointer dummy3 = DummyTestClass::New(); + CPPUNIT_ASSERT(mitk::Equal(dummy3,dummy1,mitk::eps,true)); + } + + void TestGetMatrixColumn(){ + DummyTestClass::Pointer dummy = DummyTestClass::New(); + dummy->SetIndexToWorldTransform(anotherTransform); + mitk::Vector3D testVector,refVector; + + testVector.SetVnlVector(dummy->GetMatrixColumn(0)); + mitk::FillVector3D(refVector,1,0,0); + CPPUNIT_ASSERT(testVector==refVector); + + testVector.SetVnlVector(dummy->GetMatrixColumn(1)); + mitk::FillVector3D(refVector,0,2,0); + CPPUNIT_ASSERT(testVector==refVector); + + testVector.SetVnlVector(dummy->GetMatrixColumn(2)); + mitk::FillVector3D(refVector,0,0,1); + CPPUNIT_ASSERT(testVector==refVector); + + //dummy must not have changed + DummyTestClass::Pointer newDummy = DummyTestClass::New(); + newDummy->SetIndexToWorldTransform(anotherTransform); + CPPUNIT_ASSERT(mitk::Equal(dummy,newDummy,mitk::eps,true)); + } + + /* + + void (){ + DummyTestClass::Pointer dummy = DummyTestClass::New(); + + CPPUNIT_ASSERT(); + + //undo changes, new and changed object need to be the same! + + DummyTestClass::Pointer newDummy = DummyTestClass::New(); + CPPUNIT_ASSERT(mitk::Equal(dummy,newDummy,mitk::eps,true)); + } + + */ +};//end class mitkBaseGeometryTestSuite + +MITK_TEST_SUITE_REGISTRATION(mitkBaseGeometry) diff --git a/Core/Code/Testing/mitkClippedSurfaceBoundsCalculatorTest.cpp b/Core/Code/Testing/mitkClippedSurfaceBoundsCalculatorTest.cpp index 13d97a5ff8..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::Geometry3D::Pointer geometry3D) +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::Geometry3D::Pointer geometry3D) +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::Geometry3D::Pointer geometry3D) +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::Geometry3D::Pointer firstGeometry3D, mitk::Geometry3D::Pointer secondGeometry3D) +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::Geometry3D::Pointer geometry3D ) +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); - mitk::Geometry3D::Pointer geometry3D = dynamic_cast< mitk::Geometry3D* > ( slicedGeometry3D.GetPointer() ); + 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); - mitk::Geometry3D::Pointer secondGeometry3D = dynamic_cast< mitk::Geometry3D* > ( secondSlicedGeometry3D.GetPointer() ); + 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/mitkDataStorageTest.cpp b/Core/Code/Testing/mitkDataStorageTest.cpp index 1f53a1806f..139e05d796 100644 --- a/Core/Code/Testing/mitkDataStorageTest.cpp +++ b/Core/Code/Testing/mitkDataStorageTest.cpp @@ -1,876 +1,876 @@ /*=================================================================== 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 "mitkImage.h" #include "mitkSurface.h" #include "mitkStringProperty.h" #include "mitkColorProperty.h" #include "mitkGroupTagProperty.h" #include "mitkDataNode.h" #include "mitkReferenceCountWatcher.h" #include "mitkDataStorage.h" #include "mitkStandaloneDataStorage.h" #include "mitkNodePredicateProperty.h" #include "mitkNodePredicateDataType.h" #include "mitkNodePredicateDimension.h" #include "mitkNodePredicateData.h" #include "mitkNodePredicateNot.h" #include "mitkNodePredicateAnd.h" #include "mitkNodePredicateOr.h" #include "mitkNodePredicateSource.h" #include "mitkMessage.h" //#include "mitkPicFileReader.h" #include "mitkTestingMacros.h" #include "mitkItkImageFileReader.h" void TestDataStorage(mitk::DataStorage* ds, std::string filename); namespace mitk { class TestStandaloneDataStorage: public StandaloneDataStorage { public: mitkClassMacro(TestStandaloneDataStorage, mitk::DataStorage); itkFactorylessNewMacro(Self) itkCloneMacro(Self) std::map GetModifiedObserverTags() const {return m_NodeModifiedObserverTags;} std::map GetDeletedObserverTags() const { return m_NodeDeleteObserverTags; } protected: TestStandaloneDataStorage() {} }; } class DSEventReceiver // Helper class for event testing { public: const mitk::DataNode* m_NodeAdded; const mitk::DataNode* m_NodeRemoved; DSEventReceiver() : m_NodeAdded(NULL), m_NodeRemoved(NULL) { } void OnAdd(const mitk::DataNode* node) { m_NodeAdded = node; } void OnRemove(const mitk::DataNode* node) { m_NodeRemoved = node; } }; /// /// \brief a class for checking if the datastorage is really thread safe /// /// Therefore it listens to a node contained in the datastorage. when this node /// gets removed and deleted, this class gets informed by calling OnObjectDelete(). /// in OnObjectDelete() an empty node gets added. this must not cause a deadlock /// struct ItkDeleteEventListener { ItkDeleteEventListener( mitk::DataStorage* ds ) : m_Node(0), m_DataStorage(ds), m_DeleteObserverTag(0) { } void SetNode( mitk::DataNode* _Node ) { if(m_Node) return; m_Node = _Node; itk::MemberCommand::Pointer onObjectDelete = itk::MemberCommand::New(); onObjectDelete->SetCallbackFunction(this, &ItkDeleteEventListener::OnObjectDelete); m_DeleteObserverTag = m_Node->AddObserver(itk::DeleteEvent(), onObjectDelete); } void OnObjectDelete( const itk::Object* /*caller*/, const itk::EventObject & ) { mitk::DataNode::Pointer node = mitk::DataNode::New(); m_DataStorage->Add( node ); // SHOULD NOT CAUSE A DEADLOCK! m_DataStorage->Remove( node ); // tidy up: remove the empty node again m_Node = 0; } protected: mitk::DataNode* m_Node; mitk::DataStorage::Pointer m_DataStorage; unsigned int m_DeleteObserverTag; }; //## Documentation //## main testing method //## NOTE: the current Singleton implementation of DataTreeStorage will lead to crashes if a testcase fails //## and therefore mitk::DataStorage::ShutdownSingleton() is not called. int mitkDataStorageTest(int argc, char* argv[]) { MITK_TEST_BEGIN("DataStorageTest"); // muellerm: test observer tag remove mitk::TestStandaloneDataStorage::Pointer testDS = mitk::TestStandaloneDataStorage::New(); mitk::DataNode::Pointer n1 = mitk::DataNode::New(); testDS->Add(n1); MITK_TEST_CONDITION_REQUIRED( testDS->GetModifiedObserverTags().size()==1, "Testing if modified" " observer was added."); MITK_TEST_CONDITION_REQUIRED( testDS->GetDeletedObserverTags().size()==1, "Testing if delete" " observer was added."); testDS->Remove(n1); MITK_TEST_CONDITION_REQUIRED( testDS->GetModifiedObserverTags().size()==0, "Testing if modified" " observer was removed."); MITK_TEST_CONDITION_REQUIRED( testDS->GetDeletedObserverTags().size()==0, "Testing if delete" " observer was removed."); /* Create StandaloneDataStorage */ MITK_TEST_OUTPUT( << "Create StandaloneDataStorage : "); mitk::StandaloneDataStorage::Pointer sds; try { sds = mitk::StandaloneDataStorage::New(); MITK_TEST_CONDITION_REQUIRED(sds.IsNotNull(), "Testing Instatiation"); } catch (...) { MITK_TEST_FAILED_MSG( << "Exception during creation of StandaloneDataStorage"); } MITK_TEST_OUTPUT( << "Testing StandaloneDataStorage: "); MITK_TEST_CONDITION_REQUIRED(argc>1, "Testing correct test invocation"); TestDataStorage(sds,argv[1]); // TODO: Add specific StandaloneDataStorage Tests here sds = NULL; MITK_TEST_END(); } //##Documentation //## @brief Test for the DataStorage class and its associated classes (e.g. the predicate classes) //## This method will be called once for each subclass of DataStorage void TestDataStorage( mitk::DataStorage* ds, std::string filename ) { /* DataStorage valid? */ MITK_TEST_CONDITION_REQUIRED(ds != NULL, "DataStorage valid?"); // Take the ItkImageFile Reader for the .nrrd data format. // (was previously pic which is now deprecated format) mitk::ItkImageFileReader::Pointer reader = mitk::ItkImageFileReader::New(); reader -> SetFileName(filename.c_str()); reader -> Update(); mitk::Image::Pointer image = reader->GetOutput(); // create some DataNodes to fill the ds mitk::DataNode::Pointer n1 = mitk::DataNode::New(); // node with image and name property // mitk::Image::Pointer image = mitk::Image::New(); // unsigned int imageDimensions[] = { 10, 10, 10, 10 }; // mitk::PixelType pt(typeid(int)); // image->Initialize( pt, 4, imageDimensions ); n1->SetData(image); n1->SetProperty("name", mitk::StringProperty::New("Node 1 - Image Node")); mitk::DataStorage::SetOfObjects::Pointer parents1 = mitk::DataStorage::SetOfObjects::New(); mitk::DataNode::Pointer n2 = mitk::DataNode::New(); // node with surface and name and color properties mitk::Surface::Pointer surface = mitk::Surface::New(); n2->SetData(surface); n2->SetProperty("name", mitk::StringProperty::New("Node 2 - Surface Node")); mitk::Color color; color.Set(1.0f, 1.0f, 0.0f); n2->SetColor(color); n2->SetProperty("Resection Proposal 1", mitk::GroupTagProperty::New()); mitk::DataStorage::SetOfObjects::Pointer parents2 = mitk::DataStorage::SetOfObjects::New(); parents2->InsertElement(0, n1); // n1 (image node) is source of n2 (surface node) mitk::DataNode::Pointer n3 = mitk::DataNode::New(); // node without data but with name property n3->SetProperty("name", mitk::StringProperty::New("Node 3 - Empty Node")); n3->SetProperty("Resection Proposal 1", mitk::GroupTagProperty::New()); n3->SetProperty("Resection Proposal 2", mitk::GroupTagProperty::New()); mitk::DataStorage::SetOfObjects::Pointer parents3 = mitk::DataStorage::SetOfObjects::New(); parents3->InsertElement(0, n2); // n2 is source of n3 mitk::DataNode::Pointer n4 = mitk::DataNode::New(); // node without data but with color property n4->SetColor(color); n4->SetProperty("Resection Proposal 2", mitk::GroupTagProperty::New()); mitk::DataStorage::SetOfObjects::Pointer parents4 = mitk::DataStorage::SetOfObjects::New(); parents4->InsertElement(0, n2); parents4->InsertElement(1, n3); // n2 and n3 are sources of n4 mitk::DataNode::Pointer n5 = mitk::DataNode::New(); // extra node n5->SetProperty("name", mitk::StringProperty::New("Node 5")); try /* adding objects */ { /* Add an object */ ds->Add(n1, parents1); MITK_TEST_CONDITION_REQUIRED((ds->GetAll()->Size() == 1) && (ds->GetAll()->GetElement(0) == n1), "Testing Adding a new object"); /* Check exception on adding the same object again */ MITK_TEST_OUTPUT( << "Check exception on adding the same object again: "); MITK_TEST_FOR_EXCEPTION(..., ds->Add(n1, parents1)); MITK_TEST_CONDITION(ds->GetAll()->Size() == 1, "Test if object count is correct after exception"); /* Add an object that has a source object */ ds->Add(n2, parents2); MITK_TEST_CONDITION_REQUIRED(ds->GetAll()->Size() == 2, "Testing Adding an object that has a source object"); /* Add some more objects needed for further tests */ ds->Add(n3, parents3); // n3 object that has name property and one parent ds->Add(n4, parents4); // n4 object that has color property ds->Add(n5); // n5 has no parents MITK_TEST_CONDITION_REQUIRED(ds->GetAll()->Size() == 5, "Adding some more objects needed for further tests"); } catch(...) { MITK_TEST_FAILED_MSG( << "Exeption during object creation"); } try /* object retrieval methods */ { /* Requesting all Objects */ { const mitk::DataStorage::SetOfObjects::ConstPointer all = ds->GetAll(); std::vector stlAll = all->CastToSTLConstContainer(); MITK_TEST_CONDITION( (stlAll.size() == 5) // check if all tree nodes are in resultset && (std::find(stlAll.begin(), stlAll.end(), n1) != stlAll.end()) && (std::find(stlAll.begin(), stlAll.end(), n2) != stlAll.end()) && (std::find(stlAll.begin(), stlAll.end(), n3) != stlAll.end()) && (std::find(stlAll.begin(), stlAll.end(), n4) != stlAll.end()) && (std::find(stlAll.begin(), stlAll.end(), n5) != stlAll.end()), "Testing GetAll()" ); } /* Requesting a named object */ { mitk::NodePredicateProperty::Pointer predicate(mitk::NodePredicateProperty::New("name", mitk::StringProperty::New("Node 2 - Surface Node"))); mitk::DataStorage::SetOfObjects::ConstPointer all = ds->GetSubset(predicate); MITK_TEST_CONDITION((all->Size() == 1) && (all->GetElement(0) == n2), "Requesting a named object"); } /* Requesting objects of specific data type */ { mitk::NodePredicateDataType::Pointer predicate(mitk::NodePredicateDataType::New("Image")); mitk::DataStorage::SetOfObjects::ConstPointer all = ds->GetSubset(predicate); MITK_TEST_CONDITION((all->Size() == 1) && (all->GetElement(0) == n1), "Requesting objects of specific data type") } /* Requesting objects of specific dimension */ { mitk::NodePredicateDimension::Pointer predicate(mitk::NodePredicateDimension::New( 4 )); mitk::DataStorage::SetOfObjects::ConstPointer all = ds->GetSubset(predicate); MITK_TEST_CONDITION((all->Size() == 1) && (all->GetElement(0) == n1), "Requesting objects of specific dimension") } /* Requesting objects with specific data object */ { mitk::NodePredicateData::Pointer predicate(mitk::NodePredicateData::New(image)); mitk::DataStorage::SetOfObjects::ConstPointer all = ds->GetSubset(predicate); MITK_TEST_CONDITION((all->Size() == 1) && (all->GetElement(0) == n1), "Requesting objects with specific data object") } /* Requesting objects with NULL data */ { mitk::NodePredicateData::Pointer predicate(mitk::NodePredicateData::New(NULL)); mitk::DataStorage::SetOfObjects::ConstPointer all = ds->GetSubset(predicate); MITK_TEST_CONDITION( (all->Size() == 3) && (std::find(all->begin(), all->end(), n3) != all->end()) && (std::find(all->begin(), all->end(), n4) != all->end()) && (std::find(all->begin(), all->end(), n5) != all->end()) , "Requesting objects with NULL data"); } /* Requesting objects that meet a conjunction criteria */ { mitk::NodePredicateDataType::Pointer p1 = mitk::NodePredicateDataType::New("Surface"); mitk::NodePredicateProperty::Pointer p2 = mitk::NodePredicateProperty::New("color", mitk::ColorProperty::New(color)); mitk::NodePredicateAnd::Pointer predicate = mitk::NodePredicateAnd::New(); predicate->AddPredicate(p1); predicate->AddPredicate(p2); // objects must be of datatype "Surface" and have red color (= n2) const mitk::DataStorage::SetOfObjects::ConstPointer all = ds->GetSubset(predicate); MITK_TEST_CONDITION((all->Size() == 1) && (all->GetElement(0) == n2), "Requesting objects that meet a conjunction criteria"); } /* Requesting objects that meet a disjunction criteria */ { mitk::NodePredicateDataType::Pointer p1(mitk::NodePredicateDataType::New("Image")); mitk::NodePredicateProperty::Pointer p2(mitk::NodePredicateProperty::New("color", mitk::ColorProperty::New(color))); mitk::NodePredicateOr::Pointer predicate = mitk::NodePredicateOr::New(); predicate->AddPredicate(p1); predicate->AddPredicate(p2); // objects must be of datatype "Surface" or have red color (= n1, n2, n4) const mitk::DataStorage::SetOfObjects::ConstPointer all = ds->GetSubset(predicate); MITK_TEST_CONDITION( (all->Size() == 3) && (std::find(all->begin(), all->end(), n1) != all->end()) && (std::find(all->begin(), all->end(), n2) != all->end()) && (std::find(all->begin(), all->end(), n4) != all->end()), "Requesting objects that meet a disjunction criteria"); } /* Requesting objects that do not meet a criteria */ { mitk::ColorProperty::Pointer cp = mitk::ColorProperty::New(color); mitk::NodePredicateProperty::Pointer proppred(mitk::NodePredicateProperty::New("color", cp)); mitk::NodePredicateNot::Pointer predicate(mitk::NodePredicateNot::New(proppred)); const mitk::DataStorage::SetOfObjects::ConstPointer all = ds->GetSubset(predicate); std::vector stlAll = all->CastToSTLConstContainer(); MITK_TEST_CONDITION( (all->Size() == 3) // check if correct objects are in resultset && (std::find(stlAll.begin(), stlAll.end(), n1) != stlAll.end()) && (std::find(stlAll.begin(), stlAll.end(), n3) != stlAll.end()) && (std::find(stlAll.begin(), stlAll.end(), n5) != stlAll.end()), "Requesting objects that do not meet a criteria"); } /* Requesting *direct* source objects */ { const mitk::DataStorage::SetOfObjects::ConstPointer all = ds->GetSources(n3, NULL, true); // Get direct parents of n3 (=n2) std::vector stlAll = all->CastToSTLConstContainer(); MITK_TEST_CONDITION( (all->Size() == 1) && (std::find(stlAll.begin(), stlAll.end(), n2) != stlAll.end()), "Requesting *direct* source objects"); } /* Requesting *all* source objects */ { const mitk::DataStorage::SetOfObjects::ConstPointer all = ds->GetSources(n3, NULL, false); // Get all parents of n3 (= n1 + n2) std::vector stlAll = all->CastToSTLConstContainer(); MITK_TEST_CONDITION( (all->Size() == 2) && (std::find(stlAll.begin(), stlAll.end(), n1) != stlAll.end()) && (std::find(stlAll.begin(), stlAll.end(), n2) != stlAll.end()), "Requesting *all* source objects"); // check if n1 and n2 are the resultset } /* Requesting *all* sources of object with multiple parents */ { const mitk::DataStorage::SetOfObjects::ConstPointer all = ds->GetSources(n4, NULL, false); // Get all parents of n4 (= n1 + n2 + n3) std::vector stlAll = all->CastToSTLConstContainer(); MITK_TEST_CONDITION( (all->Size() == 3) && (std::find(stlAll.begin(), stlAll.end(), n1) != stlAll.end()) && (std::find(stlAll.begin(), stlAll.end(), n2) != stlAll.end()) && (std::find(stlAll.begin(), stlAll.end(), n3) != stlAll.end()) // check if n1 and n2 and n3 are the resultset , "Requesting *all* sources of object with multiple parents"); } /* Requesting *direct* derived objects */ { const mitk::DataStorage::SetOfObjects::ConstPointer all = ds->GetDerivations(n1, NULL, true); // Get direct childs of n1 (=n2) std::vector stlAll = all->CastToSTLConstContainer(); MITK_TEST_CONDITION( (all->Size() == 1) && (std::find(stlAll.begin(), stlAll.end(), n2) != stlAll.end())// check if n1 is the resultset , "Requesting *direct* derived objects"); } ///* Requesting *direct* derived objects with multiple parents/derivations */ { const mitk::DataStorage::SetOfObjects::ConstPointer all = ds->GetDerivations(n2, NULL, true); // Get direct childs of n2 (=n3 + n4) std::vector stlAll = all->CastToSTLConstContainer(); MITK_TEST_CONDITION( (all->Size() == 2) && (std::find(stlAll.begin(), stlAll.end(), n3) != stlAll.end()) // check if n3 is the resultset && (std::find(stlAll.begin(), stlAll.end(), n4) != stlAll.end()) // check if n4 is the resultset , "Requesting *direct* derived objects with multiple parents/derivations"); } //* Requesting *all* derived objects */ { const mitk::DataStorage::SetOfObjects::ConstPointer all = ds->GetDerivations(n1, NULL, false); // Get all childs of n1 (=n2, n3, n4) std::vector stlAll = all->CastToSTLConstContainer(); MITK_TEST_CONDITION( (all->Size() == 3) && (std::find(stlAll.begin(), stlAll.end(), n2) != stlAll.end()) && (std::find(stlAll.begin(), stlAll.end(), n3) != stlAll.end()) && (std::find(stlAll.begin(), stlAll.end(), n4) != stlAll.end()) , "Requesting *all* derived objects"); } /* Checking for circular source relationships */ { parents1->InsertElement(0, n4); // make n1 derived from n4 (which is derived from n2, which is derived from n1) const mitk::DataStorage::SetOfObjects::ConstPointer all = ds->GetSources(n4, NULL, false); // Get all parents of n4 (= n1 + n2 + n3, not n4 itself and not multiple versions of the nodes!) std::vector stlAll = all->CastToSTLConstContainer(); MITK_TEST_CONDITION( (all->Size() == 3) && (std::find(stlAll.begin(), stlAll.end(), n1) != stlAll.end()) && (std::find(stlAll.begin(), stlAll.end(), n2) != stlAll.end()) && (std::find(stlAll.begin(), stlAll.end(), n3) != stlAll.end()) // check if n1 and n2 and n3 are the resultset , "Checking for circular source relationships"); } ///* Checking for circular derivation relationships can not be performed, because the internal derivations datastructure // can not be accessed from the outside. (Therefore it should not be possible to create these circular relations */ //* Checking GroupTagProperty */ { mitk::GroupTagProperty::Pointer tp = mitk::GroupTagProperty::New(); mitk::NodePredicateProperty::Pointer pred(mitk::NodePredicateProperty::New("Resection Proposal 1", tp)); const mitk::DataStorage::SetOfObjects::ConstPointer all = ds->GetSubset(pred); std::vector stlAll = all->CastToSTLConstContainer(); MITK_TEST_CONDITION( (all->Size() == 2) // check if n2 and n3 are in resultset && (std::find(stlAll.begin(), stlAll.end(), n2) != stlAll.end()) && (std::find(stlAll.begin(), stlAll.end(), n3) != stlAll.end()) , "Checking GroupTagProperty"); } /* Checking GroupTagProperty 2 */ { mitk::GroupTagProperty::Pointer tp = mitk::GroupTagProperty::New(); mitk::NodePredicateProperty::Pointer pred(mitk::NodePredicateProperty::New("Resection Proposal 2", tp)); const mitk::DataStorage::SetOfObjects::ConstPointer all = ds->GetSubset(pred); std::vector stlAll = all->CastToSTLConstContainer(); MITK_TEST_CONDITION( (all->Size() == 2) // check if n3 and n4 are in resultset && (std::find(stlAll.begin(), stlAll.end(), n3) != stlAll.end()) && (std::find(stlAll.begin(), stlAll.end(), n4) != stlAll.end()) , "Checking GroupTagProperty 2"); } /* Checking direct sources with condition */ { mitk::NodePredicateDataType::Pointer pred = mitk::NodePredicateDataType::New("Surface"); const mitk::DataStorage::SetOfObjects::ConstPointer all = ds->GetSources(n4, pred, true); std::vector stlAll = all->CastToSTLConstContainer(); MITK_TEST_CONDITION( (all->Size() == 1) // check if n2 is in resultset && (std::find(stlAll.begin(), stlAll.end(), n2) != stlAll.end()) , "checking direct sources with condition"); } /* Checking all sources with condition */ { mitk::NodePredicateDataType::Pointer pred = mitk::NodePredicateDataType::New("Image"); const mitk::DataStorage::SetOfObjects::ConstPointer all = ds->GetSources(n4, pred, false); std::vector stlAll = all->CastToSTLConstContainer(); MITK_TEST_CONDITION( (all->Size() == 1) // check if n1 is in resultset && (std::find(stlAll.begin(), stlAll.end(), n1) != stlAll.end()) , "Checking all sources with condition"); } /* Checking all sources with condition with empty resultset */ { mitk::NodePredicateDataType::Pointer pred = mitk::NodePredicateDataType::New("VesselTree"); const mitk::DataStorage::SetOfObjects::ConstPointer all = ds->GetSources(n4, pred, false); MITK_TEST_CONDITION(all->Size() == 0 , "Checking all sources with condition with empty resultset"); // check if resultset is empty } /* Checking direct derivations with condition */ { mitk::NodePredicateProperty::Pointer pred = mitk::NodePredicateProperty::New("color"); const mitk::DataStorage::SetOfObjects::ConstPointer all = ds->GetDerivations(n1, pred, true); std::vector stlAll = all->CastToSTLConstContainer(); MITK_TEST_CONDITION( (all->Size() == 1) // check if n2 is in resultset && (std::find(stlAll.begin(), stlAll.end(), n2) != stlAll.end()) , "Checking direct derivations with condition"); } /* Checking all derivations with condition */ { mitk::NodePredicateProperty::Pointer pred = mitk::NodePredicateProperty::New("color"); const mitk::DataStorage::SetOfObjects::ConstPointer all = ds->GetDerivations(n1, pred, false); std::vector stlAll = all->CastToSTLConstContainer(); MITK_TEST_CONDITION( (all->Size() == 2) // check if n2 and n4 are in resultset && (std::find(stlAll.begin(), stlAll.end(), n2) != stlAll.end()) && (std::find(stlAll.begin(), stlAll.end(), n4) != stlAll.end()) , "Checking direct derivations with condition"); } /* Checking named node method */ MITK_TEST_CONDITION(ds->GetNamedNode("Node 2 - Surface Node") == n2, "Checking named node method"); MITK_TEST_CONDITION(ds->GetNamedNode(std::string("Node 2 - Surface Node")) == n2, "Checking named node(std::string) method"); /* Checking named node method with wrong name */ MITK_TEST_CONDITION(ds->GetNamedNode("This name does not exist") == NULL, "Checking named node method with wrong name"); /* Checking named object method */ MITK_TEST_CONDITION(ds->GetNamedObject("Node 1 - Image Node") == image, "Checking named object method"); MITK_TEST_CONDITION(ds->GetNamedObject(std::string("Node 1 - Image Node")) == image, "Checking named object(std::string) method"); /* Checking named object method with wrong DataType */ MITK_TEST_CONDITION(ds->GetNamedObject("Node 1 - Image Node") == NULL, "Checking named object method with wrong DataType"); /* Checking named object method with wrong name */ MITK_TEST_CONDITION(ds->GetNamedObject("This name does not exist") == NULL, "Checking named object method with wrong name"); /* Checking GetNamedDerivedNode with valid name and direct derivation only */ MITK_TEST_CONDITION(ds->GetNamedDerivedNode("Node 2 - Surface Node", n1, true) == n2, "Checking GetNamedDerivedNode with valid name & direct derivation only"); /* Checking GetNamedDerivedNode with invalid Name and direct derivation only */ MITK_TEST_CONDITION(ds->GetNamedDerivedNode("wrong name", n1, true) == NULL, "Checking GetNamedDerivedNode with invalid name & direct derivation only"); /* Checking GetNamedDerivedNode with invalid Name and direct derivation only */ MITK_TEST_CONDITION(ds->GetNamedDerivedNode("Node 3 - Empty Node", n1, false) == n3, "Checking GetNamedDerivedNode with invalid name & direct derivation only"); /* Checking GetNamedDerivedNode with valid Name but direct derivation only */ MITK_TEST_CONDITION(ds->GetNamedDerivedNode("Node 3 - Empty Node", n1, true) == NULL, "Checking GetNamedDerivedNode with valid Name but direct derivation only"); /* Checking GetNode with valid predicate */ { mitk::NodePredicateDataType::Pointer p(mitk::NodePredicateDataType::New("Image")); MITK_TEST_CONDITION(ds->GetNode(p) == n1, "Checking GetNode with valid predicate"); } /* Checking GetNode with invalid predicate */ { mitk::NodePredicateDataType::Pointer p(mitk::NodePredicateDataType::New("PointSet")); MITK_TEST_CONDITION(ds->GetNode(p) == NULL, "Checking GetNode with invalid predicate"); } } // object retrieval methods catch(...) { MITK_TEST_FAILED_MSG( << "Exeption during object retrieval (GetXXX() Methods)"); } try /* object removal methods */ { /* Checking removal of a node without relations */ { mitk::DataNode::Pointer extra = mitk::DataNode::New(); extra->SetProperty("name", mitk::StringProperty::New("extra")); mitk::ReferenceCountWatcher::Pointer watcher = new mitk::ReferenceCountWatcher(extra); int refCountbeforeDS = watcher->GetReferenceCount(); ds->Add(extra); MITK_TEST_CONDITION(ds->GetNamedNode("extra") == extra, "Adding extra node"); ds->Remove(extra); MITK_TEST_CONDITION( (ds->GetNamedNode("extra") == NULL) && (refCountbeforeDS == watcher->GetReferenceCount()) , "Checking removal of a node without relations"); extra = NULL; } /* Checking removal of a node with a parent */ { mitk::DataNode::Pointer extra = mitk::DataNode::New(); extra->SetProperty("name", mitk::StringProperty::New("extra")); mitk::ReferenceCountWatcher::Pointer watcher = new mitk::ReferenceCountWatcher(extra); int refCountbeforeDS = watcher->GetReferenceCount(); ds->Add(extra, n1); // n1 is parent of extra MITK_TEST_CONDITION( (ds->GetNamedNode("extra") == extra) && (ds->GetDerivations(n1)->Size() == 2) // n2 and extra should be derived from n1 , "Adding extra node"); ds->Remove(extra); MITK_TEST_CONDITION( (ds->GetNamedNode("extra") == NULL) && (refCountbeforeDS == watcher->GetReferenceCount()) && (ds->GetDerivations(n1)->Size() == 1) , "Checking removal of a node with a parent"); extra = NULL; } /* Checking removal of a node with two parents */ { mitk::DataNode::Pointer extra = mitk::DataNode::New(); extra->SetProperty("name", mitk::StringProperty::New("extra")); mitk::ReferenceCountWatcher::Pointer watcher = new mitk::ReferenceCountWatcher(extra); int refCountbeforeDS = watcher->GetReferenceCount(); mitk::DataStorage::SetOfObjects::Pointer p = mitk::DataStorage::SetOfObjects::New(); p->push_back(n1); p->push_back(n2); ds->Add(extra, p); // n1 and n2 are parents of extra MITK_TEST_CONDITION( (ds->GetNamedNode("extra") == extra) && (ds->GetDerivations(n1)->Size() == 2) // n2 and extra should be derived from n1 && (ds->GetDerivations(n2)->Size() == 3) , "add extra node"); ds->Remove(extra); MITK_TEST_CONDITION( (ds->GetNamedNode("extra") == NULL) && (refCountbeforeDS == watcher->GetReferenceCount()) && (ds->GetDerivations(n1)->Size() == 1) // after remove, only n2 should be derived from n1 && (ds->GetDerivations(n2)->Size() == 2) // after remove, only n3 and n4 should be derived from n2 , "Checking removal of a node with two parents"); extra = NULL; } /* Checking removal of a node with two derived nodes */ { mitk::DataNode::Pointer extra = mitk::DataNode::New(); extra->SetProperty("name", mitk::StringProperty::New("extra")); mitk::ReferenceCountWatcher::Pointer watcher = new mitk::ReferenceCountWatcher(extra); int refCountbeforeDS = watcher->GetReferenceCount(); ds->Add(extra); mitk::DataNode::Pointer d1 = mitk::DataNode::New(); d1->SetProperty("name", mitk::StringProperty::New("d1")); ds->Add(d1, extra); mitk::DataNode::Pointer d2 = mitk::DataNode::New(); d2->SetProperty("name", mitk::StringProperty::New("d2")); ds->Add(d2, extra); MITK_TEST_CONDITION( (ds->GetNamedNode("extra") == extra) && (ds->GetNamedNode("d1") == d1) && (ds->GetNamedNode("d2") == d2) && (ds->GetSources(d1)->Size() == 1) // extra should be source of d1 && (ds->GetSources(d2)->Size() == 1) // extra should be source of d2 && (ds->GetDerivations(extra)->Size() == 2) // d1 and d2 should be derived from extra , "add extra node"); ds->Remove(extra); MITK_TEST_CONDITION( (ds->GetNamedNode("extra") == NULL) && (ds->GetNamedNode("d1") == d1) && (ds->GetNamedNode("d2") == d2) && (refCountbeforeDS == watcher->GetReferenceCount()) && (ds->GetSources(d1)->Size() == 0) // after remove, d1 should not have a source anymore && (ds->GetSources(d2)->Size() == 0) // after remove, d2 should not have a source anymore , "Checking removal of a node with two derived nodes"); extra = NULL; } /* Checking removal of a node with two parents and two derived nodes */ { mitk::DataNode::Pointer extra = mitk::DataNode::New(); extra->SetProperty("name", mitk::StringProperty::New("extra")); mitk::ReferenceCountWatcher::Pointer watcher = new mitk::ReferenceCountWatcher(extra); mitk::ReferenceCountWatcher::Pointer n1watcher = new mitk::ReferenceCountWatcher(n1); int refCountbeforeDS = watcher->GetReferenceCount(); mitk::DataStorage::SetOfObjects::Pointer p = mitk::DataStorage::SetOfObjects::New(); p->push_back(n1); p->push_back(n2); ds->Add(extra, p); // n1 and n2 are parents of extra mitk::DataNode::Pointer d1 = mitk::DataNode::New(); d1->SetProperty("name", mitk::StringProperty::New("d1x")); ds->Add(d1, extra); mitk::DataNode::Pointer d2 = mitk::DataNode::New(); d2->SetProperty("name", mitk::StringProperty::New("d2x")); ds->Add(d2, extra); MITK_TEST_CONDITION( (ds->GetNamedNode("extra") == extra) && (ds->GetNamedNode("d1x") == d1) && (ds->GetNamedNode("d2x") == d2) && (ds->GetSources(d1)->Size() == 1) // extra should be source of d1 && (ds->GetSources(d2)->Size() == 1) // extra should be source of d2 && (ds->GetDerivations(n1)->Size() == 2) // n2 and extra should be derived from n1 && (ds->GetDerivations(n2)->Size() == 3) // n3, n4 and extra should be derived from n2 && (ds->GetDerivations(extra)->Size() == 2) // d1 and d2 should be derived from extra , "add extra node"); ds->Remove(extra); MITK_TEST_CONDITION( (ds->GetNamedNode("extra") == NULL) && (ds->GetNamedNode("d1x") == d1) && (ds->GetNamedNode("d2x") == d2) && (refCountbeforeDS == watcher->GetReferenceCount()) && (ds->GetDerivations(n1)->Size() == 1) // after remove, only n2 should be derived from n1 && (ds->GetDerivations(n2)->Size() == 2) // after remove, only n3 and n4 should be derived from n2 && (ds->GetSources(d1)->Size() == 0) // after remove, d1 should not have a source anymore && (ds->GetSources(d2)->Size() == 0) // after remove, d2 should not have a source anymore , "Checking removal of a node with two parents and two derived nodes"); extra = NULL; } } catch(...) { MITK_TEST_FAILED_MSG( << "Exeption during object removal methods"); } /* Checking for node is it's own parent exception */ { MITK_TEST_FOR_EXCEPTION_BEGIN(...); mitk::DataNode::Pointer extra = mitk::DataNode::New(); extra->SetProperty("name", mitk::StringProperty::New("extra")); mitk::DataStorage::SetOfObjects::Pointer p = mitk::DataStorage::SetOfObjects::New(); p->push_back(n1); p->push_back(extra); // extra is parent of extra!!! ds->Add(extra, p); MITK_TEST_FOR_EXCEPTION_END(...); } /* Checking reference count of node after add and remove */ { mitk::DataNode::Pointer extra = mitk::DataNode::New(); mitk::ReferenceCountWatcher::Pointer watcher = new mitk::ReferenceCountWatcher(extra); extra->SetProperty("name", mitk::StringProperty::New("extra")); mitk::DataStorage::SetOfObjects::Pointer p = mitk::DataStorage::SetOfObjects::New(); p->push_back(n1); p->push_back(n3); ds->Add(extra, p); extra = NULL; ds->Remove(ds->GetNamedNode("extra")); MITK_TEST_CONDITION(watcher->GetReferenceCount() == 0, "Checking reference count of node after add and remove"); } /* Checking removal of a node with two derived nodes [ dataStorage->GetDerivations( rootNode )] see bug #3426 */ { mitk::DataNode::Pointer extra = mitk::DataNode::New(); extra->SetProperty("name", mitk::StringProperty::New("extra")); ds->Add(extra); mitk::DataNode::Pointer d1y = mitk::DataNode::New(); d1y->SetProperty("name", mitk::StringProperty::New("d1y")); mitk::ReferenceCountWatcher::Pointer watcherD1y = new mitk::ReferenceCountWatcher(d1y); int refCountbeforeDS = watcherD1y->GetReferenceCount(); ds->Add(d1y, extra); mitk::DataNode::Pointer d2y = mitk::DataNode::New(); d2y->SetProperty("name", mitk::StringProperty::New("d2y")); ds->Add(d2y, extra); MITK_TEST_CONDITION( (ds->GetNamedNode("extra") == extra) && (ds->GetNamedNode("d1y") == d1y) && (ds->GetNamedNode("d2y") == d2y) && (ds->GetSources(d1y)->Size() == 1) // extra should be source of d1y && (ds->GetSources(d2y)->Size() == 1) // extra should be source of d2y && (ds->GetDerivations(extra)->Size() == 2) // d1y and d2y should be derived from extra , "add extra node"); ds->Remove(ds->GetDerivations( extra)); MITK_TEST_CONDITION( (ds->GetNamedNode("extra") == extra) && (ds->GetNamedNode("d1y") == NULL) // d1y should be NULL now && (ds->GetNamedNode("d2y") == NULL) // d2y should be NULL now && (refCountbeforeDS == watcherD1y->GetReferenceCount()) , "Checking removal of subset of two derived nodes from one parent node"); ds->Remove(extra); MITK_TEST_CONDITION( (ds->GetNamedNode("extra") == NULL) , "Checking removal of a parent node"); extra = NULL; } /* Checking GetGrouptags() */ { const std::set groupTags = ds->GetGroupTags(); MITK_TEST_CONDITION( (groupTags.size() == 2) && (std::find(groupTags.begin(), groupTags.end(), "Resection Proposal 1") != groupTags.end()) && (std::find(groupTags.begin(), groupTags.end(), "Resection Proposal 2") != groupTags.end()) , "Checking GetGrouptags()"); } /* Checking Event handling */ DSEventReceiver listener; try { ds->AddNodeEvent += mitk::MessageDelegate1(&listener, &DSEventReceiver::OnAdd); ds->RemoveNodeEvent += mitk::MessageDelegate1(&listener, &DSEventReceiver::OnRemove); mitk::DataNode::Pointer extra = mitk::DataNode::New(); mitk::ReferenceCountWatcher::Pointer watcher = new mitk::ReferenceCountWatcher(extra); ds->Add(extra); MITK_TEST_CONDITION(listener.m_NodeAdded == extra.GetPointer(), "Checking AddEvent"); ds->Remove(extra); MITK_TEST_CONDITION(listener.m_NodeRemoved == extra.GetPointer(), "Checking RemoveEvent"); /* RemoveListener */ ds->AddNodeEvent -= mitk::MessageDelegate1(&listener, &DSEventReceiver::OnAdd); ds->RemoveNodeEvent -= mitk::MessageDelegate1(&listener, &DSEventReceiver::OnRemove); listener.m_NodeAdded = NULL; listener.m_NodeRemoved = NULL; ds->Add(extra); ds->Remove(extra); MITK_TEST_CONDITION((listener.m_NodeRemoved == NULL) && (listener.m_NodeAdded == NULL), "Checking RemoveListener"); std::cout << "Pointer handling after event handling: " << std::flush; extra = NULL; // delete reference to the node. its memory should be freed now MITK_TEST_CONDITION(watcher->GetReferenceCount() == 0, "Pointer handling after event handling"); } catch(...) { /* cleanup */ ds->AddNodeEvent -= mitk::MessageDelegate1(&listener, &DSEventReceiver::OnAdd); ds->RemoveNodeEvent -= mitk::MessageDelegate1(&listener, &DSEventReceiver::OnRemove); MITK_TEST_FAILED_MSG( << "Exception during object removal methods"); } //Checking ComputeBoundingGeometry3D method*/ const mitk::DataStorage::SetOfObjects::ConstPointer all = ds->GetAll(); mitk::TimeGeometry::Pointer geometry = ds->ComputeBoundingGeometry3D(); MITK_TEST_CONDITION(geometry->CountTimeSteps()==4, "Test for number or time steps with ComputeBoundingGeometry()"); mitk::TimeBounds timebounds = geometry->GetTimeBounds(); MITK_TEST_CONDITION((timebounds[0]==0)&&(timebounds[1]==4),"Test for timebounds with ComputeBoundingGeometry()"); for (unsigned int i=0; iCountTimeSteps(); i++) { - mitk::Geometry3D::Pointer subGeometry = geometry->GetGeometryForTimeStep(i); + mitk::BaseGeometry::Pointer subGeometry = geometry->GetGeometryForTimeStep(i); mitk::TimeBounds bounds = subGeometry->GetTimeBounds(); MITK_TEST_CONDITION((bounds[0]==i)&&(bounds[1]==i+1),"Test for timebounds of geometry at different time steps with ComputeBoundingGeometry()"); } geometry = ds->ComputeBoundingGeometry3D(all); MITK_TEST_CONDITION(geometry->CountTimeSteps()==4, "Test for number or time steps with ComputeBoundingGeometry(allNodes)"); timebounds = geometry->GetTimeBounds(); MITK_TEST_CONDITION((timebounds[0]==0)&&(timebounds[1]==4),"Test for timebounds with ComputeBoundingGeometry(allNodes)"); for (unsigned int i=0; iCountTimeSteps(); i++) { - mitk::Geometry3D::Pointer subGeometry = geometry->GetGeometryForTimeStep(i); + mitk::BaseGeometry::Pointer subGeometry = geometry->GetGeometryForTimeStep(i); mitk::TimeBounds bounds = subGeometry->GetTimeBounds(); MITK_TEST_CONDITION((bounds[0]==i)&&(bounds[1]==i+1),"Test for timebounds of geometry at different time steps with ComputeBoundingGeometry()"); } // test for thread safety of DataStorage try { mitk::StandaloneDataStorage::Pointer standaloneDataStorage = mitk::StandaloneDataStorage::New(); ItkDeleteEventListener listener( standaloneDataStorage ); { mitk::DataNode::Pointer emptyNode = mitk::DataNode::New(); mitk::DataNode* pEmptyNode = emptyNode; listener.SetNode( emptyNode ); standaloneDataStorage->Add( emptyNode ); emptyNode = 0; // emptyNode is still alive because standaloneDataStorage // owns it standaloneDataStorage->Remove( pEmptyNode ); // this should not freeze the whole thing } } catch(...) { MITK_TEST_FAILED_MSG( << "Exception during testing DataStorage thread safe"); } /* Clear DataStorage */ ds->Remove(ds->GetAll()); MITK_TEST_CONDITION(ds->GetAll()->Size() == 0, "Checking Clear DataStorage"); } 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/mitkGeometry3DTest.cpp b/Core/Code/Testing/mitkGeometry3DTest.cpp index fff1266cea..486683b9cd 100644 --- a/Core/Code/Testing/mitkGeometry3DTest.cpp +++ b/Core/Code/Testing/mitkGeometry3DTest.cpp @@ -1,614 +1,614 @@ /*=================================================================== 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 bool testGetAxisVectorVariants(mitk::Geometry3D* geometry) { - int direction; - for(direction=0; direction<3; ++direction) - { - mitk::Vector3D frontToBack; - switch(direction) - { - case 0: frontToBack = geometry->GetCornerPoint(false, false, false)-geometry->GetCornerPoint(true , false, false); break; //7-3 - case 1: frontToBack = geometry->GetCornerPoint(false, false, false)-geometry->GetCornerPoint(false, true , false); break; //7-5 - case 2: frontToBack = geometry->GetCornerPoint(false, false, false)-geometry->GetCornerPoint(false , false, true); break; //7-2 - } - std::cout << "Testing GetAxisVector(int) vs GetAxisVector(bool, bool, bool): "; - if(mitk::Equal(geometry->GetAxisVector(direction), frontToBack) == false) - { - std::cout<<"[FAILED]"<GetCornerPoint(false, false, false)-geometry->GetCornerPoint(true , false, false); break; //7-3 + case 1: frontToBack = geometry->GetCornerPoint(false, false, false)-geometry->GetCornerPoint(false, true , false); break; //7-5 + case 2: frontToBack = geometry->GetCornerPoint(false, false, false)-geometry->GetCornerPoint(false , false, true); break; //7-2 + } + std::cout << "Testing GetAxisVector(int) vs GetAxisVector(bool, bool, bool): "; + if(mitk::Equal(geometry->GetAxisVector(direction), frontToBack) == false) + { + std::cout<<"[FAILED]"<GetAxisVector(direction).GetNorm(), geometry->GetExtentInMM(direction)) == false) - { - std::cout<<"[FAILED]"<GetAxisVector(direction).GetNorm(), geometry->GetExtentInMM(direction)) == false) + { + std::cout<<"[FAILED]"<GetOrigin(); - mitk::Point3D dummy; - - MITK_TEST_OUTPUT( << " Testing index->world->index conversion consistency"); - geometry3d->WorldToIndex(origin, dummy); - geometry3d->IndexToWorld(dummy, dummy); - MITK_TEST_CONDITION_REQUIRED(dummy == origin, ""); - - MITK_TEST_OUTPUT( << " Testing WorldToIndex(origin, mitk::Point3D)==(0,0,0)"); - mitk::Point3D globalOrigin; - mitk::FillVector3D(globalOrigin, 0,0,0); - - mitk::Point3D originContinuousIndex; - geometry3d->WorldToIndex(origin, originContinuousIndex); - MITK_TEST_CONDITION_REQUIRED(originContinuousIndex == globalOrigin, ""); - - MITK_TEST_OUTPUT( << " Testing WorldToIndex(origin, itk::Index)==(0,0,0)"); - itk::Index<3> itkindex; - geometry3d->WorldToIndex(origin, itkindex); - itk::Index<3> globalOriginIndex; - mitk::vtk2itk(globalOrigin, globalOriginIndex); - MITK_TEST_CONDITION_REQUIRED(itkindex == globalOriginIndex, ""); - - MITK_TEST_OUTPUT( << " Testing WorldToIndex(origin-0.5*spacing, itk::Index)==(0,0,0)"); - mitk::Vector3D halfSpacingStep = geometry3d->GetSpacing()*0.5; - mitk::Matrix3D rotation; - mitk::Point3D originOffCenter = origin-halfSpacingStep; - geometry3d->WorldToIndex(originOffCenter, itkindex); - MITK_TEST_CONDITION_REQUIRED(itkindex == globalOriginIndex, ""); - - MITK_TEST_OUTPUT( << " Testing WorldToIndex(origin+0.5*spacing-eps, itk::Index)==(0,0,0)"); - originOffCenter = origin+halfSpacingStep; - originOffCenter -= 0.0001; - geometry3d->WorldToIndex( originOffCenter, itkindex); - MITK_TEST_CONDITION_REQUIRED(itkindex == globalOriginIndex, ""); - - MITK_TEST_OUTPUT( << " Testing WorldToIndex(origin+0.5*spacing, itk::Index)==(1,1,1)"); - originOffCenter = origin+halfSpacingStep; - itk::Index<3> global111; - mitk::FillVector3D(global111, 1,1,1); - geometry3d->WorldToIndex( originOffCenter, itkindex); - MITK_TEST_CONDITION_REQUIRED(itkindex == global111, ""); - - MITK_TEST_OUTPUT( << " Testing WorldToIndex(GetCenter())==BoundingBox.GetCenter: "); - mitk::Point3D center = geometry3d->GetCenter(); - mitk::Point3D centerContIndex; - geometry3d->WorldToIndex(center, centerContIndex); - mitk::BoundingBox::ConstPointer boundingBox = geometry3d->GetBoundingBox(); - mitk::BoundingBox::PointType centerBounds = boundingBox->GetCenter(); - MITK_TEST_CONDITION_REQUIRED(mitk::Equal(centerContIndex,centerBounds), ""); - - MITK_TEST_OUTPUT( << " Testing GetCenter()==IndexToWorld(BoundingBox.GetCenter): "); - center = geometry3d->GetCenter(); - mitk::Point3D centerBoundsInWorldCoords; - geometry3d->IndexToWorld(centerBounds, centerBoundsInWorldCoords); - MITK_TEST_CONDITION_REQUIRED(mitk::Equal(center,centerBoundsInWorldCoords), ""); - - return EXIT_SUCCESS; + MITK_TEST_OUTPUT( << "Testing consistency of index and world coordinate systems: "); + mitk::Point3D origin = geometry3d->GetOrigin(); + mitk::Point3D dummy; + + MITK_TEST_OUTPUT( << " Testing index->world->index conversion consistency"); + geometry3d->WorldToIndex(origin, dummy); + geometry3d->IndexToWorld(dummy, dummy); + MITK_TEST_CONDITION_REQUIRED(dummy == origin, ""); + + MITK_TEST_OUTPUT( << " Testing WorldToIndex(origin, mitk::Point3D)==(0,0,0)"); + mitk::Point3D globalOrigin; + mitk::FillVector3D(globalOrigin, 0,0,0); + + mitk::Point3D originContinuousIndex; + geometry3d->WorldToIndex(origin, originContinuousIndex); + MITK_TEST_CONDITION_REQUIRED(originContinuousIndex == globalOrigin, ""); + + MITK_TEST_OUTPUT( << " Testing WorldToIndex(origin, itk::Index)==(0,0,0)"); + itk::Index<3> itkindex; + geometry3d->WorldToIndex(origin, itkindex); + itk::Index<3> globalOriginIndex; + mitk::vtk2itk(globalOrigin, globalOriginIndex); + MITK_TEST_CONDITION_REQUIRED(itkindex == globalOriginIndex, ""); + + MITK_TEST_OUTPUT( << " Testing WorldToIndex(origin-0.5*spacing, itk::Index)==(0,0,0)"); + mitk::Vector3D halfSpacingStep = geometry3d->GetSpacing()*0.5; + mitk::Matrix3D rotation; + mitk::Point3D originOffCenter = origin-halfSpacingStep; + geometry3d->WorldToIndex(originOffCenter, itkindex); + MITK_TEST_CONDITION_REQUIRED(itkindex == globalOriginIndex, ""); + + MITK_TEST_OUTPUT( << " Testing WorldToIndex(origin+0.5*spacing-eps, itk::Index)==(0,0,0)"); + originOffCenter = origin+halfSpacingStep; + originOffCenter -= 0.0001; + geometry3d->WorldToIndex( originOffCenter, itkindex); + MITK_TEST_CONDITION_REQUIRED(itkindex == globalOriginIndex, ""); + + MITK_TEST_OUTPUT( << " Testing WorldToIndex(origin+0.5*spacing, itk::Index)==(1,1,1)"); + originOffCenter = origin+halfSpacingStep; + itk::Index<3> global111; + mitk::FillVector3D(global111, 1,1,1); + geometry3d->WorldToIndex( originOffCenter, itkindex); + MITK_TEST_CONDITION_REQUIRED(itkindex == global111, ""); + + MITK_TEST_OUTPUT( << " Testing WorldToIndex(GetCenter())==BoundingBox.GetCenter: "); + mitk::Point3D center = geometry3d->GetCenter(); + mitk::Point3D centerContIndex; + geometry3d->WorldToIndex(center, centerContIndex); + mitk::BoundingBox::ConstPointer boundingBox = geometry3d->GetBoundingBox(); + mitk::BoundingBox::PointType centerBounds = boundingBox->GetCenter(); + MITK_TEST_CONDITION_REQUIRED(mitk::Equal(centerContIndex,centerBounds), ""); + + MITK_TEST_OUTPUT( << " Testing GetCenter()==IndexToWorld(BoundingBox.GetCenter): "); + center = geometry3d->GetCenter(); + mitk::Point3D centerBoundsInWorldCoords; + geometry3d->IndexToWorld(centerBounds, centerBoundsInWorldCoords); + MITK_TEST_CONDITION_REQUIRED(mitk::Equal(center,centerBoundsInWorldCoords), ""); + + return EXIT_SUCCESS; } int testIndexAndWorldConsistencyForVectors(mitk::Geometry3D* geometry3d) { - MITK_TEST_OUTPUT( << "Testing consistency of index and world coordinate systems for vectors: "); - mitk::Vector3D xAxisMM = geometry3d->GetAxisVector(0); - mitk::Vector3D xAxisContinuousIndex; - mitk::Vector3D xAxisContinuousIndexDeprecated; - - mitk::Point3D p, pIndex, origin; - origin = geometry3d->GetOrigin(); - p[0] = xAxisMM[0]; - p[1] = xAxisMM[1]; - p[2] = xAxisMM[2]; - - geometry3d->WorldToIndex(p,pIndex); - - geometry3d->WorldToIndex(xAxisMM, xAxisContinuousIndexDeprecated); - geometry3d->WorldToIndex(xAxisMM,xAxisContinuousIndex); - MITK_TEST_CONDITION_REQUIRED(xAxisContinuousIndex[0] == pIndex[0],""); - MITK_TEST_CONDITION_REQUIRED(xAxisContinuousIndex[1] == pIndex[1],""); - MITK_TEST_CONDITION_REQUIRED(xAxisContinuousIndex[2] == pIndex[2],""); - - MITK_TEST_CONDITION_REQUIRED(xAxisContinuousIndex[0] == xAxisContinuousIndexDeprecated[0],""); - MITK_TEST_CONDITION_REQUIRED(xAxisContinuousIndex[1] == xAxisContinuousIndexDeprecated[1],""); - MITK_TEST_CONDITION_REQUIRED(xAxisContinuousIndex[2] == xAxisContinuousIndexDeprecated[2],""); - - MITK_TEST_CONDITION_REQUIRED(xAxisContinuousIndexDeprecated[0] == pIndex[0],""); - MITK_TEST_CONDITION_REQUIRED(xAxisContinuousIndexDeprecated[1] == pIndex[1],""); - MITK_TEST_CONDITION_REQUIRED(xAxisContinuousIndexDeprecated[2] == pIndex[2],""); - - geometry3d->IndexToWorld(xAxisContinuousIndex,xAxisContinuousIndex); - geometry3d->IndexToWorld(xAxisContinuousIndexDeprecated,xAxisContinuousIndexDeprecated); - geometry3d->IndexToWorld(pIndex,p); - - MITK_TEST_CONDITION_REQUIRED(xAxisContinuousIndex == xAxisMM,""); - MITK_TEST_CONDITION_REQUIRED(xAxisContinuousIndex[0] == p[0],""); - MITK_TEST_CONDITION_REQUIRED(xAxisContinuousIndex[1] == p[1],""); - MITK_TEST_CONDITION_REQUIRED(xAxisContinuousIndex[2] == p[2],""); - - MITK_TEST_CONDITION_REQUIRED(xAxisContinuousIndexDeprecated == xAxisMM,""); - MITK_TEST_CONDITION_REQUIRED(xAxisContinuousIndexDeprecated[0] == p[0],""); - MITK_TEST_CONDITION_REQUIRED(xAxisContinuousIndexDeprecated[1] == p[1],""); - MITK_TEST_CONDITION_REQUIRED(xAxisContinuousIndexDeprecated[2] == p[2],""); - - return EXIT_SUCCESS; + MITK_TEST_OUTPUT( << "Testing consistency of index and world coordinate systems for vectors: "); + mitk::Vector3D xAxisMM = geometry3d->GetAxisVector(0); + mitk::Vector3D xAxisContinuousIndex; + mitk::Vector3D xAxisContinuousIndexDeprecated; + + mitk::Point3D p, pIndex, origin; + origin = geometry3d->GetOrigin(); + p[0] = xAxisMM[0]; + p[1] = xAxisMM[1]; + p[2] = xAxisMM[2]; + + geometry3d->WorldToIndex(p,pIndex); + + geometry3d->WorldToIndex(xAxisMM, xAxisContinuousIndexDeprecated); + geometry3d->WorldToIndex(xAxisMM,xAxisContinuousIndex); + MITK_TEST_CONDITION_REQUIRED(xAxisContinuousIndex[0] == pIndex[0],""); + MITK_TEST_CONDITION_REQUIRED(xAxisContinuousIndex[1] == pIndex[1],""); + MITK_TEST_CONDITION_REQUIRED(xAxisContinuousIndex[2] == pIndex[2],""); + + MITK_TEST_CONDITION_REQUIRED(xAxisContinuousIndex[0] == xAxisContinuousIndexDeprecated[0],""); + MITK_TEST_CONDITION_REQUIRED(xAxisContinuousIndex[1] == xAxisContinuousIndexDeprecated[1],""); + MITK_TEST_CONDITION_REQUIRED(xAxisContinuousIndex[2] == xAxisContinuousIndexDeprecated[2],""); + + MITK_TEST_CONDITION_REQUIRED(xAxisContinuousIndexDeprecated[0] == pIndex[0],""); + MITK_TEST_CONDITION_REQUIRED(xAxisContinuousIndexDeprecated[1] == pIndex[1],""); + MITK_TEST_CONDITION_REQUIRED(xAxisContinuousIndexDeprecated[2] == pIndex[2],""); + + geometry3d->IndexToWorld(xAxisContinuousIndex,xAxisContinuousIndex); + geometry3d->IndexToWorld(xAxisContinuousIndexDeprecated,xAxisContinuousIndexDeprecated); + geometry3d->IndexToWorld(pIndex,p); + + MITK_TEST_CONDITION_REQUIRED(xAxisContinuousIndex == xAxisMM,""); + MITK_TEST_CONDITION_REQUIRED(xAxisContinuousIndex[0] == p[0],""); + MITK_TEST_CONDITION_REQUIRED(xAxisContinuousIndex[1] == p[1],""); + MITK_TEST_CONDITION_REQUIRED(xAxisContinuousIndex[2] == p[2],""); + + MITK_TEST_CONDITION_REQUIRED(xAxisContinuousIndexDeprecated == xAxisMM,""); + MITK_TEST_CONDITION_REQUIRED(xAxisContinuousIndexDeprecated[0] == p[0],""); + MITK_TEST_CONDITION_REQUIRED(xAxisContinuousIndexDeprecated[1] == p[1],""); + MITK_TEST_CONDITION_REQUIRED(xAxisContinuousIndexDeprecated[2] == p[2],""); + + return EXIT_SUCCESS; } int testIndexAndWorldConsistencyForIndex(mitk::Geometry3D* geometry3d) { - MITK_TEST_OUTPUT( << "Testing consistency of index and world coordinate systems: "); - - // creating testing data - itk::Index<4> itkIndex4, itkIndex4b; - itk::Index<3> itkIndex3, itkIndex3b; - itk::Index<2> itkIndex2, itkIndex2b; - mitk::Index3D mitkIndex, mitkIndexb; - - itkIndex4[0] = itkIndex4[1] = itkIndex4[2] = itkIndex4[3] = 4; - itkIndex3[0] = itkIndex3[1] = itkIndex3[2] = 6; - itkIndex2[0] = itkIndex2[1] = 2; - mitkIndex[0] = mitkIndex[1] = mitkIndex[2] = 13; - - // check for constistency - mitk::Point3D point; - geometry3d->IndexToWorld(itkIndex2,point); - geometry3d->WorldToIndex(point,itkIndex2b); - - MITK_TEST_CONDITION_REQUIRED( - ((itkIndex2b[0] == itkIndex2[0]) && - (itkIndex2b[1] == itkIndex2[1])), - "Testing itk::index<2> for IndexToWorld/WorldToIndex consistency"); - - geometry3d->IndexToWorld(itkIndex3,point); - geometry3d->WorldToIndex(point,itkIndex3b); - - MITK_TEST_CONDITION_REQUIRED( - ((itkIndex3b[0] == itkIndex3[0]) && - (itkIndex3b[1] == itkIndex3[1]) && - (itkIndex3b[2] == itkIndex3[2])), - "Testing itk::index<3> for IndexToWorld/WorldToIndex consistency"); - - geometry3d->IndexToWorld(itkIndex4,point); - geometry3d->WorldToIndex(point,itkIndex4b); - - MITK_TEST_CONDITION_REQUIRED( - ((itkIndex4b[0] == itkIndex4[0]) && - (itkIndex4b[1] == itkIndex4[1]) && - (itkIndex4b[2] == itkIndex4[2]) && - (itkIndex4b[3] == 0)), - "Testing itk::index<3> for IndexToWorld/WorldToIndex consistency"); - - geometry3d->IndexToWorld(mitkIndex,point); - geometry3d->WorldToIndex(point,mitkIndexb); - - MITK_TEST_CONDITION_REQUIRED( - ((mitkIndexb[0] == mitkIndex[0]) && - (mitkIndexb[1] == mitkIndex[1]) && - (mitkIndexb[2] == mitkIndex[2])), - "Testing mitk::Index for IndexToWorld/WorldToIndex consistency"); - - return EXIT_SUCCESS; + MITK_TEST_OUTPUT( << "Testing consistency of index and world coordinate systems: "); + + // creating testing data + itk::Index<4> itkIndex4, itkIndex4b; + itk::Index<3> itkIndex3, itkIndex3b; + itk::Index<2> itkIndex2, itkIndex2b; + mitk::Index3D mitkIndex, mitkIndexb; + + itkIndex4[0] = itkIndex4[1] = itkIndex4[2] = itkIndex4[3] = 4; + itkIndex3[0] = itkIndex3[1] = itkIndex3[2] = 6; + itkIndex2[0] = itkIndex2[1] = 2; + mitkIndex[0] = mitkIndex[1] = mitkIndex[2] = 13; + + // check for constistency + mitk::Point3D point; + geometry3d->IndexToWorld(itkIndex2,point); + geometry3d->WorldToIndex(point,itkIndex2b); + + MITK_TEST_CONDITION_REQUIRED( + ((itkIndex2b[0] == itkIndex2[0]) && + (itkIndex2b[1] == itkIndex2[1])), + "Testing itk::index<2> for IndexToWorld/WorldToIndex consistency"); + + geometry3d->IndexToWorld(itkIndex3,point); + geometry3d->WorldToIndex(point,itkIndex3b); + + MITK_TEST_CONDITION_REQUIRED( + ((itkIndex3b[0] == itkIndex3[0]) && + (itkIndex3b[1] == itkIndex3[1]) && + (itkIndex3b[2] == itkIndex3[2])), + "Testing itk::index<3> for IndexToWorld/WorldToIndex consistency"); + + geometry3d->IndexToWorld(itkIndex4,point); + geometry3d->WorldToIndex(point,itkIndex4b); + + MITK_TEST_CONDITION_REQUIRED( + ((itkIndex4b[0] == itkIndex4[0]) && + (itkIndex4b[1] == itkIndex4[1]) && + (itkIndex4b[2] == itkIndex4[2]) && + (itkIndex4b[3] == 0)), + "Testing itk::index<3> for IndexToWorld/WorldToIndex consistency"); + + geometry3d->IndexToWorld(mitkIndex,point); + geometry3d->WorldToIndex(point,mitkIndexb); + + MITK_TEST_CONDITION_REQUIRED( + ((mitkIndexb[0] == mitkIndex[0]) && + (mitkIndexb[1] == mitkIndex[1]) && + (mitkIndexb[2] == mitkIndex[2])), + "Testing mitk::Index for IndexToWorld/WorldToIndex consistency"); + + return EXIT_SUCCESS; } #include int testItkImageIsCenterBased() { - MITK_TEST_OUTPUT(<< "Testing whether itk::Image coordinates are center-based."); - typedef itk::Image ItkIntImage3D; - ItkIntImage3D::Pointer itkintimage = ItkIntImage3D::New(); - ItkIntImage3D::SizeType size; - size.Fill(10); - mitk::Point3D origin; - mitk::FillVector3D(origin, 2,3,7); - itkintimage->Initialize(); - itkintimage->SetRegions(size); - itkintimage->SetOrigin(origin); - std::cout<<"[PASSED]"< originContinuousIndex; - itkintimage->TransformPhysicalPointToContinuousIndex(origin, originContinuousIndex); - MITK_TEST_CONDITION_REQUIRED(originContinuousIndex == globalOrigin, ""); - - MITK_TEST_OUTPUT( << " Testing itk::Image::TransformPhysicalPointToIndex(origin)==(0,0,0)"); - itk::Index<3> itkindex; - itkintimage->TransformPhysicalPointToIndex(origin, itkindex); - itk::Index<3> globalOriginIndex; - mitk::vtk2itk(globalOrigin, globalOriginIndex); - MITK_TEST_CONDITION_REQUIRED(itkindex == globalOriginIndex, ""); - - MITK_TEST_OUTPUT( << " Testing itk::Image::TransformPhysicalPointToIndex(origin-0.5*spacing)==(0,0,0)"); - mitk::Vector3D halfSpacingStep = itkintimage->GetSpacing()*0.5; - mitk::Matrix3D rotation; - mitk::Point3D originOffCenter = origin-halfSpacingStep; - itkintimage->TransformPhysicalPointToIndex(originOffCenter, itkindex); - MITK_TEST_CONDITION_REQUIRED(itkindex == globalOriginIndex, ""); - - MITK_TEST_OUTPUT( << " Testing itk::Image::TransformPhysicalPointToIndex(origin+0.5*spacing-eps, itk::Index)==(0,0,0)"); - originOffCenter = origin+halfSpacingStep; - originOffCenter -= 0.0001; - itkintimage->TransformPhysicalPointToIndex( originOffCenter, itkindex); - MITK_TEST_CONDITION_REQUIRED(itkindex == globalOriginIndex, ""); - - MITK_TEST_OUTPUT( << " Testing itk::Image::TransformPhysicalPointToIndex(origin+0.5*spacing, itk::Index)==(1,1,1)"); - originOffCenter = origin+halfSpacingStep; - itk::Index<3> global111; - mitk::FillVector3D(global111, 1,1,1); - itkintimage->TransformPhysicalPointToIndex( originOffCenter, itkindex); - MITK_TEST_CONDITION_REQUIRED(itkindex == global111, ""); - - MITK_TEST_OUTPUT( << "=> Yes, itk::Image coordinates are center-based."); - - return EXIT_SUCCESS; + MITK_TEST_OUTPUT(<< "Testing whether itk::Image coordinates are center-based."); + typedef itk::Image ItkIntImage3D; + ItkIntImage3D::Pointer itkintimage = ItkIntImage3D::New(); + ItkIntImage3D::SizeType size; + size.Fill(10); + mitk::Point3D origin; + mitk::FillVector3D(origin, 2,3,7); + itkintimage->Initialize(); + itkintimage->SetRegions(size); + itkintimage->SetOrigin(origin); + std::cout<<"[PASSED]"< originContinuousIndex; + itkintimage->TransformPhysicalPointToContinuousIndex(origin, originContinuousIndex); + MITK_TEST_CONDITION_REQUIRED(originContinuousIndex == globalOrigin, ""); + + MITK_TEST_OUTPUT( << " Testing itk::Image::TransformPhysicalPointToIndex(origin)==(0,0,0)"); + itk::Index<3> itkindex; + itkintimage->TransformPhysicalPointToIndex(origin, itkindex); + itk::Index<3> globalOriginIndex; + mitk::vtk2itk(globalOrigin, globalOriginIndex); + MITK_TEST_CONDITION_REQUIRED(itkindex == globalOriginIndex, ""); + + MITK_TEST_OUTPUT( << " Testing itk::Image::TransformPhysicalPointToIndex(origin-0.5*spacing)==(0,0,0)"); + mitk::Vector3D halfSpacingStep = itkintimage->GetSpacing()*0.5; + mitk::Matrix3D rotation; + mitk::Point3D originOffCenter = origin-halfSpacingStep; + itkintimage->TransformPhysicalPointToIndex(originOffCenter, itkindex); + MITK_TEST_CONDITION_REQUIRED(itkindex == globalOriginIndex, ""); + + MITK_TEST_OUTPUT( << " Testing itk::Image::TransformPhysicalPointToIndex(origin+0.5*spacing-eps, itk::Index)==(0,0,0)"); + originOffCenter = origin+halfSpacingStep; + originOffCenter -= 0.0001; + itkintimage->TransformPhysicalPointToIndex( originOffCenter, itkindex); + MITK_TEST_CONDITION_REQUIRED(itkindex == globalOriginIndex, ""); + + MITK_TEST_OUTPUT( << " Testing itk::Image::TransformPhysicalPointToIndex(origin+0.5*spacing, itk::Index)==(1,1,1)"); + originOffCenter = origin+halfSpacingStep; + itk::Index<3> global111; + mitk::FillVector3D(global111, 1,1,1); + itkintimage->TransformPhysicalPointToIndex( originOffCenter, itkindex); + MITK_TEST_CONDITION_REQUIRED(itkindex == global111, ""); + + MITK_TEST_OUTPUT( << "=> Yes, itk::Image coordinates are center-based."); + + return EXIT_SUCCESS; } int testGeometry3D(bool imageGeometry) { - // Build up a new image Geometry - mitk::Geometry3D::Pointer geometry3d = mitk::Geometry3D::New(); - float bounds[ ] = {-10.0, 17.0, -12.0, 188.0, 13.0, 211.0}; - - MITK_TEST_OUTPUT( << "Initializing"); - geometry3d->Initialize(); - - MITK_TEST_OUTPUT(<< "Setting ImageGeometry to " << imageGeometry); - geometry3d->SetImageGeometry(imageGeometry); - - MITK_TEST_OUTPUT(<< "Setting bounds by SetFloatBounds(): " << bounds); - geometry3d->SetFloatBounds(bounds); - - MITK_TEST_OUTPUT( << "Testing AxisVectors"); - if(testGetAxisVectorVariants(geometry3d) == false) - return EXIT_FAILURE; - - if(testGetAxisVectorExtent(geometry3d) == false) - return EXIT_FAILURE; - - MITK_TEST_OUTPUT( << "Creating an AffineTransform3D transform"); - mitk::AffineTransform3D::MatrixType matrix; - matrix.SetIdentity(); - matrix(1,1) = 2; - mitk::AffineTransform3D::Pointer transform; - transform = mitk::AffineTransform3D::New(); - transform->SetMatrix(matrix); - - MITK_TEST_OUTPUT( << "Testing a SetIndexToWorldTransform"); - geometry3d->SetIndexToWorldTransform(transform); - - MITK_TEST_OUTPUT( << "Testing correctness of value returned by GetSpacing"); - const mitk::Vector3D& spacing1 = geometry3d->GetSpacing(); - mitk::Vector3D expectedSpacing; - expectedSpacing.Fill(1.0); - expectedSpacing[1] = 2; - if( mitk::Equal(spacing1, expectedSpacing) == false ) - { - MITK_TEST_OUTPUT( << " [FAILED]"); - return EXIT_FAILURE; - } - - MITK_TEST_OUTPUT( << "Testing a Compose(transform)"); - geometry3d->Compose(transform); - - MITK_TEST_OUTPUT( << "Testing correctness of value returned by GetSpacing"); - const mitk::Vector3D& spacing2 = geometry3d->GetSpacing(); - expectedSpacing[1] = 4; - if( mitk::Equal(spacing2, expectedSpacing) == false ) - { - MITK_TEST_OUTPUT( << " [FAILED]"); - return EXIT_FAILURE; - } - - MITK_TEST_OUTPUT( << "Testing correctness of SetSpacing"); - mitk::Vector3D newspacing; - mitk::FillVector3D(newspacing, 1.5, 2.5, 3.5); - geometry3d->SetSpacing(newspacing); - const mitk::Vector3D& spacing3 = geometry3d->GetSpacing(); - if( mitk::Equal(spacing3, newspacing) == false ) - { - MITK_TEST_OUTPUT( << " [FAILED]"); - return EXIT_FAILURE; - } - - // Seperate Test function for Index and World consistency - testIndexAndWorldConsistency(geometry3d); - testIndexAndWorldConsistencyForVectors(geometry3d); - testIndexAndWorldConsistencyForIndex(geometry3d); - - MITK_TEST_OUTPUT( << "Testing a rotation of the geometry"); - double angle = 35.0; - mitk::Vector3D rotationVector; mitk::FillVector3D( rotationVector, 1, 0, 0 ); - mitk::Point3D center = geometry3d->GetCenter(); - mitk::RotationOperation* op = new mitk::RotationOperation( mitk::OpROTATE, center, rotationVector, angle ); - geometry3d->ExecuteOperation(op); - - MITK_TEST_OUTPUT( << "Testing mitk::GetRotation() and success of rotation"); - mitk::Matrix3D rotation; - mitk::GetRotation(geometry3d, rotation); - mitk::Vector3D voxelStep=rotation*newspacing; - mitk::Vector3D voxelStepIndex; - geometry3d->WorldToIndex(voxelStep, voxelStepIndex); - mitk::Vector3D expectedVoxelStepIndex; - expectedVoxelStepIndex.Fill(1); - MITK_TEST_CONDITION_REQUIRED(mitk::Equal(voxelStepIndex,expectedVoxelStepIndex), ""); - delete op; - std::cout<<"[PASSED]"<GetImageGeometry() == imageGeometry, ""); - - //Test if the translate function moves the origin correctly. - mitk::Point3D oldOrigin = geometry3d->GetOrigin(); - - //use some random values for translation - mitk::Vector3D translationVector; - translationVector.SetElement(0, 17.5f); - translationVector.SetElement(1, -32.3f); - translationVector.SetElement(2, 4.0f); - //compute ground truth - mitk::Point3D tmpResult = geometry3d->GetOrigin() + translationVector; - geometry3d->Translate(translationVector); - MITK_TEST_CONDITION( mitk::Equal( geometry3d->GetOrigin(), tmpResult ), "Testing if origin was translated."); - - translationVector*=-1; //vice versa - geometry3d->Translate(translationVector); - - MITK_TEST_CONDITION( mitk::Equal( geometry3d->GetOrigin(), oldOrigin ), "Testing if the translation could be done vice versa." ); - - return EXIT_SUCCESS; + // Build up a new image Geometry + mitk::Geometry3D::Pointer geometry3d = mitk::Geometry3D::New(); + float bounds[ ] = {-10.0, 17.0, -12.0, 188.0, 13.0, 211.0}; + + MITK_TEST_OUTPUT( << "Initializing"); + geometry3d->Initialize(); + + MITK_TEST_OUTPUT(<< "Setting ImageGeometry to " << imageGeometry); + geometry3d->SetImageGeometry(imageGeometry); + + MITK_TEST_OUTPUT(<< "Setting bounds by SetFloatBounds(): " << bounds); + geometry3d->SetFloatBounds(bounds); + + MITK_TEST_OUTPUT( << "Testing AxisVectors"); + if(testGetAxisVectorVariants(geometry3d) == false) + return EXIT_FAILURE; + + if(testGetAxisVectorExtent(geometry3d) == false) + return EXIT_FAILURE; + + MITK_TEST_OUTPUT( << "Creating an AffineTransform3D transform"); + mitk::AffineTransform3D::MatrixType matrix; + matrix.SetIdentity(); + matrix(1,1) = 2; + mitk::AffineTransform3D::Pointer transform; + transform = mitk::AffineTransform3D::New(); + transform->SetMatrix(matrix); + + MITK_TEST_OUTPUT( << "Testing a SetIndexToWorldTransform"); + geometry3d->SetIndexToWorldTransform(transform); + + MITK_TEST_OUTPUT( << "Testing correctness of value returned by GetSpacing"); + const mitk::Vector3D& spacing1 = geometry3d->GetSpacing(); + mitk::Vector3D expectedSpacing; + expectedSpacing.Fill(1.0); + expectedSpacing[1] = 2; + if( mitk::Equal(spacing1, expectedSpacing) == false ) + { + MITK_TEST_OUTPUT( << " [FAILED]"); + return EXIT_FAILURE; + } + + MITK_TEST_OUTPUT( << "Testing a Compose(transform)"); + geometry3d->Compose(transform); + + MITK_TEST_OUTPUT( << "Testing correctness of value returned by GetSpacing"); + const mitk::Vector3D& spacing2 = geometry3d->GetSpacing(); + expectedSpacing[1] = 4; + if( mitk::Equal(spacing2, expectedSpacing) == false ) + { + MITK_TEST_OUTPUT( << " [FAILED]"); + return EXIT_FAILURE; + } + + MITK_TEST_OUTPUT( << "Testing correctness of SetSpacing"); + mitk::Vector3D newspacing; + mitk::FillVector3D(newspacing, 1.5, 2.5, 3.5); + geometry3d->SetSpacing(newspacing); + const mitk::Vector3D& spacing3 = geometry3d->GetSpacing(); + if( mitk::Equal(spacing3, newspacing) == false ) + { + MITK_TEST_OUTPUT( << " [FAILED]"); + return EXIT_FAILURE; + } + + // Seperate Test function for Index and World consistency + testIndexAndWorldConsistency(geometry3d); + testIndexAndWorldConsistencyForVectors(geometry3d); + testIndexAndWorldConsistencyForIndex(geometry3d); + + MITK_TEST_OUTPUT( << "Testing a rotation of the geometry"); + double angle = 35.0; + mitk::Vector3D rotationVector; mitk::FillVector3D( rotationVector, 1, 0, 0 ); + mitk::Point3D center = geometry3d->GetCenter(); + mitk::RotationOperation* op = new mitk::RotationOperation( mitk::OpROTATE, center, rotationVector, angle ); + geometry3d->ExecuteOperation(op); + + MITK_TEST_OUTPUT( << "Testing mitk::GetRotation() and success of rotation"); + mitk::Matrix3D rotation; + mitk::GetRotation(geometry3d, rotation); + mitk::Vector3D voxelStep=rotation*newspacing; + mitk::Vector3D voxelStepIndex; + geometry3d->WorldToIndex(voxelStep, voxelStepIndex); + mitk::Vector3D expectedVoxelStepIndex; + expectedVoxelStepIndex.Fill(1); + MITK_TEST_CONDITION_REQUIRED(mitk::Equal(voxelStepIndex,expectedVoxelStepIndex), ""); + delete op; + std::cout<<"[PASSED]"<GetImageGeometry() == imageGeometry, ""); + + //Test if the translate function moves the origin correctly. + mitk::Point3D oldOrigin = geometry3d->GetOrigin(); + + //use some random values for translation + mitk::Vector3D translationVector; + translationVector.SetElement(0, 17.5f); + translationVector.SetElement(1, -32.3f); + translationVector.SetElement(2, 4.0f); + //compute ground truth + mitk::Point3D tmpResult = geometry3d->GetOrigin() + translationVector; + geometry3d->Translate(translationVector); + MITK_TEST_CONDITION( mitk::Equal( geometry3d->GetOrigin(), tmpResult ), "Testing if origin was translated."); + + translationVector*=-1; //vice versa + geometry3d->Translate(translationVector); + + MITK_TEST_CONDITION( mitk::Equal( geometry3d->GetOrigin(), oldOrigin ), "Testing if the translation could be done vice versa." ); + + return EXIT_SUCCESS; } int testGeometryAfterCasting() { - // Epsilon. Allowed difference for rotationvalue - float eps = 0.0001; - - // Cast ITK and MITK images and see if geometry stays - typedef itk::Image Image2DType; - typedef itk::Image Image3DType; - - // Create 3D ITK Image from Scratch, cast to 3D MITK image, compare Geometries - Image3DType::Pointer image3DItk = Image3DType::New(); - Image3DType::RegionType myRegion; - Image3DType::SizeType mySize; - Image3DType::IndexType myIndex; - Image3DType::SpacingType mySpacing; - Image3DType::DirectionType myDirection, rotMatrixX, rotMatrixY, rotMatrixZ; - mySpacing[0] = 31; - mySpacing[1] = 0.1; - mySpacing[2] = 2.9; - myIndex[0] = -15; - myIndex[1] = 15; - myIndex[2] = 12; - mySize[0] = 10; - mySize[1] = 2; - mySize[2] = 555; - myRegion.SetSize( mySize); - myRegion.SetIndex( myIndex ); - image3DItk->SetSpacing(mySpacing); - image3DItk->SetRegions( myRegion); - image3DItk->Allocate(); - image3DItk->FillBuffer(0); - - myDirection.SetIdentity(); - rotMatrixX.SetIdentity(); - rotMatrixY.SetIdentity(); - rotMatrixZ.SetIdentity(); - - mitk::Image::Pointer mitkImage; - - // direction [row] [coloum] - MITK_TEST_OUTPUT( << "Casting a rotated 3D ITK Image to a MITK Image and check if Geometry is still same" ); - for (double rotX=0; rotX < (itk::Math::pi*2); rotX+=0.4 ) - { - // Set Rotation X - rotMatrixX[1][1] = cos( rotX ); - rotMatrixX[1][2] = -sin( rotX ); - rotMatrixX[2][1] = sin( rotX ); - rotMatrixX[2][2] = cos( rotX ); - - for (double rotY=0; rotY < (itk::Math::pi*2); rotY+=0.33 ) + // Epsilon. Allowed difference for rotationvalue + float eps = 0.0001; + + // Cast ITK and MITK images and see if geometry stays + typedef itk::Image Image2DType; + typedef itk::Image Image3DType; + + // Create 3D ITK Image from Scratch, cast to 3D MITK image, compare Geometries + Image3DType::Pointer image3DItk = Image3DType::New(); + Image3DType::RegionType myRegion; + Image3DType::SizeType mySize; + Image3DType::IndexType myIndex; + Image3DType::SpacingType mySpacing; + Image3DType::DirectionType myDirection, rotMatrixX, rotMatrixY, rotMatrixZ; + mySpacing[0] = 31; + mySpacing[1] = 0.1; + mySpacing[2] = 2.9; + myIndex[0] = -15; + myIndex[1] = 15; + myIndex[2] = 12; + mySize[0] = 10; + mySize[1] = 2; + mySize[2] = 555; + myRegion.SetSize( mySize); + myRegion.SetIndex( myIndex ); + image3DItk->SetSpacing(mySpacing); + image3DItk->SetRegions( myRegion); + image3DItk->Allocate(); + image3DItk->FillBuffer(0); + + myDirection.SetIdentity(); + rotMatrixX.SetIdentity(); + rotMatrixY.SetIdentity(); + rotMatrixZ.SetIdentity(); + + mitk::Image::Pointer mitkImage; + + // direction [row] [coloum] + MITK_TEST_OUTPUT( << "Casting a rotated 3D ITK Image to a MITK Image and check if Geometry is still same" ); + for (double rotX=0; rotX < (itk::Math::pi*2); rotX+=0.4 ) + { + // Set Rotation X + rotMatrixX[1][1] = cos( rotX ); + rotMatrixX[1][2] = -sin( rotX ); + rotMatrixX[2][1] = sin( rotX ); + rotMatrixX[2][2] = cos( rotX ); + + for (double rotY=0; rotY < (itk::Math::pi*2); rotY+=0.33 ) + { + // Set Rotation Y + rotMatrixY[0][0] = cos( rotY ); + rotMatrixY[0][2] = sin( rotY ); + rotMatrixY[2][0] = -sin( rotY ); + rotMatrixY[2][2] = cos( rotY ); + + for (double rotZ=0; rotZ < (itk::Math::pi*2); rotZ+=0.5 ) { - // Set Rotation Y - rotMatrixY[0][0] = cos( rotY ); - rotMatrixY[0][2] = sin( rotY ); - rotMatrixY[2][0] = -sin( rotY ); - rotMatrixY[2][2] = cos( rotY ); - - for (double rotZ=0; rotZ < (itk::Math::pi*2); rotZ+=0.5 ) - { - // Set Rotation Z - rotMatrixZ[0][0] = cos( rotZ ); - rotMatrixZ[0][1] = -sin( rotZ ); - rotMatrixZ[1][0] = sin( rotZ ); - rotMatrixZ[1][1] = cos( rotZ ); - - // Multiply matrizes - myDirection = myDirection * rotMatrixX * rotMatrixY * rotMatrixZ; - image3DItk->SetDirection(myDirection); - mitk::CastToMitkImage(image3DItk, mitkImage); - const mitk::AffineTransform3D::MatrixType& matrix = mitkImage->GetGeometry()->GetIndexToWorldTransform()->GetMatrix(); - - for (int row=0; row<3; row++) + // Set Rotation Z + rotMatrixZ[0][0] = cos( rotZ ); + rotMatrixZ[0][1] = -sin( rotZ ); + rotMatrixZ[1][0] = sin( rotZ ); + rotMatrixZ[1][1] = cos( rotZ ); + + // Multiply matrizes + myDirection = myDirection * rotMatrixX * rotMatrixY * rotMatrixZ; + image3DItk->SetDirection(myDirection); + mitk::CastToMitkImage(image3DItk, mitkImage); + const mitk::AffineTransform3D::MatrixType& matrix = mitkImage->GetGeometry()->GetIndexToWorldTransform()->GetMatrix(); + + for (int row=0; row<3; row++) + { + for (int col=0; col<3; col++) + { + double mitkValue = matrix[row][col] / mitkImage->GetGeometry()->GetSpacing()[col]; + double itkValue = myDirection[row][col]; + double diff = mitkValue - itkValue; + // if you decrease this value, you can see that there might be QUITE high inaccuracy!!! + if (diff > eps) // need to check, how exact it SHOULD be .. since it is NOT EXACT! { - for (int col=0; col<3; col++) - { - double mitkValue = matrix[row][col] / mitkImage->GetGeometry()->GetSpacing()[col]; - double itkValue = myDirection[row][col]; - double diff = mitkValue - itkValue; - // if you decrease this value, you can see that there might be QUITE high inaccuracy!!! - if (diff > eps) // need to check, how exact it SHOULD be .. since it is NOT EXACT! - { - std::cout << "Had a difference of : " << diff; - std::cout << "Error: Casting altered Geometry!"; - std::cout << "ITK Matrix:\n" << myDirection; - std::cout << "Mitk Matrix (With Spacing):\n" << matrix; - std::cout << "Mitk Spacing: " << mitkImage->GetGeometry()->GetSpacing(); - MITK_TEST_CONDITION_REQUIRED(false == true, ""); - return false; - } - } + std::cout << "Had a difference of : " << diff; + std::cout << "Error: Casting altered Geometry!"; + std::cout << "ITK Matrix:\n" << myDirection; + std::cout << "Mitk Matrix (With Spacing):\n" << matrix; + std::cout << "Mitk Spacing: " << mitkImage->GetGeometry()->GetSpacing(); + MITK_TEST_CONDITION_REQUIRED(false == true, ""); + return false; } - } + } + } } - } - - // Create 2D ITK Image from Scratch, cast to 2D MITK image, compare Geometries - Image2DType::Pointer image2DItk = Image2DType::New(); - Image2DType::RegionType myRegion2D; - Image2DType::SizeType mySize2D; - Image2DType::IndexType myIndex2D; - Image2DType::SpacingType mySpacing2D; - Image2DType::DirectionType myDirection2D, rotMatrix; - mySpacing2D[0] = 31; - mySpacing2D[1] = 0.1; - myIndex2D[0] = -15; - myIndex2D[1] = 15; - mySize2D[0] = 10; - mySize2D[1] = 2; - myRegion2D.SetSize( mySize2D); - myRegion2D.SetIndex( myIndex2D ); - image2DItk->SetSpacing(mySpacing2D); - image2DItk->SetRegions( myRegion2D); - image2DItk->Allocate(); - image2DItk->FillBuffer(0); - - myDirection2D.SetIdentity(); - rotMatrix.SetIdentity(); - - // direction [row] [coloum] - MITK_TEST_OUTPUT( << "Casting a rotated 2D ITK Image to a MITK Image and check if Geometry is still same" ); - for (double rotTheta=0; rotTheta < (itk::Math::pi*2); rotTheta+=0.1 ) - { - // Set Rotation - rotMatrix[0][0] = cos(rotTheta); - rotMatrix[0][1] = -sin(rotTheta); - rotMatrix[1][0] = sin(rotTheta); - rotMatrix[1][1] = cos(rotTheta); - - // Multiply matrizes - myDirection2D = myDirection2D * rotMatrix; - image2DItk->SetDirection(myDirection2D); - mitk::CastToMitkImage(image2DItk, mitkImage); - const mitk::AffineTransform3D::MatrixType& matrix = mitkImage->GetGeometry()->GetIndexToWorldTransform()->GetMatrix(); - - // Compare MITK and ITK matrix - for (int row=0; row<3; row++) + } + } + + // Create 2D ITK Image from Scratch, cast to 2D MITK image, compare Geometries + Image2DType::Pointer image2DItk = Image2DType::New(); + Image2DType::RegionType myRegion2D; + Image2DType::SizeType mySize2D; + Image2DType::IndexType myIndex2D; + Image2DType::SpacingType mySpacing2D; + Image2DType::DirectionType myDirection2D, rotMatrix; + mySpacing2D[0] = 31; + mySpacing2D[1] = 0.1; + myIndex2D[0] = -15; + myIndex2D[1] = 15; + mySize2D[0] = 10; + mySize2D[1] = 2; + myRegion2D.SetSize( mySize2D); + myRegion2D.SetIndex( myIndex2D ); + image2DItk->SetSpacing(mySpacing2D); + image2DItk->SetRegions( myRegion2D); + image2DItk->Allocate(); + image2DItk->FillBuffer(0); + + myDirection2D.SetIdentity(); + rotMatrix.SetIdentity(); + + // direction [row] [coloum] + MITK_TEST_OUTPUT( << "Casting a rotated 2D ITK Image to a MITK Image and check if Geometry is still same" ); + for (double rotTheta=0; rotTheta < (itk::Math::pi*2); rotTheta+=0.1 ) + { + // Set Rotation + rotMatrix[0][0] = cos(rotTheta); + rotMatrix[0][1] = -sin(rotTheta); + rotMatrix[1][0] = sin(rotTheta); + rotMatrix[1][1] = cos(rotTheta); + + // Multiply matrizes + myDirection2D = myDirection2D * rotMatrix; + image2DItk->SetDirection(myDirection2D); + mitk::CastToMitkImage(image2DItk, mitkImage); + const mitk::AffineTransform3D::MatrixType& matrix = mitkImage->GetGeometry()->GetIndexToWorldTransform()->GetMatrix(); + + // Compare MITK and ITK matrix + for (int row=0; row<3; row++) + { + for (int col=0; col<3; col++) { - for (int col=0; col<3; col++) - { - double mitkValue = matrix[row][col] / mitkImage->GetGeometry()->GetSpacing()[col]; - if ((row == 2) && (col == row)) - { - if (mitkValue != 1) - { - MITK_TEST_OUTPUT(<< "After casting a 2D ITK to 3D MITK images, MITK matrix values for 0|2, 1|2, 2|0, 2|1 MUST be 0 and value for 2|2 must be 1"); - return false; - } - } - else if ((row == 2) || (col == 2)) - { - if (mitkValue != 0) - { - MITK_TEST_OUTPUT(<< "After casting a 2D ITK to 3D MITK images, MITK matrix values for 0|2, 1|2, 2|0, 2|1 MUST be 0 and value for 2|2 must be 1"); - return false; - } - } - else - { - double itkValue = myDirection2D[row][col]; - double diff = mitkValue - itkValue; - // if you decrease this value, you can see that there might be QUITE high inaccuracy!!! - if (diff > eps) // need to check, how exact it SHOULD be .. since it is NOT EXACT! - { - std::cout << "Had a difference of : " << diff; - std::cout << "Error: Casting altered Geometry!"; - std::cout << "ITK Matrix:\n" << myDirection2D; - std::cout << "Mitk Matrix (With Spacing):\n" << matrix; - std::cout << "Mitk Spacing: " << mitkImage->GetGeometry()->GetSpacing(); - MITK_TEST_CONDITION_REQUIRED(false == true, ""); - return false; - } - } - } + double mitkValue = matrix[row][col] / mitkImage->GetGeometry()->GetSpacing()[col]; + if ((row == 2) && (col == row)) + { + if (mitkValue != 1) + { + MITK_TEST_OUTPUT(<< "After casting a 2D ITK to 3D MITK images, MITK matrix values for 0|2, 1|2, 2|0, 2|1 MUST be 0 and value for 2|2 must be 1"); + return false; + } + } + else if ((row == 2) || (col == 2)) + { + if (mitkValue != 0) + { + MITK_TEST_OUTPUT(<< "After casting a 2D ITK to 3D MITK images, MITK matrix values for 0|2, 1|2, 2|0, 2|1 MUST be 0 and value for 2|2 must be 1"); + return false; + } + } + else + { + double itkValue = myDirection2D[row][col]; + double diff = mitkValue - itkValue; + // if you decrease this value, you can see that there might be QUITE high inaccuracy!!! + if (diff > eps) // need to check, how exact it SHOULD be .. since it is NOT EXACT! + { + std::cout << "Had a difference of : " << diff; + std::cout << "Error: Casting altered Geometry!"; + std::cout << "ITK Matrix:\n" << myDirection2D; + std::cout << "Mitk Matrix (With Spacing):\n" << matrix; + std::cout << "Mitk Spacing: " << mitkImage->GetGeometry()->GetSpacing(); + MITK_TEST_CONDITION_REQUIRED(false == true, ""); + return false; + } + } } - } + } + } - // THIS WAS TESTED: - // 2D ITK -> 2D MITK, - // 3D ITK -> 3D MITK, + // THIS WAS TESTED: + // 2D ITK -> 2D MITK, + // 3D ITK -> 3D MITK, - // Still need to test: 2D MITK Image with ADDITIONAL INFORMATION IN MATRIX -> 2D ITK - // 1. Possibility: 3x3 MITK matrix can be converted without loss into 2x2 ITK matrix - // 2. Possibility: 3x3 MITK matrix can only be converted with loss into 2x2 ITK matrix - // .. before implementing this, we wait for further development in geometry classes (e.g. Geoemtry3D::SetRotation(..)) + // Still need to test: 2D MITK Image with ADDITIONAL INFORMATION IN MATRIX -> 2D ITK + // 1. Possibility: 3x3 MITK matrix can be converted without loss into 2x2 ITK matrix + // 2. Possibility: 3x3 MITK matrix can only be converted with loss into 2x2 ITK matrix + // .. before implementing this, we wait for further development in geometry classes (e.g. Geoemtry3D::SetRotation(..)) - return EXIT_SUCCESS; + return EXIT_SUCCESS; } int mitkGeometry3DTest(int /*argc*/, char* /*argv*/[]) { - MITK_TEST_BEGIN(mitkGeometry3DTest); + MITK_TEST_BEGIN(mitkGeometry3DTest); - int result; + int result; - MITK_TEST_CONDITION_REQUIRED( (result = testItkImageIsCenterBased()) == EXIT_SUCCESS, ""); + MITK_TEST_CONDITION_REQUIRED( (result = testItkImageIsCenterBased()) == EXIT_SUCCESS, ""); - MITK_TEST_OUTPUT(<< "Running main part of test with ImageGeometry = false"); - MITK_TEST_CONDITION_REQUIRED( (result = testGeometry3D(false)) == EXIT_SUCCESS, ""); + MITK_TEST_OUTPUT(<< "Running main part of test with ImageGeometry = false"); + MITK_TEST_CONDITION_REQUIRED( (result = testGeometry3D(false)) == EXIT_SUCCESS, ""); - MITK_TEST_OUTPUT(<< "Running main part of test with ImageGeometry = true"); - MITK_TEST_CONDITION_REQUIRED( (result = testGeometry3D(true)) == EXIT_SUCCESS, ""); + MITK_TEST_OUTPUT(<< "Running main part of test with ImageGeometry = true"); + MITK_TEST_CONDITION_REQUIRED( (result = testGeometry3D(true)) == EXIT_SUCCESS, ""); - MITK_TEST_OUTPUT(<< "Running test to see if Casting MITK to ITK and the other way around destroys geometry"); - MITK_TEST_CONDITION_REQUIRED( (result = testGeometryAfterCasting()) == EXIT_SUCCESS, ""); + MITK_TEST_OUTPUT(<< "Running test to see if Casting MITK to ITK and the other way around destroys geometry"); + MITK_TEST_CONDITION_REQUIRED( (result = testGeometryAfterCasting()) == EXIT_SUCCESS, ""); - MITK_TEST_END(); + MITK_TEST_END(); - return EXIT_SUCCESS; -} \ No newline at end of file + return EXIT_SUCCESS; +} diff --git a/Core/Code/Testing/mitkGeometryDataToSurfaceFilterTest.cpp b/Core/Code/Testing/mitkGeometryDataToSurfaceFilterTest.cpp index dbd63895d4..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::Geometry3D* geometry, TScalarType expectedIndexBounds[6]) +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::Geometry3D* geometry, TScalarType expectedAxisParallelBounds[6]) +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 #include class mitkIOUtilTestSuite : public mitk::TestFixture { - CPPUNIT_TEST_SUITE(mitkIOUtilTestSuite); MITK_TEST(TestTempMethods); MITK_TEST(TestLoadAndSaveImage); MITK_TEST(TestLoadAndSavePointSet); MITK_TEST(TestLoadAndSaveSurface); MITK_TEST(TestTempMethodsForUniqueFilenames); CPPUNIT_TEST_SUITE_END(); private: std::string m_ImagePath; std::string m_SurfacePath; std::string m_PointSetPath; public: void setUp() { m_ImagePath = GetTestDataFilePath("Pic3D.nrrd"); m_SurfacePath = GetTestDataFilePath("binary.stl"); m_PointSetPath = GetTestDataFilePath("pointSet.mps"); } void TestTempMethods() { std::string tmpPath = mitk::IOUtil::GetTempPath(); CPPUNIT_ASSERT(!tmpPath.empty()); std::ofstream tmpFile; std::string tmpFilePath = mitk::IOUtil::CreateTemporaryFile(tmpFile); CPPUNIT_ASSERT(tmpFile && tmpFile.is_open()); CPPUNIT_ASSERT(tmpFilePath.size() > tmpPath.size()); CPPUNIT_ASSERT(tmpFilePath.substr(0, tmpPath.size()) == tmpPath); tmpFile.close(); CPPUNIT_ASSERT(std::remove(tmpFilePath.c_str()) == 0); std::string programPath = mitk::IOUtil::GetProgramPath(); CPPUNIT_ASSERT(!programPath.empty()); std::ofstream tmpFile2; std::string tmpFilePath2 = mitk::IOUtil::CreateTemporaryFile(tmpFile2, "my-XXXXXX", programPath); CPPUNIT_ASSERT(tmpFile2 && tmpFile2.is_open()); CPPUNIT_ASSERT(tmpFilePath2.size() > programPath.size()); CPPUNIT_ASSERT(tmpFilePath2.substr(0, programPath.size()) == programPath); tmpFile2.close(); CPPUNIT_ASSERT(std::remove(tmpFilePath2.c_str()) == 0); std::ofstream tmpFile3; std::string tmpFilePath3 = mitk::IOUtil::CreateTemporaryFile(tmpFile3, std::ios_base::binary, - "my-XXXXXX.TXT", programPath); + "my-XXXXXX.TXT", programPath); CPPUNIT_ASSERT(tmpFile3 && tmpFile3.is_open()); CPPUNIT_ASSERT(tmpFilePath3.size() > programPath.size()); CPPUNIT_ASSERT(tmpFilePath3.substr(0, programPath.size()) == programPath); CPPUNIT_ASSERT(tmpFilePath3.substr(tmpFilePath3.size() - 13, 3) == "my-"); CPPUNIT_ASSERT(tmpFilePath3.substr(tmpFilePath3.size() - 4) == ".TXT"); tmpFile3.close(); //CPPUNIT_ASSERT(std::remove(tmpFilePath3.c_str()) == 0) std::string tmpFilePath4 = mitk::IOUtil::CreateTemporaryFile(); std::ofstream file; file.open(tmpFilePath4.c_str()); CPPUNIT_ASSERT_MESSAGE("Testing if file exists after CreateTemporaryFile()",file.is_open()); CPPUNIT_ASSERT_THROW(mitk::IOUtil::CreateTemporaryFile(tmpFile2, "XX"), mitk::Exception); std::string tmpDir = mitk::IOUtil::CreateTemporaryDirectory(); CPPUNIT_ASSERT(tmpDir.size() > tmpPath.size()); CPPUNIT_ASSERT(tmpDir.substr(0, tmpPath.size()) == tmpPath); CPPUNIT_ASSERT(itksys::SystemTools::RemoveADirectory(tmpDir.c_str())); std::string tmpDir2 = mitk::IOUtil::CreateTemporaryDirectory("my-XXXXXX", programPath); CPPUNIT_ASSERT(tmpDir2.size() > programPath.size()); CPPUNIT_ASSERT(tmpDir2.substr(0, programPath.size()) == programPath); CPPUNIT_ASSERT(itksys::SystemTools::RemoveADirectory(tmpDir2.c_str())); } void TestTempMethodsForUniqueFilenames() { int numberOfFiles = 100; //create 100 empty files std::vector v100filenames; for(int i=0; i(4,4,4,1); std::string imagePath3 = mitk::IOUtil::CreateTemporaryFile(tmpStream, "XXXXXX.nrrd"); tmpStream.close(); mitk::IOUtil::SaveImage(relativImage, imagePath3); CPPUNIT_ASSERT_NO_THROW(mitk::IOUtil::LoadImage(imagePath3)); std::remove(imagePath3.c_str()); } void TestLoadAndSavePointSet() { mitk::PointSet::Pointer pointset = mitk::IOUtil::LoadPointSet(m_PointSetPath); CPPUNIT_ASSERT( pointset.IsNotNull()); std::ofstream tmpStream; std::string pointSetPath = mitk::IOUtil::CreateTemporaryFile(tmpStream, "XXXXXX.mps"); tmpStream.close(); std::string pointSetPathWithDefaultExtension = mitk::IOUtil::CreateTemporaryFile(tmpStream, "XXXXXX.mps"); tmpStream.close(); std::string pointSetPathWithoutDefaultExtension = mitk::IOUtil::CreateTemporaryFile(tmpStream, "XXXXXX.xXx"); tmpStream.close(); // the cases where no exception should be thrown CPPUNIT_ASSERT(mitk::IOUtil::SavePointSet(pointset, pointSetPathWithDefaultExtension)); // test if defaultextension is inserted if no extension is present CPPUNIT_ASSERT(mitk::IOUtil::SavePointSet(pointset, pointSetPathWithoutDefaultExtension.c_str())); //delete the files after the test is done std::remove(pointSetPath.c_str()); std::remove(pointSetPathWithDefaultExtension.c_str()); std::remove(pointSetPathWithoutDefaultExtension.c_str()); } void TestLoadAndSaveSurface() { mitk::Surface::Pointer surface = mitk::IOUtil::LoadSurface(m_SurfacePath); CPPUNIT_ASSERT( surface.IsNotNull()); std::ofstream tmpStream; std::string surfacePath = mitk::IOUtil::CreateTemporaryFile(tmpStream, "diffsurface-XXXXXX.stl"); // the cases where no exception should be thrown CPPUNIT_ASSERT(mitk::IOUtil::SaveSurface(surface, surfacePath)); // test if exception is thrown as expected on unknown extsension CPPUNIT_ASSERT_THROW(mitk::IOUtil::SaveSurface(surface,"testSurface.xXx"), mitk::Exception); //delete the files after the test is done std::remove(surfacePath.c_str()); } - }; MITK_TEST_SUITE_REGISTRATION(mitkIOUtil) diff --git a/Core/Code/Testing/mitkImageTest.cpp b/Core/Code/Testing/mitkImageTest.cpp index ad3aedf736..ddcfe231d3 100644 --- a/Core/Code/Testing/mitkImageTest.cpp +++ b/Core/Code/Testing/mitkImageTest.cpp @@ -1,526 +1,526 @@ /*=================================================================== 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 includes #include #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::Geometry3D::Pointer imageGeometry = image->GetGeometry(); + 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!"); + 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/mitkImportItkImageTest.cpp b/Core/Code/Testing/mitkImportItkImageTest.cpp index db55e5bdc7..d27804a90a 100644 --- a/Core/Code/Testing/mitkImportItkImageTest.cpp +++ b/Core/Code/Testing/mitkImportItkImageTest.cpp @@ -1,240 +1,240 @@ /*=================================================================== 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 "mitkITKImageImport.h" #include "mitkImagePixelReadAccessor.h" #include #include /** * Create a test image with random pixel values. The image size is determined by the input parameter. * * @param size the number of voxels in each dimension */ template typename itk::Image::Pointer CreateTestImageRandom( short int size ) { typedef typename itk::Image< TPixel, VDimension> ImageType; typedef typename ImageType::Pointer ImagePointer; itk::Size regionSize; regionSize.Fill(size); typename itk::RandomImageSource::Pointer randomImageSource = itk::RandomImageSource::New(); randomImageSource->SetNumberOfThreads(1); // to produce non-random results randomImageSource->SetSize(regionSize); randomImageSource->Update(); return randomImageSource->GetOutput(); } /** * Create a test image with a single pixel value. The image size is determined by the input parameter. * * @param value the pixel value the created image is filled with * @param size the number of voxels in each dimension */ template typename itk::Image::Pointer CreateTestImageFixedValue(size_t size, TPixel value) { typedef typename itk::Image< TPixel, VDimension> ImageType; typedef typename ImageType::Pointer ImagePointer; typename ImageType::RegionType imageRegion; typename ImageType::RegionType::SizeType regionSize; regionSize.Fill(size); typename ImageType::RegionType::IndexType regionIndex; regionIndex.Fill(0); imageRegion.SetSize( regionSize ); imageRegion.SetIndex( regionIndex ); typename ImageType::SpacingType imageSpacing; imageSpacing.Fill(1.0f); typename ImageType::PointType imageOrigin; imageOrigin.Fill(0.0f); ImagePointer itkImage = ImageType::New(); itkImage->SetRegions( imageRegion ); itkImage->SetOrigin( imageOrigin ); itkImage->SetSpacing( imageSpacing ); itkImage->Allocate(); itkImage->FillBuffer( value ); return itkImage; } /** * Compares the meta information of both given images for equality. */ template bool Assert_ImageMetaData_AreEqual( typename ImageType::Pointer itkImage, mitk::Image::Pointer mitkImage ) { bool return_value = true; typename ImageType::RegionType itkRegion = itkImage->GetLargestPossibleRegion(); typename ImageType::SizeType itkImageSize = itkRegion.GetSize(); // check dimension for( unsigned int idx=0; idx < mitkImage->GetDimension(); idx++) { return_value &= ( itkImageSize[idx] == mitkImage->GetDimension(idx) ); } MITK_TEST_CONDITION( return_value, " - Dimensions equal!") // check pixel type bool ptype_compare = ( mitkImage->GetPixelType() == mitk::MakePixelType() ); return_value &= ptype_compare; MITK_TEST_CONDITION( ptype_compare, " - Pixel types equal!") - mitk::Geometry3D* imageGeometry = mitkImage->GetGeometry(); + mitk::BaseGeometry* imageGeometry = mitkImage->GetGeometry(); const mitk::Point3D origin = imageGeometry->GetOrigin(); bool origin_compare = true; for( unsigned int idx=0; idx < 3; idx++) { origin_compare &= ( itkImage->GetOrigin()[idx] == origin[idx] ); } return_value &= origin_compare; MITK_TEST_CONDITION( origin_compare, " - Origin equals!") return return_value; } /** * Generates a random itk image and imports it to mitk image through ImportItkImage and compares the values * voxel-wise afterwards */ template void Assert_ItkImageImportRandomValuesSucceded_ReturnsTrue() { std::stringstream msg; msg << "Current type: (Random Image, " << VDimension << "D):" << typeid(TPixel).name() << "\n"; std::cout << msg.str(); bool assert_value = true; typedef typename itk::Image< TPixel, VDimension> ImageType; typedef typename ImageType::Pointer ImagePointer; ImagePointer itkImage = CreateTestImageRandom(5); mitk::Image::Pointer output_import = mitk::ImportItkImage( itkImage ); itk::ImageRegionConstIteratorWithIndex< ImageType > iter( itkImage, itkImage->GetLargestPossibleRegion() ); iter.GoToBegin(); mitk::ImagePixelReadAccessor< TPixel, VDimension > readAccessor( output_import ); bool difference = false; while( !iter.IsAtEnd() ) { TPixel ref = iter.Get(); TPixel val = readAccessor.GetPixelByIndex( iter.GetIndex() ); difference |= ( ref != val ); if( difference ) { std::cout << iter.GetIndex() << ":" << ref << " ? " << val << "\n"; } ++iter; } assert_value = Assert_ImageMetaData_AreEqual( itkImage, output_import ); MITK_TEST_CONDITION( assert_value && (!difference), "Pixel values are same in voxel-wise comparison." ); } /** * Generates an itk image with fixed pixel value and imports it to mitk image through ImportItkImage * and compares the values voxel-wise afterwards */ template void Assert_ItkImageImportSucceded_ReturnsTrue() { std::stringstream msg; msg << "Current type: " << VDimension << "D):" << typeid(TPixel).name() << "\n"; std::cout << msg.str(); bool assert_value = true; typedef typename itk::Image< TPixel, VDimension> ImageType; typedef typename ImageType::Pointer ImagePointer; ImagePointer itkImage = CreateTestImageFixedValue(5, itk::NumericTraits::min()); mitk::Image::Pointer output_import = mitk::ImportItkImage( itkImage ); itk::ImageRegionConstIteratorWithIndex< ImageType > iter( itkImage, itkImage->GetLargestPossibleRegion() ); iter.GoToBegin(); mitk::ImagePixelReadAccessor< TPixel, VDimension > readAccessor( output_import ); bool difference = false; while( !iter.IsAtEnd() ) { TPixel ref = iter.Get(); TPixel val = readAccessor.GetPixelByIndex( iter.GetIndex() ); difference |= ( ref != val ); if( difference ) { std::cout << iter.GetIndex() << ":" << ref << " ? " << val << "\n"; } ++iter; } assert_value = Assert_ImageMetaData_AreEqual( itkImage, output_import ); MITK_TEST_CONDITION( assert_value && (!difference), "Pixel values are same in voxel-wise comparison." ); } int mitkImportItkImageTest(int /*argc*/, char* /*argv*/[]) { MITK_TEST_BEGIN("mitkImportItkImageTest") Assert_ItkImageImportSucceded_ReturnsTrue();// "Import succesfull on 3D short"); Assert_ItkImageImportSucceded_ReturnsTrue();// "Import succesfull on float"); Assert_ItkImageImportSucceded_ReturnsTrue();// "Import succesfull on uchar"); Assert_ItkImageImportSucceded_ReturnsTrue();// "Import succesfull on int"); Assert_ItkImageImportRandomValuesSucceded_ReturnsTrue();// "Import succesfull on 3D short"); Assert_ItkImageImportRandomValuesSucceded_ReturnsTrue();// "Import succesfull on float"); Assert_ItkImageImportRandomValuesSucceded_ReturnsTrue();// "Import succesfull on uchar"); Assert_ItkImageImportRandomValuesSucceded_ReturnsTrue();// "Import succesfull on int"); Assert_ItkImageImportRandomValuesSucceded_ReturnsTrue();// "Import succesfull on 3D short"); Assert_ItkImageImportRandomValuesSucceded_ReturnsTrue();// "Import succesfull on float"); Assert_ItkImageImportRandomValuesSucceded_ReturnsTrue();// "Import succesfull on uchar"); Assert_ItkImageImportRandomValuesSucceded_ReturnsTrue();// "Import succesfull on int"); MITK_TEST_END() } diff --git a/Core/Code/Testing/mitkSliceNavigationControllerTest.cpp b/Core/Code/Testing/mitkSliceNavigationControllerTest.cpp index 77340c28fc..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::Geometry3D::Pointer geometry= timeGeometry.GetGeometryForTimeStep(0); + 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::Geometry3D* subSliceGeo2D_first = slicedGeo3D->GetGeometry2D(0); - mitk::Geometry3D* subSliceGeo2D_last = slicedGeo3D->GetGeometry2D(num_slices-1); + 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 "mitkTestingMacros.h" #include #include #include #include "mitkImageGenerator.h" #include "mitkPointSet.h" #include static const mitk::ScalarType test_eps = 1E-6; /* some reference values in the test seem to have been calculated with float precision. Thus, set this to float precision epsilon.*/ static const mitk::ScalarType test_eps_square = 1E-3; class mitkTimeGeometryTestClass { public: void Translation_Image_MovedOrigin(unsigned int DimX, unsigned int DimY, unsigned int DimZ, unsigned int DimT) { // DimX, DimY, DimZ, mitk::Image::Pointer image = mitk::ImageGenerator::GenerateRandomImage(DimX, DimY, DimZ, DimT,0.5,0.33,0.78,100); - mitk::Geometry3D::Pointer geometry = image->GetTimeGeometry()->GetGeometryForTimeStep(0); + mitk::BaseGeometry::Pointer geometry = image->GetTimeGeometry()->GetGeometryForTimeStep(0); mitk::Point3D imageOrigin = geometry->GetOrigin(); mitk::Point3D expectedOrigin; expectedOrigin[0] = 0; expectedOrigin[1] = 0; expectedOrigin[2] = 0; MITK_TEST_CONDITION(mitk::Equal(imageOrigin, expectedOrigin), "Original origin match expected origin"); expectedOrigin[0] = 0.325; expectedOrigin[1] = 0.487; expectedOrigin[2] = 0.78; mitk::Vector3D translationVector; translationVector[0] = expectedOrigin[0]; translationVector[1] = expectedOrigin[1]; translationVector[2] = expectedOrigin[2]; for (mitk::TimeStepType timeStep = 0; timeStep < image->GetTimeGeometry()->CountTimeSteps(); ++timeStep) { image->GetTimeGeometry()->GetGeometryForTimeStep(timeStep)->Translate(translationVector); } imageOrigin = image->GetGeometry(0)->GetOrigin(); MITK_TEST_CONDITION(mitk::Equal(imageOrigin, expectedOrigin), "Translated origin match expected origin"); expectedOrigin[0] = 2*translationVector[0]; expectedOrigin[1] = 2*translationVector[1]; expectedOrigin[2] = 2*translationVector[2]; for (mitk::TimeStepType timeStep = 0; timeStep < image->GetTimeGeometry()->CountTimeSteps(); ++timeStep) { image->GetTimeGeometry()->GetGeometryForTimeStep(timeStep)->Translate(translationVector); } imageOrigin = image->GetGeometry(0)->GetOrigin(); MITK_TEST_CONDITION(mitk::Equal(imageOrigin, expectedOrigin), "Translated origin match expected origin"); } void Rotate_Image_RotatedPoint(mitk::BaseData* baseData, unsigned int DimX, unsigned int DimY, unsigned int DimZ, unsigned int DimT) { mitk::StandaloneDataStorage::Pointer ds = mitk::StandaloneDataStorage::New(); mitk::DataNode::Pointer dataNode = mitk::DataNode::New(); // DimX, DimY, DimZ, dataNode->SetData(baseData); ds->Add(dataNode); - mitk::Geometry3D::Pointer geometry = baseData->GetTimeGeometry()->GetGeometryForTimeStep(0); + mitk::BaseGeometry::Pointer geometry = baseData->GetTimeGeometry()->GetGeometryForTimeStep(0); mitk::Point3D expectedPoint; expectedPoint[0] = 3*0.5; expectedPoint[1] = 3*0.33; expectedPoint[2] = 3*0.78; mitk::Point3D originalPoint; originalPoint[0] = 3; originalPoint[1] = 3; originalPoint[2] = 3; mitk::Point3D worldPoint; geometry->IndexToWorld(originalPoint, worldPoint); MITK_TEST_CONDITION(mitk::Equal(worldPoint, expectedPoint, test_eps), "Index-to-World without rotation as expected "); mitk::Point3D pointOfRotation; pointOfRotation[0] = 0; pointOfRotation[1] = 0; pointOfRotation[2] = 0; mitk::Vector3D vectorOfRotation; vectorOfRotation[0] = 1; vectorOfRotation[1] = 0.5; vectorOfRotation[2] = 0.2; mitk::ScalarType angleOfRotation = 73.0; mitk::RotationOperation* rotation = new mitk::RotationOperation(mitk::OpROTATE,pointOfRotation, vectorOfRotation, angleOfRotation); baseData->GetTimeGeometry()->ExecuteOperation(rotation); delete rotation; expectedPoint[0] = 2.6080379; expectedPoint[1] = -0.75265157; expectedPoint[2] = 1.1564401; baseData->GetGeometry(0)->IndexToWorld(originalPoint,worldPoint); MITK_TEST_CONDITION(mitk::Equal(worldPoint, expectedPoint, test_eps), "Rotation returns expected values "); } void Scale_Image_ScaledPoint(unsigned int DimX, unsigned int DimY, unsigned int DimZ, unsigned int DimT) { // DimX, DimY, DimZ, mitk::Image::Pointer image = mitk::ImageGenerator::GenerateRandomImage(DimX, DimY, DimZ, DimT,0.5,0.33,0.78,100); - mitk::Geometry3D::Pointer geometry = image->GetTimeGeometry()->GetGeometryForTimeStep(0); + mitk::BaseGeometry::Pointer geometry = image->GetTimeGeometry()->GetGeometryForTimeStep(0); mitk::Point3D expectedPoint; expectedPoint[0] = 3*0.5; expectedPoint[1] = 3*0.33; expectedPoint[2] = 3*0.78; mitk::Point3D originalPoint; originalPoint[0] = 3; originalPoint[1] = 3; originalPoint[2] = 3; mitk::Point3D worldPoint; geometry->IndexToWorld(originalPoint, worldPoint); MITK_TEST_CONDITION(mitk::Equal(worldPoint, expectedPoint, test_eps), "Index-to-World with old Scaling as expected "); mitk::Vector3D newSpacing; newSpacing[0] = 2; newSpacing[1] = 1.254; newSpacing[2] = 0.224; image->SetSpacing(newSpacing); expectedPoint[0] = 3*2; expectedPoint[1] = 3*1.254; expectedPoint[2] = 3*0.224; image->GetGeometry(0)->IndexToWorld(originalPoint,worldPoint); MITK_TEST_CONDITION(mitk::Equal(worldPoint, expectedPoint), "Index-toWorld with new Scaling returns expected values "); } void GetMinimumTimePoint_4DBaseData_Zero(mitk::BaseData* baseData, unsigned int DimT) { baseData->Update(); mitk::TimeGeometry::Pointer geometry = baseData->GetTimeGeometry(); mitk::TimePointType expectedTimePoint = geometry->GetMinimumTimePoint(); MITK_TEST_CONDITION(mitk::Equal(expectedTimePoint, 0), "Returns correct minimum time point "); } void GetMaximumTimePoint_4DBaseData_DimT(mitk::BaseData* baseData, unsigned int DimT) { baseData->Update(); mitk::TimeGeometry::Pointer geometry = baseData->GetTimeGeometry(); mitk::TimePointType expectedTimePoint = geometry->GetMaximumTimePoint(); MITK_TEST_CONDITION(mitk::Equal(expectedTimePoint, DimT), "Returns correct maximum time point "); } void CountTimeSteps_Image_ReturnDimT(mitk::BaseData* baseData, unsigned int DimX, unsigned int DimY, unsigned int DimZ, unsigned int DimT) { mitk::TimeGeometry::Pointer geometry = baseData->GetTimeGeometry(); mitk::TimeStepType expectedTimeSteps = geometry->CountTimeSteps(); MITK_TEST_CONDITION(mitk::Equal(expectedTimeSteps, DimT), "Returns correct number of time Steps "); } void GetMinimumTimePoint_3DImage_Min(mitk::BaseData* baseData, unsigned int DimX, unsigned int DimY, unsigned int DimZ, unsigned int DimT) { mitk::TimeGeometry::Pointer geometry = baseData->GetTimeGeometry(); mitk::TimePointType expectedTimePoint = geometry->GetMinimumTimePoint(); MITK_TEST_CONDITION(mitk::Equal(expectedTimePoint, -std::numeric_limits().max()), "Returns correct minimum time point "); } void GetMaximumTimePoint_3DImage_Max(mitk::BaseData* baseData,unsigned int DimX, unsigned int DimY, unsigned int DimZ, unsigned int DimT) { mitk::TimeGeometry::Pointer geometry = baseData->GetTimeGeometry(); mitk::TimePointType expectedTimePoint = geometry->GetMaximumTimePoint(); MITK_INFO << expectedTimePoint; MITK_INFO << std::numeric_limits().max(); MITK_TEST_CONDITION(mitk::Equal(expectedTimePoint, std::numeric_limits().max()), "Returns correct maximum time point "); } void GetTimeBounds_4DImage_ZeroAndDimT(unsigned int DimX, unsigned int DimY, unsigned int DimZ, unsigned int DimT) { mitk::Image::Pointer image = mitk::ImageGenerator::GenerateRandomImage(DimX, DimY, DimZ, DimT,0.5,0.33,0.78,100); mitk::TimeGeometry::Pointer geometry = image->GetTimeGeometry(); mitk::TimeBounds expectedTimeBounds = geometry->GetTimeBounds(); MITK_TEST_CONDITION(mitk::Equal(expectedTimeBounds[0], 0), "Returns correct minimum time point "); MITK_TEST_CONDITION(mitk::Equal(expectedTimeBounds[1], DimT), "Returns correct maximum time point "); } void GetTimeBounds_3DImage_ZeroAndDimT(mitk::BaseData* baseData, unsigned int DimX, unsigned int DimY, unsigned int DimZ, unsigned int DimT) { baseData->Update(); mitk::TimeGeometry::Pointer geometry = baseData->GetTimeGeometry(); mitk::TimeBounds expectedTimeBounds = geometry->GetTimeBounds(); MITK_TEST_CONDITION(mitk::Equal(expectedTimeBounds[0], -std::numeric_limits().max()), "Returns correct minimum time point "); MITK_TEST_CONDITION(mitk::Equal(expectedTimeBounds[1], std::numeric_limits().max()), "Returns correct maximum time point "); } void IsValidTimePoint_ImageValidTimePoint_True(mitk::BaseData* baseData, unsigned int DimX, unsigned int DimY, unsigned int DimZ, unsigned int DimT) { baseData->Update(); mitk::TimeGeometry::Pointer geometry = baseData->GetTimeGeometry(); bool isValid = geometry->IsValidTimePoint(DimT-1); MITK_TEST_CONDITION(mitk::Equal(isValid, true), "Is valid time Point correct minimum time point "); } void IsValidTimePoint_ImageNegativInvalidTimePoint_False(unsigned int DimX, unsigned int DimY, unsigned int DimZ, unsigned int DimT) { mitk::Image::Pointer image = mitk::ImageGenerator::GenerateRandomImage(DimX, DimY, DimZ, DimT,0.5,0.33,0.78,100); mitk::TimeGeometry::Pointer geometry = image->GetTimeGeometry(); bool isValid = geometry->IsValidTimePoint(-DimT); MITK_TEST_CONDITION(mitk::Equal(isValid, false), "Is invalid time Point correct minimum time point "); } void IsValidTimePoint_ImageInvalidTimePoint_False(unsigned int DimX, unsigned int DimY, unsigned int DimZ, unsigned int DimT) { mitk::Image::Pointer image = mitk::ImageGenerator::GenerateRandomImage(DimX, DimY, DimZ, DimT,0.5,0.33,0.78,100); mitk::TimeGeometry::Pointer geometry = image->GetTimeGeometry(); bool isValid = geometry->IsValidTimePoint(DimT+1); MITK_TEST_CONDITION(mitk::Equal(isValid, false), "Is invalid time Point correct minimum time point "); } void IsValidTimeStep_ImageValidTimeStep_True(mitk::BaseData* baseData, unsigned int DimX, unsigned int DimY, unsigned int DimZ, unsigned int DimT) { mitk::TimeGeometry::Pointer geometry = baseData->GetTimeGeometry(); bool isValid = geometry->IsValidTimeStep(DimT-1); MITK_TEST_CONDITION(mitk::Equal(isValid, true), "Is valid time Point correct minimum time point "); } void IsValidTimeStep_ImageNegativInvalidTimeStep_False(mitk::BaseData* baseData, unsigned int DimX, unsigned int DimY, unsigned int DimZ, unsigned int DimT) { mitk::TimeGeometry::Pointer geometry = baseData->GetTimeGeometry(); bool isValid = geometry->IsValidTimeStep(-DimT); MITK_TEST_CONDITION(mitk::Equal(isValid, false), "Is invalid time Point correct minimum time point "); } void IsValidTimeStep_ImageInvalidTimeStep_False(mitk::BaseData* baseData, unsigned int DimX, unsigned int DimY, unsigned int DimZ, unsigned int DimT) { mitk::TimeGeometry::Pointer geometry = baseData->GetTimeGeometry(); bool isValid = geometry->IsValidTimeStep(DimT); MITK_TEST_CONDITION(mitk::Equal(isValid, false), "Is invalid time Point correct minimum time point "); } void TimeStepToTimePoint_ImageValidTimeStep_TimePoint(mitk::BaseData* baseData, unsigned int DimX, unsigned int DimY, unsigned int DimZ, unsigned int DimT) { mitk::TimeGeometry::Pointer geometry = baseData->GetTimeGeometry(); mitk::TimePointType timePoint= geometry->TimeStepToTimePoint(DimT-1); MITK_TEST_CONDITION(mitk::Equal(timePoint, DimT-1), "Calculated right time Point for Time Step "); } void TimeStepToTimePoint_ImageInvalidTimeStep_TimePoint(mitk::BaseData* baseData, unsigned int DimX, unsigned int DimY, unsigned int DimZ, unsigned int DimT) { mitk::TimeGeometry::Pointer geometry = baseData->GetTimeGeometry(); mitk::TimePointType timePoint= geometry->TimeStepToTimePoint(DimT+1); MITK_TEST_CONDITION(mitk::Equal(timePoint, DimT+1), "Calculated right time Point for invalid Time Step "); } void TimePointToTimeStep_ImageValidTimePoint_TimePoint(mitk::BaseData* baseData, unsigned int DimX, unsigned int DimY, unsigned int DimZ, unsigned int DimT) { mitk::TimeGeometry::Pointer geometry = baseData->GetTimeGeometry(); mitk::TimeStepType timePoint= geometry->TimePointToTimeStep(DimT-0.5); MITK_TEST_CONDITION(mitk::Equal(timePoint, DimT-1), "Calculated right time step for valid time point"); } void TimePointToTimeStep_4DImageInvalidTimePoint_TimePoint(unsigned int DimX, unsigned int DimY, unsigned int DimZ, unsigned int DimT) { mitk::Image::Pointer image = mitk::ImageGenerator::GenerateRandomImage(DimX, DimY, DimZ, DimT,0.5,0.33,0.78,100); mitk::TimeGeometry::Pointer geometry = image->GetTimeGeometry(); mitk::TimeStepType timePoint= geometry->TimePointToTimeStep(DimT+1.5); MITK_TEST_CONDITION(mitk::Equal(timePoint, DimT+1), "Calculated right time step for invalid time point"); } void TimePointToTimeStep_4DImageNegativInvalidTimePoint_TimePoint(unsigned int DimX, unsigned int DimY, unsigned int DimZ, unsigned int DimT) { mitk::Image::Pointer image = mitk::ImageGenerator::GenerateRandomImage(DimX, DimY, DimZ, DimT,0.5,0.33,0.78,100); mitk::TimeGeometry::Pointer geometry = image->GetTimeGeometry(); mitk::TimePointType negativTimePoint = (-1.0*DimT) - 1.5; mitk::TimeStepType timePoint= geometry->TimePointToTimeStep(negativTimePoint); MITK_TEST_CONDITION(mitk::Equal(timePoint, 0), "Calculated right time step for negativ invalid time point"); } void GetGeometryForTimeStep_BaseDataValidTimeStep_CorrectGeometry(mitk::BaseData* baseData, mitk::ScalarType inputX, mitk::ScalarType inputY, mitk::ScalarType inputZ, mitk::ScalarType outputX, mitk::ScalarType outputY, mitk::ScalarType outputZ, unsigned int DimT) { baseData->Update(); mitk::TimeGeometry::Pointer geometry = baseData->GetTimeGeometry(); - mitk::Geometry3D::Pointer geometry3D = geometry->GetGeometryForTimeStep(DimT-1); + mitk::BaseGeometry::Pointer geometry3D = geometry->GetGeometryForTimeStep(DimT-1); MITK_TEST_CONDITION(geometry3D.IsNotNull(), "Non-zero geometry returned"); mitk::Point3D expectedPoint; expectedPoint[0] = outputX; expectedPoint[1] = outputY; expectedPoint[2] = outputZ; mitk::Point3D originalPoint; originalPoint[0] = inputX; originalPoint[1] = inputY; originalPoint[2] = inputZ; mitk::Point3D worldPoint; geometry3D->IndexToWorld(originalPoint, worldPoint); MITK_TEST_CONDITION(mitk::Equal(worldPoint, expectedPoint, test_eps), "Geometry transformation match expection. "); } void GetGeometryForTimeStep_ImageInvalidTimeStep_NullPointer(mitk::BaseData* baseData, unsigned int DimX, unsigned int DimY, unsigned int DimZ, unsigned int DimT) { mitk::TimeGeometry::Pointer geometry = baseData->GetTimeGeometry(); - mitk::Geometry3D::Pointer geometry3D = geometry->GetGeometryForTimeStep(DimT+1); + mitk::BaseGeometry::Pointer geometry3D = geometry->GetGeometryForTimeStep(DimT+1); MITK_TEST_CONDITION(geometry3D.IsNull(), "Null-Pointer geometry returned"); } void GetGeometryForTimePoint_BaseDataValidTimePoint_CorrectGeometry(mitk::BaseData* baseData, mitk::ScalarType inputX, mitk::ScalarType inputY, mitk::ScalarType inputZ, mitk::ScalarType outputX, mitk::ScalarType outputY, mitk::ScalarType outputZ, unsigned int DimT) { baseData->Update(); mitk::TimeGeometry::Pointer geometry = baseData->GetTimeGeometry(); - mitk::Geometry3D::Pointer geometry3D = geometry->GetGeometryForTimePoint(DimT-0.5); + mitk::BaseGeometry::Pointer geometry3D = geometry->GetGeometryForTimePoint(DimT-0.5); MITK_TEST_CONDITION(geometry3D.IsNotNull(), "Non-zero geometry returned"); mitk::Point3D expectedPoint; expectedPoint[0] = outputX; expectedPoint[1] = outputY; expectedPoint[2] = outputZ; mitk::Point3D originalPoint; originalPoint[0] = inputX; originalPoint[1] = inputY; originalPoint[2] = inputZ; mitk::Point3D worldPoint; geometry3D->IndexToWorld(originalPoint, worldPoint); MITK_TEST_CONDITION(mitk::Equal(worldPoint, expectedPoint, test_eps), "Geometry transformation match expection. "); } void GetGeometryForTimePoint_4DImageInvalidTimePoint_NullPointer(unsigned int DimX, unsigned int DimY, unsigned int DimZ, unsigned int DimT) { mitk::Image::Pointer image = mitk::ImageGenerator::GenerateRandomImage(DimX, DimY, DimZ, DimT,0.5,0.33,0.78,100); mitk::TimeGeometry::Pointer geometry = image->GetTimeGeometry(); - mitk::Geometry3D::Pointer geometry3D = geometry->GetGeometryForTimePoint(DimT+1); + mitk::BaseGeometry::Pointer geometry3D = geometry->GetGeometryForTimePoint(DimT+1); MITK_TEST_CONDITION(geometry3D.IsNull(), "Null-Pointer geometry returned with invalid time point"); } void GetGeometryForTimePoint_4DImageNEgativInvalidTimePoint_NullPointer(unsigned int DimX, unsigned int DimY, unsigned int DimZ, unsigned int DimT) { mitk::Image::Pointer image = mitk::ImageGenerator::GenerateRandomImage(DimX, DimY, DimZ, DimT,0.5,0.33,0.78,100); mitk::TimeGeometry::Pointer geometry = image->GetTimeGeometry(); mitk::TimePointType timePoint = (-1.0*(DimT)) -1; - mitk::Geometry3D::Pointer geometry3D = geometry->GetGeometryForTimePoint(timePoint); + mitk::BaseGeometry::Pointer geometry3D = geometry->GetGeometryForTimePoint(timePoint); MITK_TEST_CONDITION(geometry3D.IsNull(), "Null-Pointer geometry returned with invalid negativ time point"); } void GetGeometryCloneForTimeStep_BaseDataValidTimeStep_CorrectGeometry(mitk::BaseData* baseData, unsigned int DimT) { mitk::TimeGeometry::Pointer geometry = baseData->GetTimeGeometry(); - mitk::Geometry3D::Pointer geometry3D = geometry->GetGeometryCloneForTimeStep(DimT-1); + mitk::BaseGeometry::Pointer geometry3D = geometry->GetGeometryCloneForTimeStep(DimT-1); MITK_TEST_CONDITION(geometry3D.IsNotNull(), "Non-zero geometry returned"); mitk::Point3D expectedPoint; mitk::Point3D originalPoint; originalPoint[0] = 3; originalPoint[1] = 3; originalPoint[2] = 3; mitk::Point3D worldPoint; geometry3D->IndexToWorld(originalPoint, expectedPoint); mitk::Vector3D translationVector; translationVector[0] = 5; translationVector[1] = 8; translationVector[2] = 7; geometry3D->Translate(translationVector); geometry3D = geometry->GetGeometryForTimeStep(DimT-1); geometry3D->IndexToWorld(originalPoint, worldPoint); MITK_TEST_CONDITION(mitk::Equal(worldPoint, expectedPoint), "Geometry transformation not changed. "); } void GetGeometryCloneForTimeStep_ImageInvalidTimeStep_NullPointer(mitk::BaseData* baseData, unsigned int DimX, unsigned int DimY, unsigned int DimZ, unsigned int DimT) { mitk::TimeGeometry::Pointer geometry = baseData->GetTimeGeometry(); - mitk::Geometry3D::Pointer geometry3D = geometry->GetGeometryCloneForTimeStep(DimT+1); + mitk::BaseGeometry::Pointer geometry3D = geometry->GetGeometryCloneForTimeStep(DimT+1); MITK_TEST_CONDITION(geometry3D.IsNull(), "Null-Pointer geometry returned"); } void SetTimeStepGeometry_BaseDataValidTimeStep_CorrectGeometry(mitk::BaseData* baseData, mitk::ScalarType scaleX, mitk::ScalarType scaleY, mitk::ScalarType scaleZ, unsigned int DimT) { baseData->Update(); mitk::TimeGeometry::Pointer geometry = baseData->GetTimeGeometry(); - mitk::Geometry3D::Pointer geometry3D = geometry->GetGeometryCloneForTimeStep(DimT-1); + mitk::BaseGeometry::Pointer geometry3D = geometry->GetGeometryCloneForTimeStep(DimT-1); MITK_TEST_CONDITION(geometry3D.IsNotNull(), "Non-zero geometry returned"); mitk::Vector3D translationVector; translationVector[0] = 5; translationVector[1] = 8; translationVector[2] = 7; geometry3D->Translate(translationVector); geometry->SetTimeStepGeometry(geometry3D,DimT-1); mitk::Point3D expectedPoint; expectedPoint[0] = 3*scaleX+5; expectedPoint[1] = 3*scaleY+8; expectedPoint[2] = 3*scaleZ+7; mitk::Point3D originalPoint; originalPoint[0] = 3; originalPoint[1] = 3; originalPoint[2] = 3; mitk::Point3D worldPoint; geometry->GetGeometryForTimeStep(DimT-1)->IndexToWorld(originalPoint, worldPoint); MITK_TEST_CONDITION(mitk::Equal(worldPoint, expectedPoint, test_eps), "Geometry transformation match expection. "); } void Expand_BaseDataDoubleSize_SizeChanged(mitk::BaseData* baseData, unsigned int DimT) { mitk::TimeGeometry::Pointer geometry = baseData->GetTimeGeometry(); MITK_TEST_CONDITION(geometry->CountTimeSteps()==DimT, "Number of time Steps match expection. "); geometry->Expand(DimT * 2); MITK_TEST_CONDITION(geometry->CountTimeSteps()==DimT*2, "Number of time Steps match expection. "); - mitk::Geometry3D::Pointer geometry3D = geometry->GetGeometryForTimeStep(DimT*2 -1); + mitk::BaseGeometry::Pointer geometry3D = geometry->GetGeometryForTimeStep(DimT*2 -1); MITK_TEST_CONDITION(geometry3D.IsNotNull(), "Non-zero geometry is generated. "); } void CheckBounds_BaseData_PointsAsExpected(mitk::BaseData* baseData, mitk::ScalarType minX, mitk::ScalarType minY, mitk::ScalarType minZ, mitk::ScalarType maxX, mitk::ScalarType maxY, mitk::ScalarType maxZ) { baseData->Update(); mitk::TimeGeometry::Pointer geometry = baseData->GetTimeGeometry(); mitk::Point3D expectedPoint; expectedPoint[0] = minX; expectedPoint[1] = minY; expectedPoint[2] = minZ; mitk::Point3D point = geometry->GetCornerPointInWorld(0); MITK_TEST_CONDITION(mitk::Equal(expectedPoint, point, test_eps), "Bounding Point 0 as expected "); point = geometry->GetCornerPointInWorld(true,true,true); MITK_TEST_CONDITION(mitk::Equal(expectedPoint, point, test_eps), "Bounding Point 0 as expected "); point = geometry->GetCornerPointInWorld(1); expectedPoint[0] = minX; expectedPoint[1] = minY; expectedPoint[2] = maxZ; MITK_TEST_CONDITION(mitk::Equal(expectedPoint, point, test_eps), "GBounding Point 1 as expected "); point = geometry->GetCornerPointInWorld(true,true,false); MITK_TEST_CONDITION(mitk::Equal(expectedPoint, point, test_eps), "Bounding Point 1 as expected "); point = geometry->GetCornerPointInWorld(2); expectedPoint[0] = minX; expectedPoint[1] = maxY; expectedPoint[2] = minZ; MITK_TEST_CONDITION(mitk::Equal(expectedPoint, point, test_eps), "Bounding Point 2 as expected "); point = geometry->GetCornerPointInWorld(true,false,true); MITK_TEST_CONDITION(mitk::Equal(expectedPoint, point, test_eps), "Bounding Point 2 as expected "); point = geometry->GetCornerPointInWorld(3); expectedPoint[0] = minX; expectedPoint[1] = maxY; expectedPoint[2] = maxZ; MITK_TEST_CONDITION(mitk::Equal(expectedPoint, point, test_eps), "Bounding Point 3 as expected "); point = geometry->GetCornerPointInWorld(true,false,false); MITK_TEST_CONDITION(mitk::Equal(expectedPoint, point, test_eps), "Bounding Point 3 as expected "); point = geometry->GetCornerPointInWorld(4); expectedPoint[0] = maxX; expectedPoint[1] = minY; expectedPoint[2] = minZ; MITK_TEST_CONDITION(mitk::Equal(expectedPoint, point, test_eps), "Bounding Point 4 as expected "); point = geometry->GetCornerPointInWorld(false,true,true); MITK_TEST_CONDITION(mitk::Equal(expectedPoint, point, test_eps), "Bounding Point 4 as expected "); point = geometry->GetCornerPointInWorld(5); expectedPoint[0] = maxX; expectedPoint[1] = minY; expectedPoint[2] = maxZ; MITK_TEST_CONDITION(mitk::Equal(expectedPoint, point, test_eps), "Bounding Point 5 as expected "); point = geometry->GetCornerPointInWorld(false,true,false); MITK_TEST_CONDITION(mitk::Equal(expectedPoint, point, test_eps), "Bounding Point 5 as expected "); point = geometry->GetCornerPointInWorld(6); expectedPoint[0] = maxX; expectedPoint[1] = maxY; expectedPoint[2] = minZ; MITK_TEST_CONDITION(mitk::Equal(expectedPoint, point, test_eps), "Bounding Point 6 as expected "); point = geometry->GetCornerPointInWorld(false,false,true); MITK_TEST_CONDITION(mitk::Equal(expectedPoint, point, test_eps), "Bounding Point 6 as expected "); point = geometry->GetCornerPointInWorld(7); expectedPoint[0] = maxX; expectedPoint[1] = maxY; expectedPoint[2] = maxZ; MITK_TEST_CONDITION(mitk::Equal(expectedPoint, point, test_eps), "Bounding Point 7 as expected "); point = geometry->GetCornerPointInWorld(false,false,false); MITK_TEST_CONDITION(mitk::Equal(expectedPoint, point, test_eps), "Bounding Point 7 as expected "); } void CheckLength_BaseData_AsExpected(mitk::BaseData* baseData, double length, double squareLength) { baseData->Update(); mitk::TimeGeometry::Pointer geometry = baseData->GetTimeGeometry(); double dimension = geometry->GetDiagonalLengthInWorld(); MITK_TEST_CONDITION(mitk::Equal(dimension,length, test_eps ), "Length as expected "); dimension = geometry->GetDiagonalLength2InWorld(); MITK_TEST_CONDITION(mitk::Equal(dimension, squareLength, test_eps_square ), "Square length as expected "); } void CheckPointInside_BaseDataPointInside_True(mitk::BaseData* baseData, mitk::ScalarType pointX, mitk::ScalarType pointY, mitk::ScalarType pointZ) { baseData->Update(); mitk::TimeGeometry::Pointer geometry = baseData->GetTimeGeometry(); mitk::Point3D expectedPoint; expectedPoint[0] = pointX; expectedPoint[1] = pointY; expectedPoint[2] = pointZ; bool isInside = geometry->IsWorldPointInside(expectedPoint); MITK_TEST_CONDITION(isInside, "Point is inside Image..."); } void CheckPointInside_BaseDataPointOutside_False(mitk::BaseData* baseData, mitk::ScalarType pointX, mitk::ScalarType pointY, mitk::ScalarType pointZ) { baseData->Update(); mitk::TimeGeometry::Pointer geometry = baseData->GetTimeGeometry(); mitk::Point3D expectedPoint; expectedPoint[0] = pointX; expectedPoint[1] = pointY; expectedPoint[2] = pointZ; bool isInside = geometry->IsWorldPointInside(expectedPoint); MITK_TEST_CONDITION(!isInside, "Point is outside Image..."); } void CheckBounds_Image_AsSet(unsigned int DimX, unsigned int DimY, unsigned int DimZ, unsigned int DimT) { mitk::Image::Pointer image = mitk::ImageGenerator::GenerateRandomImage(DimX, DimY, DimZ, DimT,0.5,0.33,0.78,100); mitk::TimeGeometry::Pointer geometry = image->GetTimeGeometry(); mitk::BoundingBox::BoundsArrayType bound = geometry->GetBoundsInWorld(); bool isEqual = true; isEqual = isEqual && mitk::Equal(bound[0], -0.5*0.5, test_eps); isEqual = isEqual && mitk::Equal(bound[1], 29.5*0.5, test_eps); isEqual = isEqual && mitk::Equal(bound[2], -0.5*0.33, test_eps); isEqual = isEqual && mitk::Equal(bound[3], 24.5*0.33, test_eps); isEqual = isEqual && mitk::Equal(bound[4], -0.5*0.78, test_eps); isEqual = isEqual && mitk::Equal(bound[5], 19.5*0.78, test_eps); MITK_TEST_CONDITION(isEqual, "Bounds as precalculated..."); } void CheckBounds_BaseData_AsSet(mitk::BaseData* baseData, mitk::ScalarType minBoundX, mitk::ScalarType maxBoundX, mitk::ScalarType minBoundY, mitk::ScalarType maxBoundY, mitk::ScalarType minBoundZ, mitk::ScalarType maxBoundZ) { baseData->Update(); mitk::TimeGeometry::Pointer geometry = baseData->GetTimeGeometry(); mitk::BoundingBox::BoundsArrayType bound = geometry->GetBoundsInWorld(); bool isEqual = true; isEqual = isEqual && mitk::Equal(bound[0], minBoundX); isEqual = isEqual && mitk::Equal(bound[1], maxBoundX); isEqual = isEqual && mitk::Equal(bound[2], minBoundY); isEqual = isEqual && mitk::Equal(bound[3], maxBoundY); isEqual = isEqual && mitk::Equal(bound[4], minBoundZ); isEqual = isEqual && mitk::Equal(bound[5], maxBoundZ); MITK_TEST_CONDITION(isEqual, "Bounds as precalculated..."); } void CheckExtent_BaseData_AsSet(mitk::BaseData* baseData, double extentX, double extentY, double extentZ) { baseData->Update(); mitk::TimeGeometry::Pointer geometry = baseData->GetTimeGeometry(); bool isEqual = true; isEqual = isEqual && mitk::Equal(geometry->GetExtentInWorld(0), extentX, test_eps);//30*0.5); isEqual = isEqual && mitk::Equal(geometry->GetExtentInWorld(1), extentY, test_eps);//25*0.33); isEqual = isEqual && mitk::Equal(geometry->GetExtentInWorld(2), extentZ, test_eps);//20*0.78); MITK_TEST_CONDITION(isEqual, "Extent as precalculated..."); } mitk::PointSet::Pointer makePointset() { mitk::PointSet::Pointer pointSet = mitk::PointSet::New(); mitk::Point3D pointA, pointB, pointC; pointA.Fill(1); pointB.Fill(2); pointC.Fill(3); pointSet->SetPoint(1,pointA); pointSet->SetPoint(2,pointB); pointSet->SetPoint(3,pointC); pointSet->Update(); MITK_INFO<< pointSet->GetPoint(0); MITK_INFO<< pointSet->GetPoint(1); MITK_INFO<< pointSet->GetPoint(2); MITK_INFO<< pointSet->GetPoint(3); mitk::PointSet::Pointer pointSet2 = pointSet->Clone(); MITK_INFO<< pointSet2->GetPoint(0); MITK_INFO<< pointSet2->GetPoint(1); MITK_INFO<< pointSet2->GetPoint(2); MITK_INFO<< pointSet2->GetPoint(3); return pointSet; } }; int mitkTimeGeometryTest(int /*argc*/, char* /*argv*/[]) { MITK_TEST_BEGIN(mitkTimeGeometryTest); mitkTimeGeometryTestClass testClass; MITK_TEST_OUTPUT(<< "Test for 3D image"); mitk::Image::Pointer image = mitk::ImageGenerator::GenerateRandomImage(30, 25, 20, 1,0.5,0.33,0.78,100); testClass.Translation_Image_MovedOrigin(30,25,20,1); testClass.Rotate_Image_RotatedPoint(image->Clone(),30,25,20,1); testClass.Scale_Image_ScaledPoint(30,25,20,1); testClass.CountTimeSteps_Image_ReturnDimT(image->Clone(),30,25,20,1); testClass.GetMinimumTimePoint_3DImage_Min(image->Clone(),30,25,20,1); testClass.GetMaximumTimePoint_3DImage_Max(image->Clone(),30,25,20,1); testClass.GetTimeBounds_3DImage_ZeroAndDimT(image->Clone(),30,25,20,1); testClass.IsValidTimePoint_ImageValidTimePoint_True(image->Clone(),30,25,20,1); testClass.IsValidTimeStep_ImageValidTimeStep_True(image->Clone(), 30,25,20,1); testClass.IsValidTimeStep_ImageNegativInvalidTimeStep_False(image->Clone(), 30,25,20,1); testClass.IsValidTimeStep_ImageInvalidTimeStep_False(image->Clone(), 30,25,20,1); testClass.TimeStepToTimePoint_ImageValidTimeStep_TimePoint(image->Clone(), 30,25,20,1); testClass.TimeStepToTimePoint_ImageInvalidTimeStep_TimePoint(image->Clone(), 30,25,20,1); testClass.TimePointToTimeStep_ImageValidTimePoint_TimePoint(image->Clone(), 30,25,20,1); testClass.GetGeometryForTimeStep_BaseDataValidTimeStep_CorrectGeometry(image->Clone(), 3,3,3,3*0.5,3*0.33,3*0.78,1); testClass.GetGeometryForTimeStep_ImageInvalidTimeStep_NullPointer(image->Clone(), 30,25,20,1); testClass.GetGeometryForTimePoint_BaseDataValidTimePoint_CorrectGeometry(image->Clone(), 3,3,3,3*0.5,3*0.33,3*0.78,1); testClass.GetGeometryCloneForTimeStep_BaseDataValidTimeStep_CorrectGeometry(image->Clone(),1); testClass.GetGeometryCloneForTimeStep_ImageInvalidTimeStep_NullPointer(image->Clone(), 30,25,20,1); testClass.SetTimeStepGeometry_BaseDataValidTimeStep_CorrectGeometry(image->Clone(),0.5,0.33,0.78,1); testClass.Expand_BaseDataDoubleSize_SizeChanged(image->Clone(),1); testClass.CheckBounds_BaseData_PointsAsExpected(image->Clone(),-0.5*0.5,-0.5*0.33,-0.5*0.78,29.5*0.5,24.5*0.33,19.5*0.78); testClass.CheckLength_BaseData_AsExpected(image->Clone(), 23.160796233014466, 536.42248214721712); testClass.CheckPointInside_BaseDataPointInside_True(image->Clone(),10,5,5); testClass.CheckPointInside_BaseDataPointOutside_False(image->Clone(),100,500,100); testClass.CheckBounds_Image_AsSet(30,25,20,1); testClass.CheckExtent_BaseData_AsSet(image->Clone(), 30*0.5,25*0.33,20*0.78); MITK_TEST_OUTPUT(<< "Test for 2D image"); image = mitk::ImageGenerator::GenerateRandomImage(30, 25, 1, 1,0.5,0.33,0.78,100); testClass.Translation_Image_MovedOrigin(30,25,1,1); testClass.Rotate_Image_RotatedPoint(image->Clone(),30,25,1,1); testClass.Scale_Image_ScaledPoint(30,25,1,1); testClass.CountTimeSteps_Image_ReturnDimT(image->Clone(),30,25,1,1); testClass.GetMinimumTimePoint_3DImage_Min(image->Clone(),30,25,1,1); testClass.GetMaximumTimePoint_3DImage_Max(image->Clone(),30,25,1,1); testClass.GetTimeBounds_3DImage_ZeroAndDimT(image->Clone(),30,25,1,1); testClass.IsValidTimePoint_ImageValidTimePoint_True(image->Clone(),30,25,1,1); testClass.IsValidTimeStep_ImageValidTimeStep_True(image->Clone(), 30,25,1,1); testClass.IsValidTimeStep_ImageNegativInvalidTimeStep_False(image->Clone(), 30,25,1,1); testClass.IsValidTimeStep_ImageInvalidTimeStep_False(image->Clone(), 30,25,1,1); testClass.TimeStepToTimePoint_ImageValidTimeStep_TimePoint(image->Clone(), 30,25,1,1); testClass.TimeStepToTimePoint_ImageInvalidTimeStep_TimePoint(image->Clone(), 30,25,1,1); testClass.TimePointToTimeStep_ImageValidTimePoint_TimePoint(image->Clone(), 30,25,1,1); testClass.GetGeometryForTimeStep_BaseDataValidTimeStep_CorrectGeometry(image->Clone(), 3,3,3,3*0.5,3*0.33,3*0.78,1); testClass.GetGeometryForTimeStep_ImageInvalidTimeStep_NullPointer(image->Clone(), 30,25,1,1); testClass.GetGeometryForTimePoint_BaseDataValidTimePoint_CorrectGeometry(image->Clone(), 3,3,3,3*0.5,3*0.33,3*0.78,1); testClass.GetGeometryCloneForTimeStep_BaseDataValidTimeStep_CorrectGeometry(image->Clone(),1); testClass.GetGeometryCloneForTimeStep_ImageInvalidTimeStep_NullPointer(image->Clone(), 30,25,1,1); testClass.SetTimeStepGeometry_BaseDataValidTimeStep_CorrectGeometry(image->Clone(),0.5,0.33,0.78,1); testClass.Expand_BaseDataDoubleSize_SizeChanged(image->Clone(),1); testClass.CheckBounds_BaseData_PointsAsExpected(image->Clone(),-0.5*0.5,-0.5*0.33,-0.5*0.78,29.5*0.5,24.5*0.33,0.5*0.78); testClass.CheckLength_BaseData_AsExpected(image->Clone(), 17.1368287615, 293.6709); testClass.CheckPointInside_BaseDataPointInside_True(image->Clone(),10,5,0); testClass.CheckPointInside_BaseDataPointOutside_False(image->Clone(),100,500,0.5); testClass.CheckExtent_BaseData_AsSet(image->Clone(), 30*0.5,25*0.33,1*0.78); MITK_TEST_OUTPUT(<< "Test for 3D+time image"); image = mitk::ImageGenerator::GenerateRandomImage(30, 25, 20, 5,0.5,0.33,0.78,100); testClass.Translation_Image_MovedOrigin(30,25,20,5); // Test with 3D+t-Image testClass.Rotate_Image_RotatedPoint(image->Clone(),30,25,20,5); // Test with 3D+t-Image testClass.Scale_Image_ScaledPoint(30,25,20,5); // Test with 3D+t-Image testClass.CountTimeSteps_Image_ReturnDimT(image->Clone(),30,25,20,5); testClass.GetMinimumTimePoint_4DBaseData_Zero(image->Clone(),5); testClass.GetMaximumTimePoint_4DBaseData_DimT(image->Clone(),5); testClass.GetTimeBounds_4DImage_ZeroAndDimT(30,25,20,5); testClass.IsValidTimePoint_ImageValidTimePoint_True(image->Clone(),30,25,20,5); testClass.IsValidTimePoint_ImageNegativInvalidTimePoint_False(30,25,20,5); testClass.IsValidTimePoint_ImageInvalidTimePoint_False(30,25,20,5); testClass.IsValidTimeStep_ImageValidTimeStep_True(image->Clone(), 30,25,20,5); testClass.IsValidTimeStep_ImageNegativInvalidTimeStep_False(image->Clone(), 30,25,20,5); testClass.IsValidTimeStep_ImageInvalidTimeStep_False(image->Clone(), 30,25,20,5); testClass.TimeStepToTimePoint_ImageValidTimeStep_TimePoint(image->Clone(), 30,25,20,5); testClass.TimeStepToTimePoint_ImageInvalidTimeStep_TimePoint(image->Clone(), 30,25,20,5); testClass.TimePointToTimeStep_ImageValidTimePoint_TimePoint(image->Clone(), 30,25,20,5); testClass.TimePointToTimeStep_4DImageInvalidTimePoint_TimePoint(30,25,20,5); testClass.TimePointToTimeStep_4DImageNegativInvalidTimePoint_TimePoint(30,25,20,5); testClass.GetGeometryForTimeStep_BaseDataValidTimeStep_CorrectGeometry(image->Clone(), 3,3,3,3*0.5,3*0.33,3*0.78,5); testClass.GetGeometryForTimeStep_ImageInvalidTimeStep_NullPointer(image->Clone(), 30,25,20,5); testClass.GetGeometryForTimePoint_BaseDataValidTimePoint_CorrectGeometry(image->Clone(), 3,3,3,3*0.5,3*0.33,3*0.78,5); testClass.GetGeometryForTimePoint_4DImageInvalidTimePoint_NullPointer(30,25,20,5); testClass.GetGeometryForTimePoint_4DImageNEgativInvalidTimePoint_NullPointer(30,25,20,5); testClass.GetGeometryCloneForTimeStep_BaseDataValidTimeStep_CorrectGeometry(image->Clone(),5); testClass.GetGeometryCloneForTimeStep_ImageInvalidTimeStep_NullPointer(image->Clone(), 30,25,20,5); testClass.SetTimeStepGeometry_BaseDataValidTimeStep_CorrectGeometry(image->Clone(),0.5,0.33,0.78,5); testClass.Expand_BaseDataDoubleSize_SizeChanged(image->Clone(),5); testClass.CheckBounds_BaseData_PointsAsExpected(image->Clone(),-0.5*0.5,-0.5*0.33,-0.5*0.78,29.5*0.5,24.5*0.33,19.5*0.78); testClass.CheckLength_BaseData_AsExpected(image->Clone(), 23.160796233014466, 536.42248214721712); testClass.CheckPointInside_BaseDataPointInside_True(image->Clone(),10,5,5); testClass.CheckPointInside_BaseDataPointOutside_False(image->Clone(), 100,100,500); testClass.CheckBounds_Image_AsSet(30,25,20,5); testClass.CheckExtent_BaseData_AsSet(image->Clone(), 30*0.5,25*0.33,20*0.78); /* MITK_TEST_OUTPUT(<< "Test for 2D+time image"); testClass.Translation_Image_MovedOrigin(30,25,1 ,5); // Test with 2D+t-Image testClass.Rotate_Image_RotatedPoint(30,25,1 ,5); // Test with 2D+t-Image testClass.Scale_Image_ScaledPoint(30,25,1 ,5); // Test with 2D+t-Image */ mitk::PointSet::Pointer pointSet = mitk::PointSet::New(); mitk::Point3D pointA, pointB, pointC; pointA.Fill(1); pointB.Fill(2); pointC.Fill(3); pointSet->SetPoint(0,pointA); pointSet->SetPoint(1,pointB); pointSet->SetPoint(2,pointC); testClass.CountTimeSteps_Image_ReturnDimT(pointSet->Clone(),30,25,20,1); //testClass.GetMinimumTimePoint_3DImage_Min(pointSet->Clone(),30,25,20,1); //testClass.GetMaximumTimePoint_3DImage_Max(pointSet->Clone(),30,25,20,1); //testClass.GetTimeBounds_3DImage_ZeroAndDimT(pointSet->Clone(),30,25,20,1); testClass.IsValidTimePoint_ImageValidTimePoint_True(pointSet->Clone(),30,25,20,1); testClass.IsValidTimeStep_ImageValidTimeStep_True(pointSet->Clone(),30,25,20,1); testClass.IsValidTimeStep_ImageNegativInvalidTimeStep_False(pointSet->Clone(),30,25,20,1); testClass.IsValidTimeStep_ImageInvalidTimeStep_False(pointSet->Clone(),30,25,20,1); testClass.TimeStepToTimePoint_ImageValidTimeStep_TimePoint(pointSet->Clone(),30,25,20,1); testClass.TimeStepToTimePoint_ImageInvalidTimeStep_TimePoint(pointSet->Clone(),30,25,20,1); testClass.TimePointToTimeStep_ImageValidTimePoint_TimePoint(pointSet->Clone(),30,25,20,1); testClass.GetGeometryForTimeStep_BaseDataValidTimeStep_CorrectGeometry(pointSet->Clone(), 3,3,3,3,3,3,1); testClass.GetGeometryForTimeStep_ImageInvalidTimeStep_NullPointer(pointSet->Clone(), 30,25,20,1); testClass.GetGeometryForTimePoint_BaseDataValidTimePoint_CorrectGeometry(pointSet->Clone(), 3,3,3,3,3,3,1); testClass.GetGeometryCloneForTimeStep_BaseDataValidTimeStep_CorrectGeometry(pointSet->Clone(),1); testClass.GetGeometryCloneForTimeStep_ImageInvalidTimeStep_NullPointer(pointSet->Clone(), 30,25,20,1); testClass.SetTimeStepGeometry_BaseDataValidTimeStep_CorrectGeometry(pointSet->Clone(), 1,1,1,1); testClass.Expand_BaseDataDoubleSize_SizeChanged(pointSet->Clone(),1); testClass.CheckBounds_BaseData_PointsAsExpected(pointSet->Clone(),1,1,1,3,3,3); testClass.CheckLength_BaseData_AsExpected(pointSet->Clone(),3.46410161,12); testClass.CheckPointInside_BaseDataPointInside_True(pointSet->Clone(),2,2,3); testClass.CheckPointInside_BaseDataPointOutside_False(pointSet->Clone(),4,5,1); testClass.CheckBounds_BaseData_AsSet(pointSet->Clone(),1,3,1,3,1,3); testClass.CheckExtent_BaseData_AsSet(pointSet->Clone(),2,2,2 ); MITK_TEST_END(); return EXIT_SUCCESS; } diff --git a/Core/Code/files.cmake b/Core/Code/files.cmake index 48d311471f..8c53bd7ac0 100644 --- a/Core/Code/files.cmake +++ b/Core/Code/files.cmake @@ -1,405 +1,404 @@ set(H_FILES Algorithms/itkImportMitkImageContainer.h Algorithms/itkImportMitkImageContainer.txx Algorithms/itkMITKScalarImageToHistogramGenerator.h Algorithms/itkMITKScalarImageToHistogramGenerator.txx Algorithms/mitkInstantiateAccessFunctions.h Algorithms/mitkPixelTypeList.h Algorithms/mitkPPArithmeticDec.h Algorithms/mitkPPArgCount.h Algorithms/mitkPPCat.h Algorithms/mitkPPConfig.h Algorithms/mitkPPControlExprIIf.h Algorithms/mitkPPControlIf.h Algorithms/mitkPPControlIIf.h Algorithms/mitkPPDebugError.h Algorithms/mitkPPDetailAutoRec.h Algorithms/mitkPPDetailDMCAutoRec.h Algorithms/mitkPPExpand.h Algorithms/mitkPPFacilitiesEmpty.h Algorithms/mitkPPFacilitiesExpand.h Algorithms/mitkPPLogicalBool.h Algorithms/mitkPPRepetitionDetailDMCFor.h Algorithms/mitkPPRepetitionDetailEDGFor.h Algorithms/mitkPPRepetitionDetailFor.h Algorithms/mitkPPRepetitionDetailMSVCFor.h Algorithms/mitkPPRepetitionFor.h Algorithms/mitkPPSeqElem.h Algorithms/mitkPPSeqForEach.h Algorithms/mitkPPSeqForEachProduct.h Algorithms/mitkPPSeq.h Algorithms/mitkPPSeqEnum.h Algorithms/mitkPPSeqSize.h Algorithms/mitkPPSeqToTuple.h Algorithms/mitkPPStringize.h Algorithms/mitkPPTupleEat.h Algorithms/mitkPPTupleElem.h Algorithms/mitkPPTupleRem.h Algorithms/mitkClippedSurfaceBoundsCalculator.h Algorithms/mitkExtractSliceFilter.h Algorithms/mitkConvert2Dto3DImageFilter.h Algorithms/mitkPlaneClipping.h Common/mitkCommon.h Common/mitkExceptionMacro.h DataManagement/mitkProportionalTimeGeometry.h DataManagement/mitkTimeGeometry.h DataManagement/mitkImageAccessByItk.h DataManagement/mitkImageCast.h DataManagement/mitkImagePixelAccessor.h DataManagement/mitkImagePixelReadAccessor.h DataManagement/mitkImagePixelWriteAccessor.h DataManagement/mitkImageReadAccessor.h DataManagement/mitkImageWriteAccessor.h DataManagement/mitkITKImageImport.h DataManagement/mitkITKImageImport.txx DataManagement/mitkImageToItk.h DataManagement/mitkShaderProperty.h DataManagement/mitkImageToItk.txx DataManagement/mitkTimeSlicedGeometry.h # Deprecated, empty for compatibilty reasons. DataManagement/mitkPropertyListReplacedObserver.cpp Interactions/mitkEventMapperAddOn.h Interfaces/mitkIDataNodeReader.h Rendering/mitkLocalStorageHandler.h Rendering/Colortables/HotIron.h Rendering/Colortables/Jet.h Rendering/Colortables/PET20.h Rendering/Colortables/PETColor.h IO/mitkPixelTypeTraits.h ) set(CPP_FILES Algorithms/mitkBaseDataSource.cpp Algorithms/mitkCompareImageDataFilter.cpp Algorithms/mitkMultiComponentImageDataComparisonFilter.cpp Algorithms/mitkDataNodeSource.cpp Algorithms/mitkGeometry2DDataToSurfaceFilter.cpp Algorithms/mitkHistogramGenerator.cpp Algorithms/mitkImageChannelSelector.cpp Algorithms/mitkImageSliceSelector.cpp Algorithms/mitkImageSource.cpp Algorithms/mitkImageTimeSelector.cpp Algorithms/mitkImageToImageFilter.cpp Algorithms/mitkImageToSurfaceFilter.cpp Algorithms/mitkPointSetSource.cpp Algorithms/mitkPointSetToPointSetFilter.cpp Algorithms/mitkRGBToRGBACastImageFilter.cpp Algorithms/mitkSubImageSelector.cpp Algorithms/mitkSurfaceSource.cpp Algorithms/mitkSurfaceToImageFilter.cpp Algorithms/mitkSurfaceToSurfaceFilter.cpp Algorithms/mitkUIDGenerator.cpp Algorithms/mitkVolumeCalculator.cpp Algorithms/mitkClippedSurfaceBoundsCalculator.cpp Algorithms/mitkExtractSliceFilter.cpp Algorithms/mitkConvert2Dto3DImageFilter.cpp Controllers/mitkBaseController.cpp Controllers/mitkCallbackFromGUIThread.cpp Controllers/mitkCameraController.cpp Controllers/mitkCameraRotationController.cpp Controllers/mitkCoreActivator.cpp Controllers/mitkFocusManager.cpp Controllers/mitkLimitedLinearUndo.cpp Controllers/mitkOperationEvent.cpp Controllers/mitkPlanePositionManager.cpp Controllers/mitkProgressBar.cpp Controllers/mitkRenderingManager.cpp Controllers/mitkSliceNavigationController.cpp Controllers/mitkSlicesCoordinator.cpp Controllers/mitkSlicesRotator.cpp Controllers/mitkSlicesSwiveller.cpp Controllers/mitkStatusBar.cpp Controllers/mitkStepper.cpp Controllers/mitkTestManager.cpp Controllers/mitkUndoController.cpp Controllers/mitkVerboseLimitedLinearUndo.cpp Controllers/mitkVtkInteractorCameraController.cpp Controllers/mitkVtkLayerController.cpp DataManagement/mitkProportionalTimeGeometry.cpp DataManagement/mitkTimeGeometry.cpp DataManagement/mitkAbstractTransformGeometry.cpp DataManagement/mitkAnnotationProperty.cpp DataManagement/mitkApplicationCursor.cpp DataManagement/mitkBaseData.cpp + DataManagement/mitkBaseGeometry.cpp DataManagement/mitkBaseProperty.cpp DataManagement/mitkClippingProperty.cpp DataManagement/mitkChannelDescriptor.cpp DataManagement/mitkColorProperty.cpp DataManagement/mitkDataStorage.cpp # DataManagement/mitkDataTree.cpp DataManagement/mitkDataNode.cpp DataManagement/mitkDataNodeFactory.cpp # DataManagement/mitkDataTreeStorage.cpp DataManagement/mitkDisplayGeometry.cpp DataManagement/mitkEnumerationProperty.cpp - DataManagement/mitkGeometry2D.cpp DataManagement/mitkGeometry2DData.cpp DataManagement/mitkGeometry3D.cpp DataManagement/mitkGeometryData.cpp DataManagement/mitkGroupTagProperty.cpp DataManagement/mitkImage.cpp DataManagement/mitkImageAccessorBase.cpp DataManagement/mitkImageCaster.cpp DataManagement/mitkImageCastPart1.cpp DataManagement/mitkImageCastPart2.cpp DataManagement/mitkImageCastPart3.cpp DataManagement/mitkImageCastPart4.cpp DataManagement/mitkImageDataItem.cpp DataManagement/mitkImageDescriptor.cpp DataManagement/mitkImageVtkAccessor.cpp DataManagement/mitkImageStatisticsHolder.cpp - DataManagement/mitkLandmarkBasedCurvedGeometry.cpp DataManagement/mitkLandmarkProjectorBasedCurvedGeometry.cpp DataManagement/mitkLandmarkProjector.cpp DataManagement/mitkLevelWindow.cpp DataManagement/mitkLevelWindowManager.cpp DataManagement/mitkLevelWindowPreset.cpp DataManagement/mitkLevelWindowProperty.cpp DataManagement/mitkLookupTable.cpp DataManagement/mitkLookupTables.cpp # specializations of GenericLookupTable DataManagement/mitkMemoryUtilities.cpp DataManagement/mitkModalityProperty.cpp DataManagement/mitkModeOperation.cpp DataManagement/mitkNodePredicateAnd.cpp DataManagement/mitkNodePredicateBase.cpp DataManagement/mitkNodePredicateCompositeBase.cpp DataManagement/mitkNodePredicateData.cpp DataManagement/mitkNodePredicateDataType.cpp DataManagement/mitkNodePredicateDimension.cpp DataManagement/mitkNodePredicateFirstLevel.cpp DataManagement/mitkNodePredicateNot.cpp DataManagement/mitkNodePredicateOr.cpp DataManagement/mitkNodePredicateProperty.cpp DataManagement/mitkNodePredicateSource.cpp DataManagement/mitkPlaneOrientationProperty.cpp DataManagement/mitkPlaneGeometry.cpp DataManagement/mitkPlaneOperation.cpp DataManagement/mitkPointOperation.cpp DataManagement/mitkPointSet.cpp DataManagement/mitkProperties.cpp DataManagement/mitkPropertyList.cpp DataManagement/mitkPropertyObserver.cpp DataManagement/mitkRestorePlanePositionOperation.cpp DataManagement/mitkApplyTransformMatrixOperation.cpp DataManagement/mitkRotationOperation.cpp DataManagement/mitkSlicedData.cpp DataManagement/mitkSlicedGeometry3D.cpp DataManagement/mitkSmartPointerProperty.cpp DataManagement/mitkStandaloneDataStorage.cpp DataManagement/mitkStateTransitionOperation.cpp DataManagement/mitkStringProperty.cpp DataManagement/mitkSurface.cpp DataManagement/mitkSurfaceOperation.cpp DataManagement/mitkThinPlateSplineCurvedGeometry.cpp DataManagement/mitkTransferFunction.cpp DataManagement/mitkTransferFunctionProperty.cpp DataManagement/mitkTransferFunctionInitializer.cpp DataManagement/mitkVector.cpp DataManagement/mitkVtkInterpolationProperty.cpp DataManagement/mitkVtkRepresentationProperty.cpp DataManagement/mitkVtkResliceInterpolationProperty.cpp DataManagement/mitkVtkScalarModeProperty.cpp DataManagement/mitkVtkVolumeRenderingProperty.cpp DataManagement/mitkWeakPointerProperty.cpp DataManagement/mitkRenderingModeProperty.cpp DataManagement/mitkResliceMethodProperty.cpp DataManagement/mitkMaterial.cpp DataManagement/mitkPointSetShapeProperty.cpp DataManagement/mitkFloatPropertyExtension.cpp DataManagement/mitkIntPropertyExtension.cpp DataManagement/mitkPropertyExtension.cpp DataManagement/mitkPropertyFilter.cpp DataManagement/mitkPropertyAliases.cpp DataManagement/mitkPropertyDescriptions.cpp DataManagement/mitkPropertyExtensions.cpp DataManagement/mitkPropertyFilters.cpp DataManagement/mitkShaderProperty.cpp Interactions/mitkAction.cpp Interactions/mitkAffineInteractor.cpp Interactions/mitkBindDispatcherInteractor.cpp Interactions/mitkCoordinateSupplier.cpp Interactions/mitkDataInteractor.cpp Interactions/mitkDispatcher.cpp Interactions/mitkDisplayCoordinateOperation.cpp Interactions/mitkDisplayInteractor.cpp Interactions/mitkDisplayPositionEvent.cpp # Interactions/mitkDisplayVectorInteractorLevelWindow.cpp # legacy, prob even now unneeded # Interactions/mitkDisplayVectorInteractorScroll.cpp Interactions/mitkEvent.cpp Interactions/mitkEventConfig.cpp Interactions/mitkEventDescription.cpp Interactions/mitkEventFactory.cpp Interactions/mitkInteractionEventHandler.cpp Interactions/mitkEventMapper.cpp Interactions/mitkEventRecorder.cpp Interactions/mitkEventStateMachine.cpp Interactions/mitkGlobalInteraction.cpp Interactions/mitkInteractor.cpp Interactions/mitkInternalEvent.cpp Interactions/mitkInteractionEvent.cpp Interactions/mitkInteractionEventConst.cpp Interactions/mitkInteractionPositionEvent.cpp Interactions/mitkInteractionKeyEvent.cpp Interactions/mitkMousePressEvent.cpp Interactions/mitkMouseMoveEvent.cpp Interactions/mitkMouseReleaseEvent.cpp Interactions/mitkMouseWheelEvent.cpp Interactions/mitkMouseDoubleClickEvent.cpp Interactions/mitkMouseModeSwitcher.cpp Interactions/mitkMouseMovePointSetInteractor.cpp Interactions/mitkMoveBaseDataInteractor.cpp Interactions/mitkNodeDepententPointSetInteractor.cpp Interactions/mitkPointSetDataInteractor.cpp Interactions/mitkPointSetInteractor.cpp Interactions/mitkPositionEvent.cpp Interactions/mitkPositionTracker.cpp Interactions/mitkStateMachineAction.cpp Interactions/mitkStateMachineCondition.cpp Interactions/mitkStateMachineState.cpp Interactions/mitkStateMachineTransition.cpp Interactions/mitkState.cpp Interactions/mitkStateMachineContainer.cpp Interactions/mitkStateEvent.cpp Interactions/mitkStateMachine.cpp Interactions/mitkStateMachineFactory.cpp Interactions/mitkTransition.cpp Interactions/mitkWheelEvent.cpp Interactions/mitkKeyEvent.cpp Interactions/mitkVtkEventAdapter.cpp Interactions/mitkVtkInteractorStyle.cxx Interactions/mitkCrosshairPositionEvent.cpp Interactions/mitkXML2EventParser.cpp Interfaces/mitkInteractionEventObserver.cpp Interfaces/mitkIShaderRepository.cpp Interfaces/mitkIPropertyAliases.cpp Interfaces/mitkIPropertyDescriptions.cpp Interfaces/mitkIPropertyExtensions.cpp Interfaces/mitkIPropertyFilters.cpp Interfaces/mitkIPersistenceService.cpp IO/mitkBaseDataIOFactory.cpp IO/mitkCoreDataNodeReader.cpp IO/mitkDicomSeriesReader.cpp IO/mitkDicomSR_LoadDICOMScalar.cpp IO/mitkDicomSR_LoadDICOMScalar4D.cpp IO/mitkDicomSR_LoadDICOMRGBPixel.cpp IO/mitkDicomSR_LoadDICOMRGBPixel4D.cpp IO/mitkDicomSR_ImageBlockDescriptor.cpp IO/mitkDicomSR_GantryTiltInformation.cpp IO/mitkDicomSR_SliceGroupingResult.cpp IO/mitkFileReader.cpp IO/mitkFileSeriesReader.cpp IO/mitkFileWriter.cpp # IO/mitkIpPicGet.c IO/mitkImageGenerator.cpp IO/mitkImageWriter.cpp IO/mitkImageWriterFactory.cpp IO/mitkItkImageFileIOFactory.cpp IO/mitkItkImageFileReader.cpp IO/mitkItkLoggingAdapter.cpp IO/mitkItkPictureWrite.cpp IO/mitkIOUtil.cpp IO/mitkLookupTableProperty.cpp IO/mitkOperation.cpp # IO/mitkPicFileIOFactory.cpp # IO/mitkPicFileReader.cpp # IO/mitkPicFileWriter.cpp # IO/mitkPicHelper.cpp # IO/mitkPicVolumeTimeSeriesIOFactory.cpp # IO/mitkPicVolumeTimeSeriesReader.cpp IO/mitkPixelType.cpp IO/mitkPointSetIOFactory.cpp IO/mitkPointSetReader.cpp IO/mitkPointSetWriter.cpp IO/mitkPointSetWriterFactory.cpp IO/mitkRawImageFileReader.cpp IO/mitkStandardFileLocations.cpp IO/mitkSTLFileIOFactory.cpp IO/mitkSTLFileReader.cpp IO/mitkSurfaceVtkWriter.cpp IO/mitkSurfaceVtkWriterFactory.cpp IO/mitkVtkLoggingAdapter.cpp IO/mitkVtiFileIOFactory.cpp IO/mitkVtiFileReader.cpp IO/mitkVtkImageIOFactory.cpp IO/mitkVtkImageReader.cpp IO/mitkVtkSurfaceIOFactory.cpp IO/mitkVtkSurfaceReader.cpp IO/vtkPointSetXMLParser.cpp IO/mitkLog.cpp Rendering/mitkBaseRenderer.cpp Rendering/mitkVtkMapper.cpp Rendering/mitkRenderWindowFrame.cpp Rendering/mitkGeometry2DDataMapper2D.cpp Rendering/mitkGeometry2DDataVtkMapper3D.cpp Rendering/mitkGLMapper.cpp Rendering/mitkGradientBackground.cpp Rendering/mitkManufacturerLogo.cpp Rendering/mitkMapper.cpp Rendering/mitkPointSetGLMapper2D.cpp Rendering/mitkPointSetVtkMapper2D.cpp Rendering/mitkPointSetVtkMapper3D.cpp Rendering/mitkSurfaceGLMapper2D.cpp Rendering/mitkSurfaceVtkMapper3D.cpp Rendering/mitkVolumeDataVtkMapper3D.cpp Rendering/mitkVtkPropRenderer.cpp Rendering/mitkVtkWidgetRendering.cpp Rendering/vtkMitkRectangleProp.cpp Rendering/vtkMitkRenderProp.cpp Rendering/mitkVtkEventProvider.cpp Rendering/mitkRenderWindow.cpp Rendering/mitkRenderWindowBase.cpp Rendering/mitkImageVtkMapper2D.cpp Rendering/vtkMitkThickSlicesFilter.cpp Rendering/vtkMitkLevelWindowFilter.cpp Rendering/vtkNeverTranslucentTexture.cpp Rendering/mitkOverlay.cpp Rendering/mitkVtkOverlay.cpp Rendering/mitkVtkOverlay2D.cpp Rendering/mitkVtkOverlay3D.cpp Rendering/mitkOverlayManager.cpp Rendering/mitkAbstractOverlayLayouter.cpp Rendering/mitkTextOverlay2D.cpp Rendering/mitkTextOverlay3D.cpp Rendering/mitkLabelOverlay3D.cpp Rendering/mitkOverlay2DLayouter.cpp Rendering/mitkScaleLegendOverlay Common/mitkException.cpp Common/mitkCommon.h Common/mitkCoreObjectFactoryBase.cpp Common/mitkCoreObjectFactory.cpp Common/mitkCoreServices.cpp ) set(RESOURCE_FILES Interactions/globalConfig.xml Interactions/DisplayInteraction.xml Interactions/DisplayConfig.xml Interactions/DisplayConfigPACS.xml Interactions/DisplayConfigPACSPan.xml Interactions/DisplayConfigPACSScroll.xml Interactions/DisplayConfigPACSZoom.xml Interactions/DisplayConfigPACSLevelWindow.xml Interactions/DisplayConfigMITK.xml Interactions/PointSet.xml Interactions/Legacy/StateMachine.xml Interactions/Legacy/DisplayConfigMITKTools.xml Interactions/PointSetConfig.xml mitkLevelWindowPresets.xml ) diff --git a/Modules/AlgorithmsExt/mitkBoundingObjectCutter.cpp b/Modules/AlgorithmsExt/mitkBoundingObjectCutter.cpp index d09fb325eb..5cc238802e 100644 --- a/Modules/AlgorithmsExt/mitkBoundingObjectCutter.cpp +++ b/Modules/AlgorithmsExt/mitkBoundingObjectCutter.cpp @@ -1,226 +1,226 @@ /*=================================================================== 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 "mitkBoundingObjectCutter.h" #include "mitkBoundingObjectCutter.txx" #include "mitkTimeHelper.h" #include "mitkImageAccessByItk.h" #include "mitkBoundingObject.h" #include "mitkGeometry3D.h" #include namespace mitk { void BoundingObjectCutter::SetBoundingObject( const mitk::BoundingObject* boundingObject ) { m_BoundingObject = const_cast(boundingObject); // Process object is not const-correct so the const_cast is required here this->ProcessObject::SetNthInput(1, const_cast< mitk::BoundingObject * >( boundingObject ) ); } const mitk::BoundingObject* BoundingObjectCutter::GetBoundingObject() const { return m_BoundingObject.GetPointer(); } BoundingObjectCutter::BoundingObjectCutter() : m_BoundingObject(NULL), m_InsideValue(1), m_OutsideValue(0), m_AutoOutsideValue(false), m_UseInsideValue(false), m_OutsidePixelCount(0), m_InsidePixelCount(0), m_UseWholeInputRegion(false) { this->SetNumberOfIndexedInputs(2); this->SetNumberOfRequiredInputs(2); m_InputTimeSelector = mitk::ImageTimeSelector::New(); m_OutputTimeSelector = mitk::ImageTimeSelector::New(); } BoundingObjectCutter::~BoundingObjectCutter() { } const mitk::PixelType BoundingObjectCutter::GetOutputPixelType() { return this->GetInput()->GetPixelType(); } void BoundingObjectCutter::GenerateInputRequestedRegion() { mitk::Image* output = this->GetOutput(); if((output->IsInitialized()==false) || (m_BoundingObject.IsNull()) || (m_BoundingObject->GetTimeGeometry()->CountTimeSteps() == 0)) return; // we have already calculated the spatial part of the // input-requested-region in m_InputRequestedRegion in // GenerateOutputInformation (which is called before // GenerateInputRequestedRegion). GenerateTimeInInputRegion(output, const_cast< mitk::Image * > ( this->GetInput() )); GenerateTimeInInputRegion(output, m_BoundingObject.GetPointer()); } void BoundingObjectCutter::GenerateOutputInformation() { mitk::Image::Pointer output = this->GetOutput(); if ((output->IsInitialized()) && (output->GetPipelineMTime() <= m_TimeOfHeaderInitialization.GetMTime())) return; mitk::Image::Pointer input = const_cast< mitk::Image * > ( this->GetInput() ); if(input.IsNull()) { MITK_WARN << "Input is not a mitk::Image"; return; } itkDebugMacro(<<"GenerateOutputInformation()"); unsigned int dimension = input->GetDimension(); if (dimension < 3) { MITK_WARN << "ImageCropper cannot handle 1D or 2D Objects. Operation aborted."; return; } if((m_BoundingObject.IsNull()) || (m_BoundingObject->GetTimeGeometry()->CountTimeSteps() == 0)) return; - mitk::Geometry3D* boGeometry = m_BoundingObject->GetGeometry(); - mitk::Geometry3D* inputImageGeometry = input->GetSlicedGeometry(); + mitk::BaseGeometry* boGeometry = m_BoundingObject->GetGeometry(); + mitk::BaseGeometry* inputImageGeometry = input->GetSlicedGeometry(); // calculate bounding box of bounding-object relative to the geometry // of the input image. The result is in pixel coordinates of the input // image (because the m_IndexToWorldTransform includes the spacing). mitk::BoundingBox::Pointer boBoxRelativeToImage = boGeometry->CalculateBoundingBoxRelativeToTransform( inputImageGeometry->GetIndexToWorldTransform() ); // PART I: initialize input requested region. We do this already here (and not // later when GenerateInputRequestedRegion() is called), because we // also need the information to setup the output. // pre-initialize input-requested-region to largest-possible-region // and correct time-region; spatial part will be cropped by // bounding-box of bounding-object below m_InputRequestedRegion = input->GetLargestPossibleRegion(); // build region out of bounding-box of bounding-object mitk::SlicedData::IndexType index=m_InputRequestedRegion.GetIndex(); //init times and channels mitk::BoundingBox::PointType min = boBoxRelativeToImage->GetMinimum(); index[0] = (mitk::SlicedData::IndexType::IndexValueType)(std::ceil(min[0])); index[1] = (mitk::SlicedData::IndexType::IndexValueType)(std::ceil(min[1])); index[2] = (mitk::SlicedData::IndexType::IndexValueType)(std::ceil(min[2])); mitk::SlicedData::SizeType size = m_InputRequestedRegion.GetSize(); //init times and channels mitk::BoundingBox::PointType max = boBoxRelativeToImage->GetMaximum(); size[0] = (mitk::SlicedData::SizeType::SizeValueType)(std::ceil(max[0])-index[0]); size[1] = (mitk::SlicedData::SizeType::SizeValueType)(std::ceil(max[1])-index[1]); size[2] = (mitk::SlicedData::SizeType::SizeValueType)(std::ceil(max[2])-index[2]); mitk::SlicedData::RegionType boRegion(index, size); if(m_UseWholeInputRegion == false) { // crop input-requested-region with region of bounding-object if(m_InputRequestedRegion.Crop(boRegion)==false) { // crop not possible => do nothing: set time size to 0. size.Fill(0); m_InputRequestedRegion.SetSize(size); boRegion.SetSize(size); m_BoundingObject->SetRequestedRegion(&boRegion); return; } } // set input-requested-region, because we access it later in // GenerateInputRequestedRegion (there we just set the time) input->SetRequestedRegion(&m_InputRequestedRegion); // PART II: initialize output image unsigned int *dimensions = new unsigned int [dimension]; itk2vtk(m_InputRequestedRegion.GetSize(), dimensions); if(dimension>3) memcpy(dimensions+3, input->GetDimensions()+3, (dimension-3)*sizeof(unsigned int)); output->Initialize(mitk::PixelType(GetOutputPixelType()), dimension, dimensions); delete [] dimensions; // now we have everything to initialize the transform of the output mitk::SlicedGeometry3D* slicedGeometry = output->GetSlicedGeometry(); // set the transform: use the transform of the input; // the origin will be replaced afterwards AffineTransform3D::Pointer indexToWorldTransform = AffineTransform3D::New(); indexToWorldTransform->SetParameters(input->GetSlicedGeometry()->GetIndexToWorldTransform()->GetParameters()); slicedGeometry->SetIndexToWorldTransform(indexToWorldTransform); // Position the output Image to match the corresponding region of the input image const mitk::SlicedData::IndexType& start = m_InputRequestedRegion.GetIndex(); mitk::Point3D origin; vtk2itk(start, origin); inputImageGeometry->IndexToWorld(origin, origin); slicedGeometry->SetOrigin(origin); m_TimeOfHeaderInitialization.Modified(); } void BoundingObjectCutter::ComputeData(mitk::Image* input3D, int boTimeStep) { AccessFixedDimensionByItk_2(input3D, CutImage, 3, this, boTimeStep); } void BoundingObjectCutter::GenerateData() { mitk::Image::ConstPointer input = this->GetInput(); mitk::Image::Pointer output = this->GetOutput(); if(input.IsNull()) return; if((output->IsInitialized()==false) || (m_BoundingObject.IsNull()) || (m_BoundingObject->GetTimeGeometry()->CountTimeSteps() == 0)) return; m_InputTimeSelector->SetInput(input); m_OutputTimeSelector->SetInput(this->GetOutput()); mitk::Surface::RegionType outputRegion = output->GetRequestedRegion(); const mitk::TimeGeometry *outputTimeGeometry = output->GetTimeGeometry(); const mitk::TimeGeometry *inputTimeGeometry = input->GetTimeGeometry(); const mitk::TimeGeometry *boundingObjectTimeGeometry = m_BoundingObject->GetTimeGeometry(); TimePointType 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(); timestep = boundingObjectTimeGeometry->TimePointToTimeStep( timeInMS ); ComputeData(m_InputTimeSelector->GetOutput(), timestep); } m_InputTimeSelector->SetInput(NULL); m_OutputTimeSelector->SetInput(NULL); m_TimeOfHeaderInitialization.Modified(); } } // of namespace mitk diff --git a/Modules/AlgorithmsExt/mitkBoundingObjectCutter.txx b/Modules/AlgorithmsExt/mitkBoundingObjectCutter.txx index 9c602b2a60..a1062d802a 100644 --- a/Modules/AlgorithmsExt/mitkBoundingObjectCutter.txx +++ b/Modules/AlgorithmsExt/mitkBoundingObjectCutter.txx @@ -1,151 +1,151 @@ /*=================================================================== 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 MITKBOUNDINGOBJECTCUTTER_TXX #define MITKBOUNDINGOBJECTCUTTER_TXX #include "mitkStatusBar.h" #include "mitkImageToItk.h" #include "itkImageRegionIteratorWithIndex.h" namespace mitk { template < typename TPixel, unsigned int VImageDimension, typename TOutputPixel > void CutImageWithOutputTypeSelect ( itk::Image* inputItkImage, mitk::BoundingObjectCutter* cutter, int /* boTimeStep */, TOutputPixel* /* dummy */) { typedef itk::Image ItkInputImageType; typedef itk::Image ItkOutputImageType; typedef typename itk::ImageBase::RegionType ItkRegionType; typedef itk::ImageRegionIteratorWithIndex< ItkInputImageType > ItkInputImageIteratorType; typedef itk::ImageRegionIteratorWithIndex< ItkOutputImageType > ItkOutputImageIteratorType; if(cutter->m_BoundingObject.IsNull()) return; if (inputItkImage == NULL) { mitk::StatusBar::GetInstance()->DisplayErrorText ("An internal error occurred. Can't convert Image. Please report to bugs@mitk.org"); std::cout << " image is NULL...returning" << std::endl; return; } // PART 1: convert m_InputRequestedReg ion (type mitk::SlicedData::RegionType) // into ITK-image-region (ItkImageType::RegionType) // unfortunately, we cannot use input->GetRequestedRegion(), because it // has been destroyed by the mitk::CastToItkImage call of PART 1 // (which sets the m_RequestedRegion to the LargestPossibleRegion). // Thus, use our own member m_InputRequestedRegion insead. // first convert the index typename ItkRegionType::IndexType::IndexValueType tmpIndex[3]; itk2vtk(cutter->m_InputRequestedRegion.GetIndex(), tmpIndex); typename ItkRegionType::IndexType index; index.SetIndex(tmpIndex); // then convert the size typename ItkRegionType::SizeType::SizeValueType tmpSize[3]; itk2vtk(cutter->m_InputRequestedRegion.GetSize(), tmpSize); typename ItkRegionType::SizeType size; size.SetSize(tmpSize); //create the ITK-image-region out of index and size ItkRegionType inputRegionOfInterest(index, size); // PART 2: get access to the MITK output image via an ITK image typename mitk::ImageToItk::Pointer outputimagetoitk = mitk::ImageToItk::New(); outputimagetoitk->SetInput(cutter->m_OutputTimeSelector->GetOutput()); outputimagetoitk->Update(); typename ItkOutputImageType::Pointer outputItkImage = outputimagetoitk->GetOutput(); // PART 3: iterate over input and output using ITK iterators // create the iterators ItkInputImageIteratorType inputIt( inputItkImage, inputRegionOfInterest ); ItkOutputImageIteratorType outputIt( outputItkImage, outputItkImage->GetLargestPossibleRegion() ); // Cut the boundingbox out of the image by iterating through // all pixels and checking if they are inside using IsInside() cutter->m_OutsidePixelCount = 0; cutter->m_InsidePixelCount = 0; mitk::Point3D p; - mitk::Geometry3D* inputGeometry = cutter->GetInput()->GetGeometry(); + mitk::BaseGeometry* inputGeometry = cutter->GetInput()->GetGeometry(); TOutputPixel outsideValue; if(cutter->m_AutoOutsideValue) { outsideValue = itk::NumericTraits::min(); } else { outsideValue = (TOutputPixel) cutter->m_OutsideValue; } //shall we use a fixed value for each inside pixel? if (cutter->GetUseInsideValue()) { TOutputPixel insideValue = (TOutputPixel) cutter->m_InsideValue; // yes, use a fixed value for each inside pixel (create a binary mask of the bounding object) for ( inputIt.GoToBegin(), outputIt.GoToBegin(); !inputIt.IsAtEnd(); ++inputIt, ++outputIt) { vtk2itk(inputIt.GetIndex(), p); inputGeometry->IndexToWorld(p, p); if(cutter->m_BoundingObject->IsInside(p)) { outputIt.Set(insideValue); ++cutter->m_InsidePixelCount; } else { outputIt.Set(outsideValue); ++cutter->m_OutsidePixelCount; } } } else { // no, use the pixel value of the original image (normal cutting) for ( inputIt.GoToBegin(), outputIt.GoToBegin(); !inputIt.IsAtEnd(); ++inputIt, ++outputIt) { vtk2itk(inputIt.GetIndex(), p); inputGeometry->IndexToWorld(p, p); if(cutter->m_BoundingObject->IsInside(p)) { outputIt.Set( (TOutputPixel) inputIt.Value() ); ++cutter->m_InsidePixelCount; } else { outputIt.Set( outsideValue ); ++cutter->m_OutsidePixelCount; } } } } template < typename TPixel, unsigned int VImageDimension > void CutImage( itk::Image< TPixel, VImageDimension >* inputItkImage, mitk::BoundingObjectCutter* cutter, int boTimeStep ) { TPixel* dummy = NULL; CutImageWithOutputTypeSelect(inputItkImage, cutter, boTimeStep, dummy); } } // of namespace mitk #include "mitkImageCast.h" #endif // of MITKBOUNDINGOBJECTCUTTER_TXX diff --git a/Modules/AlgorithmsExt/mitkBoundingObjectToSegmentationFilter.cpp b/Modules/AlgorithmsExt/mitkBoundingObjectToSegmentationFilter.cpp index a8e61637e1..09e089d73b 100644 --- a/Modules/AlgorithmsExt/mitkBoundingObjectToSegmentationFilter.cpp +++ b/Modules/AlgorithmsExt/mitkBoundingObjectToSegmentationFilter.cpp @@ -1,121 +1,121 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkBoundingObjectToSegmentationFilter.h" #include "mitkImageCast.h" #include mitk::BoundingObjectToSegmentationFilter::BoundingObjectToSegmentationFilter() { this->SetNumberOfRequiredInputs(1); } mitk::BoundingObjectToSegmentationFilter::~BoundingObjectToSegmentationFilter() { } void mitk::BoundingObjectToSegmentationFilter::SetBoundingObject(mitk::BoundingObject::Pointer boundingObject) { mitk::BoundingObjectGroup* testgroup = dynamic_cast (boundingObject.GetPointer()); if (testgroup) m_boundingObjectGroup = testgroup; else { m_boundingObjectGroup = mitk::BoundingObjectGroup::New(); m_boundingObjectGroup->AddBoundingObject(boundingObject); } } void mitk::BoundingObjectToSegmentationFilter::GenerateData() { typedef itk::Image itkImageType; mitk::Image::Pointer outputImage = this->GetOutput(); mitk::Image::ConstPointer inputImage = this->GetInput(); outputImage->Initialize(inputImage); itkImageType::Pointer itkImage; CastToItkImage(outputImage, itkImage); itkImage->FillBuffer(0); for (unsigned int i=0; iGetCount(); i++) { //create region for boundingobject mitk::BoundingObject::Pointer boundingObject = m_boundingObjectGroup->GetBoundingObjects().at(i); - mitk::Geometry3D::Pointer boGeometry = boundingObject->GetGeometry(); - mitk::Geometry3D::Pointer inputImageGeometry = inputImage->GetSlicedGeometry(); + mitk::BaseGeometry::Pointer boGeometry = boundingObject->GetGeometry(); + mitk::BaseGeometry::Pointer inputImageGeometry = inputImage->GetSlicedGeometry(); mitk::BoundingBox::Pointer boToIm = boGeometry->CalculateBoundingBoxRelativeToTransform(inputImageGeometry->GetIndexToWorldTransform()); mitk::BoundingBox::ConstPointer imgBB = inputImageGeometry->GetBoundingBox(); mitk::BoundingBox::PointType minImg = imgBB->GetMinimum(); mitk::BoundingBox::PointType maxImg = imgBB->GetMaximum(); itkImageType::IndexType boIndex; itkImageType::SizeType boSize; mitk::BoundingBox::PointType min = boToIm->GetMinimum(); mitk::BoundingBox::PointType max = boToIm->GetMaximum(); //check if boundingbox is inside imageregion for (int i =0; i<3; i++) { if (min [i] < minImg[i]) min[i] = minImg[i]; if (max [i] < minImg[i]) max[i] = minImg[i]; if (max[i] > maxImg[i]) max[i] = maxImg[i]; if (min [i] > maxImg[i]) min[i] = maxImg[i]-1; } // add 0.5 (boGeometry is no image geometry) boIndex[0] = (mitk::SlicedData::IndexType::IndexValueType)(min[0] + 0.5); boIndex[1] = (mitk::SlicedData::IndexType::IndexValueType)(min[1] + 0.5); boIndex[2] = (mitk::SlicedData::IndexType::IndexValueType)(min[2] + 0.5); // add 1 because we need 0.5 for each index boSize[0] = (mitk::SlicedData::IndexType::IndexValueType) (max[0]-min[0]); boSize[1] = (mitk::SlicedData::IndexType::IndexValueType) (max[1]-min[1]); boSize[2] = (mitk::SlicedData::IndexType::IndexValueType) (max[2]-min[2]); itkImageType::RegionType region(boIndex, boSize); //create region iterator itk::ImageRegionIteratorWithIndex itBoundingObject = itk::ImageRegionIteratorWithIndex(itkImage, region ); itBoundingObject.GoToBegin(); while(!itBoundingObject.IsAtEnd()) { itkImageType::IndexType index = itBoundingObject.GetIndex(); mitk::Point3D p; p[0] = index[0]; p[1] = index[1]; p[2] = index[2]; inputImageGeometry->IndexToWorld(p,p); if (boundingObject->IsInside(p) && boundingObject->GetPositive()) itBoundingObject.Set(1); else if (boundingObject->IsInside(p) && !boundingObject->GetPositive()) itBoundingObject.Set(0); ++itBoundingObject; } } CastToMitkImage(itkImage, outputImage); } diff --git a/Modules/AlgorithmsExt/mitkGeometryClipImageFilter.cpp b/Modules/AlgorithmsExt/mitkGeometryClipImageFilter.cpp index dcce736a30..f105518a38 100644 --- a/Modules/AlgorithmsExt/mitkGeometryClipImageFilter.cpp +++ b/Modules/AlgorithmsExt/mitkGeometryClipImageFilter.cpp @@ -1,263 +1,263 @@ /*=================================================================== 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 "mitkGeometryClipImageFilter.h" #include "mitkImageTimeSelector.h" #include "mitkTimeHelper.h" #include "mitkProperties.h" #include "mitkImageToItk.h" #include "itkImageRegionConstIterator.h" #include "itkImageRegionIteratorWithIndex.h" #include 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::Geometry3D* aClippingGeometry) +void mitk::GeometryClipImageFilter::SetClippingGeometry(const mitk::BaseGeometry* aClippingGeometry) { if(aClippingGeometry != m_ClippingGeometry.GetPointer()) { m_ClippingGeometry = aClippingGeometry; - m_ClippingGeometryData->SetGeometry(const_cast(aClippingGeometry)); + m_ClippingGeometryData->SetGeometry(const_cast(aClippingGeometry)); SetNthInput(1, m_ClippingGeometryData); Modified(); } } -const mitk::Geometry3D* mitk::GeometryClipImageFilter::GetClippingGeometry() const +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->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::Geometry3D* inputGeometry = geometryClipper->m_InputTimeSelector->GetOutput()->GetGeometry(); + 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/AlgorithmsExt/mitkGeometryClipImageFilter.h b/Modules/AlgorithmsExt/mitkGeometryClipImageFilter.h index b25314fbfc..4c65f5fa54 100644 --- a/Modules/AlgorithmsExt/mitkGeometryClipImageFilter.h +++ b/Modules/AlgorithmsExt/mitkGeometryClipImageFilter.h @@ -1,181 +1,181 @@ /*=================================================================== 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); itkFactorylessNewMacro(Self) itkCloneMacro(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::Geometry3D* aClippingGeometry); + 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::Geometry3D* GetClippingGeometry() const; + 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::Geometry3D::ConstPointer m_ClippingGeometry; + 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/AlgorithmsExt/mitkHeightFieldSurfaceClipImageFilter.cpp b/Modules/AlgorithmsExt/mitkHeightFieldSurfaceClipImageFilter.cpp index 55e85258b1..f52d9dbef1 100644 --- a/Modules/AlgorithmsExt/mitkHeightFieldSurfaceClipImageFilter.cpp +++ b/Modules/AlgorithmsExt/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/AlgorithmsExt/mitkLabeledImageToSurfaceFilter.cpp b/Modules/AlgorithmsExt/mitkLabeledImageToSurfaceFilter.cpp index abeee6c26b..6e08103e14 100644 --- a/Modules/AlgorithmsExt/mitkLabeledImageToSurfaceFilter.cpp +++ b/Modules/AlgorithmsExt/mitkLabeledImageToSurfaceFilter.cpp @@ -1,362 +1,362 @@ /*=================================================================== 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 mitk::LabeledImageToSurfaceFilter::LabeledImageToSurfaceFilter() : m_GaussianStandardDeviation(1.5), m_GenerateAllLabels(true), m_Label(1), m_BackgroundLabel(0) { } mitk::LabeledImageToSurfaceFilter::~LabeledImageToSurfaceFilter() { } void mitk::LabeledImageToSurfaceFilter::GenerateOutputInformation() { Superclass::GenerateOutputInformation(); // // check which labels are available in the image // m_AvailableLabels = this->GetAvailableLabels(); m_IdxToLabels.clear(); // // if we don't want to generate surfaces for all labels // we have to remove all labels except m_Label and m_BackgroundLabel // from the list of available labels // if ( ! m_GenerateAllLabels ) { LabelMapType tmp; LabelMapType::iterator it; it = m_AvailableLabels.find( m_Label ); if ( it != m_AvailableLabels.end() ) tmp[m_Label] = it->second; else tmp[m_Label] = 0; it = m_AvailableLabels.find( m_BackgroundLabel ); if ( it != m_AvailableLabels.end() ) tmp[m_BackgroundLabel] = it->second; else tmp[m_BackgroundLabel] = 0; m_AvailableLabels = tmp; } // // check for the number of labels: if the whole image is filled, no // background is available and thus the numberOfOutpus is equal to the // number of available labels in the image (which is a special case). // If we have background voxels, the number of outputs is one less than // then number of available labels. // unsigned int numberOfOutputs = 0; if ( m_AvailableLabels.find( m_BackgroundLabel ) == m_AvailableLabels.end() ) numberOfOutputs = m_AvailableLabels.size(); else numberOfOutputs = m_AvailableLabels.size() - 1; if ( numberOfOutputs == 0 ) { itkWarningMacro("Number of outputs == 0"); } // // determine the number of time steps of the input image // mitk::Image* image = ( mitk::Image* )GetInput(); unsigned int numberOfTimeSteps = image->GetTimeGeometry()->CountTimeSteps(); // // set the number of outputs to the number of labels used. // initialize the output surfaces accordingly (incl. time steps) // this->SetNumberOfIndexedOutputs( numberOfOutputs ); this->SetNumberOfRequiredOutputs( numberOfOutputs ); for ( unsigned int i = 0 ; i < numberOfOutputs; ++i ) { if ( ! this->GetOutput( i ) ) { mitk::Surface::Pointer output = static_cast( this->MakeOutput(0).GetPointer() ); assert ( output.IsNotNull() ); output->Expand( numberOfTimeSteps ); this->SetNthOutput( i, output.GetPointer() ); } } } void mitk::LabeledImageToSurfaceFilter::GenerateData() { mitk::Image* image = ( mitk::Image* )GetInput(); if ( image == NULL ) { itkWarningMacro("Image is NULL"); return; } mitk::Image::RegionType outputRegion = image->GetRequestedRegion(); m_IdxToLabels.clear(); if ( this->GetNumberOfOutputs() == 0 ) return; // // traverse the known labels and create surfaces for them. // unsigned int currentOutputIndex = 0; for ( LabelMapType::iterator it = m_AvailableLabels.begin() ; it != m_AvailableLabels.end() ; ++it ) { if ( it->first == m_BackgroundLabel ) continue; if ( ( it->second == 0 ) && m_GenerateAllLabels ) continue; assert ( currentOutputIndex < this->GetNumberOfOutputs() ); mitk::Surface::Pointer surface = this->GetOutput( currentOutputIndex ); assert( surface.IsNotNull() ); int tstart=outputRegion.GetIndex(3); int tmax=tstart+outputRegion.GetSize(3); //GetSize()==1 - will aber 0 haben, wenn nicht zeitaufgeloet int t; for( t=tstart; t < tmax; ++t) { vtkImageData *vtkimagedata = image->GetVtkImageData( t ); CreateSurface( t,vtkimagedata,surface.GetPointer(), it->first ); } m_IdxToLabels[ currentOutputIndex ] = it->first; currentOutputIndex++; } } void mitk::LabeledImageToSurfaceFilter::CreateSurface( int time, vtkImageData *vtkimage, mitk::Surface * surface, mitk::LabeledImageToSurfaceFilter::LabelType label ) { vtkImageChangeInformation *indexCoordinatesImageFilter = vtkImageChangeInformation::New(); indexCoordinatesImageFilter->SetInputData(vtkimage); indexCoordinatesImageFilter->SetOutputOrigin(0.0,0.0,0.0); vtkImageThreshold* threshold = vtkImageThreshold::New(); threshold->SetInputConnection( indexCoordinatesImageFilter->GetOutputPort() ); //indexCoordinatesImageFilter->Delete(); threshold->SetInValue( 100 ); threshold->SetOutValue( 0 ); threshold->ThresholdBetween( label, label ); threshold->SetOutputScalarTypeToUnsignedChar(); threshold->ReleaseDataFlagOn(); vtkImageGaussianSmooth *gaussian = vtkImageGaussianSmooth::New(); gaussian->SetInputConnection( threshold->GetOutputPort() ); //threshold->Delete(); gaussian->SetDimensionality( 3 ); gaussian->SetRadiusFactor( 0.49 ); gaussian->SetStandardDeviation( GetGaussianStandardDeviation() ); gaussian->ReleaseDataFlagOn(); gaussian->UpdateInformation(); gaussian->Update(); //MarchingCube -->create Surface vtkMarchingCubes *skinExtractor = vtkMarchingCubes::New(); skinExtractor->ReleaseDataFlagOn(); skinExtractor->SetInputConnection(gaussian->GetOutputPort());//RC++ indexCoordinatesImageFilter->Delete(); skinExtractor->SetValue(0, 50); vtkPolyData *polydata; skinExtractor->Update(); polydata = skinExtractor->GetOutput(); polydata->Register(NULL);//RC++ skinExtractor->Delete(); if (m_Smooth) { vtkSmoothPolyDataFilter *smoother = vtkSmoothPolyDataFilter::New(); //read poly1 (poly1 can be the original polygon, or the decimated polygon) smoother->SetInputData(polydata);//RC++ smoother->SetNumberOfIterations( m_SmoothIteration ); smoother->SetRelaxationFactor( m_SmoothRelaxation ); smoother->SetFeatureAngle( 60 ); smoother->FeatureEdgeSmoothingOff(); smoother->BoundarySmoothingOff(); smoother->SetConvergence( 0 ); polydata->Delete();//RC-- smoother->Update(); polydata = smoother->GetOutput(); polydata->Register(NULL);//RC++ smoother->Delete(); } //decimate = to reduce number of polygons if(m_Decimate==DecimatePro) { vtkDecimatePro *decimate = vtkDecimatePro::New(); decimate->SplittingOff(); decimate->SetErrorIsAbsolute(5); decimate->SetFeatureAngle(30); decimate->PreserveTopologyOn(); decimate->BoundaryVertexDeletionOff(); decimate->SetDegree(10); //std-value is 25! decimate->SetInputData(polydata);//RC++ decimate->SetTargetReduction(m_TargetReduction); decimate->SetMaximumError(0.002); polydata->Delete();//RC-- decimate->Update(); polydata = decimate->GetOutput(); polydata->Register(NULL);//RC++ decimate->Delete(); } if(polydata->GetNumberOfPoints() > 0) { mitk::Vector3D spacing = GetInput()->GetGeometry(time)->GetSpacing(); vtkPoints * points = polydata->GetPoints(); vtkMatrix4x4 *vtkmatrix = vtkMatrix4x4::New(); GetInput()->GetGeometry(time)->GetVtkTransform()->GetMatrix(vtkmatrix); double (*matrix)[4] = vtkmatrix->Element; unsigned int i,j; for(i=0;i<3;++i) for(j=0;j<3;++j) matrix[i][j]/=spacing[j]; unsigned int n = points->GetNumberOfPoints(); double point[3]; for (i = 0; i < n; i++) { points->GetPoint(i, point); mitkVtkLinearTransformPoint(matrix,point,point); points->SetPoint(i, point); } vtkmatrix->Delete(); } surface->SetVtkPolyData(polydata, time); polydata->UnRegister(NULL); gaussian->Delete(); threshold->Delete(); } template < typename TPixel, unsigned int VImageDimension > void GetAvailableLabelsInternal( itk::Image* image, mitk::LabeledImageToSurfaceFilter::LabelMapType& availableLabels ) { typedef itk::Image ImageType; typedef itk::ImageRegionIterator< ImageType > ImageRegionIteratorType; availableLabels.clear(); ImageRegionIteratorType it( image, image->GetLargestPossibleRegion() ); it.GoToBegin(); mitk::LabeledImageToSurfaceFilter::LabelMapType::iterator labelIt; while( ! it.IsAtEnd() ) { labelIt = availableLabels.find( ( mitk::LabeledImageToSurfaceFilter::LabelType ) ( it.Get() ) ); if ( labelIt == availableLabels.end() ) { availableLabels[ ( mitk::LabeledImageToSurfaceFilter::LabelType ) ( it.Get() ) ] = 1; } else { labelIt->second += 1; } ++it; } } #define InstantiateAccessFunction_GetAvailableLabelsInternal(pixelType, dim) \ template void GetAvailableLabelsInternal(itk::Image*, mitk::LabeledImageToSurfaceFilter::LabelMapType&); InstantiateAccessFunctionForFixedDimension(GetAvailableLabelsInternal, 3); mitk::LabeledImageToSurfaceFilter::LabelMapType mitk::LabeledImageToSurfaceFilter::GetAvailableLabels() { mitk::Image::Pointer image = ( mitk::Image* )GetInput(); LabelMapType availableLabels; AccessFixedDimensionByItk_1( image, GetAvailableLabelsInternal, 3, availableLabels ); return availableLabels; } void mitk::LabeledImageToSurfaceFilter::CreateSurface(int, vtkImageData*, mitk::Surface*, const ScalarType) { itkWarningMacro( "This function should never be called!" ); assert(false); } mitk::LabeledImageToSurfaceFilter::LabelType mitk::LabeledImageToSurfaceFilter::GetLabelForNthOutput( const unsigned int& idx ) { IdxToLabelMapType::iterator it = m_IdxToLabels.find( idx ); if ( it != m_IdxToLabels.end() ) { return it->second; } else { itkWarningMacro( "Unknown index encountered: " << idx << ". There are " << this->GetNumberOfOutputs() << " outputs available." ); return itk::NumericTraits::max(); } } mitk::ScalarType mitk::LabeledImageToSurfaceFilter::GetVolumeForNthOutput( const unsigned int& i ) { return GetVolumeForLabel( GetLabelForNthOutput( i ) ); } mitk::ScalarType mitk::LabeledImageToSurfaceFilter::GetVolumeForLabel( const mitk::LabeledImageToSurfaceFilter::LabelType& label ) { // get the image spacing mitk::Image* image = ( mitk::Image* )GetInput(); - const float* spacing = image->GetSlicedGeometry()->GetFloatSpacing(); + const mitk::Vector3D spacing = image->GetSlicedGeometry()->GetSpacing(); // get the number of voxels encountered for the given label, // calculate the volume and return it. LabelMapType::iterator it = m_AvailableLabels.find( label ); if ( it != m_AvailableLabels.end() ) { return static_cast(it->second) * ( spacing[0] * spacing[1] * spacing[2] / 1000.0f ); } else { itkWarningMacro( "Unknown label encountered: " << label ); return 0.0; } } diff --git a/Modules/AlgorithmsExt/mitkPadImageFilter.cpp b/Modules/AlgorithmsExt/mitkPadImageFilter.cpp index 46dc40f6d5..8c1f134da6 100644 --- a/Modules/AlgorithmsExt/mitkPadImageFilter.cpp +++ b/Modules/AlgorithmsExt/mitkPadImageFilter.cpp @@ -1,113 +1,113 @@ /*=================================================================== 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 "mitkPadImageFilter.h" #include "mitkImageCast.h" #include "itkBinaryThresholdImageFilter.h" #include "itkConstantPadImageFilter.h" mitk::PadImageFilter::PadImageFilter() { this->SetNumberOfIndexedInputs(2); this->SetNumberOfRequiredInputs(2); m_BinaryFilter = false; m_PadConstant = -32766; m_LowerThreshold = -32766; m_UpperThreshold = -32765; } mitk::PadImageFilter::~PadImageFilter() { } void mitk::PadImageFilter::GenerateData() { mitk::Image::ConstPointer image = this->GetInput( 0 ); mitk::Image::ConstPointer referenceImage = this->GetInput( 1 ); typedef itk::Image< short, 3 > ImageType; ImageType::Pointer itkImage = ImageType::New(); mitk::CastToItkImage( image, itkImage ); - mitk::Geometry3D *imageGeometry = image->GetGeometry(); + mitk::BaseGeometry *imageGeometry = image->GetGeometry(); mitk::Point3D origin = imageGeometry->GetOrigin(); mitk::Vector3D spacing = imageGeometry->GetSpacing(); - mitk::Geometry3D *referenceImageGeometry = referenceImage->GetGeometry(); + mitk::BaseGeometry *referenceImageGeometry = referenceImage->GetGeometry(); mitk::Point3D referenceOrigin = referenceImageGeometry->GetOrigin(); double outputOrigin[3]; unsigned long padLowerBound[3]; unsigned long padUpperBound[3]; int i; for ( i = 0; i < 3; ++i ) { outputOrigin[i] = referenceOrigin[i]; padLowerBound[i] = static_cast< unsigned long > ((origin[i] - referenceOrigin[i]) / spacing[i] + 0.5); padUpperBound[i] = referenceImage->GetDimension( i ) - image->GetDimension( i ) - padLowerBound[i]; } // The origin of the input image is passed through the filter and used as // output origin as well. Hence, it needs to be overwritten accordingly. itkImage->SetOrigin( outputOrigin ); typedef itk::ConstantPadImageFilter< ImageType, ImageType > PadFilterType; PadFilterType::Pointer padFilter = PadFilterType::New(); padFilter->SetInput( itkImage ); padFilter->SetConstant( m_PadConstant ); padFilter->SetPadLowerBound( padLowerBound ); padFilter->SetPadUpperBound( padUpperBound ); mitk::Image::Pointer outputImage = this->GetOutput(); // If the Binary flag is set, use an additional binary threshold filter after // padding. if ( m_BinaryFilter ) { typedef itk::Image< unsigned char, 3 > BinaryImageType; typedef itk::BinaryThresholdImageFilter< ImageType, BinaryImageType > BinaryFilterType; BinaryFilterType::Pointer binaryFilter = BinaryFilterType::New(); binaryFilter->SetInput( padFilter->GetOutput() ); binaryFilter->SetLowerThreshold( m_LowerThreshold ); binaryFilter->SetUpperThreshold( m_UpperThreshold ); binaryFilter->SetInsideValue( 1 ); binaryFilter->SetOutsideValue( 0 ); binaryFilter->Update(); mitk::CastToMitkImage( binaryFilter->GetOutput(), outputImage ); } else { padFilter->Update(); mitk::CastToMitkImage( padFilter->GetOutput(), outputImage ); } outputImage->SetRequestedRegionToLargestPossibleRegion(); } diff --git a/Modules/ContourModel/Algorithms/mitkContourModelUtils.cpp b/Modules/ContourModel/Algorithms/mitkContourModelUtils.cpp index ee9986dbeb..8a382321e0 100755 --- a/Modules/ContourModel/Algorithms/mitkContourModelUtils.cpp +++ b/Modules/ContourModel/Algorithms/mitkContourModelUtils.cpp @@ -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. ===================================================================*/ #include "mitkContourModelUtils.h" #include "mitkImageCast.h" #include "mitkImageAccessByItk.h" #include "mitkContourModel.h" #include "itkCastImageFilter.h" #include "mitkImage.h" #include "mitkSurface.h" #include "vtkPolyData.h" #include "mitkContourModelToSurfaceFilter.h" #include "vtkPolyDataToImageStencil.h" #include "vtkImageStencil.h" #include "mitkImageVtkAccessor.h" #include "vtkSmartPointer.h" #include "vtkImageData.h" #include "vtkImageLogic.h" #include "vtkPointData.h" mitk::ContourModelUtils::ContourModelUtils() { } mitk::ContourModelUtils::~ContourModelUtils() { } mitk::ContourModel::Pointer mitk::ContourModelUtils::ProjectContourTo2DSlice(Image* slice, ContourModel* contourIn3D, bool itkNotUsed( correctionForIpSegmentation ), bool constrainToInside) { if ( !slice || !contourIn3D ) return NULL; ContourModel::Pointer projectedContour = ContourModel::New(); projectedContour->Initialize(*contourIn3D); - const Geometry3D* sliceGeometry = slice->GetGeometry(); + const BaseGeometry* sliceGeometry = slice->GetGeometry(); int numberOfTimesteps = contourIn3D->GetTimeGeometry()->CountTimeSteps(); for(int currentTimestep = 0; currentTimestep < numberOfTimesteps; currentTimestep++) { ContourModel::VertexIterator iter = contourIn3D->Begin(currentTimestep); ContourModel::VertexIterator end = contourIn3D->End(currentTimestep); while( iter != end) { Point3D currentPointIn3D = (*iter)->Coordinates; Point3D projectedPointIn2D; projectedPointIn2D.Fill(0.0); sliceGeometry->WorldToIndex( currentPointIn3D, projectedPointIn2D ); // MITK_INFO << "world point " << currentPointIn3D << " in index is " << projectedPointIn2D; if ( !sliceGeometry->IsIndexInside( projectedPointIn2D ) && constrainToInside ) { MITK_INFO << "**" << currentPointIn3D << " is " << projectedPointIn2D << " --> correct it (TODO)" << std::endl; } projectedContour->AddVertex( projectedPointIn2D, currentTimestep ); iter++; } } return projectedContour; } -mitk::ContourModel::Pointer mitk::ContourModelUtils::BackProjectContourFrom2DSlice(const Geometry3D* sliceGeometry, ContourModel* contourIn2D, bool itkNotUsed( correctionForIpSegmentation ) ) +mitk::ContourModel::Pointer mitk::ContourModelUtils::BackProjectContourFrom2DSlice(const BaseGeometry* sliceGeometry, ContourModel* contourIn2D, bool itkNotUsed( correctionForIpSegmentation ) ) { if ( !sliceGeometry || !contourIn2D ) return NULL; ContourModel::Pointer worldContour = ContourModel::New(); worldContour->Initialize(*contourIn2D); int numberOfTimesteps = contourIn2D->GetTimeGeometry()->CountTimeSteps(); for(int currentTimestep = 0; currentTimestep < numberOfTimesteps; currentTimestep++) { ContourModel::VertexIterator iter = contourIn2D->Begin(currentTimestep); ContourModel::VertexIterator end = contourIn2D->End(currentTimestep); while( iter != end) { Point3D currentPointIn2D = (*iter)->Coordinates; Point3D worldPointIn3D; worldPointIn3D.Fill(0.0); sliceGeometry->IndexToWorld( currentPointIn2D, worldPointIn3D ); //MITK_INFO << "index " << currentPointIn2D << " world " << worldPointIn3D << std::endl; worldContour->AddVertex( worldPointIn3D, currentTimestep ); iter++; } } return worldContour; } void mitk::ContourModelUtils::FillContourInSlice( ContourModel* projectedContour, Image* sliceImage, int paintingPixelValue ) { mitk::ContourModelUtils::FillContourInSlice(projectedContour, 0, sliceImage, paintingPixelValue); } void mitk::ContourModelUtils::FillContourInSlice( ContourModel* projectedContour, unsigned int timeStep, Image* sliceImage, int paintingPixelValue ) { //create a surface of the input ContourModel mitk::Surface::Pointer surface = mitk::Surface::New(); mitk::ContourModelToSurfaceFilter::Pointer contourModelFilter = mitk::ContourModelToSurfaceFilter::New(); contourModelFilter->SetInput(projectedContour); contourModelFilter->Update(); surface = contourModelFilter->GetOutput(); // that's our vtkPolyData-Surface vtkSmartPointer surface2D = vtkSmartPointer::New(); surface2D->SetPoints(surface->GetVtkPolyData(timeStep)->GetPoints()); surface2D->SetLines(surface->GetVtkPolyData(timeStep)->GetLines()); surface2D->Modified(); //surface2D->Update(); // prepare the binary image's voxel grid vtkSmartPointer whiteImage = vtkSmartPointer::New(); whiteImage->DeepCopy(sliceImage->GetVtkImageData()); // fill the image with foreground voxels: unsigned char inval = 255; unsigned char outval = 0; vtkIdType count = whiteImage->GetNumberOfPoints(); for (vtkIdType i = 0; i < count; ++i) { whiteImage->GetPointData()->GetScalars()->SetTuple1(i, inval); } // polygonal data --> image stencil: vtkSmartPointer pol2stenc = vtkSmartPointer::New(); pol2stenc->SetTolerance(0); pol2stenc->SetInputData(surface2D); pol2stenc->Update(); // cut the corresponding white image and set the background: vtkSmartPointer imgstenc = vtkSmartPointer::New(); imgstenc->SetInputData(whiteImage); imgstenc->SetStencilConnection(pol2stenc->GetOutputPort()); imgstenc->ReverseStencilOff(); imgstenc->SetBackgroundValue(outval); imgstenc->Update(); //Fill according to painting value vtkSmartPointer booleanOperation = vtkSmartPointer::New(); booleanOperation->SetInput2Data(sliceImage->GetVtkImageData()); booleanOperation->SetOperationToOr(); booleanOperation->SetOutputTrueValue(1.0); if(paintingPixelValue == 1) { //COMBINE //slice or stencil booleanOperation->SetInputConnection(imgstenc->GetOutputPort()); booleanOperation->SetOperationToOr(); } else { //CUT //slice and not(stencil) vtkSmartPointer booleanOperationNOT = vtkSmartPointer::New(); booleanOperationNOT->SetInputConnection(imgstenc->GetOutputPort()); booleanOperationNOT->SetOperationToNot(); booleanOperationNOT->Update(); booleanOperation->SetInputConnection(booleanOperationNOT->GetOutputPort()); booleanOperation->SetOperationToAnd(); } booleanOperation->Update(); //copy scalars to output image slice sliceImage->SetVolume(booleanOperation->GetOutput()->GetScalarPointer()); } diff --git a/Modules/ContourModel/Algorithms/mitkContourModelUtils.h b/Modules/ContourModel/Algorithms/mitkContourModelUtils.h index 26ded28b24..a8d55a564c 100644 --- a/Modules/ContourModel/Algorithms/mitkContourModelUtils.h +++ b/Modules/ContourModel/Algorithms/mitkContourModelUtils.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 mitkContourModelUtilshIncludett #define mitkContourModelUtilshIncludett #include "mitkImage.h" #include #include "mitkContourModel.h" #include namespace mitk { /** * \brief Helpful methods for working with contours and images * * */ class MitkContourModel_EXPORT ContourModelUtils : public itk::Object { public: mitkClassMacro(ContourModelUtils, itk::Object); /** \brief Projects a contour onto an image point by point. Converts from world to index coordinates. \param correctionForIpSegmentation adds 0.5 to x and y index coordinates (difference between ipSegmentation and MITK contours) */ static ContourModel::Pointer ProjectContourTo2DSlice(Image* slice, ContourModel* contourIn3D, bool correctionForIpSegmentation, bool constrainToInside); /** \brief Projects a slice index coordinates of a contour back into world coordinates. \param correctionForIpSegmentation subtracts 0.5 to x and y index coordinates (difference between ipSegmentation and MITK contours) */ - static ContourModel::Pointer BackProjectContourFrom2DSlice(const Geometry3D* sliceGeometry, ContourModel* contourIn2D, bool correctionForIpSegmentation = false); + static ContourModel::Pointer BackProjectContourFrom2DSlice(const BaseGeometry* sliceGeometry, ContourModel* contourIn2D, bool correctionForIpSegmentation = false); /** \brief Fill a contour in a 2D slice with a specified pixel value at time step 0. */ static void FillContourInSlice( ContourModel* projectedContour, Image* sliceImage, int paintingPixelValue = 1 ); /** \brief Fill a contour in a 2D slice with a specified pixel value at a given time step. */ static void FillContourInSlice( ContourModel* projectedContour, unsigned int timeStep, Image* sliceImage, int paintingPixelValue = 1 ); protected: ContourModelUtils(); virtual ~ContourModelUtils(); }; } #endif diff --git a/Modules/ContourModel/DataManagement/mitkContourModel.cpp b/Modules/ContourModel/DataManagement/mitkContourModel.cpp index e269a20678..47b9ed2558 100644 --- a/Modules/ContourModel/DataManagement/mitkContourModel.cpp +++ b/Modules/ContourModel/DataManagement/mitkContourModel.cpp @@ -1,709 +1,709 @@ /*=================================================================== 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 mitk::ContourModel::ContourModel() : m_UpdateBoundingBox(true) { //set to initial state this->InitializeEmpty(); } mitk::ContourModel::ContourModel(const mitk::ContourModel &other) : mitk::BaseData(other), m_ContourSeries(other.m_ContourSeries), m_lineInterpolation(other.m_lineInterpolation) { m_SelectedVertex = NULL; } mitk::ContourModel::~ContourModel() { m_SelectedVertex = NULL; this->m_ContourSeries.clear();//TODO check destruction } void mitk::ContourModel::AddVertex(mitk::Point3D &vertex, int timestep) { if(!this->IsEmptyTimeStep(timestep) ) { this->AddVertex(vertex, false, timestep); } } void mitk::ContourModel::AddVertex(mitk::Point3D &vertex, bool isControlPoint, int timestep) { if(!this->IsEmptyTimeStep(timestep)) { this->m_ContourSeries[timestep]->AddVertex(vertex, isControlPoint); this->InvokeEvent( ContourModelSizeChangeEvent() ); this->Modified();this->m_UpdateBoundingBox = true; } } void mitk::ContourModel::AddVertex(VertexType &vertex, int timestep) { if(!this->IsEmptyTimeStep(timestep)) { this->m_ContourSeries[timestep]->AddVertex(vertex); this->InvokeEvent( ContourModelSizeChangeEvent() ); this->Modified();this->m_UpdateBoundingBox = true; } } void mitk::ContourModel::AddVertex(const VertexType* vertex, int timestep) { if(vertex != NULL) { this->m_ContourSeries[timestep]->AddVertex(*const_cast(vertex)); } } void mitk::ContourModel::AddVertexAtFront(mitk::Point3D &vertex, int timestep) { if(!this->IsEmptyTimeStep(timestep) ) { this->AddVertexAtFront(vertex, false, timestep); } } void mitk::ContourModel::AddVertexAtFront(mitk::Point3D &vertex, bool isControlPoint, int timestep) { if(!this->IsEmptyTimeStep(timestep)) { this->m_ContourSeries[timestep]->AddVertexAtFront(vertex, isControlPoint); this->InvokeEvent( ContourModelSizeChangeEvent() ); this->Modified(); this->m_UpdateBoundingBox = true; } } void mitk::ContourModel::AddVertexAtFront(VertexType &vertex, int timestep) { if(!this->IsEmptyTimeStep(timestep)) { this->m_ContourSeries[timestep]->AddVertexAtFront(vertex); this->InvokeEvent( ContourModelSizeChangeEvent() ); this->Modified(); this->m_UpdateBoundingBox = true; } } bool mitk::ContourModel::SetVertexAt(int pointId, const Point3D &point, unsigned int timestep) { if(!this->IsEmptyTimeStep(timestep)) { if(pointId >= 0 && this->m_ContourSeries[timestep]->GetSize() > pointId) { this->m_ContourSeries[timestep]->SetVertexAt( pointId, point ); this->Modified(); this->m_UpdateBoundingBox = true; return true; } return false; } return false; } bool mitk::ContourModel::SetVertexAt(int pointId, const VertexType *vertex, unsigned int timestep) { if(vertex==NULL) return false; if(!this->IsEmptyTimeStep(timestep)) { if(pointId >= 0 && this->m_ContourSeries[timestep]->GetSize() > pointId) { this->m_ContourSeries[timestep]->SetVertexAt( pointId, vertex ); this->Modified(); this->m_UpdateBoundingBox = true; return true; } return false; } return false; } void mitk::ContourModel::InsertVertexAtIndex(mitk::Point3D &vertex, int index, bool isControlPoint, int timestep) { if(!this->IsEmptyTimeStep(timestep)) { if(index >= 0 && this->m_ContourSeries[timestep]->GetSize() > index) { this->m_ContourSeries[timestep]->InsertVertexAtIndex(vertex, isControlPoint, index); this->InvokeEvent( ContourModelSizeChangeEvent() ); this->Modified(); this->m_UpdateBoundingBox = true; } } } bool mitk::ContourModel::IsEmpty( int timestep) const { if(!this->IsEmptyTimeStep(timestep)) { return this->m_ContourSeries[timestep]->IsEmpty(); } return true; } bool mitk::ContourModel::IsEmpty() const { return this->IsEmpty(0); } int mitk::ContourModel::GetNumberOfVertices( int timestep) const { if(!this->IsEmptyTimeStep(timestep)) { return this->m_ContourSeries[timestep]->GetSize(); } return -1; } const mitk::ContourModel::VertexType* mitk::ContourModel::GetVertexAt(int index, int timestep) const { if(!this->IsEmptyTimeStep(timestep)) { return this->m_ContourSeries[timestep]->GetVertexAt(index); } return NULL; } int mitk::ContourModel::GetIndex(const VertexType *vertex, int timestep) { if(!this->IsEmptyTimeStep(timestep)) { return this->m_ContourSeries[timestep]->GetIndex(vertex); } return -1; } void mitk::ContourModel::Close( int timestep) { if(!this->IsEmptyTimeStep(timestep)) { this->m_ContourSeries[timestep]->Close(); this->InvokeEvent( ContourModelClosedEvent() ); this->Modified();this->m_UpdateBoundingBox = true; } } void mitk::ContourModel::Open( int timestep) { if(!this->IsEmptyTimeStep(timestep)) { this->m_ContourSeries[timestep]->Open(); this->InvokeEvent( ContourModelClosedEvent() ); this->Modified();this->m_UpdateBoundingBox = true; } } void mitk::ContourModel::SetClosed(bool isClosed, int timestep) { if(!this->IsEmptyTimeStep(timestep)) { this->m_ContourSeries[timestep]->SetClosed(isClosed); this->InvokeEvent( ContourModelClosedEvent() ); this->Modified();this->m_UpdateBoundingBox = true; } } bool mitk::ContourModel::IsEmptyTimeStep(unsigned int t) const { return (this->m_ContourSeries.size() <= t); } bool mitk::ContourModel::IsNearContour(mitk::Point3D &point, float eps, int timestep) { if(!this->IsEmptyTimeStep(timestep)) { return this->m_ContourSeries[timestep]->IsNearContour(point, eps); } return false; } void mitk::ContourModel::Concatenate(mitk::ContourModel* other, int timestep, bool check) { if(!this->IsEmptyTimeStep(timestep)) { if( !this->m_ContourSeries[timestep]->IsClosed() ) { this->m_ContourSeries[timestep]->Concatenate(other->m_ContourSeries[timestep], check); this->InvokeEvent( ContourModelSizeChangeEvent() ); this->Modified();this->m_UpdateBoundingBox = true; } } } mitk::ContourModel::VertexIterator mitk::ContourModel::Begin( int timestep) { return this->IteratorBegin(timestep); } mitk::ContourModel::VertexIterator mitk::ContourModel::IteratorBegin( int timestep) { if(!this->IsEmptyTimeStep(timestep)) { return this->m_ContourSeries[timestep]->IteratorBegin(); } else { mitkThrow() << "No iterator at invalid timestep " << timestep << ". There are only " << this->GetTimeSteps() << " timesteps available."; } } mitk::ContourModel::VertexIterator mitk::ContourModel::End( int timestep) { return this->IteratorEnd(timestep); } mitk::ContourModel::VertexIterator mitk::ContourModel::IteratorEnd( int timestep) { if(!this->IsEmptyTimeStep(timestep)) { return this->m_ContourSeries[timestep]->IteratorEnd(); } else { mitkThrow() << "No iterator at invalid timestep " << timestep << ". There are only " << this->GetTimeSteps() << " timesteps available."; } } bool mitk::ContourModel::IsClosed( int timestep) { if(!this->IsEmptyTimeStep(timestep)) { return this->m_ContourSeries[timestep]->IsClosed(); } return false; } bool mitk::ContourModel::SelectVertexAt(mitk::Point3D &point, float eps, int timestep) { if(!this->IsEmptyTimeStep(timestep)) { this->m_SelectedVertex = this->m_ContourSeries[timestep]->GetVertexAt(point, eps); } return this->m_SelectedVertex != NULL; } bool mitk::ContourModel::SelectVertexAt(int index, int timestep) { if(!this->IsEmptyTimeStep(timestep) && index >= 0) { return (this->m_SelectedVertex = this->m_ContourSeries[timestep]->GetVertexAt(index)); } return false; } bool mitk::ContourModel::SetControlVertexAt(mitk::Point3D &point, float eps, int timestep) { if(!this->IsEmptyTimeStep(timestep)) { VertexType* vertex = this->m_ContourSeries[timestep]->GetVertexAt(point, eps); if (vertex != NULL) { vertex->IsControlPoint = true; return true; } } return false; } bool mitk::ContourModel::SetControlVertexAt(int index, int timestep) { if(!this->IsEmptyTimeStep(timestep) && index >= 0) { VertexType* vertex = this->m_ContourSeries[timestep]->GetVertexAt(index); if (vertex != NULL) { vertex->IsControlPoint = true; return true; } } return false; } bool mitk::ContourModel::RemoveVertex(const VertexType *vertex, int timestep) { if(!this->IsEmptyTimeStep(timestep)) { if(this->m_ContourSeries[timestep]->RemoveVertex(vertex)) { this->Modified();this->m_UpdateBoundingBox = true; this->InvokeEvent( ContourModelSizeChangeEvent() ); return true; } } return false; } bool mitk::ContourModel::RemoveVertexAt(int index, int timestep) { if(!this->IsEmptyTimeStep(timestep)) { if(this->m_ContourSeries[timestep]->RemoveVertexAt(index)) { this->Modified();this->m_UpdateBoundingBox = true; this->InvokeEvent( ContourModelSizeChangeEvent() ); return true; } } return false; } bool mitk::ContourModel::RemoveVertexAt(mitk::Point3D &point, float eps, int timestep) { if(!this->IsEmptyTimeStep(timestep)) { if(this->m_ContourSeries[timestep]->RemoveVertexAt(point, eps)) { this->Modified();this->m_UpdateBoundingBox = true; this->InvokeEvent( ContourModelSizeChangeEvent() ); return true; } } return false; } void mitk::ContourModel::ShiftSelectedVertex(mitk::Vector3D &translate) { if(this->m_SelectedVertex) { this->ShiftVertex(this->m_SelectedVertex,translate); this->Modified();this->m_UpdateBoundingBox = true; } } void mitk::ContourModel::ShiftContour(mitk::Vector3D &translate, int timestep) { if(!this->IsEmptyTimeStep(timestep)) { VertexListType* vList = this->m_ContourSeries[timestep]->GetVertexList(); VertexIterator it = vList->begin(); VertexIterator end = vList->end(); //shift all vertices while(it != end) { this->ShiftVertex((*it),translate); it++; } this->Modified();this->m_UpdateBoundingBox = true; this->InvokeEvent( ContourModelShiftEvent() ); } } void mitk::ContourModel::ShiftVertex(VertexType* vertex, mitk::Vector3D &vector) { vertex->Coordinates[0] += vector[0]; vertex->Coordinates[1] += vector[1]; vertex->Coordinates[2] += vector[2]; } void mitk::ContourModel::Clear(int timestep) { if(!this->IsEmptyTimeStep(timestep)) { //clear data at timestep this->m_ContourSeries[timestep]->Clear(); this->InitializeEmpty(); this->Modified();this->m_UpdateBoundingBox = true; } } void mitk::ContourModel::Expand(unsigned int timeSteps ) { std::size_t oldSize = this->m_ContourSeries.size(); if( static_cast(timeSteps) > oldSize ) { Superclass::Expand(timeSteps); //insert contours for each new timestep for( std::size_t i = oldSize; i < static_cast(timeSteps); i++) { m_ContourSeries.push_back(mitk::ContourElement::New()); } this->InvokeEvent( ContourModelExpandTimeBoundsEvent() ); } } void mitk::ContourModel::SetRequestedRegionToLargestPossibleRegion () { //no support for regions } bool mitk::ContourModel::RequestedRegionIsOutsideOfTheBufferedRegion () { //no support for regions return false; } bool mitk::ContourModel::VerifyRequestedRegion () { //no support for regions return true; } -const mitk::Geometry3D * mitk::ContourModel::GetUpdatedGeometry (int t) +const mitk::BaseGeometry * mitk::ContourModel::GetUpdatedGeometry (int t) { return Superclass::GetUpdatedGeometry(t); } -mitk::Geometry3D* mitk::ContourModel::GetGeometry (int t)const +mitk::BaseGeometry* mitk::ContourModel::GetGeometry (int t)const { return Superclass::GetGeometry(t); } void mitk::ContourModel::SetRequestedRegion( const itk::DataObject* /*data*/) { //no support for regions } void mitk::ContourModel::Clear() { //clear data and set to initial state again this->ClearData(); this->InitializeEmpty(); this->Modified();this->m_UpdateBoundingBox = true; } void mitk::ContourModel::RedistributeControlVertices(int period, int timestep) { if(!this->IsEmptyTimeStep(timestep)) { this->m_ContourSeries[timestep]->RedistributeControlVertices(this->GetSelectedVertex(), period); this->InvokeEvent( ContourModelClosedEvent() ); this->Modified();this->m_UpdateBoundingBox = true; } } void mitk::ContourModel::ClearData() { //call the superclass, this releases the data of BaseData Superclass::ClearData(); //clear out the time resolved contours this->m_ContourSeries.clear(); } void mitk::ContourModel::Initialize() { this->InitializeEmpty(); this->Modified();this->m_UpdateBoundingBox = true; } void mitk::ContourModel::Initialize(mitk::ContourModel &other) { mitk::TimeStepType numberOfTimesteps = other.GetTimeGeometry()->CountTimeSteps(); this->InitializeTimeGeometry(numberOfTimesteps); for(mitk::TimeStepType currentTimestep = 0; currentTimestep < numberOfTimesteps; currentTimestep++) { this->m_ContourSeries.push_back(mitk::ContourElement::New()); this->SetClosed(other.IsClosed(currentTimestep),currentTimestep); } m_SelectedVertex = NULL; this->m_lineInterpolation = other.m_lineInterpolation; this->Modified();this->m_UpdateBoundingBox = true; } void mitk::ContourModel::InitializeEmpty() { //clear data at timesteps this->m_ContourSeries.resize(0); this->m_ContourSeries.push_back(mitk::ContourElement::New()); //set number of timesteps to one this->InitializeTimeGeometry(1); m_SelectedVertex = NULL; this->m_lineInterpolation = ContourModel::LINEAR; } void mitk::ContourModel::UpdateOutputInformation() { if ( this->GetSource() ) { this->GetSource()->UpdateOutputInformation(); } if(this->m_UpdateBoundingBox) { //update the bounds of the geometry according to the stored vertices ScalarType mitkBounds[6]; //calculate the boundingbox at each timestep typedef itk::BoundingBox BoundingBoxType; typedef BoundingBoxType::PointsContainer PointsContainer; int timesteps = this->GetTimeSteps(); //iterate over the timesteps for(int currenTimeStep = 0; currenTimeStep < timesteps; currenTimeStep++) { if( dynamic_cast< mitk::PlaneGeometry* >(this->GetGeometry(currenTimeStep)) ) { //do not update bounds for 2D geometries, as they are unfortunately defined with min bounds 0! return; } else {//we have a 3D geometry -> let's update bounds //only update bounds if the contour was modified if (this->GetMTime() > this->GetGeometry(currenTimeStep)->GetBoundingBox()->GetMTime()) { mitkBounds[0] = 0.0; mitkBounds[1] = 0.0; mitkBounds[2] = 0.0; mitkBounds[3] = 0.0; mitkBounds[4] = 0.0; mitkBounds[5] = 0.0; BoundingBoxType::Pointer boundingBox = BoundingBoxType::New(); PointsContainer::Pointer points = PointsContainer::New(); VertexIterator it = this->IteratorBegin(currenTimeStep); VertexIterator end = this->IteratorEnd(currenTimeStep); //fill the boundingbox with the points while(it != end) { Point3D currentP = (*it)->Coordinates; BoundingBoxType::PointType p; p.CastFrom(currentP); points->InsertElement(points->Size(), p); it++; } //construct the new boundingBox boundingBox->SetPoints(points); boundingBox->ComputeBoundingBox(); BoundingBoxType::BoundsArrayType tmp = boundingBox->GetBounds(); mitkBounds[0] = tmp[0]; mitkBounds[1] = tmp[1]; mitkBounds[2] = tmp[2]; mitkBounds[3] = tmp[3]; mitkBounds[4] = tmp[4]; mitkBounds[5] = tmp[5]; //set boundingBox at current timestep - Geometry3D* geometry3d = this->GetGeometry(currenTimeStep); + BaseGeometry* geometry3d = this->GetGeometry(currenTimeStep); geometry3d->SetBounds(mitkBounds); } } } this->m_UpdateBoundingBox = false; } GetTimeGeometry()->Update(); } void mitk::ContourModel::ExecuteOperation(mitk::Operation* /*operation*/) { //not supported yet } diff --git a/Modules/ContourModel/DataManagement/mitkContourModel.h b/Modules/ContourModel/DataManagement/mitkContourModel.h index 3d00bd91e1..5451857e89 100644 --- a/Modules/ContourModel/DataManagement/mitkContourModel.h +++ b/Modules/ContourModel/DataManagement/mitkContourModel.h @@ -1,474 +1,474 @@ /*=================================================================== 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_CONTOURMODEL_H_ #define _MITK_CONTOURMODEL_H_ #include "mitkCommon.h" #include #include "mitkBaseData.h" #include namespace mitk { /** \brief ContourModel is a structure of linked vertices defining a contour in 3D space. The vertices are stored in a mitk::ContourElement is stored for each timestep. The contour line segments are implicitly defined by the given linked vertices. By default two control points are are linked by a straight line.It is possible to add vertices at front and end of the contour and to iterate in both directions. Points are specified containing coordinates and additional (data) information, see mitk::ContourElement. For accessing a specific vertex either an index or a position in 3D Space can be used. The vertices are best accessed by using a VertexIterator. Interaction with the contour is thus available without any mitk interactor class using the api of ContourModel. It is possible to shift single vertices also as shifting the whole contour. A contour can be either open like a single curved line segment or closed. A closed contour can for example represent a jordan curve. \section mitkContourModelDisplayOptions Display Options The default mappers for this data structure are mitk::ContourModelGLMapper2D and mitk::ContourModelMapper3D. See these classes for display options which can can be set via properties. */ class MitkContourModel_EXPORT ContourModel : public BaseData { public: mitkClassMacro(ContourModel, BaseData); itkFactorylessNewMacro(Self) itkCloneMacro(Self) /*+++++++++++++++ typedefs +++++++++++++++++++++++++++++++*/ typedef mitk::ContourElement::VertexType VertexType; typedef mitk::ContourElement::VertexListType VertexListType; typedef mitk::ContourElement::VertexIterator VertexIterator; typedef mitk::ContourElement::ConstVertexIterator ConstVertexIterator; typedef std::vector< mitk::ContourElement::Pointer > ContourModelSeries; /*+++++++++++++++ END typedefs ++++++++++++++++++++++++++++*/ /** \brief Possible interpolation of the line segments between control points */ enum LineSegmentInterpolation{ LINEAR, B_SPLINE }; /*++++++++++++++++ inline methods +++++++++++++++++++++++*/ /** \brief Get the current selected vertex. */ VertexType* GetSelectedVertex() { return this->m_SelectedVertex; } /** \brief Deselect vertex. */ void Deselect() { this->m_SelectedVertex = NULL; } /** \brief Set selected vertex as control point */ void SetSelectedVertexAsControlPoint(bool isControlPoint=true) { if (this->m_SelectedVertex) { m_SelectedVertex->IsControlPoint = isControlPoint; this->Modified(); } } /** \brief Set the interpolation of the line segments between control points. */ void SetLineSegmentInterpolation(LineSegmentInterpolation interpolation) { this->m_lineInterpolation = interpolation; this->Modified(); } /** \brief Get the interpolation of the line segments between control points. */ LineSegmentInterpolation GetLineSegmentInterpolation() { return this->m_lineInterpolation; } /*++++++++++++++++ END inline methods +++++++++++++++++++++++*/ /** \brief Add a vertex to the contour at given timestep. The vertex is added at the end of contour. \param vertex - coordinate representation of a control point \param timestep - the timestep at which the vertex will be add ( default 0) @Note Adding a vertex to a timestep which exceeds the timebounds of the contour will not be added, the TimeGeometry will not be expanded. */ void AddVertex(mitk::Point3D &vertex, int timestep=0); /** \brief Add a vertex to the contour at given timestep. The vertex is added at the end of contour. \param vertex - coordinate representation of a control point \param timestep - the timestep at which the vertex will be add ( default 0) @Note Adding a vertex to a timestep which exceeds the timebounds of the contour will not be added, the TimeGeometry will not be expanded. */ void AddVertex(VertexType &vertex, int timestep=0); /** \brief Add a vertex to the contour at given timestep. The vertex is added at the end of contour. \param vertex - coordinate representation of a control point \param timestep - the timestep at which the vertex will be add ( default 0) @Note Adding a vertex to a timestep which exceeds the timebounds of the contour will not be added, the TimeSlicedGeometry will not be expanded. */ void AddVertex(const VertexType* vertex, int timestep=0); /** \brief Add a vertex to the contour. \param vertex - coordinate representation of a control point \param timestep - the timestep at which the vertex will be add ( default 0) \param isControlPoint - specifies the vertex to be handled in a special way (e.g. control points will be rendered). @Note Adding a vertex to a timestep which exceeds the timebounds of the contour will not be added, the TimeGeometry will not be expanded. */ void AddVertex(mitk::Point3D &vertex, bool isControlPoint, int timestep=0); /** \brief Add a vertex to the contour at given timestep AT THE FRONT of the contour. The vertex is added at the FRONT of contour. \param vertex - coordinate representation of a control point \param timestep - the timestep at which the vertex will be add ( default 0) @Note Adding a vertex to a timestep which exceeds the timebounds of the contour will not be added, the TimeGeometry will not be expanded. */ void AddVertexAtFront(mitk::Point3D &vertex, int timestep=0); /** \brief Add a vertex to the contour at given timestep AT THE FRONT of the contour. The vertex is added at the FRONT of contour. \param vertex - coordinate representation of a control point \param timestep - the timestep at which the vertex will be add ( default 0) @Note Adding a vertex to a timestep which exceeds the timebounds of the contour will not be added, the TimeGeometry will not be expanded. */ void AddVertexAtFront(VertexType &vertex, int timestep=0); /** \brief Add a vertex to the contour at given timestep AT THE FRONT of the contour. \param vertex - coordinate representation of a control point \param timestep - the timestep at which the vertex will be add ( default 0) \param isControlPoint - specifies the vertex to be handled in a special way (e.g. control points will be rendered). @Note Adding a vertex to a timestep which exceeds the timebounds of the contour will not be added, the TimeGeometry will not be expanded. */ void AddVertexAtFront(mitk::Point3D &vertex, bool isControlPoint, int timestep=0); /** \brief Insert a vertex at given index. */ void InsertVertexAtIndex(mitk::Point3D &vertex, int index, bool isControlPoint=false, int timestep=0); /** \brief Set a coordinates for point at given index. */ bool SetVertexAt(int pointId, const mitk::Point3D &point, unsigned int timestep=0); /** \brief Set a coordinates for point at given index. */ bool SetVertexAt(int pointId, const VertexType* vertex, unsigned int timestep=0); /** \brief Return if the contour is closed or not. */ bool IsClosed( int timestep=0); /** \brief Concatenate two contours. The starting control point of the other will be added at the end of the contour. \pararm timestep - the timestep at which the vertex will be add ( default 0) \pararm check - check for intersections ( default false) */ void Concatenate(mitk::ContourModel* other, int timestep=0, bool check=false); /** \brief Returns a const VertexIterator at the start element of the contour. @throw mitk::Exception if the timestep is invalid. */ VertexIterator Begin( int timestep=0); /** \brief Returns a const VertexIterator at the start element of the contour. @throw mitk::Exception if the timestep is invalid. */ VertexIterator IteratorBegin( int timestep=0); /** \brief Returns a const VertexIterator at the end element of the contour. @throw mitk::Exception if the timestep is invalid. */ VertexIterator End( int timestep=0); /** \brief Returns a const VertexIterator at the end element of the contour. @throw mitk::Exception if the timestep is invalid. */ VertexIterator IteratorEnd( int timestep=0); /** \brief Close the contour. The last control point will be linked with the first point. */ virtual void Close( int timestep=0); /** \brief Set isClosed to false contour. The link between the last control point the first point will be removed. */ virtual void Open( int timestep=0); /** \brief Set closed property to given boolean. false - The link between the last control point the first point will be removed. true - The last control point will be linked with the first point. */ virtual void SetClosed(bool isClosed, int timestep=0); /** \brief Returns the number of vertices at a given timestep. \param timestep - default = 0 */ int GetNumberOfVertices( int timestep=0) const; /** \brief Returns whether the contour model is empty at a given timestep. \pararm timestep - default = 0 */ virtual bool IsEmpty( int timestep) const; /** \brief Returns whether the contour model is empty. */ virtual bool IsEmpty() const; /** \brief Returns the vertex at the index position within the container. */ virtual const VertexType* GetVertexAt(int index, int timestep=0) const; /** \brief Remove a vertex at given timestep within the container. \return index of vertex. -1 if not found. */ int GetIndex(const VertexType* vertex, int timestep=0); /** \brief Check if there isn't something at this timestep. */ virtual bool IsEmptyTimeStep(unsigned int t) const; /** \brief Check if mouse cursor is near the contour. */ virtual bool IsNearContour(mitk::Point3D &point, float eps, int timestep); /** \brief Mark a vertex at an index in the container as selected. */ bool SelectVertexAt(int index, int timestep=0); /** \brief Mark a vertex at an index in the container as control point. */ bool SetControlVertexAt(int index, int timestep=0); /** \brief Mark a vertex at a given position in 3D space. \param point - query point in 3D space \param eps - radius for nearest neighbour search (error bound). \param timestep - search at this timestep @return true = vertex found; false = no vertex found */ bool SelectVertexAt(mitk::Point3D &point, float eps, int timestep=0); /* \pararm point - query point in 3D space \pararm eps - radius for nearest neighbour search (error bound). \pararm timestep - search at this timestep @return true = vertex found; false = no vertex found */ bool SetControlVertexAt(mitk::Point3D &point, float eps, int timestep=0); /** \brief Remove a vertex at given index within the container. @return true = the vertex was successfuly removed; false = wrong index. */ bool RemoveVertexAt(int index, int timestep=0); /** \brief Remove a vertex at given timestep within the container. @return true = the vertex was successfuly removed. */ bool RemoveVertex(const VertexType* vertex, int timestep=0); /** \brief Remove a vertex at a query position in 3D space. The vertex to be removed will be search by nearest neighbour search. Note that possibly no vertex at this position and eps is stored inside the contour. @return true = the vertex was successfuly removed; false = no vertex found. */ bool RemoveVertexAt(mitk::Point3D &point, float eps, int timestep=0); /** \brief Shift the currently selected vertex by a translation vector. \param translate - the translation vector. */ void ShiftSelectedVertex(mitk::Vector3D &translate); /** \brief Shift the whole contour by a translation vector at given timestep. \param translate - the translation vector. \param timestep - at this timestep the contour will be shifted. */ void ShiftContour(mitk::Vector3D &translate, int timestep=0); /** \brief Clear the storage container at given timestep. All control points are removed at timestep. */ virtual void Clear(int timestep); /** \brief Initialize all data objects */ virtual void Initialize(); /** \brief Initialize object with specs of other contour. Note: No data will be copied. */ void Initialize(mitk::ContourModel &other); /*++++++++++++++++++ method inherit from base data +++++++++++++++++++++++++++*/ /** \brief Inherit from base data - no region support available for contourModel objects. */ virtual void SetRequestedRegionToLargestPossibleRegion (); /** \brief Inherit from base data - no region support available for contourModel objects. */ virtual bool RequestedRegionIsOutsideOfTheBufferedRegion (); /** \brief Inherit from base data - no region support available for contourModel objects. */ virtual bool VerifyRequestedRegion (); /** \brief Get the updated geometry with recomputed bounds. */ - virtual const mitk::Geometry3D* GetUpdatedGeometry (int t=0); + virtual const mitk::BaseGeometry* GetUpdatedGeometry (int t=0); /** \brief Get the Geometry3D for timestep t. */ - virtual mitk::Geometry3D* GetGeometry (int t=0) const; + virtual mitk::BaseGeometry* GetGeometry (int t=0) const; /** \brief Inherit from base data - no region support available for contourModel objects. */ virtual void SetRequestedRegion( const itk::DataObject *data); /** \brief Expand the timebounds of the TimeGeometry to given number of timesteps. */ virtual void Expand( unsigned int timeSteps ); /** \brief Update the OutputInformation of a ContourModel object The BoundingBox of the contour will be updated, if necessary. */ virtual void UpdateOutputInformation(); /** \brief Clear the storage container. The object is set to initial state. All control points are removed and the number of timesteps are set to 1. */ virtual void Clear(); /** \brief overwrite if the Data can be called by an Interactor (StateMachine). */ void ExecuteOperation(Operation* operation); /** \brief Redistributes ontrol vertices with a given period (as number of vertices) \param period - the number of vertices between control points. \param timestep - at this timestep all lines will be rebuilt. */ virtual void RedistributeControlVertices(int period, int timestep); protected: mitkCloneMacro(Self); ContourModel(); ContourModel(const mitk::ContourModel &other); virtual ~ContourModel(); //inherit from BaseData. called by Clear() virtual void ClearData(); //inherit from BaseData. Initial state of a contour with no vertices and a single timestep. virtual void InitializeEmpty(); //Shift a vertex void ShiftVertex(VertexType* vertex, mitk::Vector3D &vector); //Storage with time resolved support. ContourModelSeries m_ContourSeries; //The currently selected vertex. VertexType* m_SelectedVertex; //The interpolation of the line segment between control points. LineSegmentInterpolation m_lineInterpolation; //only update the bounding geometry if necessary bool m_UpdateBoundingBox; }; itkEventMacro( ContourModelEvent, itk::AnyEvent ); itkEventMacro( ContourModelShiftEvent, ContourModelEvent ); itkEventMacro( ContourModelSizeChangeEvent, ContourModelEvent ); itkEventMacro( ContourModelAddEvent, ContourModelSizeChangeEvent ); itkEventMacro( ContourModelRemoveEvent, ContourModelSizeChangeEvent ); itkEventMacro( ContourModelExpandTimeBoundsEvent, ContourModelEvent ); itkEventMacro( ContourModelClosedEvent, ContourModelEvent ); } #endif diff --git a/Modules/ContourModel/DataManagement/mitkContourModelSet.cpp b/Modules/ContourModel/DataManagement/mitkContourModelSet.cpp index 21dc6fef9a..e782bc11d8 100644 --- a/Modules/ContourModel/DataManagement/mitkContourModelSet.cpp +++ b/Modules/ContourModel/DataManagement/mitkContourModelSet.cpp @@ -1,214 +1,214 @@ /*=================================================================== 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 mitk::ContourModelSet::ContourModelSet() : m_Contours(), m_UpdateBoundingBox(true) { this->InitializeEmpty(); } mitk::ContourModelSet::ContourModelSet(const mitk::ContourModelSet &other) : mitk::BaseData(other), m_Contours(other.m_Contours) { this->InitializeTimeGeometry(1); } mitk::ContourModelSet::~ContourModelSet() { this->m_Contours.clear(); } void mitk::ContourModelSet::InitializeEmpty() { this->InitializeTimeGeometry(1); m_Contours.resize(0); } void mitk::ContourModelSet::AddContourModel(mitk::ContourModel &contourModel) { this->m_Contours.push_back(&contourModel); m_UpdateBoundingBox = true; } void mitk::ContourModelSet::AddContourModel(mitk::ContourModel::Pointer contourModel) { this->m_Contours.push_back(contourModel); m_UpdateBoundingBox = true; } mitk::ContourModel* mitk::ContourModelSet::GetContourModelAt(int index) { if( index >= 0 && static_cast(index) < this->m_Contours.size() ) { return this->m_Contours.at(index).GetPointer(); } else { return NULL; } } bool mitk::ContourModelSet::IsEmpty() const { return this->m_Contours.empty(); } mitk::ContourModelSet::ContourModelListType* mitk::ContourModelSet::GetContourModelList() { return &(this->m_Contours); } bool mitk::ContourModelSet::RemoveContourModel(mitk::ContourModel* contourModel) { ContourModelSetIterator it = this->m_Contours.begin(); ContourModelSetIterator end = this->m_Contours.end(); //search for ContourModel and remove it if exists while(it != end) { if((*it) == contourModel) { this->m_Contours.erase(it); m_UpdateBoundingBox = true; return true; } it++; } return false; } bool mitk::ContourModelSet::RemoveContourModelAt(int index) { if( index >= 0 && static_cast(index) < this->m_Contours.size() ) { this->m_Contours.erase(this->m_Contours.begin()+index); m_UpdateBoundingBox = true; return true; } else { return false; } } void mitk::ContourModelSet::Clear() { this->m_Contours.clear(); m_UpdateBoundingBox = true; } void mitk::ContourModelSet::UpdateOutputInformation() { if ( this->GetSource() ) { this->GetSource()->UpdateOutputInformation(); } if(this->m_UpdateBoundingBox) { //update the bounds of the geometry according to the stored vertices mitk::ScalarType mitkBounds[6]; //calculate the boundingbox at each timestep typedef itk::BoundingBox BoundingBoxType; typedef BoundingBoxType::PointsContainer PointsContainer; int timesteps = this->GetTimeSteps(); //iterate over the timesteps for(int currenTimeStep = 0; currenTimeStep < timesteps; currenTimeStep++) { //only update bounds if the contour was modified if (this->GetMTime() > this->GetGeometry(currenTimeStep)->GetBoundingBox()->GetMTime()) { mitkBounds[0] = 0.0; mitkBounds[1] = 0.0; mitkBounds[2] = 0.0; mitkBounds[3] = 0.0; mitkBounds[4] = 0.0; mitkBounds[5] = 0.0; BoundingBoxType::Pointer boundingBox = BoundingBoxType::New(); PointsContainer::Pointer points = PointsContainer::New(); mitk::ContourModelSet::ContourModelSetIterator contoursIt = this->Begin(); mitk::ContourModelSet::ContourModelSetIterator contoursEnd = this->End(); while(contoursIt!=contoursEnd) { mitk::ContourModel::VertexIterator it = contoursIt->GetPointer()->Begin(currenTimeStep); mitk::ContourModel::VertexIterator end = contoursIt->GetPointer()->End(currenTimeStep); //fill the boundingbox with the points while(it != end) { Point3D currentP = (*it)->Coordinates; BoundingBoxType::PointType p; p.CastFrom(currentP); points->InsertElement(points->Size(), p); it++; } ++contoursIt; } //construct the new boundingBox boundingBox->SetPoints(points); boundingBox->ComputeBoundingBox(); BoundingBoxType::BoundsArrayType tmp = boundingBox->GetBounds(); mitkBounds[0] = tmp[0]; mitkBounds[1] = tmp[1]; mitkBounds[2] = tmp[2]; mitkBounds[3] = tmp[3]; mitkBounds[4] = tmp[4]; mitkBounds[5] = tmp[5]; //set boundingBox at current timestep - Geometry3D* geometry3d = this->GetGeometry(currenTimeStep); + BaseGeometry* geometry3d = this->GetGeometry(currenTimeStep); geometry3d->SetBounds(mitkBounds); } } this->m_UpdateBoundingBox = false; } GetTimeGeometry()->Update(); } diff --git a/Modules/ContourModel/Rendering/mitkContourModelMapper2D.cpp b/Modules/ContourModel/Rendering/mitkContourModelMapper2D.cpp index 22f2c99458..4edb2fae92 100644 --- a/Modules/ContourModel/Rendering/mitkContourModelMapper2D.cpp +++ b/Modules/ContourModel/Rendering/mitkContourModelMapper2D.cpp @@ -1,392 +1,392 @@ /*=================================================================== 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 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/DataTypesExt/mitkBoundingObject.cpp b/Modules/DataTypesExt/mitkBoundingObject.cpp index c3755b4ac2..0c5340b0f5 100644 --- a/Modules/DataTypesExt/mitkBoundingObject.cpp +++ b/Modules/DataTypesExt/mitkBoundingObject.cpp @@ -1,72 +1,72 @@ /*=================================================================== 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 "mitkBoundingObject.h" #include "mitkBaseProcess.h" mitk::BoundingObject::BoundingObject() : Surface(), m_Positive(true) { // Initialize(1); /* bounding box around the unscaled bounding object */ ScalarType bounds[6]={-1,1,-1,1,-1,1}; //{xmin,x_max, ymin,y_max,zmin,z_max} GetGeometry()->SetBounds(bounds); GetTimeGeometry()->Update(); } mitk::BoundingObject::~BoundingObject() { } mitk::ScalarType mitk::BoundingObject::GetVolume() { return 0.0; } -void mitk::BoundingObject::FitGeometry(mitk::Geometry3D* aGeometry3D) +void mitk::BoundingObject::FitGeometry(mitk::BaseGeometry* aGeometry3D) { // Adjusted this function to fix // BUG 6951 - Image Cropper - Bounding Box is strange // Still, the behavior of the BoundingObject is really strange. // One would think that writing "setGeometry(aGeometry3D)" here would do the job. // But apparently the boundingObject can only be handled correctly, when it's // indexBounds are from -1 to 1 in all axis (so it is only 2x2x2 Pixels big) and the spacing // specifies it's actual bounds. This behavior needs to be analyzed and maybe changed. // Check also BUG 11406 GetGeometry()->SetIdentity(); GetGeometry()->Compose(aGeometry3D->GetIndexToWorldTransform()); // Since aGeometry (which should actually be const), is an imagegeometry and boundingObject is NOT an image, // we have to adjust the Origin by shifting it half pixel mitk::Point3D myOrigin = aGeometry3D->GetCenter(); myOrigin[0] -= (aGeometry3D->GetSpacing()[0] / 2.0); myOrigin[1] -= (aGeometry3D->GetSpacing()[1] / 2.0); myOrigin[2] -= (aGeometry3D->GetSpacing()[2] / 2.0); GetGeometry()->SetOrigin(myOrigin); mitk::Vector3D size; for(unsigned int i=0; i < 3; ++i) size[i] = (aGeometry3D->GetExtentInMM(i)/2.0); GetGeometry()->SetSpacing( size ); GetTimeGeometry()->Update(); } diff --git a/Modules/DataTypesExt/mitkBoundingObject.h b/Modules/DataTypesExt/mitkBoundingObject.h index 54db79458f..8e603d724e 100644 --- a/Modules/DataTypesExt/mitkBoundingObject.h +++ b/Modules/DataTypesExt/mitkBoundingObject.h @@ -1,69 +1,69 @@ /*=================================================================== 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 BOUNDINGOBJECT_H_HEADER_INCLUDED #define BOUNDINGOBJECT_H_HEADER_INCLUDED #include #include "MitkDataTypesExtExports.h" namespace mitk { //##Documentation //## @brief superclass of all bounding objects (cylinder, cuboid,...) //## //## Manages generic functions and provides an interface for IsInside() //## calculates a generic bounding box //## @ingroup Data class MitkDataTypesExt_EXPORT BoundingObject : public mitk::Surface //BaseData { public: mitkClassMacro(BoundingObject, mitk::Surface); virtual bool IsInside(const mitk::Point3D& p) const=0; virtual mitk::ScalarType GetVolume(); itkGetMacro(Positive, bool); itkSetMacro(Positive, bool); itkBooleanMacro(Positive); //##Documentation //## @brief Sets the Geometry3D of the bounding object to fit the given //## geometry. //## //## The fit is done once, so if the given geometry changes it will //## \em not effect the bounding object. - virtual void FitGeometry(Geometry3D* aGeometry3D); + virtual void FitGeometry(BaseGeometry* aGeometry3D); protected: BoundingObject(); virtual ~BoundingObject(); bool WriteXMLData( XMLWriter& xmlWriter ); //##Documentation //## \brief If \a true, the Boundingobject describes a positive volume, //## if \a false a negative volume. //## bool m_Positive; private: BoundingObject(const BoundingObject&); BoundingObject& operator=(const BoundingObject&); }; } #endif /* BOUNDINGOBJECT_H_HEADER_INCLUDED */ diff --git a/Modules/DataTypesExt/mitkBoundingObjectGroup.cpp b/Modules/DataTypesExt/mitkBoundingObjectGroup.cpp index d274470911..7054a19332 100644 --- a/Modules/DataTypesExt/mitkBoundingObjectGroup.cpp +++ b/Modules/DataTypesExt/mitkBoundingObjectGroup.cpp @@ -1,208 +1,208 @@ /*=================================================================== 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 "mitkBoundingObjectGroup.h" #include "mitkBaseProcess.h" #include #include mitk::BoundingObjectGroup::BoundingObjectGroup() :m_BoundingObjects(0), m_Counter(0), m_CSGMode(Union)// m_CSGMode(Difference) //m_CSGMode(Intersection) { ProportionalTimeGeometry::Pointer timeGeometry = ProportionalTimeGeometry::New(); timeGeometry->Initialize(1); SetTimeGeometry(timeGeometry); SetVtkPolyData(NULL); } mitk::BoundingObjectGroup::~BoundingObjectGroup() { } void mitk::BoundingObjectGroup::UpdateOutputInformation() { if ( this->GetSource() ) { this->GetSource()->UpdateOutputInformation(); } // calculate global bounding box if(m_BoundingObjects.size() < 1 ) // if there is no BoundingObject, the bounding box is zero { mitk::BoundingBox::BoundsArrayType boundsArray; boundsArray.Fill(0); ProportionalTimeGeometry::Pointer timeGeometry = ProportionalTimeGeometry::New(); timeGeometry->Initialize(1); SetTimeGeometry(timeGeometry); GetGeometry()->SetBounds(boundsArray); GetTimeGeometry()->Update(); return; } // initialize container mitk::BoundingBox::PointsContainer::Pointer pointscontainer=mitk::BoundingBox::PointsContainer::New(); mitk::BoundingBox::PointIdentifier pointid=0; mitk::Point3D point; mitk::AffineTransform3D* transform = GetGeometry()->GetIndexToWorldTransform(); mitk::AffineTransform3D::Pointer inverse = mitk::AffineTransform3D::New(); transform->GetInverse(inverse); // calculate a bounding box that includes all BoundingObjects // \todo probably we should do this additionally for each time-step //while (boundingObjectsIterator != boundingObjectsIteratorEnd) for(unsigned int j = 0; jGetUpdatedTimeGeometry(); unsigned char i; for(i=0; i<8; ++i) { point = inverse->TransformPoint(geometry->GetCornerPointInWorld(i)); if(point[0]*point[0]+point[1]*point[1]+point[2]*point[2] < mitk::large) pointscontainer->InsertElement( pointid++, point); else { itkGenericOutputMacro( << "Unrealistically distant corner point encountered. Ignored. BoundingObject: " << m_BoundingObjects.at(j) ); } } } mitk::BoundingBox::Pointer boundingBox = mitk::BoundingBox::New(); boundingBox->SetPoints(pointscontainer); boundingBox->ComputeBoundingBox(); - Geometry3D* geometry3d = GetGeometry(0); + BaseGeometry* geometry3d = GetGeometry(0); geometry3d->SetIndexToWorldTransform(transform); geometry3d->SetBounds(boundingBox->GetBounds()); /* the objects position is the center of all sub bounding objects */ //geometry3d->SetOrigin(center); ProportionalTimeGeometry::Pointer timeGeometry = ProportionalTimeGeometry::New(); timeGeometry->Initialize(geometry3d, GetTimeGeometry()->CountTimeSteps()); SetTimeGeometry(timeGeometry); } void mitk::BoundingObjectGroup::AddBoundingObject(mitk::BoundingObject::Pointer boundingObject) { if (boundingObject->GetPositive()) m_BoundingObjects.push_front(boundingObject); else m_BoundingObjects.push_back(boundingObject); ++m_Counter; UpdateOutputInformation(); } void mitk::BoundingObjectGroup::RemoveBoundingObject(mitk::BoundingObject::Pointer boundingObject) { std::deque::iterator it = m_BoundingObjects.begin(); for (unsigned int i=0 ; iIsInside(p) && inside; if (!inside) // shortcut, it is enough to find one object that does not contain the point i=m_BoundingObjects.size(); break; case Union: case Difference: posInside = false; negInside = false; // calculate union: each point, that is inside least one BoundingObject is considered inside the group if (m_BoundingObjects.at(i)->GetPositive()) posInside = m_BoundingObjects.at(i)->IsInside(p) || posInside; else negInside = m_BoundingObjects.at(i)->IsInside(p) || negInside; if (posInside && !negInside) inside = true; else inside = false; break; default: inside = false; // calculate union: each point, that is inside least one BoundingObject is considered inside the group inside = m_BoundingObjects.at(i)->IsInside(p) || inside; if (inside) // shortcut, it is enough to find one object that contains the point i=m_BoundingObjects.size(); break; } } return inside; } unsigned int mitk::BoundingObjectGroup::GetCount() const { return m_Counter; } bool mitk::BoundingObjectGroup::VerifyRequestedRegion() { return m_Counter > 0; } -mitk::Geometry3D * mitk::BoundingObjectGroup::GetGeometry (int t) const +mitk::BaseGeometry * mitk::BoundingObjectGroup::GetGeometry (int t) const { //if ( m_BoundingObjects == NULL ) return Superclass::GetGeometry(t); //mitk::BoundingObjectGroup::BoundingObjectContainer::ConstIterator boI = m_BoundingObjects->Begin(); //const mitk::BoundingObjectGroup::BoundingObjectContainer::ConstIterator boIEnd = m_BoundingObjects->End(); //mitk::Geometry3D* currentGeometry = NULL; //while ( boI != boIEnd ) //{ // currentGeometry = boI.Value()->GetGeometry( t ); // boI++; //} //return currentGeometry; } void mitk::BoundingObjectGroup::SetBoundingObjects(const std::deque boundingObjects) { m_BoundingObjects = boundingObjects; } std::deque mitk::BoundingObjectGroup::GetBoundingObjects() { return m_BoundingObjects; } diff --git a/Modules/DataTypesExt/mitkBoundingObjectGroup.h b/Modules/DataTypesExt/mitkBoundingObjectGroup.h index c7a18c25fe..81fe69e67b 100644 --- a/Modules/DataTypesExt/mitkBoundingObjectGroup.h +++ b/Modules/DataTypesExt/mitkBoundingObjectGroup.h @@ -1,71 +1,71 @@ /*=================================================================== 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 BOUNDINGOBJECTGROUP_H_HEADER_INCLUDED #define BOUNDINGOBJECTGROUP_H_HEADER_INCLUDED #include #include "MitkDataTypesExtExports.h" #include namespace mitk { //##Documentation //## @brief group object, that contains several mitk::BoundingObjects //## //## Calculates a bounding box that contains all sub-bounding boxes. //## @ingroup Data class MitkDataTypesExt_EXPORT BoundingObjectGroup : public mitk::BoundingObject { public: enum CSGMode { Union, Intersection, Difference }; mitkClassMacro(BoundingObjectGroup, mitk::BoundingObject);// itk::VectorContainer); itkFactorylessNewMacro(Self) itkCloneMacro(Self) virtual void UpdateOutputInformation(); virtual bool IsInside(const mitk::Point3D& p) const; void SetBoundingObjects(const std::deque boundingObjects); std::deque GetBoundingObjects(); itkSetMacro(CSGMode, mitk::BoundingObjectGroup::CSGMode); itkGetMacro(CSGMode, mitk::BoundingObjectGroup::CSGMode); void AddBoundingObject(mitk::BoundingObject::Pointer boundingObject); void RemoveBoundingObject(mitk::BoundingObject::Pointer boundingObject); unsigned int GetCount() const; - mitk::Geometry3D * GetGeometry (int t=0) const; + mitk::BaseGeometry * GetGeometry (int t=0) const; virtual bool VerifyRequestedRegion(); protected: BoundingObjectGroup(); virtual ~BoundingObjectGroup(); std::deque m_BoundingObjects; unsigned int m_Counter; CSGMode m_CSGMode; }; } #endif /* BOUNDINGOBJECTGROUP_H_HEADER_INCLUDED */ diff --git a/Modules/DataTypesExt/mitkCompressedImageContainer.h b/Modules/DataTypesExt/mitkCompressedImageContainer.h index 8a094bea72..9a5c231246 100644 --- a/Modules/DataTypesExt/mitkCompressedImageContainer.h +++ b/Modules/DataTypesExt/mitkCompressedImageContainer.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 mitkCompressedImageContainer_h_Included #define mitkCompressedImageContainer_h_Included #include "mitkCommon.h" #include "MitkDataTypesExtExports.h" #include "mitkImage.h" #include "mitkGeometry3D.h" #include "mitkImageDataItem.h" #include #include namespace mitk { /** \brief Holds one (compressed) mitk::Image Uses zlib to compress the data of an mitk::Image. $Author$ */ class MitkDataTypesExt_EXPORT CompressedImageContainer : public itk::Object { public: mitkClassMacro(CompressedImageContainer, Object); itkFactorylessNewMacro(Self) itkCloneMacro(Self) /** * \brief Creates a compressed version of the image. * * Will not hold any further SmartPointers to the image. * */ void SetImage( Image* ); /** * \brief Creates a full mitk::Image from its compressed version. * * This Method hold no buffer, so the uncompression algorithm will be * executed every time you call this method. Don't overdo it. * */ Image::Pointer GetImage(); protected: CompressedImageContainer(); // purposely hidden virtual ~CompressedImageContainer(); PixelType *m_PixelType; unsigned int m_ImageDimension; std::vector m_ImageDimensions; unsigned long m_OneTimeStepImageSizeInBytes; unsigned int m_NumberOfTimeSteps; /// one for each timestep. first = pointer to compressed data; second = size of buffer in bytes std::vector< std::pair > m_ByteBuffers; - Geometry3D::Pointer m_ImageGeometry; + BaseGeometry::Pointer m_ImageGeometry; }; } // namespace #endif diff --git a/Modules/DataTypesExt/mitkLabeledImageVolumeCalculator.cpp b/Modules/DataTypesExt/mitkLabeledImageVolumeCalculator.cpp index 02364f84b1..67e9f20dd4 100644 --- a/Modules/DataTypesExt/mitkLabeledImageVolumeCalculator.cpp +++ b/Modules/DataTypesExt/mitkLabeledImageVolumeCalculator.cpp @@ -1,146 +1,146 @@ /*=================================================================== 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 "mitkLabeledImageVolumeCalculator.h" #include "mitkImageAccessByItk.h" #include namespace mitk { LabeledImageVolumeCalculator::LabeledImageVolumeCalculator() { m_InputTimeSelector = ImageTimeSelector::New(); m_DummyPoint.Fill( 0.0 ); } LabeledImageVolumeCalculator::~LabeledImageVolumeCalculator() { } double LabeledImageVolumeCalculator::GetVolume( unsigned int label ) const { if ( label < m_VolumeVector.size() ) return m_VolumeVector[label]; else return 0.0; } const Point3D &LabeledImageVolumeCalculator::GetCentroid( unsigned int label ) const { if ( label < m_CentroidVector.size() ) return m_CentroidVector[label]; else return m_DummyPoint; } const LabeledImageVolumeCalculator::VolumeVector & LabeledImageVolumeCalculator::GetVolumes() const { return m_VolumeVector; } const LabeledImageVolumeCalculator::PointVector & LabeledImageVolumeCalculator::GetCentroids() const { return m_CentroidVector; } void LabeledImageVolumeCalculator::Calculate() { if ( m_Image.IsNull() ) { itkExceptionMacro( << "Image not set!" ); return; } m_InputTimeSelector->SetInput( m_Image ); m_InputTimeSelector->SetTimeNr( 0 ); m_InputTimeSelector->UpdateLargestPossibleRegion(); AccessByItk_2( m_InputTimeSelector->GetOutput(), _InternalCalculateVolumes, this, m_Image->GetGeometry( 0 ) ); //} } template < typename TPixel, unsigned int VImageDimension > void LabeledImageVolumeCalculator::_InternalCalculateVolumes( itk::Image< TPixel, VImageDimension > *image, LabeledImageVolumeCalculator* /*volumeCalculator*/, - Geometry3D *geometry ) + BaseGeometry *geometry ) { typedef itk::Image< TPixel, VImageDimension > ImageType; typedef typename ImageType::IndexType IndexType; typedef itk::ImageRegionConstIteratorWithIndex< ImageType > IteratorType; // Reset volume and centroid vectors m_VolumeVector.clear(); m_CentroidVector.clear(); // Iterate over image and determine number of voxels and centroid // per label IteratorType it( image, image->GetBufferedRegion() ); for ( it.GoToBegin(); !it.IsAtEnd(); ++it ) { const IndexType &index = it.GetIndex(); unsigned int pixel = static_cast( it.Get() ); if ( m_VolumeVector.size() <= pixel ) { m_VolumeVector.resize( pixel + 1 ); m_CentroidVector.resize( pixel + 1 ); } m_VolumeVector[pixel] += 1.0; m_CentroidVector[pixel][0] += index[0]; m_CentroidVector[pixel][1] += index[1]; m_CentroidVector[pixel][2] += index[2]; } // Calculate voxel volume from spacing const Vector3D &spacing = geometry->GetSpacing(); double voxelVolume = spacing[0] * spacing[1] * spacing[2]; // Calculate centroid (in world coordinates) and volumes for all labels for ( unsigned int i = 0; i < m_VolumeVector.size(); ++i ) { if ( m_VolumeVector[i] > 0.0 ) { m_CentroidVector[i][0] /= m_VolumeVector[i]; m_CentroidVector[i][1] /= m_VolumeVector[i]; m_CentroidVector[i][2] /= m_VolumeVector[i]; geometry->IndexToWorld( m_CentroidVector[i], m_CentroidVector[i] ); m_VolumeVector[i] *= voxelVolume; } } } } diff --git a/Modules/DataTypesExt/mitkLabeledImageVolumeCalculator.h b/Modules/DataTypesExt/mitkLabeledImageVolumeCalculator.h index b477e1665c..0f17e401da 100644 --- a/Modules/DataTypesExt/mitkLabeledImageVolumeCalculator.h +++ b/Modules/DataTypesExt/mitkLabeledImageVolumeCalculator.h @@ -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. ===================================================================*/ #ifndef _MITK_LABELEDIMAGEVOLUMECALCULATOR_H #define _MITK_LABELEDIMAGEVOLUMECALCULATOR_H #include #include "MitkDataTypesExtExports.h" #include "mitkImage.h" #include "mitkImageTimeSelector.h" #include namespace mitk { /** * \brief Class for calculating the volume (or area) for each label in a * labeled image. * * Labels are expected to be of an unsigned integer type. * * TODO: Extend class for time resolved images */ class MitkDataTypesExt_EXPORT LabeledImageVolumeCalculator : public itk::Object { public: typedef std::vector< double > VolumeVector; typedef std::vector< Point3D > PointVector; mitkClassMacro( LabeledImageVolumeCalculator, itk::Object ); itkFactorylessNewMacro(Self) itkCloneMacro(Self) itkSetConstObjectMacro( Image, mitk::Image ); virtual void Calculate(); double GetVolume( unsigned int label ) const; const Point3D &GetCentroid( unsigned int label ) const; const VolumeVector &GetVolumes() const; const PointVector &GetCentroids() const; protected: LabeledImageVolumeCalculator(); virtual ~LabeledImageVolumeCalculator(); template < typename TPixel, unsigned int VImageDimension > void _InternalCalculateVolumes( itk::Image< TPixel, VImageDimension > *image, LabeledImageVolumeCalculator *volumeCalculator, - Geometry3D *geometry ); + BaseGeometry *geometry ); ImageTimeSelector::Pointer m_InputTimeSelector; Image::ConstPointer m_Image; VolumeVector m_VolumeVector; PointVector m_CentroidVector; Point3D m_DummyPoint; }; } #endif // #define _MITK_LABELEDIMAGEVOLUMECALCULATOR_H diff --git a/Modules/DataTypesExt/mitkSurfaceDeformationDataInteractor3D.cpp b/Modules/DataTypesExt/mitkSurfaceDeformationDataInteractor3D.cpp index ad946c4722..54bbefb4e3 100644 --- a/Modules/DataTypesExt/mitkSurfaceDeformationDataInteractor3D.cpp +++ b/Modules/DataTypesExt/mitkSurfaceDeformationDataInteractor3D.cpp @@ -1,302 +1,302 @@ /*=================================================================== 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 "mitkSurfaceDeformationDataInteractor3D.h" #include "mitkMouseWheelEvent.h" #include #include mitk::SurfaceDeformationDataInteractor3D::SurfaceDeformationDataInteractor3D() :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::SurfaceDeformationDataInteractor3D::~SurfaceDeformationDataInteractor3D() { m_OriginalPolyData->Delete(); } void mitk::SurfaceDeformationDataInteractor3D::ConnectActionsAndFunctions() { // **Conditions** that can be used in the state machine, to ensure that certain conditions are met, before // actually executing an action CONNECT_CONDITION("isOverObject", CheckOverObject); // **Function** in the statmachine patterns also referred to as **Actions** CONNECT_FUNCTION("selectObject",SelectObject); CONNECT_FUNCTION("deselectObject",DeselectObject); CONNECT_FUNCTION("initDeformation",InitDeformation); CONNECT_FUNCTION("deformObject",DeformObject); CONNECT_FUNCTION("scaleRadius", ScaleRadius); } void mitk::SurfaceDeformationDataInteractor3D::DataNodeChanged() { if(this->GetDataNode().IsNotNull()) { m_Surface = dynamic_cast(this->GetDataNode()->GetData()); if (m_Surface == NULL) MITK_ERROR << "SurfaceDeformationDataInteractor3D::DataNodeChanged(): DataNode has to contain a surface."; } else m_Surface = NULL; } bool mitk::SurfaceDeformationDataInteractor3D::CheckOverObject(const InteractionEvent* interactionEvent) { const InteractionPositionEvent* positionEvent = dynamic_cast(interactionEvent); if(positionEvent == NULL) return false; Point2D currentPickedDisplayPoint = positionEvent->GetPointerPositionOnScreen(); Point3D currentPickedPoint; if(interactionEvent->GetSender()->PickObject(currentPickedDisplayPoint, currentPickedPoint) == this->GetDataNode().GetPointer()) { // Colorized surface at current picked position m_SurfaceColorizationCenter = currentPickedPoint; return true; } return false; } bool mitk::SurfaceDeformationDataInteractor3D::SelectObject(StateMachineAction*, InteractionEvent* interactionEvent) { const InteractionPositionEvent* positionEvent = dynamic_cast(interactionEvent); if(positionEvent == NULL) return false; int timeStep = interactionEvent->GetSender()->GetTimeStep(this->GetDataNode()->GetData()); vtkPolyData* polyData = m_Surface->GetVtkPolyData(timeStep); this->GetDataNode()->SetColor(1.0, 0.0, 0.0); // Colorize surface / wireframe dependend on distance from picked point this->ColorizeSurface(polyData, timeStep, m_SurfaceColorizationCenter, COLORIZATION_GAUSS); interactionEvent->GetSender()->GetRenderingManager()->RequestUpdateAll(); return true; } bool mitk::SurfaceDeformationDataInteractor3D::DeselectObject(StateMachineAction*, InteractionEvent* interactionEvent) { const InteractionPositionEvent* positionEvent = dynamic_cast(interactionEvent); if(positionEvent == NULL) return false; int timeStep = interactionEvent->GetSender()->GetTimeStep(this->GetDataNode()->GetData()); vtkPolyData* polyData = m_Surface->GetVtkPolyData(timeStep); this->GetDataNode()->SetColor(1.0, 1.0, 1.0); // Colorize surface / wireframe as inactive this->ColorizeSurface(polyData, timeStep, m_SurfaceColorizationCenter, COLORIZATION_CONSTANT, -1.0); interactionEvent->GetSender()->GetRenderingManager()->RequestUpdateAll(); return true; } bool mitk::SurfaceDeformationDataInteractor3D::InitDeformation(StateMachineAction*, InteractionEvent* interactionEvent) { const InteractionPositionEvent* positionEvent = dynamic_cast(interactionEvent); if(positionEvent == NULL) return false; int timeStep = interactionEvent->GetSender()->GetTimeStep(this->GetDataNode()->GetData()); vtkPolyData* polyData = m_Surface->GetVtkPolyData(timeStep); // Store current picked point interactionEvent->GetSender()->PickObject(positionEvent->GetPointerPositionOnScreen(), m_InitialPickedPoint); // Make deep copy of vtkPolyData interacted on m_OriginalPolyData->DeepCopy(polyData); return true; } bool mitk::SurfaceDeformationDataInteractor3D::DeformObject (StateMachineAction*, InteractionEvent* interactionEvent) { const InteractionPositionEvent* positionEvent = dynamic_cast(interactionEvent); if(positionEvent == NULL) return false; int timeStep = interactionEvent->GetSender()->GetTimeStep(this->GetDataNode()->GetData()); vtkPolyData* polyData = m_Surface->GetVtkPolyData(timeStep); - Geometry3D::Pointer geometry = this->GetDataNode()->GetData()->GetGeometry(timeStep); + BaseGeometry::Pointer geometry = this->GetDataNode()->GetData()->GetGeometry(timeStep); Point3D currentPickedPoint = positionEvent->GetPositionInWorld(); // Calculate mouse move in 3D space Vector3D interactionMove; interactionMove[0] = currentPickedPoint[0] - m_InitialPickedPoint[0]; interactionMove[1] = currentPickedPoint[1] - m_InitialPickedPoint[1]; interactionMove[2] = currentPickedPoint[2] - m_InitialPickedPoint[2]; // Transform mouse move into geometry space this->GetDataNode()->GetData()->UpdateOutputInformation();// make sure that the Geometry is up-to-date Vector3D interactionMoveIndex; geometry->WorldToIndex(interactionMove, interactionMoveIndex); // Get picked point and transform into local coordinates Point3D pickedPoint; geometry->WorldToIndex(m_InitialPickedPoint, pickedPoint); Vector3D v1 = pickedPoint.GetVectorFromOrigin(); 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); } Vector3D v2 = m_ObjectNormal * (interactionMoveIndex * m_ObjectNormal); vtkPoints* originalPoints = m_OriginalPolyData->GetPoints(); vtkPoints* deformedPoints = polyData->GetPoints(); double denom = m_GaussSigma * m_GaussSigma * 2; double point[3]; for (vtkIdType i = 0; i < deformedPoints->GetNumberOfPoints(); ++i) { // Get original point double* originalPoint = originalPoints->GetPoint( i ); 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(); 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; polyData->Modified(); m_Surface->Modified(); interactionEvent->GetSender()->GetRenderingManager()->RequestUpdateAll(); return true; } bool mitk::SurfaceDeformationDataInteractor3D::ScaleRadius(StateMachineAction*, InteractionEvent* interactionEvent) { const MouseWheelEvent* wheelEvent = dynamic_cast(interactionEvent); if(wheelEvent == NULL) return false; m_GaussSigma += (double) (wheelEvent->GetWheelDelta()) / 20; if ( m_GaussSigma < 10.0 ) { m_GaussSigma = 10.0; } else if ( m_GaussSigma > 128.0 ) { m_GaussSigma = 128.0; } int timeStep = interactionEvent->GetSender()->GetTimeStep(this->GetDataNode()->GetData()); vtkPolyData* polyData = m_Surface->GetVtkPolyData(timeStep); // Colorize surface / wireframe dependend on sigma and distance from picked point this->ColorizeSurface( polyData, timeStep, m_SurfaceColorizationCenter, COLORIZATION_GAUSS ); interactionEvent->GetSender()->GetRenderingManager()->RequestUpdateAll(); return true; } bool mitk::SurfaceDeformationDataInteractor3D::ColorizeSurface(vtkPolyData* polyData, int timeStep, 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 Point3D localPickedPoint; - Geometry3D::Pointer geometry = this->GetDataNode()->GetData()->GetGeometry(timeStep); + BaseGeometry::Pointer geometry = this->GetDataNode()->GetData()->GetGeometry(timeStep); geometry->WorldToIndex( pickedPoint, localPickedPoint ); Vector3D v1 = localPickedPoint.GetVectorFromOrigin(); 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); } double denom = m_GaussSigma * m_GaussSigma * 2; for (vtkIdType i = 0; i < points->GetNumberOfPoints(); ++i) { // Get original point double* point = points->GetPoint(i); 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 (vtkIdType i = 0; i < pointData->GetNumberOfTuples(); ++i) { scalars->SetComponent(i, 0, scalar); } } polyData->Modified(); pointData->Update(); return true; } diff --git a/Modules/DataTypesExt/mitkUnstructuredGrid.cpp b/Modules/DataTypesExt/mitkUnstructuredGrid.cpp index 01f34f61b2..8dfe2970d8 100644 --- a/Modules/DataTypesExt/mitkUnstructuredGrid.cpp +++ b/Modules/DataTypesExt/mitkUnstructuredGrid.cpp @@ -1,248 +1,248 @@ /*=================================================================== 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 "mitkUnstructuredGrid.h" #include void mitk::UnstructuredGrid::SetVtkUnstructuredGrid( vtkUnstructuredGrid* grid, unsigned int t ) { this->Expand(t); if(m_GridSeries[ t ] != NULL) { m_GridSeries[ t ]->Delete(); } m_GridSeries[ t ] = grid; // call m_VtkPolyData->Register(NULL) to tell the reference counting that we // want to keep a reference on the object if (m_GridSeries[t] != 0) m_GridSeries[t]->Register(grid); this->Modified(); m_CalculateBoundingBox = true; } void mitk::UnstructuredGrid::Expand(unsigned int timeSteps) { // check if the vector is long enough to contain the new element // at the given position. If not, expand it with sufficient zero-filled elements. if(timeSteps > m_GridSeries.size()) { Superclass::Expand(timeSteps); vtkUnstructuredGrid* pdnull = 0; m_GridSeries.resize( timeSteps, pdnull ); m_CalculateBoundingBox = true; } } void mitk::UnstructuredGrid::ClearData() { for ( VTKUnstructuredGridSeries::iterator it = m_GridSeries.begin(); it != m_GridSeries.end(); ++it ) { if ( ( *it ) != 0 ) ( *it )->Delete(); } m_GridSeries.clear(); Superclass::ClearData(); } void mitk::UnstructuredGrid::InitializeEmpty() { vtkUnstructuredGrid* pdnull = 0; m_GridSeries.resize( 1, pdnull ); Superclass::InitializeTimeGeometry(1); m_Initialized = true; } vtkUnstructuredGrid* mitk::UnstructuredGrid::GetVtkUnstructuredGrid(unsigned int t) { if ( t < m_GridSeries.size() ) { vtkUnstructuredGrid* grid = m_GridSeries[ t ]; if((grid == 0) && (GetSource().GetPointer() != 0)) { RegionType requestedregion; requestedregion.SetIndex(3, t); requestedregion.SetSize(3, 1); SetRequestedRegion(&requestedregion); GetSource()->Update(); } grid = m_GridSeries[ t ]; return grid; } else return 0; } mitk::UnstructuredGrid::UnstructuredGrid() : m_CalculateBoundingBox( false ) { this->InitializeEmpty(); } mitk::UnstructuredGrid::UnstructuredGrid(const mitk::UnstructuredGrid &other) : BaseData(other), m_LargestPossibleRegion(other.m_LargestPossibleRegion), m_CalculateBoundingBox( other.m_CalculateBoundingBox ) { if(!other.m_Initialized) { this->InitializeEmpty(); } else { m_GridSeries = other.m_GridSeries; m_Initialized = other.m_Initialized; } this->SetRequestedRegion( const_cast(&other) ); } mitk::UnstructuredGrid::~UnstructuredGrid() { this->ClearData(); } void mitk::UnstructuredGrid::UpdateOutputInformation() { if ( this->GetSource() ) { this->GetSource()->UpdateOutputInformation(); } if ( ( m_CalculateBoundingBox ) && ( m_GridSeries.size() > 0 ) ) CalculateBoundingBox(); else GetTimeGeometry()->Update(); } void mitk::UnstructuredGrid::CalculateBoundingBox() { // // first make sure, that the associated time sliced geometry has // the same number of geometry 3d's as vtkUnstructuredGrids are present // TimeGeometry* timeGeometry = GetTimeGeometry(); if ( timeGeometry->CountTimeSteps() != m_GridSeries.size() ) { itkExceptionMacro(<<"timeGeometry->CountTimeSteps() != m_GridSeries.size() -- use Initialize(timeSteps) with correct number of timeSteps!"); } // // Iterate over the vtkUnstructuredGrids and update the Geometry // information of each of the items. // for ( unsigned int i = 0 ; i < m_GridSeries.size() ; ++i ) { vtkUnstructuredGrid* grid = m_GridSeries[ i ]; double bounds[ ] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0}; if ( ( grid != 0 ) && ( grid->GetNumberOfCells() > 0 ) ) { // grid->Update(); //VTK6_TODO grid->ComputeBounds(); grid->GetBounds( bounds ); } - mitk::Geometry3D::Pointer g3d = timeGeometry->GetGeometryForTimeStep( i ); + mitk::BaseGeometry::Pointer g3d = timeGeometry->GetGeometryForTimeStep( i ); assert( g3d.IsNotNull() ); g3d->SetFloatBounds( bounds ); } timeGeometry->Update(); mitk::BoundingBox::Pointer bb = const_cast( timeGeometry->GetBoundingBoxInWorld() ); itkDebugMacro( << "boundingbox min: "<< bb->GetMinimum()); itkDebugMacro( << "boundingbox max: "<< bb->GetMaximum()); m_CalculateBoundingBox = false; } void mitk::UnstructuredGrid::SetRequestedRegionToLargestPossibleRegion() { m_RequestedRegion = GetLargestPossibleRegion(); } bool mitk::UnstructuredGrid::RequestedRegionIsOutsideOfTheBufferedRegion() { RegionType::IndexValueType end = m_RequestedRegion.GetIndex(3)+m_RequestedRegion.GetSize(3); if(((RegionType::IndexValueType)m_GridSeries.size()) < end) return true; for( RegionType::IndexValueType t=m_RequestedRegion.GetIndex(3); t < end; ++t ) if(m_GridSeries[t] == 0) return true; return false; } bool mitk::UnstructuredGrid::VerifyRequestedRegion() { if( (m_RequestedRegion.GetIndex(3)>=0) && (m_RequestedRegion.GetIndex(3)+m_RequestedRegion.GetSize(3)<=m_GridSeries.size()) ) return true; return false; } void mitk::UnstructuredGrid::SetRequestedRegion(const itk::DataObject *data ) { const mitk::UnstructuredGrid *gridData; gridData = dynamic_cast(data); if (gridData) { m_RequestedRegion = gridData->GetRequestedRegion(); } else { // pointer could not be cast back down itkExceptionMacro( << "mitk::UnstructuredGrid::SetRequestedRegion(DataObject*) cannot cast " << typeid(data).name() << " to " << typeid(UnstructuredGrid*).name() ); } } void mitk::UnstructuredGrid::SetRequestedRegion(UnstructuredGrid::RegionType *region) //by arin { if(region != 0) { m_RequestedRegion = *region; } else { // pointer could not be cast back down itkExceptionMacro( << "mitk::UnstructuredGrid::SetRequestedRegion(UnstructuredGrid::RegionType*) cannot cast " << typeid(region).name() << " to " << typeid(UnstructuredGrid*).name() ); } } void mitk::UnstructuredGrid::CopyInformation( const itk::DataObject * data ) { Superclass::CopyInformation(data); } void mitk::UnstructuredGrid::Update() { if ( GetSource().IsNull() ) { for ( VTKUnstructuredGridSeries::iterator it = m_GridSeries.begin() ; it != m_GridSeries.end() ; ++it ) { // if ( ( *it ) != 0 ) // ( *it )->Update(); //VTK6_TODO } } Superclass::Update(); } diff --git a/Modules/Ext/Algorithms/mitkInterpolateLinesFilter.cpp b/Modules/Ext/Algorithms/mitkInterpolateLinesFilter.cpp index f6fb1c3fb7..0a124c1776 100644 --- a/Modules/Ext/Algorithms/mitkInterpolateLinesFilter.cpp +++ b/Modules/Ext/Algorithms/mitkInterpolateLinesFilter.cpp @@ -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. ===================================================================*/ #include "mitkInterpolateLinesFilter.h" #include "mitkMesh.h" #include "mitkSurface.h" #include #include #include #include #include mitk::InterpolateLinesFilter::InterpolateLinesFilter() : m_SplineResolution(10), m_GeometryForInterpolation(NULL), m_Length(0.0) { m_SpX=vtkCardinalSpline::New(); m_SpY=vtkCardinalSpline::New(); m_SpZ=vtkCardinalSpline::New(); } mitk::InterpolateLinesFilter::~InterpolateLinesFilter() { m_SpX->Delete(); m_SpY->Delete(); m_SpZ->Delete(); } void mitk::InterpolateLinesFilter::GenerateOutputInformation() { mitk::Mesh::ConstPointer input = this->GetInput(); mitk::Surface::Pointer output = this->GetOutput(0); itkDebugMacro(<<"GenerateOutputInformation()"); if(input.IsNull()) return; if(m_GeometryForInterpolation.IsNotNull()) - output->SetGeometry(static_cast(m_GeometryForInterpolation->Clone().GetPointer())); + output->SetGeometry(static_cast(m_GeometryForInterpolation->Clone().GetPointer())); else - output->SetGeometry(static_cast(input->GetGeometry()->Clone().GetPointer())); + output->SetGeometry(static_cast(input->GetGeometry()->Clone().GetPointer())); } void mitk::InterpolateLinesFilter::GenerateData() { mitk::Mesh::ConstPointer input = this->GetInput(); mitk::Surface::Pointer output = this->GetOutput(0); vtkPolyData *polyData = vtkPolyData::New(); vtkPoints *points = vtkPoints::New(); vtkCellArray *cellarray = vtkCellArray::New(); mitk::Mesh::PointType thisPoint; m_Length = 0.0; //iterate through all cells and build tubes Mesh::ConstCellIterator cellIt, cellEnd; cellEnd = input->GetMesh()->GetCells()->End(); for( cellIt = input->GetMesh()->GetCells()->Begin(); cellIt != cellEnd; ++cellIt ) { if(((*cellIt->Value()).GetType()==mitk::Mesh::CellType::POLYGON_CELL) && ((*cellIt->Value()).GetNumberOfPoints()>=2)) BuildPointAndVectorList(*cellIt->Value(), points, cellarray); } polyData->SetPoints( points ); points->Delete(); polyData->SetLines( cellarray ); cellarray->Delete(); output->SetVtkPolyData(polyData); polyData->Delete(); } void mitk::InterpolateLinesFilter::BuildPointAndVectorList(mitk::Mesh::CellType& cell, vtkPoints* points, vtkCellArray* cellarray) { const mitk::Mesh* input = GetInput(); Mesh::PointIdIterator ptIt; Mesh::PointIdIterator ptEnd; ptEnd = cell.PointIdsEnd(); Point3D pt; int i, size=cell.GetNumberOfPoints(); int closed_loop_pre_load=0;//m_SplineResolution; if(m_GeometryForInterpolation.IsNull()) { //when the contour is closed: first point insert behind two last points for smooth crossing ptIt = ptEnd; ptIt-=closed_loop_pre_load+1; for(i=0;iGetPoint(*ptIt); m_SpX->AddPoint(i, pt[0]); m_SpY->AddPoint(i, pt[1]); m_SpZ->AddPoint(i, pt[2]); } //insert points for(ptIt = cell.PointIdsBegin();iGetPoint(*ptIt); m_SpX->AddPoint(i, pt[0]); m_SpY->AddPoint(i, pt[1]); m_SpZ->AddPoint(i, pt[2]); } //when the contour is closed: after the last point insert two first points for smooth crossing int j; for(j=0,ptIt = cell.PointIdsBegin();jGetPoint(*ptIt); m_SpX->AddPoint(i, pt[0]); m_SpY->AddPoint(i, pt[1]); m_SpZ->AddPoint(i, pt[2]); } //bool first = true; Point3D lastPt, firstPt; Vector3D vec; float t, step=1.0f/m_SplineResolution; size=(size-1)*m_SplineResolution; i=closed_loop_pre_load; cellarray->InsertNextCell(size); for(t=closed_loop_pre_load;iEvaluate(t), m_SpY->Evaluate(t), m_SpZ->Evaluate(t)); cellarray->InsertCellPoint(points->InsertNextPoint(pt)); } } else //m_GeometryForInterpolation!=NULL { Point2D pt2d; //when the contour is closed: before the first point insert two last points for smooth crossing ptIt = ptEnd; ptIt-=closed_loop_pre_load+1; for(i=0;iMap(input->GetPoint(*ptIt), pt2d); m_SpX->AddPoint(i, pt2d[0]); m_SpY->AddPoint(i, pt2d[1]); } //insert points for(ptIt = cell.PointIdsBegin();iMap(input->GetPoint(*ptIt), pt2d); m_SpX->AddPoint(i, pt2d[0]); m_SpY->AddPoint(i, pt2d[1]); } //when the contour is closed: after the last point insert two first points for smooth crossing int j; for(j=0,ptIt = cell.PointIdsBegin();jMap(input->GetPoint(*ptIt), pt2d); m_SpX->AddPoint(i, pt2d[0]); m_SpY->AddPoint(i, pt2d[1]); } bool first = true; Point3D lastPt; lastPt.Fill(0); Vector3D vec; float t, step=1.0f/m_SplineResolution; size=(size-1)*m_SplineResolution; i=closed_loop_pre_load; cellarray->InsertNextCell(size); for(t=closed_loop_pre_load;iEvaluate(t); pt2d[1] = m_SpY->Evaluate(t); m_GeometryForInterpolation->Map(pt2d, pt); if(first==false) { vec=pt-lastPt; m_Length+=vec.GetNorm(); } first=false; float pvtk[3]; itk2vtk(pt, pvtk); cellarray->InsertCellPoint(points->InsertNextPoint(pvtk)); lastPt = pt; } } } const mitk::Mesh *mitk::InterpolateLinesFilter::GetInput(void) { if (this->GetNumberOfInputs() < 1) { return 0; } return static_cast (this->ProcessObject::GetInput(0) ); } void mitk::InterpolateLinesFilter::SetInput(const mitk::Mesh *input) { // Process object is not const-correct so the const_cast is required here this->ProcessObject::SetNthInput(0, const_cast< mitk::Mesh * >( input ) ); } diff --git a/Modules/Ext/Algorithms/mitkInterpolateLinesFilter.h b/Modules/Ext/Algorithms/mitkInterpolateLinesFilter.h index bc93ec7a27..c4936fcc75 100644 --- a/Modules/Ext/Algorithms/mitkInterpolateLinesFilter.h +++ b/Modules/Ext/Algorithms/mitkInterpolateLinesFilter.h @@ -1,105 +1,105 @@ /*=================================================================== 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); itkFactorylessNewMacro(Self) itkCloneMacro(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/Ext/Algorithms/mitkPlaneCutFilter.cpp b/Modules/Ext/Algorithms/mitkPlaneCutFilter.cpp index 5dfc52cb4a..2efbca082f 100644 --- a/Modules/Ext/Algorithms/mitkPlaneCutFilter.cpp +++ b/Modules/Ext/Algorithms/mitkPlaneCutFilter.cpp @@ -1,260 +1,260 @@ /*=================================================================== 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 "mitkPlaneCutFilter.h" #include "mitkImageReadAccessor.h" #include #include #define roundf(x) ((x - floor(x)) > 0.5f ? ceil(x) : floor(x)) void mitk::PlaneCutFilter::GenerateData() { if (!this->m_Plane) { return; } InputImageType *input = const_cast(this->GetInput()); if (!input) { return; } //Allocate output. OutputImageType *output = this->GetOutput(); mitk::ImageReadAccessor inputAcc(input); output->Initialize(input); output->SetImportVolume(const_cast(inputAcc.GetData())); //Do the intersection. AccessByItk_2(output, _computeIntersection, this->m_Plane, input->GetGeometry()); } mitk::PlaneCutFilter::PlaneCutFilter() : m_BackgroundLevel(0.0f), m_Plane(0), m_FillMode(FILL) { } mitk::PlaneCutFilter::~PlaneCutFilter() { } template -void mitk::PlaneCutFilter::_computeIntersection(itk::Image *image, const PlaneGeometry *plane, const Geometry3D *geometry) +void mitk::PlaneCutFilter::_computeIntersection(itk::Image *image, const PlaneGeometry *plane, const BaseGeometry *geometry) { typedef itk::Image ImageType; const typename ImageType::RegionType &image_region = image->GetLargestPossibleRegion(); const typename ImageType::SizeValueType max_x = image_region.GetSize(0ul), max_y = image_region.GetSize(1ul), max_z = image_region.GetSize(2ul), img_size = max_x * max_y; TPixel *data = image->GetBufferPointer(); Point3D p1, p2; //TODO: Better solution required! TPixel casted_background_level = static_cast(this->m_BackgroundLevel); p1[0] = 0; p2[0] = max_x - 1ul; if (FILL == this->m_FillMode) { for (unsigned long z = 0ul; z < max_z; ++z) { p1[2] = z; p2[2] = z; for (unsigned long y = 0ul; y < max_y; ++y) { p1[1] = y; p2[1] = y; Point3D p1_t, p2_t; geometry->IndexToWorld(p1, p1_t); geometry->IndexToWorld(p2, p2_t); if (plane->IsAbove(p1_t)) { if (plane->IsAbove(p2_t)) { if (0.0f == this->m_BackgroundLevel) { memset(&data[(y * max_x) + (z * img_size)], 0, max_x * sizeof(TPixel)); } else { TPixel *subdata = &data[(y * max_x) + (z * img_size)]; for (unsigned long x = 0; x < max_x; ++x) { subdata[x] = casted_background_level; } } } else { Point3D intersection; Line3D line; line.SetPoints(p1_t, p2_t); plane->IntersectionPoint(line, intersection); geometry->WorldToIndex(intersection, intersection); if (0.0f == this->m_BackgroundLevel) { memset(&data[(y * max_x) + (z * img_size)], 0, (static_cast(roundf(intersection[0])) + 1u) * sizeof(TPixel)); } else { TPixel *subdata = &data[(y * max_x) + (z * img_size)]; const unsigned long x_size = static_cast(roundf(intersection[0])) + 1u; for (unsigned long x = 0; x < x_size; ++x) { subdata[x] = casted_background_level; } } } } else if (plane->IsAbove(p2_t)) { Point3D intersection; Line3D line; line.SetPoints(p1_t, p2_t); plane->IntersectionPoint(line, intersection); geometry->WorldToIndex(intersection, intersection); if (0.0f == this->m_BackgroundLevel) { unsigned long x = static_cast(roundf(intersection[0])); memset(&data[x + (y * max_x) + (z * img_size)], 0, (max_x - x) * sizeof(TPixel)); } else { unsigned long x = static_cast(roundf(intersection[0])); TPixel *subdata = &data[x + (y * max_x) + (z * img_size)]; const unsigned long x_size = max_x - x; for (x = 0; x < x_size; ++x) { subdata[x] = casted_background_level; } } } } } } else { for (unsigned long z = 0ul; z < max_z; ++z) { p1[2] = z; p2[2] = z; for (unsigned long y = 0ul; y < max_y; ++y) { p1[1] = y; p2[1] = y; Point3D p1_t, p2_t; geometry->IndexToWorld(p1, p1_t); geometry->IndexToWorld(p2, p2_t); if (!plane->IsAbove(p1_t)) { if (!plane->IsAbove(p2_t)) { if (0.0f == this->m_BackgroundLevel) { memset(&data[(y * max_x) + (z * img_size)], 0, max_x * sizeof(TPixel)); } else { TPixel *subdata = &data[(y * max_x) + (z * img_size)]; for (unsigned long x = 0; x < max_x; ++x) { subdata[x] = casted_background_level; } } } else { Point3D intersection; Line3D line; line.SetPoints(p1_t, p2_t); plane->IntersectionPoint(line, intersection); geometry->WorldToIndex(intersection, intersection); if (0.0f == this->m_BackgroundLevel) { memset(&data[(y * max_x) + (z * img_size)], 0, (static_cast(roundf(intersection[0])) + 1u) * sizeof(TPixel)); } else { TPixel *subdata = &data[(y * max_x) + (z * img_size)]; const unsigned long x_size = static_cast(roundf(intersection[0])) + 1u; for (unsigned long x = 0; x < x_size; ++x) { subdata[x] = casted_background_level; } } } } else if (!plane->IsAbove(p2_t)) { Point3D intersection; Line3D line; line.SetPoints(p1_t, p2_t); plane->IntersectionPoint(line, intersection); geometry->WorldToIndex(intersection, intersection); if (0.0f == this->m_BackgroundLevel) { unsigned long x = static_cast(roundf(intersection[0])); memset(&data[x + (y * max_x) + (z * img_size)], 0, (max_x - x) * sizeof(TPixel)); } else { unsigned long x = static_cast(roundf(intersection[0])); TPixel *subdata = &data[x + (y * max_x) + (z * img_size)]; const unsigned long x_size = max_x - x; for (x = 0; x < x_size; ++x) { subdata[x] = casted_background_level; } } } } } } } diff --git a/Modules/Ext/Algorithms/mitkPlaneCutFilter.h b/Modules/Ext/Algorithms/mitkPlaneCutFilter.h index d93843c41a..345b8e75a4 100644 --- a/Modules/Ext/Algorithms/mitkPlaneCutFilter.h +++ b/Modules/Ext/Algorithms/mitkPlaneCutFilter.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 PLANECUTFILTER_H_HEADER_INCLUDED_C1F48A22 #define PLANECUTFILTER_H_HEADER_INCLUDED_C1F48A22 #include "mitkCommon.h" #include "MitkExtExports.h" #include "mitkImageToImageFilter.h" #include "mitkPlaneGeometry.h" namespace itk { template class ITK_EXPORT Image; } namespace mitk { /** \brief Filter to cut an image with a plane. Everything in the direction of the normal of the planes (if fill mode is set to "FILL") will be set to a specified value. */ class MitkExt_EXPORT PlaneCutFilter : public ImageToImageFilter { public: mitkClassMacro(PlaneCutFilter, ImageToImageFilter); /** Method for creation through the object factory. */ itkFactorylessNewMacro(Self) itkCloneMacro(Self) typedef enum {FILL, FILL_INVERSE} FillMode; //##Documentation //## @brief Set background grey level itkSetMacro(BackgroundLevel, float); itkGetMacro(BackgroundLevel, float); itkSetEnumMacro(FillMode, FillMode); itkGetEnumMacro(FillMode, FillMode); itkSetObjectMacro(Plane, const PlaneGeometry); itkGetObjectMacro(Plane, const PlaneGeometry); protected: float m_BackgroundLevel; PlaneGeometry::ConstPointer m_Plane; FillMode m_FillMode; PlaneCutFilter(); ~PlaneCutFilter(); virtual void GenerateData(); template - void _computeIntersection(itk::Image *itkImage, const PlaneGeometry *plane, const Geometry3D *geometry); + void _computeIntersection(itk::Image *itkImage, const PlaneGeometry *plane, const BaseGeometry *geometry); }; } // namespace mitk #endif /* PLANECUTFILTER_H_HEADER_INCLUDED_C1F48A22 */ diff --git a/Modules/Ext/Algorithms/mitkPlanesPerpendicularToLinesFilter.cpp b/Modules/Ext/Algorithms/mitkPlanesPerpendicularToLinesFilter.cpp index 26b8115c35..c162172885 100644 --- a/Modules/Ext/Algorithms/mitkPlanesPerpendicularToLinesFilter.cpp +++ b/Modules/Ext/Algorithms/mitkPlanesPerpendicularToLinesFilter.cpp @@ -1,212 +1,212 @@ /*=================================================================== 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 "mitkPlanesPerpendicularToLinesFilter.h" #include #include #include mitk::PlanesPerpendicularToLinesFilter::PlanesPerpendicularToLinesFilter() : m_Plane(NULL), m_UseAllPoints(false), m_CreatedGeometries(NULL), normal(3), targetRight(3) { m_CreatedGeometries = mitk::SlicedGeometry3D::New(); } mitk::PlanesPerpendicularToLinesFilter::~PlanesPerpendicularToLinesFilter() { } void mitk::PlanesPerpendicularToLinesFilter::GenerateOutputInformation() { mitk::Mesh::ConstPointer input = this->GetInput(); mitk::GeometryData::Pointer output = this->GetOutput(); itkDebugMacro(<<"GenerateOutputInformation()"); if(input.IsNull()) return; output->SetGeometry(m_CreatedGeometries); } void mitk::PlanesPerpendicularToLinesFilter::CreatePlane(const mitk::Point3D& curr) { int j; for(j=0;j<3;++j) normal[j] = last[j]-curr[j]; //@todo globally define normal direction of display xxx normal.normalize(); down = vnl_cross_3d(normal, targetRight); down.normalize(); right = vnl_cross_3d(down, normal); right.normalize(); itk2vtk(last.GetVnlVector()-right*halfWidthInMM-down*halfHeightInMM, origin); right *= targetSpacing[0]; down *= targetSpacing[1]; normal *= targetSpacing[2]; mitk::Matrix3D matrix; matrix.GetVnlMatrix().set_column(0, right); matrix.GetVnlMatrix().set_column(1, down); matrix.GetVnlMatrix().set_column(2, normal); PlaneGeometry::Pointer plane = PlaneGeometry::New(); plane->GetIndexToWorldTransform()->SetMatrix(matrix); plane->SetOrigin(origin); plane->SetBounds(bounds); planes.push_back(plane); last = curr; } void mitk::PlanesPerpendicularToLinesFilter::GenerateData() { mitk::Mesh::ConstPointer input = this->GetInput(); mitk::GeometryData::Pointer output = this->GetOutput(); if(m_Plane.IsNotNull()) { targetRight = m_Plane->GetMatrixColumn(0); targetSpacing = m_Plane->GetSpacing(); bounds = m_Plane->GetBoundingBox()->GetBounds(); halfWidthInMM = m_Plane->GetExtentInMM(0)*0.5; halfHeightInMM = m_Plane->GetExtentInMM(1)*0.5; } else { FillVector3D(targetRight, 1.0, 0.0, 0.0); targetSpacing.Fill(1.0); halfWidthInMM=halfHeightInMM=100.0; ScalarType stdBounds[6] = {0.0, 2.0*halfWidthInMM, 0.0, 2.0*halfHeightInMM, 0.0, 0.0}; bounds = stdBounds; } if(m_UseAllPoints==false) { int i, size; //iterate through all cells and build planes Mesh::ConstCellIterator cellIt, cellEnd; cellEnd = input->GetMesh()->GetCells()->End(); for( cellIt = input->GetMesh()->GetCells()->Begin(); cellIt != cellEnd; ++cellIt ) { Mesh::CellType& cell = *cellIt->Value(); Mesh::PointIdIterator ptIt, ptEnd; ptEnd = cell.PointIdsEnd(); size=cell.GetNumberOfPoints(); if(size<=1) continue; ptIt = cell.PointIdsBegin(); last = input->GetPoint(*ptIt); ++ptIt; for(i=1;iGetPoint(*ptIt)); } } } else //m_UseAllPoints==true { //iterate through all points and build planes mitk::PointSet::PointsConstIterator it, pend = input->GetPointSet()->GetPoints()->End(); it=input->GetPointSet()->GetPoints()->Begin(); last = it.Value(); ++it; for(;it!=pend;++it) { CreatePlane(it.Value()); } } if(planes.size()>0) { //initialize sliced-geometry for the number of created planes m_CreatedGeometries->InitializeSlicedGeometry(planes.size()+1); //set last plane at last point with same normal as the one before the last PlaneGeometry::Pointer plane = static_cast((*planes.rbegin())->Clone().GetPointer()); itk2vtk(last.GetVnlVector()-right*halfWidthInMM-down*halfHeightInMM, origin); plane->SetOrigin(origin); m_CreatedGeometries->SetGeometry2D(plane, planes.size()); //add all planes to sliced-geometry int s; for(s=0; planes.empty()==false; planes.pop_front(), ++s) { m_CreatedGeometries->SetGeometry2D(planes.front(), s); } m_CreatedGeometries->SetEvenlySpaced(false); if(m_FrameGeometry.IsNotNull()) { m_CreatedGeometries->SetIndexToWorldTransform(m_FrameGeometry->GetIndexToWorldTransform()); m_CreatedGeometries->SetBounds(m_FrameGeometry->GetBounds()); m_CreatedGeometries->SetReferenceGeometry(m_FrameGeometry); } } output->SetGeometry(m_CreatedGeometries); } void mitk::PlanesPerpendicularToLinesFilter::SetPlane(const mitk::PlaneGeometry* aPlane) { if(aPlane!=NULL) { m_Plane = static_cast(aPlane->Clone().GetPointer()); } else { if(m_Plane.IsNull()) return; m_Plane=NULL; } Modified(); } const mitk::Mesh *mitk::PlanesPerpendicularToLinesFilter::GetInput(void) { if (this->GetNumberOfInputs() < 1) { return 0; } return static_cast (this->ProcessObject::GetInput(0) ); } void mitk::PlanesPerpendicularToLinesFilter::SetInput(const mitk::Mesh *input) { // Process object is not const-correct so the const_cast is required here this->ProcessObject::SetNthInput(0, const_cast< mitk::Mesh * >( input ) ); } -void mitk::PlanesPerpendicularToLinesFilter::SetFrameGeometry(const mitk::Geometry3D* frameGeometry) +void mitk::PlanesPerpendicularToLinesFilter::SetFrameGeometry(const mitk::BaseGeometry* frameGeometry) { if((frameGeometry != NULL) && (frameGeometry->IsValid())) { - m_FrameGeometry = static_cast(frameGeometry->Clone().GetPointer()); + m_FrameGeometry = static_cast(frameGeometry->Clone().GetPointer()); } else { m_FrameGeometry = NULL; } } diff --git a/Modules/Ext/Algorithms/mitkPlanesPerpendicularToLinesFilter.h b/Modules/Ext/Algorithms/mitkPlanesPerpendicularToLinesFilter.h index baddeb243c..d1238c239b 100644 --- a/Modules/Ext/Algorithms/mitkPlanesPerpendicularToLinesFilter.h +++ b/Modules/Ext/Algorithms/mitkPlanesPerpendicularToLinesFilter.h @@ -1,145 +1,145 @@ /*=================================================================== 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 MITKPLANESPERPENDICULARTOLINES_H_HEADER_INCLUDED_C10B22CD #define MITKPLANESPERPENDICULARTOLINES_H_HEADER_INCLUDED_C10B22CD #include "mitkGeometryDataSource.h" #include "MitkExtExports.h" #include "mitkMesh.h" #include "mitkGeometryData.h" #include "mitkPlaneGeometry.h" #include "mitkSlicedGeometry3D.h" namespace mitk { //##Documentation //## @brief Create Planes perpendicular to lines contained in a Mesh. The planes data is generated as one SlicedGeometry3D data. //## To create the planes as input a //## mitk::mesh (for example a pointSet) and as geometry hint a geometry (for example from the original image) must be given. //## //## mitk::Mesh::Pointer mesh = mitk::Mesh::New(); //## mesh->SetMesh(pointSet->GetPointSet()); //## mitk::Image* currentImage = dynamic_cast (myDataStorage->GetNamedNode(IMAGE)->GetData()); //## const mitk::Geometry3D* imagegeometry = currentImage->GetUpdatedGeometry(); //## mitk::PlanesPerpendicularToLinesFilter::Pointer perpendicularPlanes = mitk::PlanesPerpendicularToLinesFilter::New(); //## perpendicularPlanes->SetInput(mesh); //## perpendicularPlanes->SetUseAllPoints(true); //## perpendicularPlanes->SetFrameGeometry(imagegeometry); //## perpendicularPlanes->Update(); //## //## To get one single plane out of these use SlicedGeometry3D->GetGeometry2D(int slicenumber). //## @ingroup Process class MitkExt_EXPORT PlanesPerpendicularToLinesFilter : public GeometryDataSource { public: mitkClassMacro(PlanesPerpendicularToLinesFilter, GeometryDataSource); itkFactorylessNewMacro(Self) itkCloneMacro(Self) virtual void GenerateOutputInformation(); virtual void GenerateData(); const mitk::Mesh *GetInput(void); //## @brief Set the input mesh that is used to create the planes. virtual void SetInput(const mitk::Mesh *image); //##Documentation //## @brief Set plane to be used as an example of the planes to move //## along the lines in the input mesh. //## //## The size and spacing are copied from the plane. The in-plane //## orientation (right-vector) of the created planes are set as //## parallel as possible to the orientation (right-vector) of the //## the plane set using this method. //## @note The PlaneGeometry is cloned, @em not linked/referenced. virtual void SetPlane(const mitk::PlaneGeometry* aPlane); //##Documentation //## @brief Set if all points in the mesh should be interpreted as //## one long line. //## //## Cells are not used in this mode, but all points in the order //## of their indices form the line. //## Default is @a false. itkGetConstMacro(UseAllPoints, bool); //##Documentation //## @brief Set if all points of the mesh shall be used (true) or the cells (false) //## Default is @a false. itkSetMacro(UseAllPoints, bool); itkBooleanMacro(UseAllPoints); //##Documentation //## @brief Set an explicit frame of the created sliced geometry //## //## Set an explicit framegeometry for the created sliced geometry. This framegeometry is //## used as geometry for all created planes. //## Uses the IndexToWorldTransform and bounding box of the //## provided geometry. //## \sa CalculateFrameGeometry - virtual void SetFrameGeometry(const mitk::Geometry3D* frameGeometry); + virtual void SetFrameGeometry(const mitk::BaseGeometry* frameGeometry); protected: PlanesPerpendicularToLinesFilter(); virtual ~PlanesPerpendicularToLinesFilter(); //## @brief Creates the plane at point curr //## //## Creates the plane at point curr. To create this plane, the last point must //## must be renowned. //## \sa SetPlane void CreatePlane(const Point3D& curr); //## @brief Plane to be used as an example of the planes to move //## along the lines in the input mesh. //## //## The size and spacing are copied from the m_Plane. The in-plane //## orientation (right-vector) of the created planes are set as //## parallel as possible to the orientation (right-vector) of m_Plane. //## \sa SetPlane mitk::PlaneGeometry::Pointer m_Plane; bool m_UseAllPoints; //##Documentation //## @brief SlicedGeometry3D containing the created planes //## SlicedGeometry3D::Pointer m_CreatedGeometries; - mitk::Geometry3D::Pointer m_FrameGeometry; + mitk::BaseGeometry::Pointer m_FrameGeometry; private: std::deque planes; Point3D last; VnlVector normal; VnlVector right, down; VnlVector targetRight; Vector3D targetSpacing; ScalarType halfWidthInMM, halfHeightInMM; - mitk::Geometry3D::BoundsArrayType bounds; + mitk::BaseGeometry::BoundsArrayType bounds; Point3D origin; }; } // namespace mitk #endif /* MITKPLANESPERPENDICULARTOLINES_H_HEADER_INCLUDED_C10B22CD */ diff --git a/Modules/Ext/Algorithms/mitkPointSetToCurvedGeometryFilter.cpp b/Modules/Ext/Algorithms/mitkPointSetToCurvedGeometryFilter.cpp index a753331438..417a7cf365 100644 --- a/Modules/Ext/Algorithms/mitkPointSetToCurvedGeometryFilter.cpp +++ b/Modules/Ext/Algorithms/mitkPointSetToCurvedGeometryFilter.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 "mitkPointSetToCurvedGeometryFilter.h" #include "mitkThinPlateSplineCurvedGeometry.h" #include "mitkPlaneGeometry.h" #include "mitkImage.h" #include "mitkDataNode.h" #include "mitkGeometryData.h" #include "mitkGeometry2DData.h" #include "mitkProperties.h" #include "itkMesh.h" #include "itkPointSet.h" mitk::PointSetToCurvedGeometryFilter::PointSetToCurvedGeometryFilter() { m_ProjectionMode = YZPlane; m_PCAPlaneCalculator = mitk::PlaneFit::New(); m_ImageToBeMapped = NULL; m_Sigma = 1000; mitk::Geometry2DData::Pointer output = static_cast ( this->MakeOutput ( 0 ).GetPointer() ); output->Initialize(); Superclass::SetNumberOfRequiredOutputs ( 1 ); Superclass::SetNthOutput ( 0, output.GetPointer() ); } mitk::PointSetToCurvedGeometryFilter::~PointSetToCurvedGeometryFilter() {} void mitk::PointSetToCurvedGeometryFilter::GenerateOutputInformation() { mitk::PointSet::ConstPointer input = this->GetInput(); mitk::Geometry2DData::Pointer output = dynamic_cast ( this->GetOutput() ); if ( input.IsNull() ) itkGenericExceptionMacro ( "Input point set is NULL!" ); if ( input->GetTimeGeometry()->CountTimeSteps() != 1 ) itkWarningMacro ( "More than one time step is not yet supported!" ); if ( output.IsNull() ) itkGenericExceptionMacro ( "Output is NULL!" ); if ( m_ImageToBeMapped.IsNull() ) itkGenericExceptionMacro ( "Image to be mapped is NULL!" ); bool update = false; if ( output->GetGeometry() == NULL || output->GetGeometry2D() == NULL || output->GetTimeGeometry() == NULL ) update = true; if ( ( ! update ) && ( output->GetTimeGeometry()->CountTimeSteps() != input->GetTimeGeometry()->CountTimeSteps() ) ) update = true; if ( update ) { mitk::ThinPlateSplineCurvedGeometry::Pointer curvedGeometry = mitk::ThinPlateSplineCurvedGeometry::New(); output->SetGeometry(curvedGeometry); } } void mitk::PointSetToCurvedGeometryFilter::GenerateData() { mitk::PointSet::ConstPointer input = this->GetInput(); mitk::GeometryData::Pointer output = this->GetOutput(); // // check preconditions // if ( input.IsNull() ) itkGenericExceptionMacro ( "Input point set is NULL!" ); if ( output.IsNull() ) itkGenericExceptionMacro ( "output geometry data is NULL!" ); if ( output->GetTimeGeometry() == NULL ) itkGenericExceptionMacro ( "Output time sliced geometry is NULL!" ); if ( output->GetTimeGeometry()->GetGeometryForTimeStep ( 0 ).IsNull() ) itkGenericExceptionMacro ( "Output geometry3d is NULL!" ); mitk::ThinPlateSplineCurvedGeometry::Pointer curvedGeometry = dynamic_cast ( output->GetTimeGeometry()->GetGeometryForTimeStep( 0 ).GetPointer() ); if ( curvedGeometry.IsNull() ) itkGenericExceptionMacro ( "Output geometry3d is not an instance of mitk::ThinPlateSPlineCurvedGeometry!" ); if ( m_ImageToBeMapped.IsNull() ) itkGenericExceptionMacro ( "Image to be mapped is NULL!" ); // // initialize members if needed // if ( m_XYPlane.IsNull() || m_XZPlane.IsNull() || m_YZPlane.IsNull() ) { m_ImageToBeMapped->UpdateOutputInformation(); - const mitk::Geometry3D* imageGeometry = m_ImageToBeMapped->GetUpdatedGeometry(); + const mitk::BaseGeometry* imageGeometry = m_ImageToBeMapped->GetUpdatedGeometry(); imageGeometry = m_ImageToBeMapped->GetUpdatedGeometry(); m_XYPlane = mitk::PlaneGeometry::New(); m_XZPlane = mitk::PlaneGeometry::New(); m_YZPlane = mitk::PlaneGeometry::New(); m_XYPlane->InitializeStandardPlane ( imageGeometry, mitk::PlaneGeometry::Axial ); m_YZPlane->InitializeStandardPlane ( imageGeometry, mitk::PlaneGeometry::Sagittal ); m_XZPlane->InitializeStandardPlane ( imageGeometry, mitk::PlaneGeometry::Frontal ); } if ( m_PlaneLandmarkProjector.IsNull() ) { m_PlaneLandmarkProjector = mitk::PlaneLandmarkProjector::New(); m_SphereLandmarkProjector = mitk::SphereLandmarkProjector::New(); } // // set up geometry according to the current settings // if ( m_ProjectionMode == Sphere ) { curvedGeometry->SetLandmarkProjector ( m_SphereLandmarkProjector ); } else { if ( m_ProjectionMode == XYPlane ) m_PlaneLandmarkProjector->SetProjectionPlane ( m_XYPlane ); else if ( m_ProjectionMode == XZPlane ) m_PlaneLandmarkProjector->SetProjectionPlane ( m_XZPlane ); else if ( m_ProjectionMode == YZPlane ) m_PlaneLandmarkProjector->SetProjectionPlane ( m_YZPlane ); else if ( m_ProjectionMode == PCAPlane ) { itkExceptionMacro ( "PCAPlane not yet implemented!" ); m_PCAPlaneCalculator->SetInput ( input ); m_PCAPlaneCalculator->Update(); m_PlaneLandmarkProjector->SetProjectionPlane ( dynamic_cast ( m_PCAPlaneCalculator->GetOutput() ) ); } else itkExceptionMacro ( "Unknown projection mode" ); curvedGeometry->SetLandmarkProjector ( m_PlaneLandmarkProjector ); } //curvedGeometry->SetReferenceGeometry( m_ImageToBeMapped->GetGeometry() ); curvedGeometry->SetTargetLandmarks ( input->GetPointSet ( 0 )->GetPoints() ); curvedGeometry->SetSigma ( m_Sigma ); curvedGeometry->ComputeGeometry(); curvedGeometry->SetOversampling ( 1.0 ); } void mitk::PointSetToCurvedGeometryFilter::SetDefaultCurvedGeometryProperties ( mitk::DataNode* node ) { if ( node == NULL ) { itkGenericOutputMacro ( "Warning: node is NULL!" ); return; } node->SetIntProperty ( "xresolution", 50 ); node->SetIntProperty ( "yresolution", 50 ); node->SetProperty ( "name", mitk::StringProperty::New ( "Curved Plane" ) ); // exclude extent of this plane when calculating DataStorage bounding box node->SetProperty ( "includeInBoundingBox", mitk::BoolProperty::New ( false ) ); } diff --git a/Modules/Ext/Algorithms/mitkPolygonToRingFilter.cpp b/Modules/Ext/Algorithms/mitkPolygonToRingFilter.cpp index d6d8fa0c06..8fc7b9f744 100644 --- a/Modules/Ext/Algorithms/mitkPolygonToRingFilter.cpp +++ b/Modules/Ext/Algorithms/mitkPolygonToRingFilter.cpp @@ -1,341 +1,341 @@ /*=================================================================== 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 "mitkPolygonToRingFilter.h" #include "mitkMesh.h" #include "mitkSurface.h" #include "mitkPlaneGeometry.h" #include #include #include #include #include #include #include #include mitk::PolygonToRingFilter::PolygonToRingFilter() : m_RingRadius(3.5f), m_RingResolution(30), m_SplineResolution(20) { m_SplineX = vtkCardinalSpline::New(); m_SplineY = vtkCardinalSpline::New(); m_SplineZ = vtkCardinalSpline::New(); } mitk::PolygonToRingFilter::~PolygonToRingFilter() { m_SplineX->Delete(); m_SplineY->Delete(); m_SplineZ->Delete(); } void mitk::PolygonToRingFilter::GenerateOutputInformation() { mitk::Mesh::ConstPointer input = this->GetInput(); mitk::Surface::Pointer output = this->GetOutput(0); itkDebugMacro(<<"GenerateOutputInformation()"); if(input.IsNull()) return; - output->SetGeometry(static_cast(input->GetGeometry()->Clone().GetPointer())); + output->SetGeometry(static_cast(input->GetGeometry()->Clone().GetPointer())); output->Expand( input->GetPointSetSeriesSize() ); } void mitk::PolygonToRingFilter::GenerateData() { mitk::Mesh::ConstPointer input = this->GetInput(); mitk::Surface::Pointer output = this->GetOutput(0); unsigned int t; for ( t = 0; t < input->GetPointSetSeriesSize(); ++t ) { vtkPolyData *polyData = vtkPolyData::New(); vtkPoints *vPoints = vtkPoints::New(); vtkCellArray *polys = vtkCellArray::New(); mitk::Mesh::PointType thisPoint; // iterate through all cells and build tubes Mesh::ConstCellIterator cellIt, cellEnd; cellEnd = input->GetMesh( t )->GetCells()->End(); for ( cellIt = input->GetMesh( t )->GetCells()->Begin(); cellIt != cellEnd; ++cellIt ) { m_PointList.clear(); m_VectorList.clear(); this->BuildPointAndVectorList( *cellIt->Value(), m_PointList, m_VectorList, t ); this->BuildVtkTube( vPoints, polys, m_PointList, m_VectorList ); } polyData->SetPoints( vPoints ); vPoints->Delete(); polyData->SetPolys( polys ); polys->Delete(); output->SetVtkPolyData( polyData, t ); polyData->Delete(); } } //sl: last star //sc: current star //idmax: Id of the current star ray (sc), which matchs proberly to the first ray of the last star (sl). //last_p: center of the last star //cur_p: center of the current star void mitk::PolygonToRingFilter::DrawCyl(vtkPoints *vPoints, vtkCellArray *polys, VectorListType &sl, VectorListType &sc, int idmax, Point3D & last_p, Point3D & cur_p) { unsigned int i; //now we finished:sl0 will be connected with sc->at(idmax) VectorListType::iterator slit=sl.begin(), scit=sc.begin(), scend=sc.end(); scit+=idmax; Point3D a,b; Point3D a_first,b_first; int a_firstID = 0, b_firstID = 0; vtkIdType front[4]; for(i=0;iInsertNextPoint(v0[0],v0[1],v0[2]); front[2]=vPoints->InsertNextPoint(v1[0],v1[1],v1[2]); front[1]=vPoints->InsertNextPoint(v2[0],v2[1],v2[2]); front[0]=vPoints->InsertNextPoint(v3[0],v3[1],v3[2]); polys->InsertNextCell( (vtkIdType) 4, front ); if(i==1) { a_firstID=front[3]; b_firstID=front[2]; //continue; } } ++slit; ++scit; if(scit==scend) scit=sc.begin(); } front[3]=front[0]; front[2]=front[1]; front[1]=b_firstID; front[0]=a_firstID; polys->InsertNextCell( 4, front ); } void mitk::PolygonToRingFilter::BuildVtkTube(vtkPoints *vPoints, vtkCellArray *polys, PointListType& ptList, VectorListType& vecList) { PointListType::iterator pit = ptList.begin(), pend = ptList.end(); VectorListType::iterator vit = vecList.begin(); Vector3D axis, last_v, next_v, s; Point3D cur_p,last_p; //lists for the star VectorListType *sl, *sc, *swp, sfirst, buf1, buf2; sl=&buf1; sc=&buf2; Vector3D a,b; Matrix3D m; //Initialization for the first point //alternative1: // last_v=*(vl.getLast()); next_v=*vit.current(); axis=last_v+next_v; s.cross(last_v,next_v); s.normalize(); //alternative2: // last_v=*(vl.getLast()); next_v=*vit.current(); s.cross(last_v,next_v); s.normalize(); // axis=next_v-last_v; axis.normalize(); aa.set(s, M_PI/2.0); m.set(aa); m.transform(&axis); //alternative3: last_v=vecList.back(); next_v=*vit; s.SetVnlVector( vnl_cross_3d(last_v.GetVnlVector(),next_v.GetVnlVector()) ); s.Normalize(); a=last_v; b=next_v; a.Normalize(); b.Normalize(); axis=a+b; axis.Normalize(); //build the star at the first point m = vnl_quaternion(axis.GetVnlVector(),2*vnl_math::pi/(double)m_RingResolution).rotation_matrix_transpose(); unsigned int i; for(i=0;ibegin()); m = vnl_quaternion(axis.GetVnlVector(),2*vnl_math::pi/(double)m_RingResolution).rotation_matrix_transpose(); for(i=0;ipush_back(s); double tmp=s*sl0; if(tmp>max) { max=tmp; idmax=i; } s=m*s; } //sl: last star //sc: current star //idmax: Id of the current star ray (sc), which matchs proberly to the first ray of the last star (sl). //last_p: center of the last star //cur_p: center of the current star DrawCyl(vPoints, polys, *sl, *sc, idmax, last_p, cur_p); //Crossover to the next last_p=cur_p; swp=sl; sl=sc; sc=swp; sc->clear(); } //calcutate idmax for connection: double max=0; int idmax=0; Vector3D sl0=*(sl->begin()); for(i=0;imax) { max=tmp; idmax=i; } } cur_p=*ptList.begin(); DrawCyl(vPoints, polys, *sl, sfirst, idmax, last_p, cur_p); } void mitk::PolygonToRingFilter ::BuildPointAndVectorList( mitk::Mesh::CellType& cell, PointListType& ptList, VectorListType& vecList, int timeStep ) { // This method constructs a spline from the given point list and retrieves // a number of interpolated points from it to form a ring-like structure. // // To make the spline "closed", the end point is connected to the start // point. For ensuring smoothness at the start-end-point transition, the // (intrinsically non-circular) spline array is extended on both sides // by wrapping a number of points from the respective other side. // // The used VTK filters do principally support this kind of "closed" spline, // but it does not produce results as consistent as with the method used // here. Also, the spline class of VTK 4.4 has only poor support for // arbitrary parametric coordinates (t values in vtkSpline). VTK 5.0 has // better support, and also provides a new class vtkParametricSpline for // directly calculating 3D splines. // Remove points from previous call of this method m_SplineX->RemoveAllPoints(); m_SplineY->RemoveAllPoints(); m_SplineZ->RemoveAllPoints(); int numberOfPoints = cell.GetNumberOfPoints(); Mesh::PointType inputPoint; double t, tStart(0), tEnd(0); // Add input points to the spline and assign each the parametric value t // derived from the point euclidean distances. int i; Mesh::PointIdIterator pit = cell.PointIdsEnd() - 3; for ( i = -3, t = 0.0; i < numberOfPoints + 3; ++i ) { if ( i == 0 ) { tStart = t; } if ( i == numberOfPoints ) { tEnd = t; } inputPoint = this->GetInput()->GetPoint( *pit, timeStep ); m_SplineX->AddPoint( t, inputPoint[0] ); m_SplineY->AddPoint( t, inputPoint[1] ); m_SplineZ->AddPoint( t, inputPoint[2] ); ++pit; if ( pit == cell.PointIdsEnd() ) { pit = cell.PointIdsBegin(); } t += inputPoint.EuclideanDistanceTo( this->GetInput()->GetPoint( *pit, timeStep ) ); } // Evaluate the spline for the desired number of points // (number of input points) * (spline resolution) Point3D point, firstPoint, lastPoint; firstPoint.Fill(0); lastPoint.Fill(0); int numberOfSegments = numberOfPoints * m_SplineResolution; double step = (tEnd - tStart) / numberOfSegments; for ( i = 0, t = tStart; i < numberOfSegments; ++i, t += step ) { FillVector3D( point, m_SplineX->Evaluate(t), m_SplineY->Evaluate(t), m_SplineZ->Evaluate(t) ); ptList.push_back( point ); if ( i == 0 ) { firstPoint = point; } else { vecList.push_back( point - lastPoint ); } lastPoint = point; } vecList.push_back( firstPoint - lastPoint ); } const mitk::Mesh *mitk::PolygonToRingFilter::GetInput(void) { if (this->GetNumberOfInputs() < 1) { return 0; } return static_cast (this->ProcessObject::GetInput(0) ); } void mitk::PolygonToRingFilter::SetInput(const mitk::Mesh *input) { // Process object is not const-correct so the const_cast is required here this->ProcessObject::SetNthInput(0, const_cast< mitk::Mesh * >( input ) ); } diff --git a/Modules/Ext/Algorithms/mitkProbeFilter.cpp b/Modules/Ext/Algorithms/mitkProbeFilter.cpp index f3c6416b1f..27f6851664 100644 --- a/Modules/Ext/Algorithms/mitkProbeFilter.cpp +++ b/Modules/Ext/Algorithms/mitkProbeFilter.cpp @@ -1,213 +1,214 @@ /*=================================================================== 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 "mitkProbeFilter.h" #include "mitkSurface.h" #include "mitkImage.h" #include #include #include #include #include mitk::ProbeFilter::ProbeFilter() { } mitk::ProbeFilter::~ProbeFilter() { } const mitk::Surface *mitk::ProbeFilter::GetInput(void) { if (this->GetNumberOfInputs() < 1) { return 0; } return static_cast< const mitk::Surface * >(this->ProcessObject::GetInput(0) ); } const mitk::Image *mitk::ProbeFilter::GetSource(void) { return static_cast< const mitk::Image * >(this->ProcessObject::GetInput(1)); } void mitk::ProbeFilter::SetInput(const mitk::Surface *input) { this->ProcessObject::SetNthInput( 0, const_cast< mitk::Surface * >( input ) ); } void mitk::ProbeFilter::SetSource(const mitk::Image *source) { this->ProcessObject::SetNthInput( 1, const_cast< mitk::Image * >( source ) ); } void mitk::ProbeFilter::GenerateOutputInformation() { mitk::Surface::ConstPointer input = this->GetInput(); mitk::Image::ConstPointer source = this->GetSource(); mitk::Surface::Pointer output = this->GetOutput(); if(input.IsNull()) return; if(source.IsNull()) return; if(input->GetGeometry()==NULL) return; if(source->GetGeometry()==NULL) return; if( (input->GetTimeGeometry()->CountTimeSteps()==1) && (source->GetTimeGeometry()->CountTimeSteps()>1) ) { - Geometry3D::Pointer geometry3D = Geometry3D::New(); + Geometry3D::Pointer geo3D = Geometry3D::New(); + BaseGeometry::Pointer geometry3D = dynamic_cast(geo3D.GetPointer()); geometry3D->Initialize(); geometry3D->SetBounds(source->GetTimeGeometry()->GetBoundsInWorld()); geometry3D->SetTimeBounds(source->GetTimeGeometry()->GetGeometryForTimeStep(0)->GetTimeBounds()); ProportionalTimeGeometry::Pointer outputTimeGeometry = ProportionalTimeGeometry::New(); outputTimeGeometry->Initialize(geometry3D, source->GetTimeGeometry()->CountTimeSteps()); output->Expand(outputTimeGeometry->CountTimeSteps()); output->SetTimeGeometry( outputTimeGeometry ); } else - output->SetGeometry( static_cast(input->GetGeometry()->Clone().GetPointer()) ); + output->SetGeometry( static_cast(input->GetGeometry()->Clone().GetPointer()) ); itkDebugMacro(<<"GenerateOutputInformation()"); } void mitk::ProbeFilter::GenerateData() { mitk::Surface *input = const_cast< mitk::Surface * >(this->GetInput()); mitk::Image *source = const_cast< mitk::Image * >(this->GetSource()); mitk::Surface::Pointer output = this->GetOutput(); itkDebugMacro(<<"Generating Data"); if(output.IsNull()) { itkDebugMacro(<<"Output is NULL."); return; } mitk::Surface::RegionType outputRegion = output->GetRequestedRegion(); const TimeGeometry *outputTimeGeometry = output->GetTimeGeometry(); const TimeGeometry *inputTimeGeometry = input->GetTimeGeometry(); const TimeGeometry *sourceTimeGeometry = source->GetTimeGeometry(); TimePointType timeInMS; int timestep=0; int tstart, tmax; tstart=outputRegion.GetIndex(3); tmax=tstart+outputRegion.GetSize(3); int t; for(t=tstart;tTimeStepToTimePoint( t ); vtkProbeFilter* probe = vtkProbeFilter::New(); timestep = inputTimeGeometry->TimePointToTimeStep( timeInMS ); probe->SetInputData( input->GetVtkPolyData(timestep) ); timestep = sourceTimeGeometry->TimePointToTimeStep( timeInMS ); probe->SetSourceData( source->GetVtkImageData(timestep) ); output->SetVtkPolyData( probe->GetPolyDataOutput(), t ); probe->Update(); probe->Delete(); } } void mitk::ProbeFilter::GenerateInputRequestedRegion() { Superclass::GenerateInputRequestedRegion(); mitk::Surface *input = const_cast< mitk::Surface * >( this->GetInput() ); mitk::Image *source = const_cast< mitk::Image * >( this->GetSource() ); if(input==NULL) return; if(source==NULL) return; mitk::Surface::Pointer output = this->GetOutput(); mitk::Surface::RegionType outputRegion = output->GetRequestedRegion(); const TimeGeometry *outputTimeGeometry = output->GetTimeGeometry(); mitk::Surface::RegionType inputSurfaceRegion = outputRegion; Image::RegionType sourceImageRegion = source->GetLargestPossibleRegion(); if(outputRegion.GetSize(3)<1) { mitk::Surface::RegionType::SizeType surfacesize; surfacesize.Fill(0); inputSurfaceRegion.SetSize(surfacesize); input->SetRequestedRegion( &inputSurfaceRegion ); mitk::Image::RegionType::SizeType imagesize; imagesize.Fill(0); sourceImageRegion.SetSize(imagesize); source->SetRequestedRegion( &sourceImageRegion ); return; } //create and set input requested region for the input surface const TimeGeometry *inputTimeGeometry = input->GetTimeGeometry(); ScalarType timeInMS; int timestep=0; // convert the start-index-time of output in start-index-time of input via millisecond-time timeInMS = outputTimeGeometry->TimeStepToTimePoint(outputRegion.GetIndex(3)); timestep = inputTimeGeometry->TimePointToTimeStep( timeInMS ); if( ( timeInMS > ScalarTypeNumericTraits::NonpositiveMin() ) && ( inputTimeGeometry->IsValidTimeStep( timestep ) ) ) inputSurfaceRegion.SetIndex( 3, timestep ); else inputSurfaceRegion.SetIndex( 3, 0 ); // convert the end-index-time of output in end-index-time of input via millisecond-time timeInMS = outputTimeGeometry->TimeStepToTimePoint(outputRegion.GetIndex(3)+outputRegion.GetSize(3)-1); timestep = inputTimeGeometry->TimePointToTimeStep( timeInMS ); if( ( timeInMS > ScalarTypeNumericTraits::NonpositiveMin() ) && ( outputTimeGeometry->IsValidTimeStep( timestep ) ) ) inputSurfaceRegion.SetSize( 3, timestep - inputSurfaceRegion.GetIndex(3) + 1 ); else inputSurfaceRegion.SetSize( 3, 1 ); input->SetRequestedRegion( &inputSurfaceRegion ); //create and set input requested region for the source image const TimeGeometry *sourceTimeGeometry = source->GetTimeGeometry(); // convert the start-index-time of output in start-index-time of source via millisecond-time timeInMS = outputTimeGeometry->TimeStepToTimePoint(outputRegion.GetIndex(3)); timestep = sourceTimeGeometry->TimePointToTimeStep( timeInMS ); if( ( timeInMS > ScalarTypeNumericTraits::NonpositiveMin() ) && ( sourceTimeGeometry->IsValidTimeStep( timestep ) ) ) sourceImageRegion.SetIndex( 3, timestep ); else sourceImageRegion.SetIndex( 3, 0 ); // convert the end-index-time of output in end-index-time of source via millisecond-time timeInMS = outputTimeGeometry->TimeStepToTimePoint(outputRegion.GetIndex(3)+outputRegion.GetSize(3)-1); timestep = sourceTimeGeometry->TimePointToTimeStep( timeInMS ); if( ( timeInMS > ScalarTypeNumericTraits::NonpositiveMin() ) && ( outputTimeGeometry->IsValidTimeStep( timestep ) ) ) sourceImageRegion.SetSize( 3, timestep - sourceImageRegion.GetIndex(3) + 1 ); else sourceImageRegion.SetSize( 3, 1 ); sourceImageRegion.SetIndex( 4, 0 ); sourceImageRegion.SetSize( 4, 1 ); source->SetRequestedRegion( &sourceImageRegion ); } diff --git a/Modules/Ext/IO/mitkUnstructuredGridVtkWriter.txx b/Modules/Ext/IO/mitkUnstructuredGridVtkWriter.txx index c4890229c4..b544b24d0f 100644 --- a/Modules/Ext/IO/mitkUnstructuredGridVtkWriter.txx +++ b/Modules/Ext/IO/mitkUnstructuredGridVtkWriter.txx @@ -1,205 +1,203 @@ /*=================================================================== 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_UNSTRUCTURED_GRID_VTKWRITER_TXX_ #define _MITK_UNSTRUCTURED_GRID_VTKWRITER_TXX_ #include #include #include #include #include #include #include #include #include #include #include namespace mitk { template UnstructuredGridVtkWriter::UnstructuredGridVtkWriter() : m_Success(false) { this->SetNumberOfRequiredInputs(1); } template UnstructuredGridVtkWriter::~UnstructuredGridVtkWriter() { } template void UnstructuredGridVtkWriter::GenerateData() { m_Success = false; if ( m_FileName == "" ) { itkWarningMacro( << "Sorry, filename has not been set!" ); return ; } mitk::UnstructuredGrid::Pointer input = const_cast(this->GetInput()); if (input.IsNull()) { itkWarningMacro( << "Sorry, input to mitk::UnstructuredGridVtkWriter is NULL"); return; } VTKWRITER* unstructuredGridWriter = VTKWRITER::New(); vtkTransformFilter* transformPointSet = vtkTransformFilter::New(); vtkUnstructuredGrid * unstructuredGrid; - Geometry3D* geometry; + BaseGeometry* geometry; if(input->GetTimeGeometry()->CountTimeSteps()>1) { int t, timesteps; timesteps = input->GetTimeGeometry()->CountTimeSteps(); for(t = 0; t < timesteps; ++t) { std::ostringstream filename; geometry = input->GetGeometry(t); if(input->GetTimeGeometry()->IsValidTimeStep(t)) { const mitk::TimeBounds& timebounds = geometry->GetTimeBounds(); filename << m_FileName.c_str() << "_S" << std::setprecision(0) << timebounds[0] << "_E" << std::setprecision(0) << timebounds[1] << "_T" << t << GetDefaultExtension(); } else { itkWarningMacro(<<"Error on write: TimeGeometry invalid of unstructured grid " << filename.str() << "."); filename << m_FileName.c_str() << "_T" << t << GetDefaultExtension(); } - geometry->TransferItkToVtkTransform(); transformPointSet->SetInputData(input->GetVtkUnstructuredGrid(t)); transformPointSet->SetTransform(geometry->GetVtkTransform()); transformPointSet->UpdateWholeExtent(); unstructuredGrid = static_cast(transformPointSet->GetOutput()); unstructuredGridWriter->SetFileName(filename.str().c_str()); unstructuredGridWriter->SetInputData(unstructuredGrid); ExecuteWrite( unstructuredGridWriter ); } } else { geometry = input->GetGeometry(); - geometry->TransferItkToVtkTransform(); transformPointSet->SetInputData(input->GetVtkUnstructuredGrid()); transformPointSet->SetTransform(geometry->GetVtkTransform()); transformPointSet->UpdateWholeExtent(); unstructuredGrid = static_cast(transformPointSet->GetOutput()); unstructuredGridWriter->SetFileName(m_FileName.c_str()); unstructuredGridWriter->SetInputData(unstructuredGrid); ExecuteWrite( unstructuredGridWriter ); } transformPointSet->Delete(); unstructuredGridWriter->Delete(); m_Success = true; } template void UnstructuredGridVtkWriter::ExecuteWrite( VTKWRITER* vtkWriter ) { struct stat fileStatus; time_t timeBefore=0; if (!stat(vtkWriter->GetFileName(), &fileStatus)) { timeBefore = fileStatus.st_mtime; } if (!vtkWriter->Write()) { itkExceptionMacro( << "Error during unstructured grid writing."); } // check if file can be written because vtkWriter doesn't check that if (stat(vtkWriter->GetFileName(), &fileStatus) || (timeBefore == fileStatus.st_mtime)) { itkExceptionMacro(<<"Error during unstructured grid writing: file could not be written"); } } template void UnstructuredGridVtkWriter::SetInput(BaseData *input) { this->ProcessObject::SetNthInput(0, input); } template const UnstructuredGrid* UnstructuredGridVtkWriter::GetInput() { if (this->GetNumberOfInputs() < 1) { return 0; } else { return dynamic_cast(this->ProcessObject::GetInput(0)); } } template bool UnstructuredGridVtkWriter::CanWriteBaseDataType(BaseData::Pointer data) { return (dynamic_cast(data.GetPointer()) != 0); } template void UnstructuredGridVtkWriter::DoWrite(BaseData::Pointer data) { if (CanWriteBaseDataType(data)) { this->SetInput(dynamic_cast(data.GetPointer())); this->Update(); } } template std::vector UnstructuredGridVtkWriter::GetPossibleFileExtensions() { throw std::exception(); // no specialization available! } template const char* UnstructuredGridVtkWriter::GetDefaultFilename() { throw std::exception(); // no specialization available! } template const char* UnstructuredGridVtkWriter::GetFileDialogPattern() { throw std::exception(); // no specialization available! } template const char* UnstructuredGridVtkWriter::GetDefaultExtension() { throw std::exception(); // no specialization available! } } #endif diff --git a/Modules/Ext/Interactions/mitkAffineInteractor3D.cpp b/Modules/Ext/Interactions/mitkAffineInteractor3D.cpp index c34ddabfe4..70aa724730 100644 --- a/Modules/Ext/Interactions/mitkAffineInteractor3D.cpp +++ b/Modules/Ext/Interactions/mitkAffineInteractor3D.cpp @@ -1,488 +1,489 @@ /*=================================================================== 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 = Geometry3D::New(); + Geometry3D::Pointer geo3D = Geometry3D::New(); + m_OriginalGeometry = dynamic_cast(geo3D.GetPointer()); // 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< Geometry3D * >( + 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 ); - Geometry3D::Pointer newGeometry = static_cast< Geometry3D * >( + 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/Ext/Interactions/mitkAffineInteractor3D.h b/Modules/Ext/Interactions/mitkAffineInteractor3D.h index 761d480f6f..58b5b317a6 100644 --- a/Modules/Ext/Interactions/mitkAffineInteractor3D.h +++ b/Modules/Ext/Interactions/mitkAffineInteractor3D.h @@ -1,117 +1,117 @@ /*=================================================================== 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 MITKAFFINEINTERACTOR3D_H_HEADER_INCLUDED #define MITKAFFINEINTERACTOR3D_H_HEADER_INCLUDED #include "mitkInteractor.h" #include "MitkExtExports.h" #include "mitkCommon.h" #include "mitkSurface.h" #include #include namespace mitk { class DataNode; /** * \brief Affine interaction with objects in 3D windows. * * NOTE: The interaction mechanism is similar to that of vtkPlaneWidget * * \ingroup Interaction * \deprecatedSince{2013_12} mitk::AffineInteractor is deprecated. Use mitk::AffineDataInteractor instead. */ class MitkExt_EXPORT AffineInteractor3D : public Interactor { public: enum { INTERACTION_MODE_TRANSLATION, INTERACTION_MODE_ROTATION }; mitkClassMacro(AffineInteractor3D, Interactor); mitkNewMacro3Param(Self, const char *, DataNode *, int); mitkNewMacro2Param(Self, const char *, DataNode *); void SetInteractionMode( unsigned int interactionMode ); void SetInteractionModeToTranslation(); void SetInteractionModeToRotation(); unsigned int GetInteractionMode() const; /** \brief Sets the amount of precision */ void SetPrecision( ScalarType precision ); /** * \brief calculates how good the data, this statemachine handles, is hit * by the event. * * overwritten, cause we don't look at the boundingbox, we look at each point */ virtual float CanHandleEvent(StateEvent const *stateEvent) const; protected: /** * \brief Constructor with Param n for limited Set of Points * * if no n is set, then the number of points is unlimited* */ DEPRECATED(AffineInteractor3D(const char *type, DataNode *dataNode, int n = -1)); /** * \brief Default Destructor **/ virtual ~AffineInteractor3D(); virtual bool ExecuteAction( Action* action, mitk::StateEvent const* stateEvent ); bool ColorizeSurface( vtkPolyData *polyData, const Point3D &pickedPoint, double scalar = 0.0 ); private: /** \brief to store the value of precision to pick a point */ ScalarType m_Precision; bool m_InteractionMode; Point3D m_InitialPickedPoint; Point2D m_InitialPickedDisplayPoint; double m_InitialPickedPointWorld[4]; Point3D m_CurrentPickedPoint; Point2D m_CurrentPickedDisplayPoint; double m_CurrentPickedPointWorld[4]; - Geometry3D::Pointer m_Geometry; + BaseGeometry::Pointer m_Geometry; - Geometry3D::Pointer m_OriginalGeometry; + BaseGeometry::Pointer m_OriginalGeometry; Vector3D m_ObjectNormal; }; } #endif /* MITKAFFINEINTERACTOR3D_H_HEADER_INCLUDED */ diff --git a/Modules/Ext/Interactions/mitkSurfaceDeformationInteractor3D.cpp b/Modules/Ext/Interactions/mitkSurfaceDeformationInteractor3D.cpp index 749af5cc05..0089494a96 100644 --- a/Modules/Ext/Interactions/mitkSurfaceDeformationInteractor3D.cpp +++ b/Modules/Ext/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/Ext/Interactions/mitkSurfaceDeformationInteractor3D.h b/Modules/Ext/Interactions/mitkSurfaceDeformationInteractor3D.h index d31c0faabc..b61be85aeb 100644 --- a/Modules/Ext/Interactions/mitkSurfaceDeformationInteractor3D.h +++ b/Modules/Ext/Interactions/mitkSurfaceDeformationInteractor3D.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 MITKSURFACEDEFORMATIONINTERACTOR3D_H_HEADER_INCLUDED #define MITKSURFACEDEFORMATIONINTERACTOR3D_H_HEADER_INCLUDED #include "mitkInteractor.h" #include "MitkExtExports.h" #include "mitkCommon.h" #include class vtkPolyData; namespace mitk { class DataNode; class Surface; /** * \brief Affine interaction with objects in 3D windows. * * NOTE: The interaction mechanism is similar to that of vtkPlaneWidget * * \ingroup Interaction */ class MitkExt_EXPORT SurfaceDeformationInteractor3D : public Interactor { public: mitkClassMacro(SurfaceDeformationInteractor3D, Interactor); mitkNewMacro3Param(Self, const char *, DataNode *, int); mitkNewMacro2Param(Self, const char *, DataNode *); /** \brief Sets the amount of precision */ void SetPrecision( ScalarType precision ); /** * \brief calculates how good the data, this statemachine handles, is hit * by the event. * * overwritten, cause we don't look at the boundingbox, we look at each point */ virtual float CanHandleEvent(StateEvent const *stateEvent) const; protected: /** * \brief Constructor with Param n for limited Set of Points * * if no n is set, then the number of points is unlimited* */ SurfaceDeformationInteractor3D(const char *type, DataNode *dataNode, int n = -1); /** * \brief Default Destructor **/ virtual ~SurfaceDeformationInteractor3D(); virtual bool ExecuteAction( Action* action, mitk::StateEvent const* stateEvent ); enum { COLORIZATION_GAUSS, COLORIZATION_CONSTANT }; bool ColorizeSurface( vtkPolyData *polyData, const Point3D &pickedPoint, int mode, double scalar = 0.0 ); private: /** \brief to store the value of precision to pick a point */ ScalarType m_Precision; Point3D m_InitialPickedPoint; Point2D m_InitialPickedDisplayPoint; double m_InitialPickedPointWorld[4]; Point3D m_CurrentPickedPoint; Point2D m_CurrentPickedDisplayPoint; double m_CurrentPickedPointWorld[4]; Point3D m_SurfaceColorizationCenter; Vector3D m_ObjectNormal; - Geometry3D::Pointer m_Geometry; + BaseGeometry::Pointer m_Geometry; Surface *m_Surface; vtkPolyData *m_PolyData; DataNode *m_PickedSurfaceNode; Surface *m_PickedSurface; vtkPolyData *m_PickedPolyData; vtkPolyData *m_OriginalPolyData; double m_GaussSigma; }; } #endif /* MITKSURFACEDEFORMATIONINTERACTOR3D_H_HEADER_INCLUDED */ diff --git a/Modules/Ext/Testing/mitkPlaneFitTest.cpp b/Modules/Ext/Testing/mitkPlaneFitTest.cpp index 986d00ab27..2b038c967c 100644 --- a/Modules/Ext/Testing/mitkPlaneFitTest.cpp +++ b/Modules/Ext/Testing/mitkPlaneFitTest.cpp @@ -1,97 +1,98 @@ /*=================================================================== 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::Geometry3D::Pointer Geometry3D = mitk::Geometry3D::New(); + mitk::PlaneGeometry::Pointer planeGeo= mitk::PlaneGeometry::New(); + mitk::BaseGeometry::Pointer BaseGeometry = dynamic_cast(planeGeo.GetPointer()); 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]"<second; return NULL; } mitk::AffineTransform3D::Pointer mitk::NavigationDataObjectVisualizationFilter::GetOffset(int index) { OffsetPointerMap::const_iterator iter = m_OffsetList.find(index); if (iter != m_OffsetList.end()) return iter->second; return NULL; } void mitk::NavigationDataObjectVisualizationFilter::SetRepresentationObject(unsigned int idx, BaseData* data) { m_RepresentationList[idx] = RepresentationPointer(data); } void mitk::NavigationDataObjectVisualizationFilter::SetOffset(int index, mitk::AffineTransform3D::Pointer offset) { m_OffsetList[index] = offset; } void mitk::NavigationDataObjectVisualizationFilter::SetRotationMode(RotationMode r) { m_RotationMode = r; } void mitk::NavigationDataObjectVisualizationFilter::GenerateData() { /*get each input, lookup the associated BaseData and transfer the data*/ DataObjectPointerArray inputs = this->GetInputs(); //get all inputs for (unsigned int index=0; index < inputs.size(); index++) { //get the needed variables const mitk::NavigationData* nd = this->GetInput(index); assert(nd); mitk::NavigationData* output = this->GetOutput(index); assert(output); //check if the data is valid if (!nd->IsDataValid()) { output->SetDataValid(false); continue; } output->Graft(nd); // copy all information from input to output const mitk::BaseData* data = this->GetRepresentationObject(index); if (data == NULL) { MITK_WARN << "No BaseData associated with input " << index; continue; } //get the transform from data mitk::AffineTransform3D::Pointer affineTransform = data->GetGeometry()->GetIndexToWorldTransform(); if (affineTransform.IsNull()) { MITK_WARN << "AffineTransform IndexToWorldTransform not initialized!"; continue; } //check for offset mitk::AffineTransform3D::Pointer offset = this->GetOffset(index); //store the current scaling to set it after transformation mitk::Vector3D spacing = data->GetGeometry()->GetSpacing(); //clear spacing of data to be able to set it again afterwards ScalarType scale[] = {1.0, 1.0, 1.0}; data->GetGeometry()->SetSpacing(scale); /*now bring quaternion to affineTransform by using vnl_Quaternion*/ affineTransform->SetIdentity(); if (this->GetTransformOrientation(index) == true) { mitk::NavigationData::OrientationType orientation = nd->GetOrientation(); /* because of an itk bug, the transform can not be calculated with float data type. To use it in the mitk geometry classes, it has to be transfered to mitk::ScalarType which is float */ static AffineTransform3D::MatrixType m; //convert quaternion to rotation matrix depending on the rotation mode if(m_RotationMode == RotationStandard) { //calculate the transform from the quaternions static itk::QuaternionRigidTransform::Pointer quatTransform = itk::QuaternionRigidTransform::New(); // convert mitk::ScalarType quaternion to double quaternion because of itk bug vnl_quaternion doubleQuaternion(orientation.x(), orientation.y(), orientation.z(), orientation.r()); quatTransform->SetIdentity(); quatTransform->SetRotation(doubleQuaternion); quatTransform->Modified(); mitk::TransferMatrix(quatTransform->GetMatrix(), m); } else if(m_RotationMode == RotationTransposed) { vnl_matrix_fixed rot = orientation.rotation_matrix_transpose(); for(int i=0; i<3; i++) for (int j=0; j<3; j++) m[i][j] = rot[i][j]; } affineTransform->SetMatrix(m); } if (this->GetTransformPosition(index) == true) { ///*set the offset by convert from itkPoint to itkVector and setting offset of transform*/ mitk::Vector3D pos; pos.SetVnlVector(nd->GetPosition().GetVnlVector()); affineTransform->SetOffset(pos); } affineTransform->Modified(); //set the transform to data if(offset.IsNotNull()) //first use offset if there is one. { mitk::AffineTransform3D::Pointer overallTransform = mitk::AffineTransform3D::New(); overallTransform->SetIdentity(); overallTransform->Compose(offset); overallTransform->Compose(affineTransform); data->GetGeometry()->SetIndexToWorldTransform(overallTransform); } else { data->GetGeometry()->SetIndexToWorldTransform(affineTransform); } //set the original spacing to keep scaling of the geometrical object data->GetGeometry()->SetSpacing(spacing); - data->GetGeometry()->TransferItkToVtkTransform(); // update VTK Transform for rendering too data->GetGeometry()->Modified(); data->Modified(); output->SetDataValid(true); // operation was successful, therefore data of output is valid. } } void mitk::NavigationDataObjectVisualizationFilter::SetTransformPosition( unsigned int index, bool applyTransform ) { itkDebugMacro("setting TransformPosition for index " << index << " to " << applyTransform); BooleanInputMap::const_iterator it = this->m_TransformPosition.find(index); if ((it != this->m_TransformPosition.end()) && (it->second == applyTransform)) return; this->m_TransformPosition[index] = applyTransform; this->Modified(); } bool mitk::NavigationDataObjectVisualizationFilter::GetTransformPosition( unsigned int index ) const { itkDebugMacro("returning TransformPosition for index " << index); BooleanInputMap::const_iterator it = this->m_TransformPosition.find(index); if (it != this->m_TransformPosition.end()) return it->second; else return true; // default to true } void mitk::NavigationDataObjectVisualizationFilter::TransformPositionOn( unsigned int index ) { this->SetTransformPosition(index, true); } void mitk::NavigationDataObjectVisualizationFilter::TransformPositionOff( unsigned int index ) { this->SetTransformPosition(index, false); } void mitk::NavigationDataObjectVisualizationFilter::SetTransformOrientation( unsigned int index, bool applyTransform ) { itkDebugMacro("setting TransformOrientation for index " << index << " to " << applyTransform); BooleanInputMap::const_iterator it = this->m_TransformOrientation.find(index); if ((it != this->m_TransformOrientation.end()) && (it->second == applyTransform)) return; this->m_TransformOrientation[index] = applyTransform; this->Modified(); \ } bool mitk::NavigationDataObjectVisualizationFilter::GetTransformOrientation( unsigned int index ) const { itkDebugMacro("returning TransformOrientation for index " << index); BooleanInputMap::const_iterator it = this->m_TransformOrientation.find(index); if (it != this->m_TransformOrientation.end()) return it->second; else return true; // default to true } void mitk::NavigationDataObjectVisualizationFilter::TransformOrientationOn( unsigned int index ) { this->SetTransformOrientation(index, true); } void mitk::NavigationDataObjectVisualizationFilter::TransformOrientationOff( unsigned int index ) { this->SetTransformOrientation(index, false); } diff --git a/Modules/IGT/Tutorial/mitkIGTTutorialStep2.cpp b/Modules/IGT/Tutorial/mitkIGTTutorialStep2.cpp index f3a0f1be24..1f80ef16aa 100644 --- a/Modules/IGT/Tutorial/mitkIGTTutorialStep2.cpp +++ b/Modules/IGT/Tutorial/mitkIGTTutorialStep2.cpp @@ -1,156 +1,156 @@ /*=================================================================== 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 /** * \brief This tutorial shows how to compose navigation datas. Therefore we render two objects. * The first object is a cone that is tracked. The second object is a cylinder at a fixed position * relative to the cone. At the end of the tracking, the cylinder is moved to its new relative position * according to the last output of the tracking device. * In addition to IGT tutorial step 1, the objects are added to a datastorage. Furthermore, a renderwindow * is used for visual output. */ int main(int argc, char* argv[]) { // Before rendering, global interaction must be initialized mitk::GlobalInteraction::GetInstance()->Initialize("global"); //General code rendering the data in a renderwindow. See MITK Tutorial Step1 for more details. mitk::StandaloneDataStorage::Pointer dataStorage = mitk::StandaloneDataStorage::New(); mitk::RenderWindow::Pointer renderWindow = mitk::RenderWindow::New(); mitk::DataNode::Pointer dataNode = mitk::DataNode::New(); //Here, we want a 3D renderwindow renderWindow->GetRenderer()->SetMapperID(mitk::BaseRenderer::Standard3D); renderWindow->GetVtkRenderWindow()->SetSize( 500, 500 ); renderWindow->GetRenderer()->Resize( 500, 500); //Connect datastorage and renderwindow renderWindow->GetRenderer()->SetDataStorage(dataStorage); // --------------begin of moving object code -------------------------- // //Virtual tracking device to generate random positions mitk::VirtualTrackingDevice::Pointer tracker = mitk::VirtualTrackingDevice::New(); //Bounds (within the random numbers are generated) must be set before the tools are added double bound = 10.0; mitk::ScalarType bounds[] = {-bound, bound, -bound, bound, -bound, bound}; tracker->SetBounds(bounds); tracker->AddTool("tool1"); //Tracking device source to get the data mitk::TrackingDeviceSource::Pointer source = mitk::TrackingDeviceSource::New(); source->SetTrackingDevice(tracker); source->Connect(); //Cone representation for rendering of the moving object mitk::Cone::Pointer cone = mitk::Cone::New(); dataNode->SetData(cone); dataNode->SetName("My tracked object"); dataNode->SetColor(0.0, 1.0, 1.0); dataStorage->Add(dataNode); //Filter for rendering the cone at correct postion and orientation mitk::NavigationDataObjectVisualizationFilter::Pointer visualizer = mitk::NavigationDataObjectVisualizationFilter::New(); visualizer->SetInput(0, source->GetOutput()); visualizer->SetRepresentationObject(0, cone); source->StartTracking(); // --------------end of moving object code -------------------------- // // --------------begin of fixed object code ------------------------- // //Cylinder representation for rendering of the fixed object mitk::DataNode::Pointer cylinderNode = mitk::DataNode::New(); mitk::Cylinder::Pointer cylinder = mitk::Cylinder::New(); cylinderNode->SetData(cylinder); cylinderNode->SetName("My fixed object"); cylinderNode->SetColor(1.0, 0.0, 0.0); dataStorage->Add(cylinderNode); //Define a rotation and a translation for the fixed object mitk::Matrix3D rotationMatrix; rotationMatrix.SetIdentity(); double alpha = 0.3; rotationMatrix[1][1] = cos(alpha); rotationMatrix[1][2] = -sin(alpha); rotationMatrix[2][1] = sin(alpha); rotationMatrix[2][2] = cos(alpha); mitk::Vector3D offset; offset.Fill(5.0); //Add rotation and translation to affine transform mitk::AffineTransform3D::Pointer affineTransform3D = mitk::AffineTransform3D::New(); affineTransform3D->SetOffset(offset); affineTransform3D->SetMatrix(rotationMatrix); //apply rotation and translation mitk::NavigationData::Pointer fixedNavigationData = mitk::NavigationData::New(affineTransform3D); cylinder->GetGeometry()->SetIndexToWorldTransform(fixedNavigationData->GetAffineTransform3D()); // --------------end of fixed object code ------------------------- // // Global reinit with the bounds of the virtual tracking device mitk::TimeGeometry::Pointer timeGeometry = dataStorage->ComputeBoundingGeometry3D(dataStorage->GetAll()); - mitk::Geometry3D::Pointer geometry = timeGeometry->GetGeometryForTimeStep(0); + mitk::BaseGeometry::Pointer geometry = timeGeometry->GetGeometryForTimeStep(0); geometry->SetBounds(bounds); mitk::RenderingManager::GetInstance()->InitializeViews( geometry ); //Generate and render 75 time steps to move the tracked object for(int i=0; i < 75; ++i) { //Update the cone position visualizer->Update(); //Update rendering renderWindow->GetVtkRenderWindow()->Render(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); MITK_INFO << "Position " << source->GetOutput()->GetPosition(); //Slight delay for the random numbers itksys::SystemTools::Delay(100); } //Stop the tracking device and disconnect it //The tracking is done, now we want to move the fixed object to its correct relative position regarding the tracked object. source->StopTracking(); source->Disconnect(); //Now the tracking is finished and we can use the transformation to move //the fixed object to its correct position relative to the new position //of the moving/tracked object. Therefore, we compose the navigation datas. fixedNavigationData->Compose(source->GetOutput(), false); //Update the transformation matrix of the cylinder cylinder->GetGeometry()->SetIndexToWorldTransform(fixedNavigationData->GetAffineTransform3D()); //Update the rendering renderWindow->GetVtkRenderWindow()->Render(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); //Wait a little before closing the renderwindow itksys::SystemTools::Delay(2000); } diff --git a/Modules/IGTUI/Qmitk/QmitkInteractiveTransformationWidget.cpp b/Modules/IGTUI/Qmitk/QmitkInteractiveTransformationWidget.cpp index 982c315ee8..bdd930bfd1 100644 --- a/Modules/IGTUI/Qmitk/QmitkInteractiveTransformationWidget.cpp +++ b/Modules/IGTUI/Qmitk/QmitkInteractiveTransformationWidget.cpp @@ -1,288 +1,288 @@ /*=================================================================== 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 "QmitkInteractiveTransformationWidget.h" // mitk includes #include "mitkRenderingManager.h" #include "mitkNavigationData.h" // vtk includes #include "vtkMatrix4x4.h" #include "vtkLinearTransform.h" const std::string QmitkInteractiveTransformationWidget::VIEW_ID = "org.mitk.views.interactivetransformationwidget"; QmitkInteractiveTransformationWidget::QmitkInteractiveTransformationWidget(QWidget* parent, Qt::WindowFlags f) : QWidget(parent, f), m_Geometry(NULL), m_ResetGeometry(NULL), m_Controls(NULL) { CreateQtPartControl(this); CreateConnections(); m_TranslationVector.Fill(0.0f); m_RotateSliderPos.Fill(0.0f); } QmitkInteractiveTransformationWidget::~QmitkInteractiveTransformationWidget() { } void QmitkInteractiveTransformationWidget::CreateQtPartControl(QWidget *parent) { if (!m_Controls) { // create GUI widgets m_Controls = new Ui::QmitkInteractiveTransformationWidgetControls; m_Controls->setupUi(parent); } } void QmitkInteractiveTransformationWidget::CreateConnections() { if ( m_Controls ) { // translations connect( (QObject*)(m_Controls->m_XTransSlider), SIGNAL(valueChanged(int)), this, SLOT(OnXTranslationValueChanged(int)) ); connect( (QObject*)(m_Controls->m_XTransSpinBox), SIGNAL(valueChanged(int)), this, SLOT(OnXTranslationValueChanged(int)) ); connect( (QObject*)(m_Controls->m_YTransSlider), SIGNAL(valueChanged(int)), this, SLOT(OnYTranslationValueChanged(int)) ); connect( (QObject*)(m_Controls->m_YTransSpinBox), SIGNAL(valueChanged(int)), this, SLOT(OnYTranslationValueChanged(int)) ); connect( (QObject*)(m_Controls->m_ZTransSlider), SIGNAL(valueChanged(int)), this, SLOT(OnZTranslationValueChanged(int)) ); connect( (QObject*)(m_Controls->m_ZTransSpinBox), SIGNAL(valueChanged(int)), this, SLOT(OnZTranslationValueChanged(int)) ); // rotations connect( (QObject*)(m_Controls->m_XRotSlider), SIGNAL(valueChanged(int)), this, SLOT(OnXRotationValueChanged(int)) ); connect( (QObject*)(m_Controls->m_XRotSpinBox), SIGNAL(valueChanged(int)), this, SLOT(OnXRotationValueChanged(int)) ); connect( (QObject*)(m_Controls->m_YRotSlider), SIGNAL(valueChanged(int)), this, SLOT(OnYRotationValueChanged(int)) ); connect( (QObject*)(m_Controls->m_YRotSpinBox), SIGNAL(valueChanged(int)), this, SLOT(OnYRotationValueChanged(int)) ); connect( (QObject*)(m_Controls->m_ZRotSlider), SIGNAL(valueChanged(int)), this, SLOT(OnZRotationValueChanged(int)) ); connect( (QObject*)(m_Controls->m_ZRotSpinBox), SIGNAL(valueChanged(int)), this, SLOT(OnZRotationValueChanged(int)) ); connect( (QObject*)(m_Controls->m_ResetPB), SIGNAL(clicked()), this, SLOT(OnResetGeometry()) ); connect( (QObject*)(m_Controls->m_UseManipulatedToolTipPB), SIGNAL(clicked()), this, SLOT(OnApplyManipulatedToolTip()) ); } } -void QmitkInteractiveTransformationWidget::SetGeometry( mitk::Geometry3D::Pointer geometry, mitk::Geometry3D::Pointer defaultValues ) +void QmitkInteractiveTransformationWidget::SetGeometry( mitk::BaseGeometry::Pointer geometry, mitk::BaseGeometry::Pointer defaultValues ) { m_Geometry = geometry; - m_ResetGeometry = geometry->Clone(); + itk::LightObject::Pointer lopointer = geometry->Clone(); + m_ResetGeometry = dynamic_cast(lopointer.GetPointer()); //set default values if (defaultValues.IsNotNull()) { //first: some conversion mitk::NavigationData::Pointer transformConversionHelper = mitk::NavigationData::New(defaultValues->GetIndexToWorldTransform()); double eulerAlphaDegrees = transformConversionHelper->GetOrientation().rotation_euler_angles()[0] / vnl_math::pi * 180; double eulerBetaDegrees = transformConversionHelper->GetOrientation().rotation_euler_angles()[1] / vnl_math::pi * 180; double eulerGammaDegrees = transformConversionHelper->GetOrientation().rotation_euler_angles()[2] / vnl_math::pi * 180; //set translation OnXTranslationValueChanged(defaultValues->GetIndexToWorldTransform()->GetOffset()[0]); OnYTranslationValueChanged(defaultValues->GetIndexToWorldTransform()->GetOffset()[1]); OnZTranslationValueChanged(defaultValues->GetIndexToWorldTransform()->GetOffset()[2]); //set rotation OnXRotationValueChanged(eulerAlphaDegrees); OnYRotationValueChanged(eulerBetaDegrees); OnZRotationValueChanged(eulerGammaDegrees); } else { //reset everything OnXTranslationValueChanged(0); OnYTranslationValueChanged(0); OnZTranslationValueChanged(0); OnXRotationValueChanged(0); OnYRotationValueChanged(0); OnZRotationValueChanged(0); } } -mitk::Geometry3D::Pointer QmitkInteractiveTransformationWidget::GetGeometry() +mitk::BaseGeometry::Pointer QmitkInteractiveTransformationWidget::GetGeometry() { return m_Geometry; } ///////////////////////////////////////////////////////////////////////////////////////////// // Section to allow interactive positioning of the moving surface ///////////////////////////////////////////////////////////////////////////////////////////// void QmitkInteractiveTransformationWidget::OnXTranslationValueChanged( int v ) { mitk::Vector3D translationParams; translationParams[0] = v; translationParams[1] = m_Controls->m_YTransSlider->value(); translationParams[2] = m_Controls->m_ZTransSlider->value(); SetSliderX(v); this->Translate(translationParams); } void QmitkInteractiveTransformationWidget::SetSliderX(int v) { m_Controls->m_XTransSlider->setValue(v); m_Controls->m_XTransSpinBox->setValue(v); } void QmitkInteractiveTransformationWidget::OnYTranslationValueChanged( int v ) { mitk::Vector3D translationParams; translationParams[0] = m_Controls->m_XTransSlider->value(); translationParams[1] = v; translationParams[2] = m_Controls->m_ZTransSlider->value(); SetSliderY(v); this->Translate(translationParams); } void QmitkInteractiveTransformationWidget::SetSliderY(int v) { m_Controls->m_YTransSlider->setValue(v); m_Controls->m_YTransSpinBox->setValue(v); } void QmitkInteractiveTransformationWidget::OnZTranslationValueChanged( int v ) { mitk::Vector3D translationParams; translationParams[0] = m_Controls->m_XTransSlider->value(); translationParams[1] = m_Controls->m_YTransSlider->value(); translationParams[2] = v; SetSliderZ(v); this->Translate(translationParams); } void QmitkInteractiveTransformationWidget::SetSliderZ(int v) { m_Controls->m_ZTransSlider->setValue(v); m_Controls->m_ZTransSpinBox->setValue(v); } void QmitkInteractiveTransformationWidget::Translate( mitk::Vector3D translateVector) { mitk::Vector3D translateVec; // transform the translation vector translateVec[0] = translateVector[0] - m_TranslationVector[0]; translateVec[1] = translateVector[1] - m_TranslationVector[1]; translateVec[2] = translateVector[2] - m_TranslationVector[2]; // set the new translation vector to member variable m_TranslationVector[0] = translateVector[0]; m_TranslationVector[1] = translateVector[1]; m_TranslationVector[2] = translateVector[2]; m_Geometry->Translate( translateVec ); qApp->processEvents(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkInteractiveTransformationWidget::OnXRotationValueChanged( int v ) { mitk::Vector3D rotationParams; rotationParams[0] = v; rotationParams[1] = m_Controls->m_YRotSlider->value(); rotationParams[2] = m_Controls->m_ZRotSlider->value(); m_Controls->m_XRotSlider->setValue(v); m_Controls->m_XRotSpinBox->setValue(v); this->Rotate(rotationParams); } void QmitkInteractiveTransformationWidget::OnYRotationValueChanged( int v ) { mitk::Vector3D rotationParams; rotationParams[0] = m_Controls->m_XRotSlider->value(); rotationParams[1] = v; rotationParams[2] = m_Controls->m_ZRotSlider->value(); m_Controls->m_YRotSlider->setValue(v); m_Controls->m_YRotSpinBox->setValue(v); this->Rotate(rotationParams); } void QmitkInteractiveTransformationWidget::OnZRotationValueChanged( int v ) { mitk::Vector3D rotationParams; rotationParams[0]=m_Controls->m_XRotSlider->value(); rotationParams[1]=m_Controls->m_YRotSlider->value(); rotationParams[2]=v; m_Controls->m_ZRotSlider->setValue(v); m_Controls->m_ZRotSpinBox->setValue(v); this->Rotate(rotationParams); } void QmitkInteractiveTransformationWidget::Rotate(mitk::Vector3D rotateVector) { //0: from degrees to radians double radianX = rotateVector[0] * vnl_math::pi / 180; double radianY = rotateVector[1] * vnl_math::pi / 180; double radianZ = rotateVector[2] * vnl_math::pi / 180; //1: from euler angles to quaternion mitk::Quaternion rotation(radianX,radianY,radianZ); //2: Conversion to navigation data / transform mitk::NavigationData::Pointer rotationTransform = mitk::NavigationData::New(); rotationTransform->SetOrientation(rotation); //3: Apply transform //also remember old transform, but without rotation, because rotation is completely stored in the sliders mitk::NavigationData::Pointer oldTransform = mitk::NavigationData::New(m_Geometry->GetIndexToWorldTransform()); mitk::Quaternion identity(0,0,0,1); oldTransform->SetOrientation(identity); //compose old transform with the new one rotationTransform->Compose(oldTransform); //and apply it... m_Geometry->SetIndexToWorldTransform(rotationTransform->GetAffineTransform3D()); qApp->processEvents(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkInteractiveTransformationWidget::OnResetGeometry() { m_Controls->m_XRotSlider->setValue(0); m_Controls->m_YRotSlider->setValue(0); m_Controls->m_ZRotSlider->setValue(0); m_Controls->m_XRotSpinBox->setValue(0); m_Controls->m_YRotSpinBox->setValue(0); m_Controls->m_ZRotSpinBox->setValue(0); m_Controls->m_XTransSlider->setValue(0); m_Controls->m_YTransSlider->setValue(0); m_Controls->m_ZTransSlider->setValue(0); m_Controls->m_XTransSpinBox->setValue(0); m_Controls->m_YTransSpinBox->setValue(0); m_Controls->m_ZTransSpinBox->setValue(0); qApp->processEvents(); // reset the input to its initial state. - m_ResetGeometry->TransferItkToVtkTransform(); m_Geometry->SetIdentity(); m_Geometry->Compose(m_ResetGeometry->GetVtkTransform()->GetMatrix()); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkInteractiveTransformationWidget::OnApplyManipulatedToolTip() { emit ApplyManipulatedToolTip(); } \ No newline at end of file diff --git a/Modules/IGTUI/Qmitk/QmitkInteractiveTransformationWidget.h b/Modules/IGTUI/Qmitk/QmitkInteractiveTransformationWidget.h index faec37487e..7b9cc00790 100644 --- a/Modules/IGTUI/Qmitk/QmitkInteractiveTransformationWidget.h +++ b/Modules/IGTUI/Qmitk/QmitkInteractiveTransformationWidget.h @@ -1,94 +1,94 @@ /*=================================================================== 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 QmitkInteractiveTransformationWidget_H #define QmitkInteractiveTransformationWidget_H //QT headers #include //Mitk headers #include "MitkIGTUIExports.h" #include "mitkVector.h" #include "mitkGeometry3D.h" //ui header #include "ui_QmitkInteractiveTransformationWidgetControls.h" /** Documentation: * \brief An object of this class offers an UI to create a widget to access the advance tool creation options. * * * \ingroup IGTUI */ class MitkIGTUI_EXPORT QmitkInteractiveTransformationWidget : public QWidget { Q_OBJECT public: static const std::string VIEW_ID; QmitkInteractiveTransformationWidget(QWidget* parent = 0, Qt::WindowFlags f = 0); ~QmitkInteractiveTransformationWidget(); /** Sets the geometry which will be modified by this widget. Default values may be * provided by the second variable. These values will be applied to the geometry * in the beginning and the UI will also hold these values. */ - void SetGeometry(mitk::Geometry3D::Pointer geometry, mitk::Geometry3D::Pointer defaultValues = NULL); + void SetGeometry(mitk::BaseGeometry::Pointer geometry, mitk::BaseGeometry::Pointer defaultValues = NULL); - mitk::Geometry3D::Pointer GetGeometry(); + mitk::BaseGeometry::Pointer GetGeometry(); protected slots: void OnZTranslationValueChanged( int v ); void OnYTranslationValueChanged( int v ); void OnXTranslationValueChanged( int v ); void OnZRotationValueChanged( int v ); void OnYRotationValueChanged( int v ); void OnXRotationValueChanged( int v ); void OnResetGeometry(); void OnApplyManipulatedToolTip(); signals: void ApplyManipulatedToolTip(); protected: virtual void CreateConnections(); virtual void CreateQtPartControl(QWidget *parent); void SetSliderX(int v); void SetSliderY(int v); void SetSliderZ(int v); /*! \brief Method performs the translation. \params translateVector New translation to be combine with geometry. */ void Translate( mitk::Vector3D translateVector); /*! \brief Method performs the rotation. \params rotateVector New rotation to be combined with geometry. */ void Rotate(mitk::Vector3D rotateVector); // Member variables Ui::QmitkInteractiveTransformationWidgetControls* m_Controls; - mitk::Geometry3D::Pointer m_Geometry; ///< \brief Initial geometry that is manipulated - mitk::Geometry3D::Pointer m_ResetGeometry; ///< \brief Lifeline to reset to the initial geometry + mitk::BaseGeometry::Pointer m_Geometry; ///< \brief Initial geometry that is manipulated + mitk::BaseGeometry::Pointer m_ResetGeometry; ///< \brief Lifeline to reset to the initial geometry mitk::Vector3D m_TranslationVector; ///< \brief Accumulated translation vector mitk::Vector3D m_RotateSliderPos; ///< \brief Accumulated rotation vector (holds degree around x,y,z direction) }; #endif // QmitkInteractiveTransformationWidget_H diff --git a/Modules/IGTUI/Qmitk/QmitkNavigationToolCreationAdvancedWidget.cpp b/Modules/IGTUI/Qmitk/QmitkNavigationToolCreationAdvancedWidget.cpp index 8aa0fb3cc8..84168dd8ce 100644 --- a/Modules/IGTUI/Qmitk/QmitkNavigationToolCreationAdvancedWidget.cpp +++ b/Modules/IGTUI/Qmitk/QmitkNavigationToolCreationAdvancedWidget.cpp @@ -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. ===================================================================*/ #include "QmitkNavigationToolCreationAdvancedWidget.h" #include "QmitkNavigationToolCreationWidget.h" // mitk includes #include #include #include // vtk includes #include #include const std::string QmitkNavigationToolCreationAdvancedWidget::VIEW_ID = "org.mitk.views.navigationtoolcreationadvancedwidget"; QmitkNavigationToolCreationAdvancedWidget::QmitkNavigationToolCreationAdvancedWidget(QWidget* parent, Qt::WindowFlags f) : QDialog(parent, f), m_Controls(NULL), m_DataStorage(NULL), m_ToolTipSurface(NULL), m_ManipulatedToolTip(NULL), m_SurfaceNodeName("") { CreateQtPartControl(this); CreateConnections(); } QmitkNavigationToolCreationAdvancedWidget::~QmitkNavigationToolCreationAdvancedWidget() { } void QmitkNavigationToolCreationAdvancedWidget::CreateQtPartControl(QWidget *parent) { if (!m_Controls) { // create GUI widgets m_Controls = new Ui::QmitkNavigationToolCreationAdvancedWidgetControls; m_Controls->setupUi(parent); } } void QmitkNavigationToolCreationAdvancedWidget::CreateConnections() { if ( m_Controls ) { connect( (QObject*)(this), SIGNAL(finished(int)), this, SLOT(OnClose())); connect( (QObject*)(m_Controls->m_InteractiveTransformation), SIGNAL(ApplyManipulatedToolTip()), this, SLOT(OnApplyManipulatedToolTip())); } } void QmitkNavigationToolCreationAdvancedWidget::OnClose() { //clean the data storage if(m_DataStorage->Exists(m_DataStorage->GetNamedNode("StandardToolTip"))) { m_DataStorage->Remove(m_DataStorage->GetNamedNode("StandardToolTip")); } if(m_DataStorage.IsNotNull() && m_DataStorage->Exists(m_DataStorage->GetNamedNode("ManipulatedToolTip"))) { m_DataStorage->Remove(m_DataStorage->GetNamedNode("ManipulatedToolTip")); } emit DialogCloseRequested(); } void QmitkNavigationToolCreationAdvancedWidget::SetDataStorage( mitk::DataStorage::Pointer dataStorage ) { m_DataStorage = dataStorage; } void QmitkNavigationToolCreationAdvancedWidget::ReInitialize() { if (m_DataStorage.IsNull()) return; //reset some variables m_ManipulatedToolTip = NULL; m_ToolTipSurface = NULL; m_SurfaceNodeName = ""; - mitk::Geometry3D::Pointer emptyGeo = mitk::Geometry3D::New(); + mitk::BaseGeometry::Pointer emptyGeo = mitk::Geometry3D::New(); m_Controls->m_InteractiveTransformation->SetGeometry(emptyGeo,emptyGeo); //call initialization method this->RetrieveAndInitializeDataForTooltipManipulation(); //care for new data nodes mitk::DataNode::Pointer manipulatedTipNode = NULL; if(!m_DataStorage->Exists(m_DataStorage->GetNamedNode("ManipulatedToolTip"))) { manipulatedTipNode = mitk::DataNode::New(); manipulatedTipNode->SetData(m_ManipulatedToolTip); manipulatedTipNode->SetName("ManipulatedToolTip"); manipulatedTipNode->SetColor(1.0, 0.0, 0.0); manipulatedTipNode->SetOpacity(0.5); m_DataStorage->Add(manipulatedTipNode); } else { manipulatedTipNode = m_DataStorage->GetNamedNode("ManipulatedToolTip"); manipulatedTipNode->SetData(m_ManipulatedToolTip); } // reinit the views with the new nodes mitk::RenderingManager::GetInstance()->InitializeViewsByBoundingObjects(m_DataStorage); } void QmitkNavigationToolCreationAdvancedWidget::RetrieveAndInitializeDataForTooltipManipulation() { // we need the tooltip surface (point or stl) emit RetrieveDataForManualToolTipManipulation(); if(m_ToolTipSurface.IsNotNull()) { mitk::DataNode::Pointer toolTipNode = NULL; if(m_SurfaceNodeName.empty()) { m_SurfaceNodeName = "StandardToolTip"; } if(!m_DataStorage->Exists(m_DataStorage->GetNamedNode(m_SurfaceNodeName))) { toolTipNode = mitk::DataNode::New(); toolTipNode->SetName(m_SurfaceNodeName); toolTipNode->SetData(m_ToolTipSurface); m_DataStorage->Add(toolTipNode); } else { toolTipNode = m_DataStorage->GetNamedNode(m_SurfaceNodeName); toolTipNode->SetData(m_ToolTipSurface); } m_ManipulatedToolTip = m_ToolTipSurface->Clone(); - mitk::Geometry3D::Pointer defaultGeo = mitk::Geometry3D::New(); + mitk::BaseGeometry::Pointer defaultGeo = mitk::Geometry3D::New(); defaultGeo->SetIndexToWorldTransform(m_DefaultToolTip); m_Controls->m_InteractiveTransformation->SetGeometry(m_ManipulatedToolTip->GetGeometry(),defaultGeo); } else { mitkThrow() << "No tooltip surface specified, operation aborted"; } } void QmitkNavigationToolCreationAdvancedWidget::SetDefaultTooltip(mitk::AffineTransform3D::Pointer defaultToolTip) { m_DefaultToolTip = defaultToolTip; } void QmitkNavigationToolCreationAdvancedWidget::SetToolTipSurface( bool cone, mitk::DataNode::Pointer node /*= NULL*/ ) { m_ToolTipSurface = NULL; if(cone) { m_ToolTipSurface = mitk::Surface::New(); vtkConeSource *vtkData = vtkConeSource::New(); vtkData->SetAngle(5.0); vtkData->SetResolution(50); vtkData->SetHeight(6.0f); vtkData->SetRadius(2.0f); vtkData->SetCenter(0.0, 0.0, 0.0); vtkData->Update(); m_ToolTipSurface->SetVtkPolyData(vtkData->GetOutput()); vtkData->Delete(); m_SurfaceNodeName =""; } else if(!cone && node.IsNotNull()) { m_ToolTipSurface = dynamic_cast(node->GetData()); m_SurfaceNodeName = node->GetName(); } } void QmitkNavigationToolCreationAdvancedWidget::OnApplyManipulatedToolTip() { //save manipulated surface object, which holds the tooltip as geometry m_ManipulatedToolTip = dynamic_cast(m_DataStorage->GetNamedNode("ManipulatedToolTip")->GetData()->Clone().GetPointer()); //then close the window OnClose(); } mitk::AffineTransform3D::Pointer QmitkNavigationToolCreationAdvancedWidget::GetManipulatedToolTip() { mitk::AffineTransform3D::Pointer returnValue = mitk::AffineTransform3D::New(); if (m_ManipulatedToolTip.IsNotNull()) returnValue = m_ManipulatedToolTip->GetGeometry()->GetIndexToWorldTransform(); else returnValue->SetIdentity(); return returnValue; } diff --git a/Modules/IOExt/Internal/mitkUnstructuredGridVtkWriter.txx b/Modules/IOExt/Internal/mitkUnstructuredGridVtkWriter.txx index c4890229c4..b544b24d0f 100644 --- a/Modules/IOExt/Internal/mitkUnstructuredGridVtkWriter.txx +++ b/Modules/IOExt/Internal/mitkUnstructuredGridVtkWriter.txx @@ -1,205 +1,203 @@ /*=================================================================== 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_UNSTRUCTURED_GRID_VTKWRITER_TXX_ #define _MITK_UNSTRUCTURED_GRID_VTKWRITER_TXX_ #include #include #include #include #include #include #include #include #include #include #include namespace mitk { template UnstructuredGridVtkWriter::UnstructuredGridVtkWriter() : m_Success(false) { this->SetNumberOfRequiredInputs(1); } template UnstructuredGridVtkWriter::~UnstructuredGridVtkWriter() { } template void UnstructuredGridVtkWriter::GenerateData() { m_Success = false; if ( m_FileName == "" ) { itkWarningMacro( << "Sorry, filename has not been set!" ); return ; } mitk::UnstructuredGrid::Pointer input = const_cast(this->GetInput()); if (input.IsNull()) { itkWarningMacro( << "Sorry, input to mitk::UnstructuredGridVtkWriter is NULL"); return; } VTKWRITER* unstructuredGridWriter = VTKWRITER::New(); vtkTransformFilter* transformPointSet = vtkTransformFilter::New(); vtkUnstructuredGrid * unstructuredGrid; - Geometry3D* geometry; + BaseGeometry* geometry; if(input->GetTimeGeometry()->CountTimeSteps()>1) { int t, timesteps; timesteps = input->GetTimeGeometry()->CountTimeSteps(); for(t = 0; t < timesteps; ++t) { std::ostringstream filename; geometry = input->GetGeometry(t); if(input->GetTimeGeometry()->IsValidTimeStep(t)) { const mitk::TimeBounds& timebounds = geometry->GetTimeBounds(); filename << m_FileName.c_str() << "_S" << std::setprecision(0) << timebounds[0] << "_E" << std::setprecision(0) << timebounds[1] << "_T" << t << GetDefaultExtension(); } else { itkWarningMacro(<<"Error on write: TimeGeometry invalid of unstructured grid " << filename.str() << "."); filename << m_FileName.c_str() << "_T" << t << GetDefaultExtension(); } - geometry->TransferItkToVtkTransform(); transformPointSet->SetInputData(input->GetVtkUnstructuredGrid(t)); transformPointSet->SetTransform(geometry->GetVtkTransform()); transformPointSet->UpdateWholeExtent(); unstructuredGrid = static_cast(transformPointSet->GetOutput()); unstructuredGridWriter->SetFileName(filename.str().c_str()); unstructuredGridWriter->SetInputData(unstructuredGrid); ExecuteWrite( unstructuredGridWriter ); } } else { geometry = input->GetGeometry(); - geometry->TransferItkToVtkTransform(); transformPointSet->SetInputData(input->GetVtkUnstructuredGrid()); transformPointSet->SetTransform(geometry->GetVtkTransform()); transformPointSet->UpdateWholeExtent(); unstructuredGrid = static_cast(transformPointSet->GetOutput()); unstructuredGridWriter->SetFileName(m_FileName.c_str()); unstructuredGridWriter->SetInputData(unstructuredGrid); ExecuteWrite( unstructuredGridWriter ); } transformPointSet->Delete(); unstructuredGridWriter->Delete(); m_Success = true; } template void UnstructuredGridVtkWriter::ExecuteWrite( VTKWRITER* vtkWriter ) { struct stat fileStatus; time_t timeBefore=0; if (!stat(vtkWriter->GetFileName(), &fileStatus)) { timeBefore = fileStatus.st_mtime; } if (!vtkWriter->Write()) { itkExceptionMacro( << "Error during unstructured grid writing."); } // check if file can be written because vtkWriter doesn't check that if (stat(vtkWriter->GetFileName(), &fileStatus) || (timeBefore == fileStatus.st_mtime)) { itkExceptionMacro(<<"Error during unstructured grid writing: file could not be written"); } } template void UnstructuredGridVtkWriter::SetInput(BaseData *input) { this->ProcessObject::SetNthInput(0, input); } template const UnstructuredGrid* UnstructuredGridVtkWriter::GetInput() { if (this->GetNumberOfInputs() < 1) { return 0; } else { return dynamic_cast(this->ProcessObject::GetInput(0)); } } template bool UnstructuredGridVtkWriter::CanWriteBaseDataType(BaseData::Pointer data) { return (dynamic_cast(data.GetPointer()) != 0); } template void UnstructuredGridVtkWriter::DoWrite(BaseData::Pointer data) { if (CanWriteBaseDataType(data)) { this->SetInput(dynamic_cast(data.GetPointer())); this->Update(); } } template std::vector UnstructuredGridVtkWriter::GetPossibleFileExtensions() { throw std::exception(); // no specialization available! } template const char* UnstructuredGridVtkWriter::GetDefaultFilename() { throw std::exception(); // no specialization available! } template const char* UnstructuredGridVtkWriter::GetFileDialogPattern() { throw std::exception(); // no specialization available! } template const char* UnstructuredGridVtkWriter::GetDefaultExtension() { throw std::exception(); // no specialization available! } } #endif diff --git a/Modules/ImageExtraction/mitkExtractDirectedPlaneImageFilter.cpp b/Modules/ImageExtraction/mitkExtractDirectedPlaneImageFilter.cpp index 25caecd178..78331a48ef 100644 --- a/Modules/ImageExtraction/mitkExtractDirectedPlaneImageFilter.cpp +++ b/Modules/ImageExtraction/mitkExtractDirectedPlaneImageFilter.cpp @@ -1,490 +1,490 @@ /*=================================================================== 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 "mitkExtractDirectedPlaneImageFilter.h" #include "mitkAbstractTransformGeometry.h" //#include "mitkImageMapperGL2D.h" #include #include #include #include #include "vtkMitkThickSlicesFilter.h" #include #include #include #include #include #include #include mitk::ExtractDirectedPlaneImageFilter::ExtractDirectedPlaneImageFilter() : m_WorldGeometry(NULL) { MITK_WARN << "Class ExtractDirectedPlaneImageFilter is deprecated! Use ExtractSliceFilter instead."; m_Reslicer = vtkImageReslice::New(); m_TargetTimestep = 0; m_InPlaneResampleExtentByGeometry = true; m_ResliceInterpolationProperty = NULL;//VtkResliceInterpolationProperty::New(); //TODO initial with value m_ThickSlicesMode = 0; m_ThickSlicesNum = 1; } mitk::ExtractDirectedPlaneImageFilter::~ExtractDirectedPlaneImageFilter() { if(m_ResliceInterpolationProperty!=NULL)m_ResliceInterpolationProperty->Delete(); m_Reslicer->Delete(); } void mitk::ExtractDirectedPlaneImageFilter::GenerateData() { // A world geometry must be set... if ( m_WorldGeometry == NULL ) { itkWarningMacro(<<"No world geometry has been set. Returning."); return; } Image *input = const_cast< ImageToImageFilter::InputImageType* >( this->GetInput() ); input->Update(); if ( input == NULL ) { itkWarningMacro(<<"No input set."); return; } const TimeGeometry *inputTimeGeometry = input->GetTimeGeometry(); if ( ( inputTimeGeometry == NULL ) || ( inputTimeGeometry->CountTimeSteps() == 0 ) ) { itkWarningMacro(<<"Error reading input image geometry."); return; } // Get the target timestep; if none is set, use the lowest given. unsigned int timestep = 0; if ( ! m_TargetTimestep ) { ScalarType time = m_WorldGeometry->GetTimeBounds()[0]; if ( time > ScalarTypeNumericTraits::NonpositiveMin() ) { timestep = inputTimeGeometry->TimePointToTimeStep( time ); } } else timestep = m_TargetTimestep; if ( inputTimeGeometry->IsValidTimeStep( timestep ) == false ) { itkWarningMacro(<<"This is not a valid timestep: "<IsVolumeSet( timestep ) ) { itkWarningMacro(<<"No volume data existent at given timestep "<GetLargestPossibleRegion(); requestedRegion.SetIndex( 3, timestep ); requestedRegion.SetSize( 3, 1 ); requestedRegion.SetSize( 4, 1 ); input->SetRequestedRegion( &requestedRegion ); input->Update(); vtkImageData* inputData = input->GetVtkImageData( timestep ); if ( inputData == NULL ) { itkWarningMacro(<<"Could not extract vtk image data for given timestep"<GetSpacing( spacing ); // how big the area is in physical coordinates: widthInMM x heightInMM pixels mitk::ScalarType widthInMM, heightInMM; // where we want to sample Point3D origin; Vector3D right, bottom, normal; Vector3D rightInIndex, bottomInIndex; assert( input->GetTimeGeometry() == inputTimeGeometry ); // take transform of input image into account - Geometry3D* inputGeometry = inputTimeGeometry->GetGeometryForTimeStep( timestep ); + BaseGeometry* inputGeometry = inputTimeGeometry->GetGeometryForTimeStep( timestep ); if ( inputGeometry == NULL ) { itkWarningMacro(<<"There is no Geometry3D at given timestep "<( m_WorldGeometry ) != NULL ) { const PlaneGeometry *planeGeometry = static_cast< const PlaneGeometry * >( m_WorldGeometry ); 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. inputGeometry->WorldToIndex( right, rightInIndex ); inputGeometry->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 ); mmPerPixel[0] = widthInMM / extent[0]; mmPerPixel[1] = heightInMM / extent[1]; right.Normalize(); bottom.Normalize(); normal.Normalize(); //origin += right * ( mmPerPixel[0] * 0.5 ); //origin += bottom * ( mmPerPixel[1] * 0.5 ); //widthInMM -= mmPerPixel[0]; //heightInMM -= mmPerPixel[1]; // Use inverse transform of the input geometry for reslicing the 3D image m_Reslicer->SetResliceTransform( inputGeometry->GetVtkTransform()->GetLinearInverse() ); // Set background level to TRANSLUCENT (see Geometry2DDataVtkMapper3D) m_Reslicer->SetBackgroundLevel( -32768 ); // Check if a reference geometry does exist (as would usually be the case for // PlaneGeometry). // Note: this is currently not strictly required, but could facilitate // correct plane clipping. if ( m_WorldGeometry->GetReferenceGeometry() ) { // 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. boundsInitialized = this->CalculateClippedPlaneBounds( m_WorldGeometry->GetReferenceGeometry(), planeGeometry, bounds ); } } // Do we have an AbstractTransformGeometry? else if ( dynamic_cast< const AbstractTransformGeometry * >( m_WorldGeometry ) ) { const mitk::AbstractTransformGeometry* abstractGeometry = dynamic_cast< const AbstractTransformGeometry * >(m_WorldGeometry); extent[0] = abstractGeometry->GetParametricExtent(0); extent[1] = abstractGeometry->GetParametricExtent(1); widthInMM = abstractGeometry->GetParametricExtentInMM(0); heightInMM = abstractGeometry->GetParametricExtentInMM(1); mmPerPixel[0] = widthInMM / extent[0]; mmPerPixel[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 vtkGeneralTransform *composedResliceTransform = vtkGeneralTransform::New(); composedResliceTransform->Identity(); composedResliceTransform->Concatenate( inputGeometry->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 ); composedResliceTransform->Delete(); } else { itkWarningMacro(<<"World Geometry has to be a PlaneGeometry or an AbstractTransformGeometry."); return; } // Make sure that the image to be resliced has a certain minimum size. if ( (extent[0] <= 2) && (extent[1] <= 2) ) { itkWarningMacro(<<"Image is too small to be resliced..."); return; } vtkSmartPointer unitSpacingImageFilter = vtkImageChangeInformation::New() ; unitSpacingImageFilter->SetOutputSpacing( 1.0, 1.0, 1.0 ); unitSpacingImageFilter->SetInputData( inputData ); m_Reslicer->SetInputConnection( unitSpacingImageFilter->GetOutputPort() ); //m_Reslicer->SetInput( inputData ); m_Reslicer->SetOutputDimensionality( 2 ); m_Reslicer->SetOutputOrigin( 0.0, 0.0, 0.0 ); Vector2D pixelsPerMM; pixelsPerMM[0] = 1.0 / mmPerPixel[0]; pixelsPerMM[1] = 1.0 / mmPerPixel[1]; //calulate the originArray and the orientations for the reslice-filter double originArray[3]; itk2vtk( origin, originArray ); m_Reslicer->SetResliceAxesOrigin( originArray ); double cosines[9]; // direction of the X-axis of the sampled result vnl2vtk( right.GetVnlVector(), cosines ); // direction of the Y-axis of the sampled result vnl2vtk( bottom.GetVnlVector(), cosines + 3 ); // normal of the plane vnl2vtk( normal.GetVnlVector(), cosines + 6 ); m_Reslicer->SetResliceAxesDirectionCosines( cosines ); int xMin, xMax, yMin, yMax; if ( boundsInitialized ) { xMin = static_cast< int >( bounds[0] / mmPerPixel[0] );//+ 0.5 ); xMax = static_cast< int >( bounds[1] / mmPerPixel[0] );//+ 0.5 ); yMin = static_cast< int >( bounds[2] / mmPerPixel[1] );//+ 0.5); yMax = static_cast< int >( bounds[3] / mmPerPixel[1] );//+ 0.5 ); } else { // If no reference geometry is available, we also don't know about the // maximum plane size; so the overlap is just ignored xMin = yMin = 0; xMax = static_cast< int >( extent[0] - pixelsPerMM[0] );//+ 0.5 ); yMax = static_cast< int >( extent[1] - pixelsPerMM[1] );//+ 0.5 ); } m_Reslicer->SetOutputSpacing( mmPerPixel[0], mmPerPixel[1], 1.0 ); // xMax and yMax are meant exclusive until now, whereas // SetOutputExtent wants an inclusive bound. Thus, we need // to subtract 1. m_Reslicer->SetOutputExtent( xMin, xMax-1, yMin, yMax-1, 0, 1 ); // 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. m_Reslicer->Modified(); m_Reslicer->ReleaseDataFlagOn(); m_Reslicer->Update(); // 1. Check the result vtkImageData* reslicedImage = m_Reslicer->GetOutput(); if((reslicedImage == NULL) || (reslicedImage->GetDataDimension() < 1)) { itkWarningMacro(<<"Reslicer returned empty image"); return; } unsigned int dimensions[2]; dimensions[0] = (unsigned int)extent[0]; dimensions[1] = (unsigned int)extent[1]; Vector3D spacingVector; FillVector3D(spacingVector, mmPerPixel[0], mmPerPixel[1], 1.0); mitk::Image::Pointer resultImage = this->GetOutput(); resultImage->Initialize(input->GetPixelType(), 2, dimensions ); resultImage->SetSpacing( spacingVector ); } void mitk::ExtractDirectedPlaneImageFilter::GenerateOutputInformation() { Superclass::GenerateOutputInformation(); } bool mitk::ExtractDirectedPlaneImageFilter -::CalculateClippedPlaneBounds( const Geometry3D *boundingGeometry, +::CalculateClippedPlaneBounds( const BaseGeometry *boundingGeometry, const PlaneGeometry *planeGeometry, double *bounds ) { // Clip the plane with the bounding geometry. To do so, the corner points // of the bounding box are transformed by the inverse transformation // matrix, and the transformed bounding box edges derived therefrom are // clipped with the plane z=0. The resulting min/max values are taken as // bounds for the image reslicer. const BoundingBox *boundingBox = boundingGeometry->GetBoundingBox(); BoundingBox::PointType bbMin = boundingBox->GetMinimum(); BoundingBox::PointType bbMax = boundingBox->GetMaximum(); vtkPoints *points = vtkPoints::New(); if(boundingGeometry->GetImageGeometry()) { points->InsertPoint( 0, bbMin[0]-0.5, bbMin[1]-0.5, bbMin[2]-0.5 ); points->InsertPoint( 1, bbMin[0]-0.5, bbMin[1]-0.5, bbMax[2]-0.5 ); points->InsertPoint( 2, bbMin[0]-0.5, bbMax[1]-0.5, bbMax[2]-0.5 ); points->InsertPoint( 3, bbMin[0]-0.5, bbMax[1]-0.5, bbMin[2]-0.5 ); points->InsertPoint( 4, bbMax[0]-0.5, bbMin[1]-0.5, bbMin[2]-0.5 ); points->InsertPoint( 5, bbMax[0]-0.5, bbMin[1]-0.5, bbMax[2]-0.5 ); points->InsertPoint( 6, bbMax[0]-0.5, bbMax[1]-0.5, bbMax[2]-0.5 ); points->InsertPoint( 7, bbMax[0]-0.5, bbMax[1]-0.5, bbMin[2]-0.5 ); } else { points->InsertPoint( 0, bbMin[0], bbMin[1], bbMin[2] ); points->InsertPoint( 1, bbMin[0], bbMin[1], bbMax[2] ); points->InsertPoint( 2, bbMin[0], bbMax[1], bbMax[2] ); points->InsertPoint( 3, bbMin[0], bbMax[1], bbMin[2] ); points->InsertPoint( 4, bbMax[0], bbMin[1], bbMin[2] ); points->InsertPoint( 5, bbMax[0], bbMin[1], bbMax[2] ); points->InsertPoint( 6, bbMax[0], bbMax[1], bbMax[2] ); points->InsertPoint( 7, bbMax[0], bbMax[1], bbMin[2] ); } vtkPoints *newPoints = vtkPoints::New(); vtkTransform *transform = vtkTransform::New(); transform->Identity(); transform->Concatenate( planeGeometry->GetVtkTransform()->GetLinearInverse() ); transform->Concatenate( boundingGeometry->GetVtkTransform() ); transform->TransformPoints( points, newPoints ); transform->Delete(); bounds[0] = bounds[2] = 10000000.0; bounds[1] = bounds[3] = -10000000.0; bounds[4] = bounds[5] = 0.0; this->LineIntersectZero( newPoints, 0, 1, bounds ); this->LineIntersectZero( newPoints, 1, 2, bounds ); this->LineIntersectZero( newPoints, 2, 3, bounds ); this->LineIntersectZero( newPoints, 3, 0, bounds ); this->LineIntersectZero( newPoints, 0, 4, bounds ); this->LineIntersectZero( newPoints, 1, 5, bounds ); this->LineIntersectZero( newPoints, 2, 6, bounds ); this->LineIntersectZero( newPoints, 3, 7, bounds ); this->LineIntersectZero( newPoints, 4, 5, bounds ); this->LineIntersectZero( newPoints, 5, 6, bounds ); this->LineIntersectZero( newPoints, 6, 7, bounds ); this->LineIntersectZero( newPoints, 7, 4, bounds ); // clean up vtk data points->Delete(); newPoints->Delete(); if ( (bounds[0] > 9999999.0) || (bounds[2] > 9999999.0) || (bounds[1] < -9999999.0) || (bounds[3] < -9999999.0) ) { return false; } else { // The resulting bounds must be adjusted by the plane spacing, since we // we have so far dealt with index coordinates - const float *planeSpacing = planeGeometry->GetFloatSpacing(); + const mitk::Vector3D planeSpacing = planeGeometry->GetSpacing(); bounds[0] *= planeSpacing[0]; bounds[1] *= planeSpacing[0]; bounds[2] *= planeSpacing[1]; bounds[3] *= planeSpacing[1]; bounds[4] *= planeSpacing[2]; bounds[5] *= planeSpacing[2]; return true; } } bool mitk::ExtractDirectedPlaneImageFilter ::LineIntersectZero( vtkPoints *points, int p1, int p2, double *bounds ) { double point1[3]; double point2[3]; points->GetPoint( p1, point1 ); points->GetPoint( p2, point2 ); if ( (point1[2] * point2[2] <= 0.0) && (point1[2] != point2[2]) ) { double x, y; x = ( point1[0] * point2[2] - point1[2] * point2[0] ) / ( point2[2] - point1[2] ); y = ( point1[1] * point2[2] - point1[2] * point2[1] ) / ( point2[2] - point1[2] ); if ( x < bounds[0] ) { bounds[0] = x; } if ( x > bounds[1] ) { bounds[1] = x; } if ( y < bounds[2] ) { bounds[2] = y; } if ( y > bounds[3] ) { bounds[3] = y; } bounds[4] = bounds[5] = 0.0; return true; } return false; } diff --git a/Modules/ImageExtraction/mitkExtractDirectedPlaneImageFilter.h b/Modules/ImageExtraction/mitkExtractDirectedPlaneImageFilter.h index 7f08422153..0214e06b7f 100644 --- a/Modules/ImageExtraction/mitkExtractDirectedPlaneImageFilter.h +++ b/Modules/ImageExtraction/mitkExtractDirectedPlaneImageFilter.h @@ -1,125 +1,125 @@ /*=================================================================== 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 #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 MitkImageExtraction_EXPORT ExtractDirectedPlaneImageFilter : public ImageToImageFilter { public: mitkClassMacro(ExtractDirectedPlaneImageFilter, ImageToImageFilter); itkFactorylessNewMacro(Self) itkCloneMacro(Self) - 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 Geometry3D *boundingGeometry, + 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/ImageExtraction/mitkExtractDirectedPlaneImageFilterNew.cpp b/Modules/ImageExtraction/mitkExtractDirectedPlaneImageFilterNew.cpp index 3c92d1308b..f416051d0d 100644 --- a/Modules/ImageExtraction/mitkExtractDirectedPlaneImageFilterNew.cpp +++ b/Modules/ImageExtraction/mitkExtractDirectedPlaneImageFilterNew.cpp @@ -1,297 +1,297 @@ /*=================================================================== 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 "mitkExtractDirectedPlaneImageFilterNew.h" #include "mitkImageCast.h" #include "mitkImageTimeSelector.h" #include "itkImageRegionIterator.h" #include mitk::ExtractDirectedPlaneImageFilterNew::ExtractDirectedPlaneImageFilterNew() :m_CurrentWorldGeometry2D(NULL), m_ActualInputTimestep(-1) { MITK_WARN << "Class ExtractDirectedPlaneImageFilterNew is deprecated! Use ExtractSliceFilter instead."; } mitk::ExtractDirectedPlaneImageFilterNew::~ExtractDirectedPlaneImageFilterNew() { } void mitk::ExtractDirectedPlaneImageFilterNew::GenerateData(){ mitk::Image::ConstPointer inputImage = ImageToImageFilter::GetInput(0); if ( !inputImage ) { MITK_ERROR << "mitk::ExtractDirectedPlaneImageFilterNew: No input available. Please set the input!" << std::endl; itkExceptionMacro("mitk::ExtractDirectedPlaneImageFilterNew: No input available. Please set the input!"); return; } m_ImageGeometry = inputImage->GetGeometry(); //If no timestep is set, the lowest given will be selected const mitk::TimeGeometry* inputTimeGeometry = this->GetInput()->GetTimeGeometry(); if ( m_ActualInputTimestep == -1) { ScalarType time = m_CurrentWorldGeometry2D->GetTimeBounds()[0]; if ( time > ScalarTypeNumericTraits::NonpositiveMin() ) { m_ActualInputTimestep = inputTimeGeometry->TimePointToTimeStep( time ); } } if ( inputImage->GetDimension() > 4 || inputImage->GetDimension() < 2) { MITK_ERROR << "mitk::ExtractDirectedPlaneImageFilterNew:GenerateData works only with 3D and 3D+t images, sorry." << std::endl; itkExceptionMacro("mitk::ExtractDirectedPlaneImageFilterNew works only with 3D and 3D+t images, sorry."); return; } else if ( inputImage->GetDimension() == 4 ) { mitk::ImageTimeSelector::Pointer timeselector = mitk::ImageTimeSelector::New(); timeselector->SetInput( inputImage ); timeselector->SetTimeNr( m_ActualInputTimestep ); timeselector->UpdateLargestPossibleRegion(); inputImage = timeselector->GetOutput(); } else if ( inputImage->GetDimension() == 2) { mitk::Image::Pointer resultImage = ImageToImageFilter::GetOutput(); resultImage = const_cast( inputImage.GetPointer() ); ImageToImageFilter::SetNthOutput( 0, resultImage); return; } if ( !m_CurrentWorldGeometry2D ) { MITK_ERROR<< "mitk::ExtractDirectedPlaneImageFilterNew::GenerateData has no CurrentWorldGeometry2D set" << std::endl; return; } AccessFixedDimensionByItk( inputImage, ItkSliceExtraction, 3 ); }//Generate Data void mitk::ExtractDirectedPlaneImageFilterNew::GenerateOutputInformation () { Superclass::GenerateOutputInformation(); } /* * The desired slice is extracted by filling the image`s corresponding pixel values in an empty 2 dimensional itk::Image * Therefor the itk image`s extent in pixel (in each direction) is doubled and its spacing (also in each direction) is divided by two * (similar to the shannon theorem). */ template void mitk::ExtractDirectedPlaneImageFilterNew::ItkSliceExtraction (itk::Image* inputImage) { typedef itk::Image InputImageType; typedef itk::Image SliceImageType; typedef itk::ImageRegionConstIterator< SliceImageType > SliceIterator; //Creating an itk::Image that represents the sampled slice typename SliceImageType::Pointer resultSlice = SliceImageType::New(); typename SliceImageType::IndexType start; start[0] = 0; start[1] = 0; Point3D origin = m_CurrentWorldGeometry2D->GetOrigin(); Vector3D right = m_CurrentWorldGeometry2D->GetAxisVector(0); Vector3D bottom = m_CurrentWorldGeometry2D->GetAxisVector(1); //Calculation the sample-spacing, i.e the half of the smallest spacing existing in the original image Vector3D newPixelSpacing = m_ImageGeometry->GetSpacing(); float minSpacing = newPixelSpacing[0]; for (unsigned int i = 1; i < newPixelSpacing.Size(); i++) { if (newPixelSpacing[i] < minSpacing ) { minSpacing = newPixelSpacing[i]; } } newPixelSpacing[0] = 0.5*minSpacing; newPixelSpacing[1] = 0.5*minSpacing; newPixelSpacing[2] = 0.5*minSpacing; float pixelSpacing[2]; pixelSpacing[0] = newPixelSpacing[0]; pixelSpacing[1] = newPixelSpacing[1]; //Calculating the size of the sampled slice typename SliceImageType::SizeType size; Vector2D extentInMM; extentInMM[0] = m_CurrentWorldGeometry2D->GetExtentInMM(0); extentInMM[1] = m_CurrentWorldGeometry2D->GetExtentInMM(1); //The maximum extent is the lenght of the diagonal of the considered plane double maxExtent = sqrt(extentInMM[0]*extentInMM[0]+extentInMM[1]*extentInMM[1]); unsigned int xTranlation = (maxExtent-extentInMM[0]); unsigned int yTranlation = (maxExtent-extentInMM[1]); size[0] = (maxExtent+xTranlation)/newPixelSpacing[0]; size[1] = (maxExtent+yTranlation)/newPixelSpacing[1]; //Creating an ImageRegion Object typename SliceImageType::RegionType region; region.SetSize( size ); region.SetIndex( start ); //Defining the image`s extent and origin by passing the region to it and allocating memory for it resultSlice->SetRegions( region ); resultSlice->SetSpacing( pixelSpacing ); resultSlice->Allocate(); /* * Here we create an new geometry so that the transformations are calculated correctly (our resulting slice has a different bounding box and spacing) * The original current worldgeometry must be cloned because we have to keep the directions of the axis vector which represents the rotation */ right.Normalize(); bottom.Normalize(); //Here we translate the origin to adapt the new geometry to the previous calculated extent origin[0] -= xTranlation*right[0]+yTranlation*bottom[0]; origin[1] -= xTranlation*right[1]+yTranlation*bottom[1]; origin[2] -= xTranlation*right[2]+yTranlation*bottom[2]; //Putting it together for the new geometry - mitk::Geometry3D::Pointer newSliceGeometryTest = dynamic_cast(m_CurrentWorldGeometry2D->Clone().GetPointer()); + mitk::BaseGeometry::Pointer newSliceGeometryTest = dynamic_cast(m_CurrentWorldGeometry2D->Clone().GetPointer()); newSliceGeometryTest->ChangeImageGeometryConsideringOriginOffset(true); //Workaround because of BUG (#6505) newSliceGeometryTest->GetIndexToWorldTransform()->SetMatrix(m_CurrentWorldGeometry2D->GetIndexToWorldTransform()->GetMatrix()); //Workaround end newSliceGeometryTest->SetOrigin(origin); ScalarType bounds[6]={0, static_cast(size[0]), 0, static_cast(size[1]), 0, 1}; newSliceGeometryTest->SetBounds(bounds); newSliceGeometryTest->SetSpacing(newPixelSpacing); newSliceGeometryTest->Modified(); //Workaround because of BUG (#6505) itk::MatrixOffsetTransformBase::MatrixType tempTransform = newSliceGeometryTest->GetIndexToWorldTransform()->GetMatrix(); //Workaround end /* * Now we iterate over the recently created slice. * For each slice - pixel we check whether there is an according * pixel in the input - image which can be set in the slice. * In this way a slice is sampled out of the input - image regrading to the given PlaneGeometry */ Point3D currentSliceIndexPointIn2D; Point3D currentImageWorldPointIn3D; typename InputImageType::IndexType inputIndex; SliceIterator sliceIterator ( resultSlice, resultSlice->GetLargestPossibleRegion() ); sliceIterator.GoToBegin(); while ( !sliceIterator.IsAtEnd() ) { /* * Here we add 0.5 to to assure that the indices are correctly transformed. * (Because of the 0.5er Bug) */ currentSliceIndexPointIn2D[0] = sliceIterator.GetIndex()[0]+0.5; currentSliceIndexPointIn2D[1] = sliceIterator.GetIndex()[1]+0.5; currentSliceIndexPointIn2D[2] = 0; newSliceGeometryTest->IndexToWorld( currentSliceIndexPointIn2D, currentImageWorldPointIn3D ); m_ImageGeometry->WorldToIndex( currentImageWorldPointIn3D, inputIndex); if ( m_ImageGeometry->IsIndexInside( inputIndex )) { resultSlice->SetPixel( sliceIterator.GetIndex(), inputImage->GetPixel(inputIndex) ); } else { resultSlice->SetPixel( sliceIterator.GetIndex(), 0); } ++sliceIterator; } Image::Pointer resultImage = ImageToImageFilter::GetOutput(); GrabItkImageMemory(resultSlice, resultImage, NULL, false); resultImage->SetClonedGeometry(newSliceGeometryTest); //Workaround because of BUG (#6505) resultImage->GetGeometry()->GetIndexToWorldTransform()->SetMatrix(tempTransform); //Workaround end } ///**TEST** May ba a little bit more efficient but doesn`t already work/ //right.Normalize(); //bottom.Normalize(); //Point3D currentImagePointIn3D = origin /*+ bottom*newPixelSpacing*/; //unsigned int columns ( 0 ); /**ENDE**/ /****TEST***/ //SliceImageType::IndexType index = sliceIterator.GetIndex(); //if ( columns == (extentInPixel[0]) ) //{ //If we are at the end of a row, then we have to go to the beginning of the next row //currentImagePointIn3D = origin; //currentImagePointIn3D += newPixelSpacing[1]*bottom*index[1]; //columns = 0; //m_ImageGeometry->WorldToIndex(currentImagePointIn3D, inputIndex); //} //else //{ //// //if ( columns != 0 ) //{ //currentImagePointIn3D += newPixelSpacing[0]*right; //} //m_ImageGeometry->WorldToIndex(currentImagePointIn3D, inputIndex); //} //if ( m_ImageGeometry->IsIndexInside( inputIndex )) //{ //resultSlice->SetPixel( sliceIterator.GetIndex(), inputImage->GetPixel(inputIndex) ); //} //else if (currentImagePointIn3D == origin) //{ //Point3D temp; //temp[0] = bottom[0]*newPixelSpacing[0]*0.5; //temp[1] = bottom[1]*newPixelSpacing[1]*0.5; //temp[2] = bottom[2]*newPixelSpacing[2]*0.5; //origin[0] += temp[0]; //origin[1] += temp[1]; //origin[2] += temp[2]; //currentImagePointIn3D = origin; //m_ImageGeometry->WorldToIndex(currentImagePointIn3D, inputIndex); //if ( m_ImageGeometry->IsIndexInside( inputIndex )) //{ //resultSlice->SetPixel( sliceIterator.GetIndex(), inputImage->GetPixel(inputIndex) ); //} //} /****TEST ENDE****/ diff --git a/Modules/ImageExtraction/mitkExtractDirectedPlaneImageFilterNew.h b/Modules/ImageExtraction/mitkExtractDirectedPlaneImageFilterNew.h index e6d0b03a7d..aaa2708314 100644 --- a/Modules/ImageExtraction/mitkExtractDirectedPlaneImageFilterNew.h +++ b/Modules/ImageExtraction/mitkExtractDirectedPlaneImageFilterNew.h @@ -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. ===================================================================*/ #ifndef mitkExtractDirectedPlaneImageFilterNew_h_Included #define mitkExtractDirectedPlaneImageFilterNew_h_Included #include #include "mitkImageToImageFilter.h" #include "itkImage.h" #include "mitkITKImageImport.h" namespace mitk { /** \deprecated This class is deprecated. Use mitk::ExtractSliceFilter instead. \sa ExtractSliceFilter \brief A filter that can extract a 2D slice from a 3D or 4D image especially if the image`s axes are rotated \sa ContourTool \sa SegTool2D \sa ExtractImageFilter \sa OverwriteSliceImageFilter \sa OverwriteDirectedPlaneImageFilter \ingroup Process \ingroup Reliver There is a separate page describing the general design of QmitkInteractiveSegmentation: \ref QmitkSegmentationTechnicalPage This class takes an 3D or 4D mitk::Image as input and extracts a slice from it. If you work with a 4D image as input you have to specify the desired timestep at which the slice shall be extracted, otherwise the lowest given timestep is selected by default. The special feature of this filter is, that the planes of the input image can be rotated in any way. To assure a proper extraction you have to set the currentWorldGeometry2D with you can obtain from the BaseRenderer, respectively the positionEvent send by the renderer. The output will not be set if there was a problem with the input image $Author: fetzer $ */ class MitkImageExtraction_EXPORT ExtractDirectedPlaneImageFilterNew : public ImageToImageFilter { public: mitkClassMacro(ExtractDirectedPlaneImageFilterNew, ImageToImageFilter); itkFactorylessNewMacro(Self) itkCloneMacro(Self) /** \brief Set macro for the current worldgeometry \a Parameter The current wordgeometry that describes the position (rotation, translation) of the plane (and therefore the slice to be extracted) in our 3D(+t) image */ - itkSetMacro(CurrentWorldGeometry2D, Geometry3D* ); + itkSetMacro(CurrentWorldGeometry2D, BaseGeometry* ); - itkSetMacro(ImageGeometry, Geometry3D* ); + itkSetMacro(ImageGeometry, BaseGeometry* ); /** \brief Set macro for the current timestep \a Parameter The timestep of the image from which the slice shall be extracted */ itkSetMacro(ActualInputTimestep, int); protected: ExtractDirectedPlaneImageFilterNew(); virtual ~ExtractDirectedPlaneImageFilterNew(); virtual void GenerateData(); virtual void GenerateOutputInformation(); private: - const Geometry3D* m_CurrentWorldGeometry2D; - const Geometry3D* m_ImageGeometry; + const BaseGeometry* m_CurrentWorldGeometry2D; + const BaseGeometry* m_ImageGeometry; int m_ActualInputTimestep; template void ItkSliceExtraction (itk::Image* inputImage); }; }//namespace #endif diff --git a/Modules/ImageExtraction/mitkExtractImageFilter.cpp b/Modules/ImageExtraction/mitkExtractImageFilter.cpp index 0941ec8e99..913bad4399 100644 --- a/Modules/ImageExtraction/mitkExtractImageFilter.cpp +++ b/Modules/ImageExtraction/mitkExtractImageFilter.cpp @@ -1,265 +1,265 @@ /*=================================================================== 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 "mitkExtractImageFilter.h" #include "mitkImageCast.h" #include "mitkPlaneGeometry.h" #include "mitkITKImageImport.h" #include "mitkImageTimeSelector.h" #include #include mitk::ExtractImageFilter::ExtractImageFilter() :m_SliceIndex(0), m_SliceDimension(0), m_TimeStep(0), m_DirectionCollapseToStrategy( DIRECTIONCOLLAPSETOGUESS ) { MITK_WARN << "Class ExtractImageFilter is deprecated! Use ExtractSliceFilter instead."; } mitk::ExtractImageFilter::~ExtractImageFilter() { } void mitk::ExtractImageFilter::GenerateData() { Image::ConstPointer input = ImageToImageFilter::GetInput(0); if ( (input->GetDimension() > 4) || (input->GetDimension() < 2) ) { MITK_ERROR << "mitk::ExtractImageFilter:GenerateData works only with 3D and 3D+t images, sorry." << std::endl; itkExceptionMacro("mitk::ExtractImageFilter works only with 3D and 3D+t images, sorry."); return; } else if (input->GetDimension() == 4) { mitk::ImageTimeSelector::Pointer timeSelector = ImageTimeSelector::New(); timeSelector->SetInput( input ); timeSelector->SetTimeNr( m_TimeStep ); timeSelector->UpdateLargestPossibleRegion(); input = timeSelector->GetOutput(); } else if (input->GetDimension() == 2) { Image::Pointer resultImage = ImageToImageFilter::GetOutput(); resultImage = const_cast(input.GetPointer()); ImageToImageFilter::SetNthOutput( 0, resultImage ); return; } if ( m_SliceDimension >= input->GetDimension() ) { MITK_ERROR << "mitk::ExtractImageFilter:GenerateData m_SliceDimension == " << m_SliceDimension << " makes no sense with an " << input->GetDimension() << "D image." << std::endl; itkExceptionMacro("This is not a sensible value for m_SliceDimension."); return; } AccessFixedDimensionByItk( input, ItkImageProcessing, 3 ); // set a nice geometry for display and point transformations - Geometry3D* inputImageGeometry = ImageToImageFilter::GetInput(0)->GetGeometry(); + BaseGeometry* inputImageGeometry = ImageToImageFilter::GetInput(0)->GetGeometry(); if (!inputImageGeometry) { MITK_ERROR << "In ExtractImageFilter::ItkImageProcessing: Input image has no geometry!" << std::endl; return; } PlaneGeometry::PlaneOrientation orientation = PlaneGeometry::Axial; switch ( m_SliceDimension ) { default: case 2: orientation = PlaneGeometry::Axial; break; case 1: orientation = PlaneGeometry::Frontal; break; case 0: orientation = PlaneGeometry::Sagittal; break; } PlaneGeometry::Pointer planeGeometry = PlaneGeometry::New(); planeGeometry->InitializeStandardPlane( inputImageGeometry, orientation, (ScalarType)m_SliceIndex, true, false ); Image::Pointer resultImage = ImageToImageFilter::GetOutput(); planeGeometry->ChangeImageGeometryConsideringOriginOffset(true); resultImage->SetGeometry( planeGeometry ); } template void mitk::ExtractImageFilter::ItkImageProcessing( itk::Image* itkImage ) { // use the itk::ExtractImageFilter to get a 2D image typedef itk::Image< TPixel, VImageDimension > ImageType3D; typedef itk::Image< TPixel, VImageDimension-1 > ImageType2D; typedef itk::ExtractImageFilter ExtractImageFilterType; typename ImageType3D::RegionType inSliceRegion = itkImage->GetLargestPossibleRegion(); inSliceRegion.SetSize( m_SliceDimension, 0 ); typename ExtractImageFilterType::Pointer sliceExtractor = ExtractImageFilterType::New(); typename ExtractImageFilterType::DIRECTIONCOLLAPSESTRATEGY collapseStrategy; switch( m_DirectionCollapseToStrategy ) { case DIRECTIONCOLLAPSETOUNKOWN: collapseStrategy = ExtractImageFilterType::DIRECTIONCOLLAPSETOUNKOWN; break; case DIRECTIONCOLLAPSETOIDENTITY: collapseStrategy = ExtractImageFilterType::DIRECTIONCOLLAPSETOIDENTITY; break; case DIRECTIONCOLLAPSETOSUBMATRIX: collapseStrategy = ExtractImageFilterType::DIRECTIONCOLLAPSETOSUBMATRIX; break; case DIRECTIONCOLLAPSETOGUESS: default: collapseStrategy = ExtractImageFilterType::DIRECTIONCOLLAPSETOGUESS; break; } sliceExtractor->SetDirectionCollapseToStrategy( collapseStrategy ); sliceExtractor->SetInput( itkImage ); inSliceRegion.SetIndex( m_SliceDimension, m_SliceIndex ); sliceExtractor->SetExtractionRegion( inSliceRegion ); // calculate the output sliceExtractor->UpdateLargestPossibleRegion(); typename ImageType2D::Pointer slice = sliceExtractor->GetOutput(); // re-import to MITK Image::Pointer resultImage = ImageToImageFilter::GetOutput(); GrabItkImageMemory(slice, resultImage, NULL, false); } /* * What is the input requested region that is required to produce the output * requested region? By default, the largest possible region is always * required but this is overridden in many subclasses. For instance, for an * image processing filter where an output pixel is a simple function of an * input pixel, the input requested region will be set to the output * requested region. For an image processing filter where an output pixel is * a function of the pixels in a neighborhood of an input pixel, then the * input requested region will need to be larger than the output requested * region (to avoid introducing artificial boundary conditions). This * function should never request an input region that is outside the the * input largest possible region (i.e. implementations of this method should * crop the input requested region at the boundaries of the input largest * possible region). */ void mitk::ExtractImageFilter::GenerateInputRequestedRegion() { Superclass::GenerateInputRequestedRegion(); ImageToImageFilter::InputImagePointer input = const_cast< ImageToImageFilter::InputImageType* > ( this->GetInput() ); Image::Pointer output = this->GetOutput(); if (input->GetDimension() == 2) { input->SetRequestedRegionToLargestPossibleRegion(); return; } Image::RegionType requestedRegion; requestedRegion = output->GetRequestedRegion(); requestedRegion.SetIndex(0, 0); requestedRegion.SetIndex(1, 0); requestedRegion.SetIndex(2, 0); requestedRegion.SetSize(0, input->GetDimension(0)); requestedRegion.SetSize(1, input->GetDimension(1)); requestedRegion.SetSize(2, input->GetDimension(2)); requestedRegion.SetIndex( m_SliceDimension, m_SliceIndex ); // only one slice needed requestedRegion.SetSize( m_SliceDimension, 1 ); input->SetRequestedRegion( &requestedRegion ); } /* * Generate the information decribing the output data. The default * implementation of this method will copy information from the input to the * output. A filter may override this method if its output will have different * information than its input. For instance, a filter that shrinks an image will * need to provide an implementation for this method that changes the spacing of * the pixels. Such filters should call their superclass' implementation of this * method prior to changing the information values they need (i.e. * GenerateOutputInformation() should call * Superclass::GenerateOutputInformation() prior to changing the information. */ void mitk::ExtractImageFilter::GenerateOutputInformation() { Image::Pointer output = this->GetOutput(); Image::ConstPointer input = this->GetInput(); if (input.IsNull()) return; if ( m_SliceDimension >= input->GetDimension() && input->GetDimension() != 2 ) { MITK_ERROR << "mitk::ExtractImageFilter:GenerateOutputInformation m_SliceDimension == " << m_SliceDimension << " makes no sense with an " << input->GetDimension() << "D image." << std::endl; itkExceptionMacro("This is not a sensible value for m_SliceDimension."); return; } unsigned int sliceDimension( m_SliceDimension ); if ( input->GetDimension() == 2) { sliceDimension = 2; } unsigned int tmpDimensions[2]; switch ( sliceDimension ) { default: case 2: // orientation = PlaneGeometry::Axial; tmpDimensions[0] = input->GetDimension(0); tmpDimensions[1] = input->GetDimension(1); break; case 1: // orientation = PlaneGeometry::Frontal; tmpDimensions[0] = input->GetDimension(0); tmpDimensions[1] = input->GetDimension(2); break; case 0: // orientation = PlaneGeometry::Sagittal; tmpDimensions[0] = input->GetDimension(1); tmpDimensions[1] = input->GetDimension(2); break; } output->Initialize(input->GetPixelType(), 2, tmpDimensions, 1 /*input->GetNumberOfChannels()*/); // initialize the spacing of the output /* Vector3D spacing = input->GetSlicedGeometry()->GetSpacing(); if(input->GetDimension()>=2) spacing[2]=spacing[1]; else spacing[2] = 1.0; output->GetSlicedGeometry()->SetSpacing(spacing); */ output->SetPropertyList(input->GetPropertyList()->Clone()); } 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 9a4869d897..d8a5e7b94a 100644 --- a/Modules/ImageStatistics/mitkImageStatisticsCalculator.cpp +++ b/Modules/ImageStatistics/mitkImageStatisticsCalculator.cpp @@ -1,1272 +1,1271 @@ /*=================================================================== 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 #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 Geometry3D *imageGeometry = timeSliceImage->GetGeometry(); + 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 Geometry3D *geometry, Vector3D vector, + 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 ); // If there is a second poly line in a closed planar figure, treat it as a hole. PlanarFigure::PolyLineType planarFigureHolePolyline; if (m_PlanarFigure->GetPolyLinesSize() == 2) planarFigureHolePolyline = m_PlanarFigure->GetPolyLine(1); - const mitk::Geometry3D *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 ); } vtkSmartPointer holePoints = NULL; if (!planarFigureHolePolyline.empty()) { holePoints = vtkSmartPointer::New(); Point3D point3D; PlanarFigure::PolyLineType::const_iterator end = planarFigureHolePolyline.end(); for (it = planarFigureHolePolyline.begin(); it != end; ++it) { planarFigureGeometry2D->Map(it->Point, point3D); imageGeometry3D->WorldToIndex(point3D, point3D); holePoints->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 ); vtkSmartPointer holeLassoStencil = NULL; if (holePoints.GetPointer() != NULL) { holeLassoStencil = vtkSmartPointer::New(); holeLassoStencil->SetShapeToPolygon(); holeLassoStencil->SetPoints(holePoints); } // 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(); vtkSmartPointer holeStencilFilter = NULL; if (holeLassoStencil.GetPointer() != NULL) { holeStencilFilter = vtkSmartPointer::New(); holeStencilFilter->SetInputConnection(imageStencilFilter->GetOutputPort()); holeStencilFilter->SetStencilConnection(holeLassoStencil->GetOutputPort()); holeStencilFilter->ReverseStencilOn(); holeStencilFilter->SetBackgroundValue(0); holeStencilFilter->Update(); } // Export from VTK back to ITK vtkSmartPointer vtkExporter = vtkSmartPointer::New(); vtkExporter->SetInputConnection( holeStencilFilter.GetPointer() == NULL ? imageStencilFilter->GetOutputPort() : holeStencilFilter->GetOutputPort()); vtkExporter->Update(); typename ImageImportType::Pointer itkImporter = ImageImportType::New(); this->ConnectPipelines( vtkExporter, itkImporter ); itkImporter->Update(); typedef itk::ImageDuplicator< ImageImportType::OutputImageType > DuplicatorType; DuplicatorType::Pointer duplicator = DuplicatorType::New(); duplicator->SetInputImage( itkImporter->GetOutput() ); duplicator->Update(); // Store mask m_InternalImageMask2D = duplicator->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/ImageStatistics/mitkImageStatisticsCalculator.h b/Modules/ImageStatistics/mitkImageStatisticsCalculator.h index 9cc9e3382d..ef06d8856b 100644 --- a/Modules/ImageStatistics/mitkImageStatisticsCalculator.h +++ b/Modules/ImageStatistics/mitkImageStatisticsCalculator.h @@ -1,334 +1,334 @@ /*=================================================================== 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_IMAGESTATISTICSCALCULATOR_H #define _MITK_IMAGESTATISTICSCALCULATOR_H #include #include "MitkImageStatisticsExports.h" #include #include #ifndef __itkHistogram_h #include #endif #include "mitkImage.h" #include "mitkImageTimeSelector.h" #include "mitkPlanarFigure.h" #include namespace mitk { /** * \brief Class for calculating statistics and histogram for an (optionally * masked) image. * * Images can be masked by either a label image (of the same dimensions as * the original image) or by a closed mitk::PlanarFigure, e.g. a circle or * polygon. When masking with a planar figure, the slice corresponding to the * plane containing the figure is extracted and then clipped with contour * defined by the figure. Planar figures need to be aligned along the main axes * of the image (axial, sagittal, coronal). Planar figures on arbitrary * rotated planes are not supported. * * For each operating mode (no masking, masking by image, masking by planar * figure), the calculated statistics and histogram are cached so that, when * switching back and forth between operation modes without modifying mask or * image, the information doesn't need to be recalculated. * * Note: currently time-resolved and multi-channel pictures are not properly * supported. */ class MitkImageStatistics_EXPORT ImageStatisticsCalculator : public itk::Object { public: enum { MASKING_MODE_NONE = 0, MASKING_MODE_IMAGE, MASKING_MODE_PLANARFIGURE }; typedef itk::Statistics::Histogram HistogramType; typedef HistogramType::ConstIterator HistogramConstIteratorType; struct Statistics { Statistics() { Reset(); } int Label; unsigned int N; double Min; double Max; double Mean; double Median; double Variance; double Sigma; double RMS; vnl_vector< int > MinIndex; vnl_vector< int > MaxIndex; void Reset() { Label = 0; N = 0; Min = 0.0; Max = 0.0; Mean = 0.0; Median = 0.0; Variance = 0.0; Sigma = 0.0; RMS = 0.0; } }; typedef std::vector< HistogramType::ConstPointer > HistogramContainer; typedef std::vector< Statistics > StatisticsContainer; mitkClassMacro( ImageStatisticsCalculator, itk::Object ); itkFactorylessNewMacro(Self) itkCloneMacro(Self) /** \brief Set image from which to compute statistics. */ void SetImage( const mitk::Image *image ); /** \brief Set image for masking. */ void SetImageMask( const mitk::Image *imageMask ); /** \brief Set planar figure for masking. */ void SetPlanarFigure( mitk::PlanarFigure *planarFigure ); /** \brief Set/Get operation mode for masking */ void SetMaskingMode( unsigned int mode ); /** \brief Set/Get operation mode for masking */ itkGetMacro( MaskingMode, unsigned int ); /** \brief Set/Get operation mode for masking */ void SetMaskingModeToNone(); /** \brief Set/Get operation mode for masking */ void SetMaskingModeToImage(); /** \brief Set/Get operation mode for masking */ void SetMaskingModeToPlanarFigure(); /** \brief Set a pixel value for pixels that will be ignored in the statistics */ void SetIgnorePixelValue(double value); /** \brief Get the pixel value for pixels that will be ignored in the statistics */ double GetIgnorePixelValue(); /** \brief Set wether a pixel value should be ignored in the statistics */ void SetDoIgnorePixelValue(bool doit); /** \brief Get wether a pixel value will be ignored in the statistics */ bool GetDoIgnorePixelValue(); /** \brief Compute statistics (together with histogram) for the current * masking mode. * * Computation is not executed if statistics is already up to date. In this * case, false is returned; otherwise, true.*/ virtual bool ComputeStatistics( unsigned int timeStep = 0 ); /** \brief Retrieve the histogram depending on the current masking mode. * * \param label The label for which to retrieve the histogram in multi-label situations (ascending order). */ const HistogramType *GetHistogram( unsigned int timeStep = 0, unsigned int label = 0 ) const; /** \brief Retrieve the histogram depending on the current masking mode (for all image labels. */ const HistogramContainer &GetHistogramVector( unsigned int timeStep = 0 ) const; /** \brief Retrieve statistics depending on the current masking mode. * * \param label The label for which to retrieve the statistics in multi-label situations (ascending order). */ const Statistics &GetStatistics( unsigned int timeStep = 0, unsigned int label = 0 ) const; /** \brief Retrieve statistics depending on the current masking mode (for all image labels). */ const StatisticsContainer &GetStatisticsVector( unsigned int timeStep = 0 ) const; protected: typedef std::vector< HistogramContainer > HistogramVector; typedef std::vector< StatisticsContainer > StatisticsVector; typedef std::vector< itk::TimeStamp > TimeStampVectorType; typedef std::vector< bool > BoolVectorType; typedef itk::Image< unsigned short, 3 > MaskImage3DType; typedef itk::Image< unsigned short, 2 > MaskImage2DType; ImageStatisticsCalculator(); virtual ~ImageStatisticsCalculator(); /** \brief Depending on the masking mode, the image and mask from which to * calculate statistics is extracted from the original input image and mask * data. * * For example, a when using a PlanarFigure as mask, the 2D image slice * corresponding to the PlanarFigure will be extracted from the original * image. If masking is disabled, the original image is simply passed * through. */ void ExtractImageAndMask( unsigned int timeStep = 0 ); /** \brief If the passed vector matches any of the three principal axes * of the passed geometry, the ínteger value corresponding to the axis * is set and true is returned. */ - bool GetPrincipalAxis( const Geometry3D *geometry, Vector3D vector, + bool GetPrincipalAxis( const BaseGeometry *geometry, Vector3D vector, unsigned int &axis ); template < typename TPixel, unsigned int VImageDimension > void InternalCalculateStatisticsUnmasked( const itk::Image< TPixel, VImageDimension > *image, StatisticsContainer* statisticsContainer, HistogramContainer *histogramContainer ); template < typename TPixel, unsigned int VImageDimension > void InternalCalculateStatisticsMasked( const itk::Image< TPixel, VImageDimension > *image, itk::Image< unsigned short, VImageDimension > *maskImage, StatisticsContainer* statisticsContainer, HistogramContainer* histogramContainer ); template < typename TPixel, unsigned int VImageDimension > void InternalCalculateMaskFromPlanarFigure( const itk::Image< TPixel, VImageDimension > *image, unsigned int axis ); template < typename TPixel, unsigned int VImageDimension > void InternalMaskIgnoredPixels( const itk::Image< TPixel, VImageDimension > *image, itk::Image< unsigned short, VImageDimension > *maskImage ); /** Connection from ITK to VTK */ template void ConnectPipelines(ITK_Exporter exporter, vtkSmartPointer importer) { importer->SetUpdateInformationCallback(exporter->GetUpdateInformationCallback()); importer->SetPipelineModifiedCallback(exporter->GetPipelineModifiedCallback()); importer->SetWholeExtentCallback(exporter->GetWholeExtentCallback()); importer->SetSpacingCallback(exporter->GetSpacingCallback()); importer->SetOriginCallback(exporter->GetOriginCallback()); importer->SetScalarTypeCallback(exporter->GetScalarTypeCallback()); importer->SetNumberOfComponentsCallback(exporter->GetNumberOfComponentsCallback()); importer->SetPropagateUpdateExtentCallback(exporter->GetPropagateUpdateExtentCallback()); importer->SetUpdateDataCallback(exporter->GetUpdateDataCallback()); importer->SetDataExtentCallback(exporter->GetDataExtentCallback()); importer->SetBufferPointerCallback(exporter->GetBufferPointerCallback()); importer->SetCallbackUserData(exporter->GetCallbackUserData()); } /** Connection from VTK to ITK */ template void ConnectPipelines(vtkSmartPointer exporter, ITK_Importer importer) { importer->SetUpdateInformationCallback(exporter->GetUpdateInformationCallback()); importer->SetPipelineModifiedCallback(exporter->GetPipelineModifiedCallback()); importer->SetWholeExtentCallback(exporter->GetWholeExtentCallback()); importer->SetSpacingCallback(exporter->GetSpacingCallback()); importer->SetOriginCallback(exporter->GetOriginCallback()); importer->SetScalarTypeCallback(exporter->GetScalarTypeCallback()); importer->SetNumberOfComponentsCallback(exporter->GetNumberOfComponentsCallback()); importer->SetPropagateUpdateExtentCallback(exporter->GetPropagateUpdateExtentCallback()); importer->SetUpdateDataCallback(exporter->GetUpdateDataCallback()); importer->SetDataExtentCallback(exporter->GetDataExtentCallback()); importer->SetBufferPointerCallback(exporter->GetBufferPointerCallback()); importer->SetCallbackUserData(exporter->GetCallbackUserData()); } void UnmaskedStatisticsProgressUpdate(); void MaskedStatisticsProgressUpdate(); /** m_Image contains the input image (e.g. 2D, 3D, 3D+t)*/ mitk::Image::ConstPointer m_Image; mitk::Image::ConstPointer m_ImageMask; mitk::PlanarFigure::Pointer m_PlanarFigure; HistogramVector m_ImageHistogramVector; HistogramVector m_MaskedImageHistogramVector; HistogramVector m_PlanarFigureHistogramVector; HistogramType::Pointer m_EmptyHistogram; HistogramContainer m_EmptyHistogramContainer; StatisticsVector m_ImageStatisticsVector; StatisticsVector m_MaskedImageStatisticsVector; StatisticsVector m_PlanarFigureStatisticsVector; Statistics m_EmptyStatistics; StatisticsContainer m_EmptyStatisticsContainer; unsigned int m_MaskingMode; bool m_MaskingModeChanged; /** m_InternalImage contains a image volume at one time step (e.g. 2D, 3D)*/ mitk::Image::ConstPointer m_InternalImage; MaskImage3DType::Pointer m_InternalImageMask3D; MaskImage2DType::Pointer m_InternalImageMask2D; TimeStampVectorType m_ImageStatisticsTimeStampVector; TimeStampVectorType m_MaskedImageStatisticsTimeStampVector; TimeStampVectorType m_PlanarFigureStatisticsTimeStampVector; BoolVectorType m_ImageStatisticsCalculationTriggerVector; BoolVectorType m_MaskedImageStatisticsCalculationTriggerVector; BoolVectorType m_PlanarFigureStatisticsCalculationTriggerVector; double m_IgnorePixelValue; bool m_DoIgnorePixelValue; bool m_IgnorePixelValueChanged; unsigned int m_PlanarFigureAxis; // Normal axis for PlanarFigure unsigned int m_PlanarFigureSlice; // Slice which contains PlanarFigure int m_PlanarFigureCoordinate0; // First plane-axis for PlanarFigure int m_PlanarFigureCoordinate1; // Second plane-axis for PlanarFigure }; } #endif // #define _MITK_IMAGESTATISTICSCALCULATOR_H diff --git a/Modules/ImageStatistics/mitkIntensityProfile.cpp b/Modules/ImageStatistics/mitkIntensityProfile.cpp index f899c6cd26..de18b0b67a 100644 --- a/Modules/ImageStatistics/mitkIntensityProfile.cpp +++ b/Modules/ImageStatistics/mitkIntensityProfile.cpp @@ -1,337 +1,337 @@ /*=================================================================== 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 "mitkIntensityProfile.h" using namespace mitk; template static void ReadPixel(const PixelType&, Image::Pointer image, const Index3D& index, ScalarType* returnValue) { switch (image->GetDimension()) { case 2: { ImagePixelReadAccessor readAccess(image, image->GetSliceData(0)); *returnValue = readAccess.GetPixelByIndex(reinterpret_cast&>(index)); break; } case 3: { ImagePixelReadAccessor readAccess(image, image->GetVolumeData(0)); *returnValue = readAccess.GetPixelByIndex(index); break; } default: *returnValue = 0; break; } } static IntensityProfile::Pointer ComputeIntensityProfile(Image::Pointer image, itk::PolyLineParametricPath<3>::Pointer path) { IntensityProfile::Pointer intensityProfile = IntensityProfile::New(); itk::PolyLineParametricPath<3>::InputType input = path->StartOfInput(); - Geometry3D* imageGeometry = image->GetGeometry(); + BaseGeometry* imageGeometry = image->GetGeometry(); const PixelType pixelType = image->GetPixelType(); IntensityProfile::MeasurementVectorType measurementVector; itk::PolyLineParametricPath<3>::OffsetType offset; Point3D worldPoint; Index3D index; do { imageGeometry->IndexToWorld(path->Evaluate(input), worldPoint); imageGeometry->WorldToIndex(worldPoint, index); mitkPixelTypeMultiplex3(ReadPixel, pixelType, image, index, measurementVector.GetDataPointer()); intensityProfile->PushBack(measurementVector); offset = path->IncrementInput(input); } while ((offset[0] | offset[1] | offset[2]) != 0); return intensityProfile; } template static typename itk::InterpolateImageFunction::Pointer CreateInterpolateImageFunction(InterpolateImageFunction::Enum interpolator) { switch (interpolator) { case InterpolateImageFunction::NearestNeighbor: return itk::NearestNeighborInterpolateImageFunction::New().GetPointer(); case InterpolateImageFunction::Linear: return itk::LinearInterpolateImageFunction::New().GetPointer(); case InterpolateImageFunction::WindowedSinc_Blackman_3: return itk::WindowedSincInterpolateImageFunction >::New().GetPointer(); case InterpolateImageFunction::WindowedSinc_Blackman_4: return itk::WindowedSincInterpolateImageFunction >::New().GetPointer(); case InterpolateImageFunction::WindowedSinc_Blackman_5: return itk::WindowedSincInterpolateImageFunction >::New().GetPointer(); case InterpolateImageFunction::WindowedSinc_Cosine_3: return itk::WindowedSincInterpolateImageFunction >::New().GetPointer(); case InterpolateImageFunction::WindowedSinc_Cosine_4: return itk::WindowedSincInterpolateImageFunction >::New().GetPointer(); case InterpolateImageFunction::WindowedSinc_Cosine_5: return itk::WindowedSincInterpolateImageFunction >::New().GetPointer(); case InterpolateImageFunction::WindowedSinc_Hamming_3: return itk::WindowedSincInterpolateImageFunction >::New().GetPointer(); case InterpolateImageFunction::WindowedSinc_Hamming_4: return itk::WindowedSincInterpolateImageFunction >::New().GetPointer(); case InterpolateImageFunction::WindowedSinc_Hamming_5: return itk::WindowedSincInterpolateImageFunction >::New().GetPointer(); case InterpolateImageFunction::WindowedSinc_Lanczos_3: return itk::WindowedSincInterpolateImageFunction >::New().GetPointer(); case InterpolateImageFunction::WindowedSinc_Lanczos_4: return itk::WindowedSincInterpolateImageFunction >::New().GetPointer(); case InterpolateImageFunction::WindowedSinc_Lanczos_5: return itk::WindowedSincInterpolateImageFunction >::New().GetPointer(); case InterpolateImageFunction::WindowedSinc_Welch_3: return itk::WindowedSincInterpolateImageFunction >::New().GetPointer(); case InterpolateImageFunction::WindowedSinc_Welch_4: return itk::WindowedSincInterpolateImageFunction >::New().GetPointer(); case InterpolateImageFunction::WindowedSinc_Welch_5: return itk::WindowedSincInterpolateImageFunction >::New().GetPointer(); default: return itk::NearestNeighborInterpolateImageFunction::New().GetPointer(); } } template static void ComputeIntensityProfile(itk::Image* image, itk::PolyLineParametricPath<3>::Pointer path, unsigned int numSamples, InterpolateImageFunction::Enum interpolator, IntensityProfile::Pointer intensityProfile) { typename itk::InterpolateImageFunction >::Pointer interpolateImageFunction = CreateInterpolateImageFunction >(interpolator); interpolateImageFunction->SetInputImage(image); const itk::PolyLineParametricPath<3>::InputType startOfInput = path->StartOfInput(); const itk::PolyLineParametricPath<3>::InputType delta = 1.0 / (numSamples - 1); IntensityProfile::MeasurementVectorType measurementVector; for (unsigned int i = 0; i < numSamples; ++i) { measurementVector[0] = interpolateImageFunction->EvaluateAtContinuousIndex(path->Evaluate(startOfInput + i * delta)); intensityProfile->PushBack(measurementVector); } } static IntensityProfile::Pointer ComputeIntensityProfile(Image::Pointer image, itk::PolyLineParametricPath<3>::Pointer path, unsigned int numSamples, InterpolateImageFunction::Enum interpolator) { IntensityProfile::Pointer intensityProfile = IntensityProfile::New(); AccessFixedDimensionByItk_n(image, ComputeIntensityProfile, 3, (path, numSamples, interpolator, intensityProfile)); return intensityProfile; } class AddPolyLineElementToPath { public: - AddPolyLineElementToPath(const Geometry2D* planarFigureGeometry, const Geometry3D* imageGeometry, itk::PolyLineParametricPath<3>::Pointer path) + AddPolyLineElementToPath(const PlaneGeometry* planarFigureGeometry, const BaseGeometry* imageGeometry, itk::PolyLineParametricPath<3>::Pointer path) : m_PlanarFigureGeometry(planarFigureGeometry), m_ImageGeometry(imageGeometry), m_Path(path) { } void operator()(const PlanarFigure::PolyLineElement& polyLineElement) { m_PlanarFigureGeometry->Map(polyLineElement.Point, m_WorldPoint); m_ImageGeometry->WorldToIndex(m_WorldPoint, m_ContinuousIndexPoint); m_Vertex.CastFrom(m_ContinuousIndexPoint); m_Path->AddVertex(m_Vertex); } private: - const Geometry2D* m_PlanarFigureGeometry; - const Geometry3D* m_ImageGeometry; + const PlaneGeometry* m_PlanarFigureGeometry; + const BaseGeometry* m_ImageGeometry; itk::PolyLineParametricPath<3>::Pointer m_Path; Point3D m_WorldPoint; Point3D m_ContinuousIndexPoint; itk::PolyLineParametricPath<3>::ContinuousIndexType m_Vertex; }; -static itk::PolyLineParametricPath<3>::Pointer CreatePathFromPlanarFigure(Geometry3D* imageGeometry, PlanarFigure* planarFigure) +static itk::PolyLineParametricPath<3>::Pointer CreatePathFromPlanarFigure(BaseGeometry* imageGeometry, PlanarFigure* planarFigure) { itk::PolyLineParametricPath<3>::Pointer path = itk::PolyLineParametricPath<3>::New(); const PlanarFigure::PolyLineType polyLine = planarFigure->GetPolyLine(0); std::for_each(polyLine.begin(), polyLine.end(), AddPolyLineElementToPath(planarFigure->GetGeometry2D(), imageGeometry, path)); return path; } -static void AddPointToPath(const Geometry3D* imageGeometry, const Point3D& point, itk::PolyLineParametricPath<3>::Pointer path) +static void AddPointToPath(const BaseGeometry* imageGeometry, const Point3D& point, itk::PolyLineParametricPath<3>::Pointer path) { Point3D continuousIndexPoint; imageGeometry->WorldToIndex(point, continuousIndexPoint); itk::PolyLineParametricPath<3>::ContinuousIndexType vertex; vertex.CastFrom(continuousIndexPoint); path->AddVertex(vertex); } -static itk::PolyLineParametricPath<3>::Pointer CreatePathFromPoints(Geometry3D* imageGeometry, const Point3D& startPoint, const Point3D& endPoint) +static itk::PolyLineParametricPath<3>::Pointer CreatePathFromPoints(BaseGeometry* imageGeometry, const Point3D& startPoint, const Point3D& endPoint) { itk::PolyLineParametricPath<3>::Pointer path = itk::PolyLineParametricPath<3>::New(); AddPointToPath(imageGeometry, startPoint, path); AddPointToPath(imageGeometry, endPoint, path); return path; } IntensityProfile::Pointer mitk::ComputeIntensityProfile(Image::Pointer image, PlanarFigure::Pointer planarFigure) { return ::ComputeIntensityProfile(image, CreatePathFromPlanarFigure(image->GetGeometry(), planarFigure)); } IntensityProfile::Pointer mitk::ComputeIntensityProfile(Image::Pointer image, PlanarLine::Pointer planarLine, unsigned int numSamples, InterpolateImageFunction::Enum interpolator) { return ::ComputeIntensityProfile(image, CreatePathFromPlanarFigure(image->GetGeometry(), planarLine.GetPointer()), numSamples, interpolator); } IntensityProfile::Pointer mitk::ComputeIntensityProfile(Image::Pointer image, const Point3D& startPoint, const Point3D& endPoint, unsigned int numSamples, InterpolateImageFunction::Enum interpolator) { return ::ComputeIntensityProfile(image, CreatePathFromPoints(image->GetGeometry(), startPoint, endPoint), numSamples, interpolator); } IntensityProfile::InstanceIdentifier mitk::ComputeGlobalMaximum(IntensityProfile::Pointer intensityProfile) { IntensityProfile::MeasurementType max = -vcl_numeric_limits::max(); IntensityProfile::InstanceIdentifier maxIndex = 0; IntensityProfile::ConstIterator end = intensityProfile->End(); IntensityProfile::MeasurementType measurement; for (IntensityProfile::ConstIterator it = intensityProfile->Begin(); it != end; ++it) { measurement = it.GetMeasurementVector()[0]; if (measurement > max) { max = measurement; maxIndex = it.GetInstanceIdentifier(); } } return maxIndex; } IntensityProfile::InstanceIdentifier mitk::ComputeGlobalMinimum(IntensityProfile::Pointer intensityProfile) { IntensityProfile::MeasurementType min = vcl_numeric_limits::max(); IntensityProfile::InstanceIdentifier minIndex = 0; IntensityProfile::ConstIterator end = intensityProfile->End(); IntensityProfile::MeasurementType measurement; for (IntensityProfile::ConstIterator it = intensityProfile->Begin(); it != end; ++it) { measurement = it.GetMeasurementVector()[0]; if (measurement < min) { min = measurement; minIndex = it.GetInstanceIdentifier(); } } return minIndex; } IntensityProfile::InstanceIdentifier mitk::ComputeCenterOfMaximumArea(IntensityProfile::Pointer intensityProfile, IntensityProfile::InstanceIdentifier radius) { const IntensityProfile::MeasurementType min = intensityProfile->GetMeasurementVector(ComputeGlobalMinimum(intensityProfile))[0]; const IntensityProfile::InstanceIdentifier areaWidth = 1 + 2 * radius; IntensityProfile::MeasurementType maxArea = 0; for (IntensityProfile::InstanceIdentifier i = 0; i < areaWidth; ++i) maxArea += intensityProfile->GetMeasurementVector(i)[0] - min; const IntensityProfile::InstanceIdentifier lastIndex = intensityProfile->Size() - areaWidth; IntensityProfile::InstanceIdentifier centerOfMaxArea = radius; IntensityProfile::MeasurementType area = maxArea; for (IntensityProfile::InstanceIdentifier i = 1; i <= lastIndex; ++i) { area += intensityProfile->GetMeasurementVector(i + areaWidth - 1)[0] - min; area -= intensityProfile->GetMeasurementVector(i - 1)[0] - min; if (area > maxArea) { maxArea = area; centerOfMaxArea = i + radius; // TODO: If multiple areas in the neighborhood have the same intensity chose the middle one instead of the first one. } } return centerOfMaxArea; } std::vector mitk::CreateVectorFromIntensityProfile(IntensityProfile::Pointer intensityProfile) { std::vector result; result.reserve(intensityProfile->Size()); IntensityProfile::ConstIterator end = intensityProfile->End(); for (IntensityProfile::ConstIterator it = intensityProfile->Begin(); it != end; ++it) result.push_back(it.GetMeasurementVector()[0]); return result; } IntensityProfile::Pointer mitk::CreateIntensityProfileFromVector(const std::vector& vector) { const IntensityProfile::InstanceIdentifier size = vector.size(); IntensityProfile::Pointer result = IntensityProfile::New(); result->Resize(size); for (IntensityProfile::InstanceIdentifier i = 0; i < size; ++i) result->SetMeasurement(i, 0, vector[i]); return result; } 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]"< #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/MapperExt/mitkUnstructuredGridMapper2D.cpp b/Modules/MapperExt/mitkUnstructuredGridMapper2D.cpp index 1cd6675f7c..85b6fcfd85 100644 --- a/Modules/MapperExt/mitkUnstructuredGridMapper2D.cpp +++ b/Modules/MapperExt/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/MapperExt/mitkVectorImageMapper2D.cpp b/Modules/MapperExt/mitkVectorImageMapper2D.cpp index 52914b27df..272e17d85a 100644 --- a/Modules/MapperExt/mitkVectorImageMapper2D.cpp +++ b/Modules/MapperExt/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/MapperExt/mitkVectorImageMapper2D.h b/Modules/MapperExt/mitkVectorImageMapper2D.h index 59e4816bd5..0d2fe6945a 100644 --- a/Modules/MapperExt/mitkVectorImageMapper2D.h +++ b/Modules/MapperExt/mitkVectorImageMapper2D.h @@ -1,94 +1,94 @@ /*=================================================================== 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 ); itkFactorylessNewMacro(Self) itkCloneMacro(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/OpenCL/mitkOclImage.cpp b/Modules/OpenCL/mitkOclImage.cpp index 230c92334f..8095672d89 100644 --- a/Modules/OpenCL/mitkOclImage.cpp +++ b/Modules/OpenCL/mitkOclImage.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 "mitkOclImage.h" #include "mitkImageDataItem.h" #include "mitkCommon.h" #include "mitkLogMacros.h" #include "mitkOclUtils.h" #include #include mitk::OclImage::OclImage() : m_gpuImage(NULL), m_context(NULL), m_bufferSize(0), m_gpuModified(false), m_cpuModified(false), m_Image(NULL), m_dim(0), m_Dims(NULL), m_BpE(1), m_formatSupported(false) { } mitk::OclImage::~OclImage() { MITK_INFO << "OclImage Destructor"; //release GMEM Image buffer if (m_gpuImage) clReleaseMemObject(m_gpuImage); } cl_mem mitk::OclImage::CreateGPUImage(unsigned int _wi, unsigned int _he, unsigned int _de, unsigned int _bpp) { MITK_INFO << "CreateGPUImage call with: BPP=" << _bpp; this->m_Dims = new unsigned int[MAX_DIMS]; m_Dims[0] = _wi; m_Dims[1] = _he; m_Dims[2] = _de; for (unsigned int i=3; i ref = GetModuleContext()->GetServiceReference(); OclResourceService* resources = GetModuleContext()->GetService(ref); cl_context gpuContext = resources->GetContext(); int clErr; m_gpuImage = clCreateBuffer( gpuContext, CL_MEM_READ_WRITE, m_bufferSize * m_BpE, NULL, &clErr); CHECK_OCL_ERR(clErr); return m_gpuImage; } void mitk::OclImage::InitializeByMitkImage(mitk::Image::Pointer _image) { this->m_Image = _image; this->m_cpuModified = true; this->m_gpuModified = false; this->m_gpuImage = NULL; // compute the size of the GMEM buffer this->m_dim = this->m_Image->GetDimension(); this->m_Dims = this->m_Image->GetDimensions(); MITK_INFO << "Image: " << this->m_Dims[0] <<"x"<< this->m_Dims[1] <<"x"<< this->m_Dims[2]; // get the dimensions this->m_bufferSize = 1; for (unsigned int i=0; im_dim; i++) { this->m_bufferSize *= this->m_Dims[i]; } // multiply by sizeof(PixelType) this->m_BpE = ( this->m_Image->GetPixelType().GetBpe() / 8); } bool mitk::OclImage::IsModified(int _type) { if (_type) return m_cpuModified; else return m_gpuModified; } void mitk::OclImage::Modified(int _type) { // defines... GPU: 0, CPU: 1 m_cpuModified = _type; m_gpuModified = !_type; } int mitk::OclImage::TransferDataToGPU(cl_command_queue gpuComQueue) { cl_int clErr = 0; // check whether an image present if (!m_Image->IsInitialized()){ MITK_ERROR("ocl.Image") << "(mitk) Image not initialized!\n"; return -1; } // there is a need for copy only if RAM-Data newer then GMEM data if (m_cpuModified) { //check the buffer if(m_gpuImage == NULL) { clErr = this->AllocateGPUImage(); } if (m_Image->IsInitialized() && (clErr == CL_SUCCESS)) { const size_t origin[3] = {0, 0, 0}; const size_t region[3] = {m_Dims[0], m_Dims[1], m_Dims[2]}; if( this->m_formatSupported ) { mitk::ImageReadAccessor accessor(m_Image); clErr = clEnqueueWriteImage( gpuComQueue, m_gpuImage, CL_TRUE, origin, region, 0, 0, accessor.GetData(), 0, NULL, NULL); } else { MITK_ERROR << "Selected image format currently not supported..."; } CHECK_OCL_ERR(clErr); } m_gpuModified = true; } return clErr; } cl_int mitk::OclImage::AllocateGPUImage() { cl_int clErr = 0; us::ServiceReference ref = GetModuleContext()->GetServiceReference(); OclResourceService* resources = GetModuleContext()->GetService(ref); cl_context gpuContext = resources->GetContext(); // initialize both proposed and supported format variables to same value this->m_proposedFormat = this->ConvertPixelTypeToOCLFormat(); this->m_supportedFormat = this->m_proposedFormat; // test the current format for HW support this->m_formatSupported = resources->GetIsFormatSupported( &(this->m_supportedFormat) ); // create an transfer kernel object in case the proposed format is not supported if( !(this->m_formatSupported) ) { MITK_ERROR << "Original format not supported on the installed graphics card."; return -1; } // create new buffer if( this->m_dim > 2) { //Create a 3D Image m_gpuImage = clCreateImage3D(gpuContext, CL_MEM_READ_ONLY, &m_supportedFormat, *(m_Dims), *(m_Dims+1), *(m_Dims+2), 0, 0, NULL, &clErr); } else { //Create a 2D Image m_gpuImage = clCreateImage2D(gpuContext, CL_MEM_READ_ONLY, &m_supportedFormat, *(m_Dims), *(m_Dims+1), 0, NULL, &clErr); } CHECK_OCL_ERR(clErr); return clErr; } cl_mem mitk::OclImage::GetGPUImage(cl_command_queue gpuComQueue) { // clGetMemObjectInfo() cl_mem_object_type memInfo; cl_int clErr = 0; // query image object info only if already initialized if( this->m_gpuImage ) { clErr = clGetMemObjectInfo(this->m_gpuImage, CL_MEM_TYPE, sizeof(cl_mem_object_type), &memInfo, NULL ); CHECK_OCL_ERR(clErr); } MITK_INFO << "Querying info for object, recieving: " << memInfo; // test if m_gpuImage CL_MEM_IMAGE_2/3D // if not, copy buffer to image if (memInfo == CL_MEM_OBJECT_BUFFER) { MITK_WARN << " Passed oclImage is a buffer-object, creating image"; //hold a copy of the buffer gmem pointer cl_mem tempBuffer = this->m_gpuImage; const size_t origin[3] = {0, 0, 0}; size_t region[3] = {this->m_Dims[0], this->m_Dims[1], 1}; clErr = this->AllocateGPUImage(); this->m_dim = 3; //copy last data to the image data clErr = clEnqueueCopyBufferToImage( gpuComQueue, tempBuffer, m_gpuImage, 0, origin, region, 0, NULL, NULL); CHECK_OCL_ERR(clErr); //release pointer clReleaseMemObject(tempBuffer); } return m_gpuImage; } void mitk::OclImage::SetPixelType(const cl_image_format *_image) { this->m_proposedFormat.image_channel_data_type = _image->image_channel_data_type; this->m_proposedFormat.image_channel_order = _image->image_channel_order; } void* mitk::OclImage::TransferDataToCPU(cl_command_queue gpuComQueue) { cl_int clErr = 0; // if image created on GPU, needs to create mitk::Image if( m_Image.IsNull() ){ MITK_INFO << "Image not initialized, creating new one."; m_Image = mitk::Image::New(); } // check buffersize/image size char* data = new char[m_bufferSize * m_BpE]; // debug info oclPrintMemObjectInfo( m_gpuImage ); clErr = clEnqueueReadBuffer( gpuComQueue, m_gpuImage, CL_FALSE, 0, m_bufferSize * m_BpE, data ,0, NULL, NULL); CHECK_OCL_ERR(clErr); clFlush( gpuComQueue ); // the cpu data is same as gpu this->m_gpuModified = false; return (void*) data; } cl_image_format mitk::OclImage::ConvertPixelTypeToOCLFormat() { cl_image_format texFormat; //single channel Gray-Valued Images texFormat.image_channel_order = CL_R; MITK_INFO << "Class own value: " << this->m_BpE; switch ( this->m_BpE ) { case 1: texFormat.image_channel_data_type = CL_UNSIGNED_INT8; MITK_INFO<< "PixelType: UCHAR => CLFormat: [CL_UNORM_INT8, CL_R]"; break; case 2: texFormat.image_channel_data_type = CL_SIGNED_INT16; // texFormat.image_channel_order = CL_R; MITK_INFO<< "PixelType: SHORT => CLFormat: [CL_SIGNED_INT16, CL_R]"; break; case 4: texFormat.image_channel_data_type = CL_FLOAT; MITK_INFO<< "Choosing CL_FLOAT"; break; default: texFormat.image_channel_data_type = CL_UNORM_INT8; texFormat.image_channel_order = CL_RG; MITK_INFO<< "Choosing (default) short: 2-Channel UCHAR"; break; } return texFormat; } int mitk::OclImage::GetDimension(int idx) const { if (this->m_dim > idx) { return m_Dims[idx]; } else { MITK_WARN << "Trying to access non-existing dimension."; return 1; } } void mitk::OclImage::SetDimensions(unsigned int* Dims) { m_Dims = Dims; } void mitk::OclImage::SetDimension(unsigned short dim) { m_dim = dim; } float mitk::OclImage::GetSpacing(int idx) { if (this->m_dim > idx) { - const float* imSpacing = m_Image->GetSlicedGeometry()->GetFloatSpacing(); + const mitk::Vector3D imSpacing = m_Image->GetSlicedGeometry()->GetSpacing(); return imSpacing[idx]; } else { MITK_WARN << "Trying to access non-existing dimension."; return 1; } } void mitk::OclImage::InitializeMITKImage() { this->m_Image = mitk::Image::New(); } void mitk::OclImage::GetOffset(float* _imOffset) const { itk::Vector result2; result2.Fill(0.0f); result2 = this->m_Image->GetGeometry()->GetIndexToWorldTransform()->GetOffset(); _imOffset[0] = result2[0]; _imOffset[1] = result2[1]; _imOffset[2] = result2[2]; } \ No newline at end of file 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 ffe49c49bf..9ed5435fed 100644 --- a/Modules/PlanarFigure/DataManagement/mitkPlanarAngle.h +++ b/Modules/PlanarFigure/DataManagement/mitkPlanarAngle.h @@ -1,88 +1,88 @@ /*=================================================================== 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 namespace mitk { -class Geometry2D; +class PlaneGeometry; /** * \brief Implementation of PlanarFigure to display an angle * through three control points */ class MitkPlanarFigure_EXPORT PlanarAngle : public PlanarFigure { public: mitkClassMacro( PlanarAngle, PlanarFigure ); itkFactorylessNewMacro(Self) itkCloneMacro(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 62e04bbd28..c0a517da91 100644 --- a/Modules/PlanarFigure/DataManagement/mitkPlanarArrow.h +++ b/Modules/PlanarFigure/DataManagement/mitkPlanarArrow.h @@ -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. ===================================================================*/ #ifndef _MITK_PLANAR_ARROW_H_ #define _MITK_PLANAR_ARROW_H_ #include "mitkPlanarFigure.h" #include namespace mitk { -class Geometry2D; +class PlaneGeometry; /** * \brief Implementation of PlanarFigure representing an arrow * through two control points */ class MitkPlanarFigure_EXPORT PlanarArrow : public PlanarFigure { public: mitkClassMacro( PlanarArrow, PlanarFigure ); itkFactorylessNewMacro(Self) itkCloneMacro(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 2e6e9b124e..bea636e3e6 100644 --- a/Modules/PlanarFigure/DataManagement/mitkPlanarCircle.h +++ b/Modules/PlanarFigure/DataManagement/mitkPlanarCircle.h @@ -1,135 +1,135 @@ /*=================================================================== 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 namespace mitk { -class Geometry2D; +class PlaneGeometry; /** * \brief Implementation of PlanarFigure representing a circle * through two control points */ class MitkPlanarFigure_EXPORT PlanarCircle : public PlanarFigure { public: mitkClassMacro( PlanarCircle, PlanarFigure ); itkFactorylessNewMacro(Self) itkCloneMacro(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 499f08d213..217ed8c5ac 100644 --- a/Modules/PlanarFigure/DataManagement/mitkPlanarCross.h +++ b/Modules/PlanarFigure/DataManagement/mitkPlanarCross.h @@ -1,132 +1,132 @@ /*=================================================================== 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 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 MitkPlanarFigure_EXPORT PlanarCross : public PlanarFigure { public: mitkClassMacro( PlanarCross, PlanarFigure ); itkFactorylessNewMacro(Self) itkCloneMacro(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 ac13384947..271f4f8f45 100644 --- a/Modules/PlanarFigure/DataManagement/mitkPlanarEllipse.cpp +++ b/Modules/PlanarFigure/DataManagement/mitkPlanarEllipse.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 "mitkPlanarEllipse.h" -#include "mitkGeometry2D.h" +#include "mitkPlaneGeometry.h" #include "mitkProperties.h" #include 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 3b6f9fdcde..3f1da90980 100644 --- a/Modules/PlanarFigure/DataManagement/mitkPlanarEllipse.h +++ b/Modules/PlanarFigure/DataManagement/mitkPlanarEllipse.h @@ -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. ===================================================================*/ #ifndef _MITK_PLANAR_ELLIPSE_H_ #define _MITK_PLANAR_ELLIPSE_H_ #include "mitkPlanarFigure.h" #include namespace mitk { -class Geometry2D; +class PlaneGeometry; /** * \brief Implementation of PlanarFigure representing a circle * through two control points */ class MitkPlanarFigure_EXPORT PlanarEllipse : public PlanarFigure { public: mitkClassMacro( PlanarEllipse, PlanarFigure ) itkFactorylessNewMacro(Self) itkCloneMacro(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 d13bbcf44f..5d5dca93c3 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 #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 MitkPlanarFigure_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 8915346fd6..b485beb721 100644 --- a/Modules/PlanarFigure/DataManagement/mitkPlanarFourPointAngle.h +++ b/Modules/PlanarFigure/DataManagement/mitkPlanarFourPointAngle.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_PLANAR_FOURPOINTANGLE_H_ #define _MITK_PLANAR_FOURPOINTANGLE_H_ #include "mitkPlanarFigure.h" #include 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 MitkPlanarFigure_EXPORT PlanarFourPointAngle : public PlanarFigure { public: mitkClassMacro( PlanarFourPointAngle, PlanarFigure ); itkFactorylessNewMacro(Self) itkCloneMacro(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 2f3e41a22d..09732fcef3 100644 --- a/Modules/PlanarFigure/DataManagement/mitkPlanarLine.h +++ b/Modules/PlanarFigure/DataManagement/mitkPlanarLine.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_PLANAR_LINE_H_ #define _MITK_PLANAR_LINE_H_ #include "mitkPlanarFigure.h" #include namespace mitk { -class Geometry2D; +class PlaneGeometry; /** * \brief Implementation of PlanarFigure representing a line * through two control points */ class MitkPlanarFigure_EXPORT PlanarLine : public PlanarFigure { public: mitkClassMacro( PlanarLine, PlanarFigure ); itkFactorylessNewMacro(Self) itkCloneMacro(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 7963fc0732..fa5c0c19b4 100644 --- a/Modules/PlanarFigure/DataManagement/mitkPlanarPolygon.h +++ b/Modules/PlanarFigure/DataManagement/mitkPlanarPolygon.h @@ -1,103 +1,103 @@ /*=================================================================== 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 namespace mitk { -class Geometry2D; +class PlaneGeometry; /** * \brief Implementation of PlanarFigure representing a polygon * with two or more control points */ class MitkPlanarFigure_EXPORT PlanarPolygon : public PlanarFigure { public: mitkClassMacro( PlanarPolygon, PlanarFigure ); itkFactorylessNewMacro(Self) itkCloneMacro(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 << ": " < namespace mitk { -class Geometry2D; +class PlaneGeometry; /** * \brief Implementation of PlanarFigure representing a polygon * with two or more control points */ class MitkPlanarFigure_EXPORT PlanarRectangle : public PlanarFigure { public: mitkClassMacro( PlanarRectangle, PlanarFigure ); itkFactorylessNewMacro(Self) itkCloneMacro(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(); virtual void PlaceFigure( const Point2D &point ); /** \brief Polygon has 2 control points per definition. */ virtual unsigned int GetMinimumNumberOfControlPoints() const { return 4; } /** \brief Polygon maximum number of control points is principally not limited. */ virtual unsigned int GetMaximumNumberOfControlPoints() const { return 4; } virtual bool SetControlPoint( unsigned int index, const Point2D &point, bool createIfDoesNotExist = false); protected: PlanarRectangle(); virtual ~PlanarRectangle(); 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; 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/mitkPlanarSubdivisionPolygon.cpp b/Modules/PlanarFigure/DataManagement/mitkPlanarSubdivisionPolygon.cpp index 08c9948cef..db3ac6657b 100644 --- a/Modules/PlanarFigure/DataManagement/mitkPlanarSubdivisionPolygon.cpp +++ b/Modules/PlanarFigure/DataManagement/mitkPlanarSubdivisionPolygon.cpp @@ -1,132 +1,132 @@ /*=================================================================== 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 "mitkPlanarSubdivisionPolygon.h" -#include "mitkGeometry2D.h" +#include "mitkPlaneGeometry.h" #include "mitkProperties.h" // stl related includes #include 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 65714615b4..9d086cb88c 100644 --- a/Modules/PlanarFigure/DataManagement/mitkPlanarSubdivisionPolygon.h +++ b/Modules/PlanarFigure/DataManagement/mitkPlanarSubdivisionPolygon.h @@ -1,107 +1,107 @@ /*=================================================================== 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 #include "mitkPlanarPolygon.h" namespace mitk { -class Geometry2D; +class PlaneGeometry; /** * \brief Implementation of PlanarFigure representing a polygon * with two or more control points */ class MitkPlanarFigure_EXPORT PlanarSubdivisionPolygon : public PlanarPolygon { public: mitkClassMacro( PlanarSubdivisionPolygon, PlanarFigure ); itkFactorylessNewMacro(Self) itkCloneMacro(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/IO/mitkPlanarFigureReader.cpp b/Modules/PlanarFigure/IO/mitkPlanarFigureReader.cpp index a4d2000bf8..564e4908ad 100644 --- a/Modules/PlanarFigure/IO/mitkPlanarFigureReader.cpp +++ b/Modules/PlanarFigure/IO/mitkPlanarFigureReader.cpp @@ -1,444 +1,444 @@ /*=================================================================== 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 "mitkPlanarFigureReader.h" #include "mitkPlanarAngle.h" #include "mitkPlanarCircle.h" #include "mitkPlanarLine.h" #include "mitkPlanarArrow.h" #include "mitkPlanarCross.h" #include "mitkPlanarFourPointAngle.h" #include "mitkPlanarPolygon.h" #include "mitkPlanarSubdivisionPolygon.h" #include "mitkPlanarRectangle.h" #include "mitkPlaneGeometry.h" #include "mitkPlanarEllipse.h" #include "mitkPlanarDoubleEllipse.h" #include "mitkPlanarBezierCurve.h" #include "mitkBasePropertySerializer.h" #include #include mitk::PlanarFigureReader::PlanarFigureReader() : PlanarFigureSource(), FileReader(), m_FileName(""), m_FilePrefix(""), m_FilePattern(""), m_Success(false) { this->SetNumberOfRequiredOutputs(1); this->SetNumberOfIndexedOutputs(1); this->SetNthOutput(0, this->MakeOutput(0)); m_CanReadFromMemory = true; //this->Modified(); //this->GetOutput()->Modified(); //this->GetOutput()->ReleaseData(); } mitk::PlanarFigureReader::~PlanarFigureReader() {} void mitk::PlanarFigureReader::GenerateData() { m_Success = false; this->SetNumberOfIndexedOutputs(0); // reset all outputs, we add new ones depending on the file content TiXmlDocument document; if(m_ReadFromMemory) { if(m_MemoryBuffer == NULL || m_MemorySize == 0) { //check itkWarningMacro( << "Sorry, memory buffer has not been set!" ); return; } if(m_MemoryBuffer[ m_MemorySize - 1 ] == '\0') { document.Parse(m_MemoryBuffer); } else { char * tmpArray = new char[(int)m_MemorySize+1]; tmpArray[m_MemorySize] = '\0'; memcpy(tmpArray,m_MemoryBuffer,m_MemorySize); document.Parse(m_MemoryBuffer); delete [] tmpArray; } } else { if (m_FileName.empty()) { itkWarningMacro( << "Sorry, filename has not been set!" ); return; } if (this->CanReadFile( m_FileName.c_str()) == false) { itkWarningMacro( << "Sorry, can't read file " << m_FileName << "!" ); return; } if (!document.LoadFile(m_FileName)) { MITK_ERROR << "Could not open/read/parse " << m_FileName << ". TinyXML reports: '" << document.ErrorDesc() << "'. " << "The error occurred in row " << document.ErrorRow() << ", column " << document.ErrorCol() << "."; return; } } int fileVersion = 1; TiXmlElement* versionObject = document.FirstChildElement("Version"); if (versionObject != NULL) { if ( versionObject->QueryIntAttribute( "FileVersion", &fileVersion ) != TIXML_SUCCESS ) { MITK_WARN << m_FileName << " does not contain version information! Trying version 1 format." << std::endl; } } else { MITK_WARN << m_FileName << " does not contain version information! Trying version 1 format." << std::endl; } if (fileVersion != 1) // add file version selection and version specific file parsing here, if newer file versions are created { MITK_WARN << "File version > 1 is not supported by this reader."; return; } /* file version 1 reader code */ for( TiXmlElement* pfElement = document.FirstChildElement("PlanarFigure"); pfElement != NULL; pfElement = pfElement->NextSiblingElement("PlanarFigure") ) { if (pfElement == NULL) continue; std::string type = pfElement->Attribute("type"); mitk::PlanarFigure::Pointer planarFigure = NULL; if (type == "PlanarAngle") { planarFigure = mitk::PlanarAngle::New(); } else if (type == "PlanarCircle") { planarFigure = mitk::PlanarCircle::New(); } else if (type == "PlanarEllipse") { planarFigure = mitk::PlanarEllipse::New(); } else if (type == "PlanarCross") { planarFigure = mitk::PlanarCross::New(); } else if (type == "PlanarFourPointAngle") { planarFigure = mitk::PlanarFourPointAngle::New(); } else if (type == "PlanarLine") { planarFigure = mitk::PlanarLine::New(); } else if (type == "PlanarPolygon") { planarFigure = mitk::PlanarPolygon::New(); } else if (type == "PlanarSubdivisionPolygon") { planarFigure = mitk::PlanarSubdivisionPolygon::New(); } else if (type == "PlanarRectangle") { planarFigure = mitk::PlanarRectangle::New(); } else if (type == "PlanarArrow") { planarFigure = mitk::PlanarArrow::New(); } else if (type == "PlanarDoubleEllipse") { planarFigure = mitk::PlanarDoubleEllipse::New(); } else if (type == "PlanarBezierCurve") { planarFigure = mitk::PlanarBezierCurve::New(); } else { // unknown type MITK_WARN << "encountered unknown planar figure type '" << type << "'. Skipping this element."; continue; } // Read properties of the planar figure for( TiXmlElement* propertyElement = pfElement->FirstChildElement("property"); propertyElement != NULL; propertyElement = propertyElement->NextSiblingElement("property") ) { const char* keya = propertyElement->Attribute("key"); std::string key( keya ? keya : ""); const char* typea = propertyElement->Attribute("type"); std::string type( typea ? typea : ""); // hand propertyElement to specific reader std::stringstream propertyDeserializerClassName; propertyDeserializerClassName << type << "Serializer"; std::list readers = itk::ObjectFactoryBase::CreateAllInstance(propertyDeserializerClassName.str().c_str()); if (readers.size() < 1) { MITK_ERROR << "No property reader found for " << type; } if (readers.size() > 1) { MITK_WARN << "Multiple property readers found for " << type << ". Using arbitrary first one."; } for ( std::list::iterator iter = readers.begin(); iter != readers.end(); ++iter ) { if (BasePropertySerializer* reader = dynamic_cast( iter->GetPointer() ) ) { BaseProperty::Pointer property = reader->Deserialize( propertyElement->FirstChildElement() ); if (property.IsNotNull()) { planarFigure->GetPropertyList()->ReplaceProperty(key, property); } else { MITK_ERROR << "There were errors while loading property '" << key << "' of type " << type << ". Your data may be corrupted"; } break; } } } // If we load a planarFigure, it has definitely been placed correctly. // If we do not set this property here, we cannot load old planarFigures // without messing up the interaction (PF-Interactor needs this property. planarFigure->GetPropertyList()->SetBoolProperty( "initiallyplaced", true ); // Read geometry of containing plane TiXmlElement* geoElement = pfElement->FirstChildElement("Geometry"); if (geoElement != NULL) { try { // Create plane geometry mitk::PlaneGeometry::Pointer planeGeo = mitk::PlaneGeometry::New(); // Extract and set plane transform parameters DoubleList transformList = this->GetDoubleAttributeListFromXMLNode( geoElement->FirstChildElement( "transformParam" ), "param", 12 ); - typedef mitk::Geometry3D::TransformType TransformType; + typedef mitk::BaseGeometry::TransformType TransformType; TransformType::ParametersType parameters; parameters.SetSize( 12 ); unsigned int i; DoubleList::iterator it; for ( it = transformList.begin(), i = 0; it != transformList.end(); ++it, ++i ) { parameters.SetElement( i, *it ); } - typedef mitk::Geometry3D::TransformType TransformType; + typedef mitk::BaseGeometry::TransformType TransformType; TransformType::Pointer affineGeometry = TransformType::New(); affineGeometry->SetParameters( parameters ); planeGeo->SetIndexToWorldTransform( affineGeometry ); // Extract and set plane bounds DoubleList boundsList = this->GetDoubleAttributeListFromXMLNode( geoElement->FirstChildElement( "boundsParam" ), "bound", 6 ); - typedef mitk::Geometry3D::BoundsArrayType BoundsArrayType; + typedef mitk::BaseGeometry::BoundsArrayType BoundsArrayType; BoundsArrayType bounds; for ( it = boundsList.begin(), i = 0; it != boundsList.end(); ++it, ++i ) { bounds[i] = *it; } planeGeo->SetBounds( bounds ); // Extract and set spacing and origin Vector3D spacing = this->GetVectorFromXMLNode(geoElement->FirstChildElement("Spacing")); planeGeo->SetSpacing( spacing ); Point3D origin = this->GetPointFromXMLNode(geoElement->FirstChildElement("Origin")); planeGeo->SetOrigin( origin ); planarFigure->SetGeometry2D(planeGeo); } catch (...) { } } TiXmlElement* cpElement = pfElement->FirstChildElement("ControlPoints"); bool first = true; if (cpElement != NULL) for( TiXmlElement* vertElement = cpElement->FirstChildElement("Vertex"); vertElement != NULL; vertElement = vertElement->NextSiblingElement("Vertex")) { if (vertElement == NULL) continue; int id = 0; mitk::Point2D::ValueType x = 0.0; mitk::Point2D::ValueType y = 0.0; if (vertElement->QueryIntAttribute("id", &id) == TIXML_WRONG_TYPE) return; // TODO: can we do a better error handling? if (vertElement->QueryDoubleAttribute("x", &x) == TIXML_WRONG_TYPE) return; // TODO: can we do a better error handling? if (vertElement->QueryDoubleAttribute("y", &y) == TIXML_WRONG_TYPE) return; // TODO: can we do a better error handling? Point2D p; p.SetElement(0, x); p.SetElement(1, y); if (first == true) // needed to set m_FigurePlaced to true { planarFigure->PlaceFigure(p); first = false; } planarFigure->SetControlPoint(id, p, true); } // Calculate feature quantities of this PlanarFigure planarFigure->EvaluateFeatures(); // Make sure that no control point is currently selected planarFigure->DeselectControlPoint(); // \TODO: what about m_FigurePlaced and m_SelectedControlPoint ?? this->SetNthOutput( this->GetNumberOfOutputs(), planarFigure ); // add planarFigure as new output of this filter } m_Success = true; } mitk::Point3D mitk::PlanarFigureReader::GetPointFromXMLNode(TiXmlElement* e) { if (e == NULL) throw std::invalid_argument("node invalid"); // TODO: can we do a better error handling? mitk::Point3D point; mitk::ScalarType p(-1.0); if (e->QueryDoubleAttribute("x", &p) == TIXML_WRONG_TYPE) throw std::invalid_argument("node malformatted"); // TODO: can we do a better error handling? point.SetElement(0, p); if (e->QueryDoubleAttribute("y", &p) == TIXML_WRONG_TYPE) throw std::invalid_argument("node malformatted"); // TODO: can we do a better error handling? point.SetElement(1, p); if (e->QueryDoubleAttribute("z", &p) == TIXML_WRONG_TYPE) throw std::invalid_argument("node malformatted"); // TODO: can we do a better error handling? point.SetElement(2, p); return point; } mitk::Vector3D mitk::PlanarFigureReader::GetVectorFromXMLNode(TiXmlElement* e) { if (e == NULL) throw std::invalid_argument("node invalid"); // TODO: can we do a better error handling? mitk::Vector3D vector; mitk::ScalarType p(-1.0); if (e->QueryDoubleAttribute("x", &p) == TIXML_WRONG_TYPE) throw std::invalid_argument("node malformatted"); // TODO: can we do a better error handling? vector.SetElement(0, p); if (e->QueryDoubleAttribute("y", &p) == TIXML_WRONG_TYPE) throw std::invalid_argument("node malformatted"); // TODO: can we do a better error handling? vector.SetElement(1, p); if (e->QueryDoubleAttribute("z", &p) == TIXML_WRONG_TYPE) throw std::invalid_argument("node malformatted"); // TODO: can we do a better error handling? vector.SetElement(2, p); return vector; } mitk::PlanarFigureReader::DoubleList mitk::PlanarFigureReader::GetDoubleAttributeListFromXMLNode(TiXmlElement* e, const char *attributeNameBase, unsigned int count) { DoubleList list; if (e == NULL) throw std::invalid_argument("node invalid"); // TODO: can we do a better error handling? for ( unsigned int i = 0; i < count; ++i ) { mitk::ScalarType p(-1.0); std::stringstream attributeName; attributeName << attributeNameBase << i; if (e->QueryDoubleAttribute( attributeName.str().c_str(), &p ) == TIXML_WRONG_TYPE) throw std::invalid_argument("node malformatted"); // TODO: can we do a better error handling? list.push_back( p ); } return list; } void mitk::PlanarFigureReader::GenerateOutputInformation() { } int mitk::PlanarFigureReader::CanReadFile ( const char *name ) { if (std::string(name).empty()) return false; return (itksys::SystemTools::LowerCase(itksys::SystemTools::GetFilenameLastExtension(name)) == ".pf"); //assume, we can read all .pf files //TiXmlDocument document(name); //if (document.LoadFile() == false) // return false; //return (document.FirstChildElement("PlanarFigure") != NULL); } bool mitk::PlanarFigureReader::CanReadFile(const std::string filename, const std::string, const std::string) { if (filename.empty()) return false; return (itksys::SystemTools::LowerCase(itksys::SystemTools::GetFilenameLastExtension(filename)) == ".pf"); //assume, we can read all .pf files //TiXmlDocument document(filename); //if (document.LoadFile() == false) // return false; //return (document.FirstChildElement("PlanarFigure") != NULL); } void mitk::PlanarFigureReader::ResizeOutputs( const unsigned int& num ) { unsigned int prevNum = this->GetNumberOfOutputs(); this->SetNumberOfIndexedOutputs( num ); for ( unsigned int i = prevNum; i < num; ++i ) { this->SetNthOutput( i, this->MakeOutput( i ).GetPointer() ); } } diff --git a/Modules/PlanarFigure/Interactions/mitkPlanarFigureInteractor.cpp b/Modules/PlanarFigure/Interactions/mitkPlanarFigureInteractor.cpp index bc7ecae241..3560052733 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 necessary 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 = positionEvent->GetPointerPositionOnScreen(); 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 82ef092012..f96489301a 100644 --- a/Modules/PlanarFigure/Interactions/mitkPlanarFigureInteractor.h +++ b/Modules/PlanarFigure/Interactions/mitkPlanarFigureInteractor.h @@ -1,205 +1,205 @@ /*=================================================================== 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 #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 MitkPlanarFigure_EXPORT PlanarFigureInteractor : public DataInteractor { public: mitkClassMacro(PlanarFigureInteractor, DataInteractor); itkFactorylessNewMacro(Self) itkCloneMacro(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 7ab352459f..caf160030e 100644 --- a/Modules/PlanarFigure/Rendering/mitkPlanarFigureMapper2D.cpp +++ b/Modules/PlanarFigure/Rendering/mitkPlanarFigureMapper2D.cpp @@ -1,927 +1,927 @@ /*=================================================================== 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 = false; m_DrawHelperDashed = false; 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->GetBoolProperty( "planarfigure.helperline.drawdashed", m_DrawHelperDashed ); 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.helperline.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(1.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 ) { glLineStipple(1, 0x00FF); // 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); if (m_DrawDashed) glEnable(GL_LINE_STIPPLE); else glDisable(GL_LINE_STIPPLE); // Draw the outline for all polylines if requested this->DrawMainLines( planarFigure, anchorPoint, planarFigureGeometry2D, rendererGeometry2D, displayGeometry ); glLineWidth( m_HelperlineWidth ); if (m_DrawHelperDashed) glEnable(GL_LINE_STIPPLE); else glDisable(GL_LINE_STIPPLE); // 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 ); if (m_DrawDashed) glEnable(GL_LINE_STIPPLE); else glDisable(GL_LINE_STIPPLE); // Draw the outline for all polylines if requested this->DrawMainLines( planarFigure, anchorPoint, planarFigureGeometry2D, rendererGeometry2D, displayGeometry ); glLineWidth( m_HelperlineWidth ); if (m_DrawHelperDashed) glEnable(GL_LINE_STIPPLE); else glDisable(GL_LINE_STIPPLE); // 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 ); if (m_DrawDashed) glEnable(GL_LINE_STIPPLE); else glDisable(GL_LINE_STIPPLE); // 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 ); glLineWidth( m_HelperlineWidth ); if (m_DrawHelperDashed) glEnable(GL_LINE_STIPPLE); else glDisable(GL_LINE_STIPPLE); // Draw helper objects this->DrawHelperLines( planarFigure, anchorPoint, planarFigureGeometry2D, rendererGeometry2D, displayGeometry ); // cleanup delete[] colorVector; delete[] helperColorVector; } if ( m_DrawDashed || m_DrawHelperDashed ) glDisable(GL_LINE_STIPPLE); } diff --git a/Modules/PlanarFigure/Rendering/mitkPlanarFigureMapper2D.h b/Modules/PlanarFigure/Rendering/mitkPlanarFigureMapper2D.h index fcc14dbc40..ae59d2b794 100644 --- a/Modules/PlanarFigure/Rendering/mitkPlanarFigureMapper2D.h +++ b/Modules/PlanarFigure/Rendering/mitkPlanarFigureMapper2D.h @@ -1,309 +1,309 @@ /*=================================================================== 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 #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 MitkPlanarFigure_EXPORT PlanarFigureMapper2D : public GLMapper { public: mitkClassMacro(PlanarFigureMapper2D, GLMapper); itkFactorylessNewMacro(Self) itkCloneMacro(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; bool m_DrawHelperDashed; // 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 64bb34165d..518d6fae79 100644 --- a/Modules/PlanarFigureSegmentation/mitkPlanarFigureSegmentationController.cpp +++ b/Modules/PlanarFigureSegmentation/mitkPlanarFigureSegmentationController.cpp @@ -1,313 +1,313 @@ /*=================================================================== 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; } if ( m_ReduceFilter.IsNull() ) { InitializeFilters(); } 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/QtWidgetsExt/QmitkSliceWidget.cpp b/Modules/QtWidgetsExt/QmitkSliceWidget.cpp index b6b5ee7821..b7b074537f 100644 --- a/Modules/QtWidgetsExt/QmitkSliceWidget.cpp +++ b/Modules/QtWidgetsExt/QmitkSliceWidget.cpp @@ -1,290 +1,291 @@ /*=================================================================== 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 "QmitkSliceWidget.h" #include "QmitkStepperAdapter.h" #include "mitkNodePredicateDataType.h" #include #include #include QmitkSliceWidget::QmitkSliceWidget(QWidget* parent, const char* name, Qt::WindowFlags f) : QWidget(parent, f) { this->setupUi(this); if (name != 0) this->setObjectName(name); popUp = new QMenu(this); popUp->addAction("Axial"); popUp->addAction("Frontal"); popUp->addAction("Sagittal"); QObject::connect(popUp, SIGNAL(triggered(QAction*)), this, SLOT(ChangeView(QAction*)) ); setPopUpEnabled(false); m_SlicedGeometry = 0; m_View = mitk::SliceNavigationController::Axial; QHBoxLayout *hlayout = new QHBoxLayout(container); hlayout->setMargin(0); // create widget QString composedName("QmitkSliceWidget::"); if (!this->objectName().isEmpty()) composedName += this->objectName(); else composedName += "QmitkGLWidget"; m_RenderWindow = new QmitkRenderWindow(container, composedName); m_Renderer = m_RenderWindow->GetRenderer(); hlayout->addWidget(m_RenderWindow); new QmitkStepperAdapter(m_NavigatorWidget, m_RenderWindow->GetSliceNavigationController()->GetSlice(), "navigation"); SetLevelWindowEnabled(true); } mitk::VtkPropRenderer* QmitkSliceWidget::GetRenderer() { return m_Renderer; } QFrame* QmitkSliceWidget::GetSelectionFrame() { return SelectionFrame; } void QmitkSliceWidget::SetDataStorage( mitk::StandaloneDataStorage::Pointer storage) { m_DataStorage = storage; m_Renderer->SetDataStorage(m_DataStorage); } mitk::StandaloneDataStorage* QmitkSliceWidget::GetDataStorage() { return m_DataStorage; } void QmitkSliceWidget::SetData( mitk::DataStorage::SetOfObjects::ConstIterator it) { SetData(it->Value(), m_View); } void QmitkSliceWidget::SetData( mitk::DataStorage::SetOfObjects::ConstIterator it, mitk::SliceNavigationController::ViewDirection view) { SetData(it->Value(), view); } void QmitkSliceWidget::SetData(mitk::DataNode::Pointer node) { try { if (m_DataStorage.IsNotNull()) { m_DataStorage->Add(node); } } catch (...) { } SetData(node, m_View); } void QmitkSliceWidget::SetData(mitk::DataNode::Pointer node, mitk::SliceNavigationController::ViewDirection view) { mitk::Image::Pointer image = dynamic_cast(node->GetData()); if (image.IsNull()) { MITK_WARN << "QmitkSliceWidget data is not an image!"; return; } m_SlicedGeometry = image->GetSlicedGeometry(); this->InitWidget(view); } void QmitkSliceWidget::InitWidget( mitk::SliceNavigationController::ViewDirection viewDirection) { m_View = viewDirection; mitk::SliceNavigationController* controller = m_RenderWindow->GetSliceNavigationController(); if (viewDirection == mitk::SliceNavigationController::Axial) { controller->SetViewDirection( mitk::SliceNavigationController::Axial); } else if (viewDirection == mitk::SliceNavigationController::Frontal) { controller->SetViewDirection(mitk::SliceNavigationController::Frontal); } // init sagittal view else { controller->SetViewDirection(mitk::SliceNavigationController::Sagittal); } if (m_SlicedGeometry.IsNull()) { return; } - mitk::Geometry3D::Pointer geometry = - static_cast (m_SlicedGeometry->Clone().GetPointer()); + + mitk::BaseGeometry::Pointer geometry = + static_cast (m_SlicedGeometry->Clone().GetPointer()); const mitk::BoundingBox::Pointer boundingbox = m_DataStorage->ComputeVisibleBoundingBox(GetRenderer(), NULL); if (boundingbox->GetPoints()->Size() > 0) { //let's see if we have data with a limited live-span ... mitk::TimeBounds timebounds = m_DataStorage->ComputeTimeBounds( GetRenderer(), NULL); if (timebounds[1] < mitk::ScalarTypeNumericTraits::max()) { timebounds[1] = timebounds[0] + 1.0f; geometry->SetTimeBounds(timebounds); } mitk::ProportionalTimeGeometry::Pointer timeGeometry = mitk::ProportionalTimeGeometry::New(); timeGeometry->Initialize(geometry,1); if (const_cast (timeGeometry->GetBoundingBoxInWorld())->GetDiagonalLength2() >= mitk::eps) { controller->SetInputWorldTimeGeometry(timeGeometry); controller->Update(); } } GetRenderer()->GetDisplayGeometry()->Fit(); mitk::RenderingManager::GetInstance()->RequestUpdate( GetRenderer()->GetRenderWindow()); } void QmitkSliceWidget::UpdateGL() { GetRenderer()->GetDisplayGeometry()->Fit(); mitk::RenderingManager::GetInstance()->RequestUpdate( GetRenderer()->GetRenderWindow()); } void QmitkSliceWidget::mousePressEvent(QMouseEvent * e) { if (e->button() == Qt::RightButton && popUpEnabled) { popUp->popup(QCursor::pos()); } } void QmitkSliceWidget::wheelEvent(QWheelEvent * e) { int val = m_NavigatorWidget->GetPos(); if (e->orientation() * e->delta() > 0) { m_NavigatorWidget->SetPos(val + 1); } else { if (val > 0) m_NavigatorWidget->SetPos(val - 1); } } void QmitkSliceWidget::ChangeView(QAction* val) { if (val->text() == "Axial") { InitWidget(mitk::SliceNavigationController::Axial); } else if (val->text() == "Frontal") { InitWidget(mitk::SliceNavigationController::Frontal); } else if (val->text() == "Sagittal") { InitWidget(mitk::SliceNavigationController::Sagittal); } } void QmitkSliceWidget::setPopUpEnabled(bool b) { popUpEnabled = b; } QmitkSliderNavigatorWidget* QmitkSliceWidget::GetNavigatorWidget() { return m_NavigatorWidget; } void QmitkSliceWidget::SetLevelWindowEnabled(bool enable) { levelWindow->setEnabled(enable); if (!enable) { levelWindow->setMinimumWidth(0); levelWindow->setMaximumWidth(0); } else { levelWindow->setMinimumWidth(28); levelWindow->setMaximumWidth(28); } } bool QmitkSliceWidget::IsLevelWindowEnabled() { return levelWindow->isEnabled(); } QmitkRenderWindow* QmitkSliceWidget::GetRenderWindow() { return m_RenderWindow; } mitk::SliceNavigationController* QmitkSliceWidget::GetSliceNavigationController() const { return m_RenderWindow->GetSliceNavigationController(); } mitk::CameraRotationController* QmitkSliceWidget::GetCameraRotationController() const { return m_RenderWindow->GetCameraRotationController(); } mitk::BaseController* QmitkSliceWidget::GetController() const { return m_RenderWindow->GetController(); } diff --git a/Modules/Segmentation/Algorithms/mitkContourUtils.cpp b/Modules/Segmentation/Algorithms/mitkContourUtils.cpp index a432e0aa16..80b9831cc1 100755 --- a/Modules/Segmentation/Algorithms/mitkContourUtils.cpp +++ b/Modules/Segmentation/Algorithms/mitkContourUtils.cpp @@ -1,79 +1,79 @@ /*=================================================================== 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 "mitkContourUtils.h" #include "mitkContourModelUtils.h" mitk::ContourUtils::ContourUtils() { } mitk::ContourUtils::~ContourUtils() { } mitk::ContourModel::Pointer mitk::ContourUtils::ProjectContourTo2DSlice(Image* slice, Contour* contourIn3D, bool itkNotUsed( correctionForIpSegmentation ), bool constrainToInside) { mitk::Contour::PointsContainerIterator it = contourIn3D->GetPoints()->Begin(); mitk::Contour::PointsContainerIterator end = contourIn3D->GetPoints()->End(); mitk::ContourModel::Pointer contour = mitk::ContourModel::New(); while(it!=end) { contour->AddVertex(it.Value()); it++; } return mitk::ContourModelUtils::ProjectContourTo2DSlice(slice, contour, false/*not used*/, constrainToInside ); } -mitk::ContourModel::Pointer mitk::ContourUtils::BackProjectContourFrom2DSlice(const Geometry3D* sliceGeometry, Contour* contourIn2D, bool itkNotUsed( correctionForIpSegmentation ) ) +mitk::ContourModel::Pointer mitk::ContourUtils::BackProjectContourFrom2DSlice(const BaseGeometry* sliceGeometry, Contour* contourIn2D, bool itkNotUsed( correctionForIpSegmentation ) ) { mitk::Contour::PointsContainerIterator it = contourIn2D->GetPoints()->Begin(); mitk::Contour::PointsContainerIterator end = contourIn2D->GetPoints()->End(); mitk::ContourModel::Pointer contour = mitk::ContourModel::New(); while(it!=end) { contour->AddVertex(it.Value()); it++; } return mitk::ContourModelUtils::BackProjectContourFrom2DSlice(sliceGeometry, contour, false/*not used*/); } void mitk::ContourUtils::FillContourInSlice( Contour* projectedContour, Image* sliceImage, int paintingPixelValue ) { mitk::Contour::PointsContainerIterator it = projectedContour->GetPoints()->Begin(); mitk::Contour::PointsContainerIterator end = projectedContour->GetPoints()->End(); mitk::ContourModel::Pointer contour = mitk::ContourModel::New(); while(it!=end) { contour->AddVertex(it.Value()); it++; } mitk::ContourModelUtils::FillContourInSlice(contour, sliceImage, paintingPixelValue); } diff --git a/Modules/Segmentation/Algorithms/mitkContourUtils.h b/Modules/Segmentation/Algorithms/mitkContourUtils.h index 19f468895b..ec1dcd96df 100644 --- a/Modules/Segmentation/Algorithms/mitkContourUtils.h +++ b/Modules/Segmentation/Algorithms/mitkContourUtils.h @@ -1,74 +1,74 @@ /*=================================================================== 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 mitkContourUtilshIncludett #define mitkContourUtilshIncludett #include "mitkImage.h" #include #include "mitkContour.h" #include "mitkContourModel.h" namespace mitk { /** * \brief Helpful methods for working with contours and images * * Legacy support for mitk::Contour * TODO remove this class when mitk::Contour is removed */ class MitkSegmentation_EXPORT ContourUtils : public itk::Object { public: mitkClassMacro(ContourUtils, itk::Object); itkFactorylessNewMacro(Self) itkCloneMacro(Self) /** \brief Projects a contour onto an image point by point. Converts from world to index coordinates. \param correctionForIpSegmentation adds 0.5 to x and y index coordinates (difference between ipSegmentation and MITK contours) */ ContourModel::Pointer ProjectContourTo2DSlice(Image* slice, Contour* contourIn3D, bool correctionForIpSegmentation, bool constrainToInside); /** \brief Projects a slice index coordinates of a contour back into world coordinates. \param correctionForIpSegmentation subtracts 0.5 to x and y index coordinates (difference between ipSegmentation and MITK contours) */ - ContourModel::Pointer BackProjectContourFrom2DSlice(const Geometry3D* sliceGeometry, Contour* contourIn2D, bool correctionForIpSegmentation = false); + ContourModel::Pointer BackProjectContourFrom2DSlice(const BaseGeometry* sliceGeometry, Contour* contourIn2D, bool correctionForIpSegmentation = false); /** \brief Fill a contour in a 2D slice with a specified pixel value. */ void FillContourInSlice( Contour* projectedContour, Image* sliceImage, int paintingPixelValue = 1 ); protected: ContourUtils(); virtual ~ContourUtils(); }; } #endif diff --git a/Modules/Segmentation/Algorithms/mitkDiffSliceOperation.cpp b/Modules/Segmentation/Algorithms/mitkDiffSliceOperation.cpp index e9b634877c..a056a622fc 100644 --- a/Modules/Segmentation/Algorithms/mitkDiffSliceOperation.cpp +++ b/Modules/Segmentation/Algorithms/mitkDiffSliceOperation.cpp @@ -1,104 +1,105 @@ /*=================================================================== 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, - Geometry3D* sliceGeometry, + SlicedGeometry3D* sliceGeometry, unsigned int timestep, - Geometry3D* currentWorldGeometry):Operation(1) + BaseGeometry* currentWorldGeometry):Operation(1) { - m_WorldGeometry = currentWorldGeometry->Clone(); + itk::LightObject::Pointer lopointer = currentWorldGeometry->Clone(); + m_WorldGeometry = dynamic_cast(lopointer.GetPointer()); /* 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::Geometry3D::New(); - m_GuardReferenceGeometry = dynamic_cast(m_WorldGeometry.GetPointer())->GetReferenceGeometry(); + //xxxx m_GuardReferenceGeometry = mitk::BaseGeometry::New(); + 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 e2766af45c..f896be2165 100644 --- a/Modules/Segmentation/Algorithms/mitkDiffSliceOperation.h +++ b/Modules/Segmentation/Algorithms/mitkDiffSliceOperation.h @@ -1,140 +1,140 @@ /*=================================================================== 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 #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 MitkSegmentation_EXPORT DiffSliceOperation : public Operation { public: mitkClassMacro(DiffSliceOperation, OperationActor); //itkFactorylessNewMacro(Self) //itkCloneMacro(Self) - //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, Geometry3D* sliceGeometry, unsigned int timestep, Geometry3D* currentWorldGeometry); + DiffSliceOperation( mitk::Image* imageVolume, vtkImageData* slice, SlicedGeometry3D* 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, Geometry3D* currentWorldGeometry)); + 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(Geometry3D* sliceGeometry){this->m_SliceGeometry = sliceGeometry;} + void SetSliceGeometry(SlicedGeometry3D* sliceGeometry){this->m_SliceGeometry = sliceGeometry;} /** \brief Get the axis where the slice has to be applied in the volume.*/ - Geometry3D* GetSliceGeometry(){return this->m_SliceGeometry;} + SlicedGeometry3D* 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(Geometry3D* worldGeometry){this->m_WorldGeometry = worldGeometry;} + void SetCurrentWorldGeometry(BaseGeometry* worldGeometry){this->m_WorldGeometry = worldGeometry;} /** \brief Get the axis where the slice has to be applied in the volume.*/ - Geometry3D* GetWorldGeometry(){return this->m_WorldGeometry;} + 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; - Geometry3D::Pointer m_SliceGeometry; + SlicedGeometry3D::Pointer m_SliceGeometry; unsigned int m_TimeStep; - Geometry3D::Pointer m_WorldGeometry; + BaseGeometry::Pointer m_WorldGeometry; bool m_ImageIsValid; unsigned long m_DeleteObserverTag; - mitk::Geometry3D::Pointer m_GuardReferenceGeometry; + 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/Algorithms/mitkImageToContourFilter.h b/Modules/Segmentation/Algorithms/mitkImageToContourFilter.h index 65c50dc58a..8f9152fe00 100644 --- a/Modules/Segmentation/Algorithms/mitkImageToContourFilter.h +++ b/Modules/Segmentation/Algorithms/mitkImageToContourFilter.h @@ -1,95 +1,95 @@ /*=================================================================== 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 mitkImageToContourFilter_h_Included #define mitkImageToContourFilter_h_Included //#include "MitkSBExports.h" #include #include "itkImage.h" #include "mitkImage.h" #include "itkContourExtractor2DImageFilter.h" #include "mitkImageToSurfaceFilter.h" #include "mitkSurface.h" #include "vtkPolyData.h" #include "vtkPolygon.h" #include "vtkCellArray.h" #include "mitkProgressBar.h" namespace mitk { /** \brief A filter that can extract contours out of a 2D binary image This class takes an 2D mitk::Image as input and extracts all contours which are drawn it. The contour extraction is done by using the itk::ContourExtractor2DImageFilter. The output is a mitk::Surface. $Author: fetzer$ */ class MitkSegmentation_EXPORT ImageToContourFilter : public ImageToSurfaceFilter { public: mitkClassMacro(ImageToContourFilter,ImageToSurfaceFilter); itkFactorylessNewMacro(Self) itkCloneMacro(Self) /** \brief Set macro for the geometry of the slice. If it is not set explicitly the geometry will be taken from the slice \a Parameter The slice`s geometry */ - itkSetMacro(SliceGeometry, Geometry3D*); + itkSetMacro(SliceGeometry, BaseGeometry*); //typedef unsigned int VDimension; typedef itk::PolyLineParametricPath<2> PolyLineParametricPath2D; typedef PolyLineParametricPath2D::VertexListType ContourPath; /** \brief Set whether the mitkProgressBar should be used \a Parameter true for using the progress bar, false otherwise */ void SetUseProgressBar(bool); /** \brief Set the stepsize which the progress bar should proceed \a Parameter The stepsize for progressing */ void SetProgressStepSize(unsigned int stepSize); protected: ImageToContourFilter(); virtual ~ImageToContourFilter(); virtual void GenerateData(); virtual void GenerateOutputInformation(); private: - const Geometry3D* m_SliceGeometry; + const BaseGeometry* m_SliceGeometry; bool m_UseProgressBar; unsigned int m_ProgressStepSize; template void Itk2DContourExtraction (itk::Image* sliceImage); };//class }//namespace #endif diff --git a/Modules/Segmentation/Algorithms/mitkOverwriteDirectedPlaneImageFilter.h b/Modules/Segmentation/Algorithms/mitkOverwriteDirectedPlaneImageFilter.h index c5abbb562e..5cd7d3bd19 100644 --- a/Modules/Segmentation/Algorithms/mitkOverwriteDirectedPlaneImageFilter.h +++ b/Modules/Segmentation/Algorithms/mitkOverwriteDirectedPlaneImageFilter.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 mitkOverwriteDirectedPlaneImageFilter_h_Included #define mitkOverwriteDirectedPlaneImageFilter_h_Included #include "mitkCommon.h" #include #include "mitkImageToImageFilter.h" #include namespace mitk { /** \deprecated This class is deprecated. Use mitkVtkImageOverwrite instead. \sa mitkVtkImageOverwrite \brief Writes a 2D slice into a 3D image. \sa SegTool2D \sa ContourTool \sa ExtractImageFilter \ingroup Process \ingroup Reliver There is a separate page describing the general design of QmitkInteractiveSegmentation: \ref QmitkInteractiveSegmentationTechnicalPage This class takes a 3D mitk::Image as input and tries to replace one slice in it with the second input image, which is specified by calling SetSliceImage with a 2D mitk::Image. Two parameters determine which slice is replaced: the "slice dimension" is that one, which is constant for all points in the plane, e.g. axial would mean 2. The "slice index" is the slice index in the image direction you specified with "affected dimension". Indices count from zero. This class works with all kind of image types, the only restrictions being that the input is 3D, and the slice image is 2D. If requested by SetCreateUndoInformation(true), this class will create instances of ApplyDiffImageOperation for the undo stack. These operations will (on user request) be executed by DiffImageApplier to perform undo. Last contributor: $Author: maleike $ */ class MitkSegmentation_EXPORT OverwriteDirectedPlaneImageFilter : public ImageToImageFilter { public: mitkClassMacro(OverwriteDirectedPlaneImageFilter, ImageToImageFilter); itkFactorylessNewMacro(Self) itkCloneMacro(Self) /** \brief Which plane to overwrite */ - const Geometry3D* GetPlaneGeometry3D() const { return m_PlaneGeometry; } - void SetPlaneGeometry3D( const Geometry3D *geometry ) { m_PlaneGeometry = geometry; } + const BaseGeometry* GetPlaneGeometry3D() const { return m_PlaneGeometry; } + void SetPlaneGeometry3D( const BaseGeometry *geometry ) { m_PlaneGeometry = geometry; } /** \brief Time step of the slice to overwrite */ itkSetMacro(TimeStep, unsigned int); itkGetConstMacro(TimeStep, unsigned int); /** \brief Whether to create undo operation in the MITK undo stack */ itkSetMacro(CreateUndoInformation, bool); itkGetConstMacro(CreateUndoInformation, bool); itkSetObjectMacro(SliceImage, Image); const Image* GetSliceImage() { return m_SliceImage.GetPointer(); } const Image* GetLastDifferenceImage() { return m_SliceDifferenceImage.GetPointer(); } protected: OverwriteDirectedPlaneImageFilter(); // purposely hidden virtual ~OverwriteDirectedPlaneImageFilter(); virtual void GenerateData(); template void ItkSliceOverwriting (itk::Image* input3D); template void ItkImageSwitch( itk::Image* image ); template void ItkImageProcessing( itk::Image* itkImage1, itk::Image* itkImage2 ); //std::string EventDescription( unsigned int sliceDimension, unsigned int sliceIndex, unsigned int timeStep ); Image::ConstPointer m_SliceImage; Image::Pointer m_SliceDifferenceImage; - const Geometry3D *m_PlaneGeometry; - const Geometry3D *m_ImageGeometry3D; + const BaseGeometry *m_PlaneGeometry; + const BaseGeometry *m_ImageGeometry3D; unsigned int m_TimeStep; unsigned int m_Dimension0; unsigned int m_Dimension1; bool m_CreateUndoInformation; }; } // namespace #endif diff --git a/Modules/Segmentation/Algorithms/mitkShapeBasedInterpolationAlgorithm.cpp b/Modules/Segmentation/Algorithms/mitkShapeBasedInterpolationAlgorithm.cpp index a4c2a404aa..1c92fd30af 100644 --- a/Modules/Segmentation/Algorithms/mitkShapeBasedInterpolationAlgorithm.cpp +++ b/Modules/Segmentation/Algorithms/mitkShapeBasedInterpolationAlgorithm.cpp @@ -1,75 +1,75 @@ /*=================================================================== 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 "mitkShapeBasedInterpolationAlgorithm.h" #include "mitkImageCast.h" #include "mitkImageDataItem.h" #include "ipSegmentation.h" mitk::Image::Pointer mitk::ShapeBasedInterpolationAlgorithm::Interpolate( Image::ConstPointer lowerSlice, unsigned int lowerSliceIndex, Image::ConstPointer upperSlice, unsigned int upperSliceIndex, unsigned int requestedIndex, unsigned int /*sliceDimension*/, // commented variables are not used Image::Pointer resultImage, unsigned int /*timeStep*/, Image::ConstPointer /*referenceImage*/) { // convert these slices to the ipSegmentation data type (into an ITK image) itk::Image< ipMITKSegmentationTYPE, 2 >::Pointer correctPixelTypeLowerITKSlice; CastToItkImage( lowerSlice, correctPixelTypeLowerITKSlice ); assert ( correctPixelTypeLowerITKSlice.IsNotNull() ); itk::Image< ipMITKSegmentationTYPE, 2 >::Pointer correctPixelTypeUpperITKSlice; CastToItkImage( upperSlice, correctPixelTypeUpperITKSlice ); assert ( correctPixelTypeUpperITKSlice.IsNotNull() ); // correct direction info itk::Image< ipMITKSegmentationTYPE, 2 >::DirectionType imageDirection; imageDirection.SetIdentity(); correctPixelTypeLowerITKSlice->SetDirection(imageDirection); correctPixelTypeUpperITKSlice->SetDirection(imageDirection); // back-convert to MITK images to access a mitkIpPicDescriptor Image::Pointer correctPixelTypeLowerMITKSlice = Image::New(); CastToMitkImage( correctPixelTypeLowerITKSlice, correctPixelTypeLowerMITKSlice ); mitkIpPicDescriptor* lowerPICSlice = mitkIpPicNew(); CastToIpPicDescriptor( correctPixelTypeLowerMITKSlice, lowerPICSlice); Image::Pointer correctPixelTypeUpperMITKSlice = Image::New(); CastToMitkImage( correctPixelTypeUpperITKSlice, correctPixelTypeUpperMITKSlice ); mitkIpPicDescriptor* upperPICSlice = mitkIpPicNew(); CastToIpPicDescriptor( correctPixelTypeUpperMITKSlice, upperPICSlice); // calculate where the current slice is in comparison to the lower and upper neighboring slices float ratio = (float)(requestedIndex - lowerSliceIndex) / (float)(upperSliceIndex - lowerSliceIndex); mitkIpPicDescriptor* ipPicResult = ipMITKSegmentationInterpolate( lowerPICSlice, upperPICSlice, ratio ); // magic if (!ipPicResult) return NULL; - Geometry3D::Pointer originalGeometry = resultImage->GetGeometry(); + BaseGeometry::Pointer originalGeometry = resultImage->GetGeometry(); resultImage->Initialize( CastToImageDescriptor( ipPicResult ) ); // FIXME resultImage->SetPicSlice( ipPicResult ); resultImage->SetSlice( ipPicResult->data ); resultImage->SetGeometry( originalGeometry ); mitkIpPicFree( ipPicResult ); return resultImage; } diff --git a/Modules/Segmentation/DataManagement/mitkContour.cpp b/Modules/Segmentation/DataManagement/mitkContour.cpp index 2359dc0e19..36a45d1b58 100644 --- a/Modules/Segmentation/DataManagement/mitkContour.cpp +++ b/Modules/Segmentation/DataManagement/mitkContour.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 "mitkContour.h" #include mitk::Contour::Contour() : m_ContourPath (PathType::New()), m_CurrentWindow ( NULL ), m_BoundingBox (BoundingBoxType::New()), m_Vertices ( BoundingBoxType::PointsContainer::New() ), m_Closed ( true ), m_Selected ( false ), m_Width (3.0) { Superclass::InitializeTimeGeometry(); } mitk::Contour::Contour( const Contour & other ): BaseData(other), m_ContourPath(other.m_ContourPath), m_CurrentWindow(other.m_CurrentWindow), m_BoundingBox(other.m_BoundingBox), m_Vertices(other.m_Vertices), m_Closed(other.m_Closed), m_Selected(other.m_Selected), m_Width(other.m_Width) { } mitk::Contour::~Contour() { } void mitk::Contour::AddVertex(mitk::Point3D newPoint) { BoundingBoxType::PointType p; p.CastFrom(newPoint); m_Vertices->InsertElement(m_Vertices->Size(), p); ContinuousIndexType idx; idx.CastFrom(newPoint); m_ContourPath->AddVertex(idx); m_BoundingBox->SetPoints(m_Vertices); Modified(); } void mitk::Contour::UpdateOutputInformation() { // \todo probably we should do this additionally for each time-step ScalarType mitkBounds[6]; if (m_Vertices->Size() == 0) { mitkBounds[0] = 0.0; mitkBounds[1] = 0.0; mitkBounds[2] = 0.0; mitkBounds[3] = 0.0; mitkBounds[4] = 0.0; mitkBounds[5] = 0.0; } else { m_BoundingBox->ComputeBoundingBox(); BoundingBoxType::BoundsArrayType tmp = m_BoundingBox->GetBounds(); mitkBounds[0] = tmp[0]; mitkBounds[1] = tmp[1]; mitkBounds[2] = tmp[2]; mitkBounds[3] = tmp[3]; mitkBounds[4] = tmp[4]; mitkBounds[5] = tmp[5]; } - Geometry3D* geometry3d = GetGeometry(0); + BaseGeometry* geometry3d = GetGeometry(0); geometry3d->SetBounds(mitkBounds); GetTimeGeometry()->Update(); } void mitk::Contour::SetRequestedRegionToLargestPossibleRegion() { } bool mitk::Contour::RequestedRegionIsOutsideOfTheBufferedRegion() { return false; } bool mitk::Contour::VerifyRequestedRegion() { return true; } void mitk::Contour::SetRequestedRegion( const itk::DataObject*) { } mitk::Contour::PathType::Pointer mitk::Contour::GetContourPath() const { return m_ContourPath; } void mitk::Contour::SetCurrentWindow(vtkRenderWindow* rw) { m_CurrentWindow = rw; } vtkRenderWindow* mitk::Contour::GetCurrentWindow() const { return m_CurrentWindow; } void mitk::Contour::Initialize() { m_ContourPath = PathType::New(); m_ContourPath->Initialize(); m_BoundingBox = BoundingBoxType::New(); m_Vertices = BoundingBoxType::PointsContainer::New(); ProportionalTimeGeometry::Pointer timeGeometry = ProportionalTimeGeometry::New(); timeGeometry->Initialize(1); SetTimeGeometry(timeGeometry); } unsigned int mitk::Contour::GetNumberOfPoints() const { return m_Vertices->Size(); } mitk::Contour::PointsContainerPointer mitk::Contour::GetPoints() const { return m_Vertices; } void mitk::Contour::SetPoints(mitk::Contour::PointsContainerPointer points) { m_Vertices = points; Modified(); } void mitk::Contour::PrintSelf( std::ostream& os, itk::Indent indent) const { Superclass::PrintSelf( os, indent ); os << indent << "Number of verticies: " << GetNumberOfPoints() << std::endl; mitk::Contour::PointsContainerIterator pointsIt = m_Vertices->Begin(), end = m_Vertices->End(); os << indent << "Verticies: " << std::endl; int i = 0; while ( pointsIt != end ) { os << indent << indent << i << ": " << pointsIt.Value() << std::endl; ++pointsIt; ++i; } } diff --git a/Modules/Segmentation/DataManagement/mitkContourSet.cpp b/Modules/Segmentation/DataManagement/mitkContourSet.cpp index 1004392ade..f7ace28270 100644 --- a/Modules/Segmentation/DataManagement/mitkContourSet.cpp +++ b/Modules/Segmentation/DataManagement/mitkContourSet.cpp @@ -1,129 +1,129 @@ /*=================================================================== 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 "mitkContourSet.h" #include mitk::ContourSet::ContourSet() : m_ContourVector( ContourVectorType() ), m_NumberOfContours (0) { ProportionalTimeGeometry::Pointer timeGeometry = ProportionalTimeGeometry::New(); timeGeometry->Initialize(1); SetTimeGeometry(timeGeometry); } mitk::ContourSet::~ContourSet() { } void mitk::ContourSet::AddContour(unsigned int index, mitk::Contour::Pointer contour) { m_ContourVector.insert(std::make_pair( index , contour) ); } void mitk::ContourSet::RemoveContour(unsigned long index) { m_ContourVector.erase( index ); } void mitk::ContourSet::UpdateOutputInformation() { mitk::ContourSet::ContourVectorType contourVec = GetContours(); mitk::ContourSet::ContourIterator contoursIterator = contourVec.begin(); mitk::ContourSet::ContourIterator contoursIteratorEnd = contourVec.end(); // initialize container mitk::BoundingBox::PointsContainer::Pointer pointscontainer=mitk::BoundingBox::PointsContainer::New(); mitk::BoundingBox::PointIdentifier pointid=0; mitk::Point3D point; mitk::AffineTransform3D* transform = GetGeometry(0)->GetIndexToWorldTransform(); mitk::AffineTransform3D::Pointer inverse = mitk::AffineTransform3D::New(); transform->GetInverse(inverse); // calculate a bounding box that includes all contours // \todo probably we should do this additionally for each time-step while (contoursIterator != contoursIteratorEnd) { const TimeGeometry* geometry = (*contoursIterator).second->GetUpdatedTimeGeometry(); unsigned char i; for(i=0; i<8; ++i) { point = inverse->TransformPoint(geometry->GetCornerPointInWorld(i)); if(point[0]*point[0]+point[1]*point[1]+point[2]*point[2] < mitk::large) pointscontainer->InsertElement( pointid++, point); else { itkGenericOutputMacro( << "Unrealistically distant corner point encountered. Ignored. BoundingObject: " << (*contoursIterator).second ); } } ++contoursIterator; } mitk::BoundingBox::Pointer boundingBox = mitk::BoundingBox::New(); boundingBox->SetPoints(pointscontainer); boundingBox->ComputeBoundingBox(); - Geometry3D* geometry3d = GetGeometry(0); + BaseGeometry* geometry3d = GetGeometry(0); geometry3d->SetIndexToWorldTransform(transform); geometry3d->SetBounds(boundingBox->GetBounds()); ProportionalTimeGeometry::Pointer timeGeometry = ProportionalTimeGeometry::New(); timeGeometry->Initialize(geometry3d,GetTimeGeometry()->CountTimeSteps()); SetTimeGeometry(timeGeometry); } void mitk::ContourSet::SetRequestedRegionToLargestPossibleRegion() { } bool mitk::ContourSet::RequestedRegionIsOutsideOfTheBufferedRegion() { return true; } bool mitk::ContourSet::VerifyRequestedRegion() { return true; } void mitk::ContourSet::SetRequestedRegion( const itk::DataObject*) { } void mitk::ContourSet::Initialize() { m_ContourVector = ContourVectorType(); ProportionalTimeGeometry::Pointer timeGeometry = ProportionalTimeGeometry::New(); timeGeometry->Initialize(1); SetTimeGeometry(timeGeometry); } unsigned int mitk::ContourSet::GetNumberOfContours() { return m_ContourVector.size(); } mitk::ContourSet::ContourVectorType mitk::ContourSet::GetContours() { return m_ContourVector; } diff --git a/Modules/Segmentation/DataManagement/mitkExtrudedContour.cpp b/Modules/Segmentation/DataManagement/mitkExtrudedContour.cpp index 429a4bdb46..dd4ba243af 100644 --- a/Modules/Segmentation/DataManagement/mitkExtrudedContour.cpp +++ b/Modules/Segmentation/DataManagement/mitkExtrudedContour.cpp @@ -1,374 +1,373 @@ /*=================================================================== 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 "mitkExtrudedContour.h" #include "mitkVector.h" #include "mitkBaseProcess.h" #include "mitkProportionalTimeGeometry.h" #include #include #include #include #include #include #include #include #include #include //vtkButterflySubdivisionFilter * subdivs; #include #include #include #include #include mitk::ExtrudedContour::ExtrudedContour() : m_Contour(NULL), m_ClippingGeometry(NULL), m_AutomaticVectorGeneration(false) { ProportionalTimeGeometry::Pointer timeGeometry = ProportionalTimeGeometry::New(); timeGeometry->Initialize(1); SetTimeGeometry(timeGeometry); FillVector3D(m_Vector, 0.0, 0.0, 1.0); m_RightVector.Fill(0.0); m_ExtrusionFilter = vtkLinearExtrusionFilter::New(); m_ExtrusionFilter->CappingOff(); m_ExtrusionFilter->SetExtrusionTypeToVectorExtrusion(); double vtkvector[3]={0,0,1}; // set extrusion vector m_ExtrusionFilter->SetVector(vtkvector); m_TriangleFilter = vtkTriangleFilter::New(); m_TriangleFilter->SetInputConnection(m_ExtrusionFilter->GetOutputPort()); m_SubdivisionFilter = vtkLinearSubdivisionFilter::New(); m_SubdivisionFilter->SetInputConnection(m_TriangleFilter->GetOutputPort()); m_SubdivisionFilter->SetNumberOfSubdivisions(4); m_ClippingBox = vtkPlanes::New(); m_ClipPolyDataFilter = vtkClipPolyData::New(); m_ClipPolyDataFilter->SetInputConnection(m_SubdivisionFilter->GetOutputPort()); m_ClipPolyDataFilter->SetClipFunction(m_ClippingBox); m_ClipPolyDataFilter->InsideOutOn(); m_Polygon = vtkPolygon::New(); m_ProjectionPlane = mitk::PlaneGeometry::New(); } mitk::ExtrudedContour::~ExtrudedContour() { m_ClipPolyDataFilter->Delete(); m_ClippingBox->Delete(); m_SubdivisionFilter->Delete(); m_TriangleFilter->Delete(); m_ExtrusionFilter->Delete(); m_Polygon->Delete(); } bool mitk::ExtrudedContour::IsInside(const Point3D& worldPoint) const { static double polygonNormal[3]={0.0,0.0,1.0}; // project point onto plane float xt[3]; itk2vtk(worldPoint, xt); xt[0] = worldPoint[0]-m_Origin[0]; xt[1] = worldPoint[1]-m_Origin[1]; xt[2] = worldPoint[2]-m_Origin[2]; float dist=xt[0]*m_Normal[0]+xt[1]*m_Normal[1]+xt[2]*m_Normal[2]; xt[0] -= dist*m_Normal[0]; xt[1] -= dist*m_Normal[1]; xt[2] -= dist*m_Normal[2]; double x[3]; x[0] = xt[0]*m_Right[0]+xt[1]*m_Right[1]+xt[2]*m_Right[2]; x[1] = xt[0]*m_Down[0] +xt[1]*m_Down[1] +xt[2]*m_Down[2]; x[2] = 0; // determine whether it's in the selection loop and then evaluate point // in polygon only if absolutely necessary. if ( x[0] >= this->m_ProjectedContourBounds[0] && x[0] <= this->m_ProjectedContourBounds[1] && x[1] >= this->m_ProjectedContourBounds[2] && x[1] <= this->m_ProjectedContourBounds[3] && this->m_Polygon->PointInPolygon(x, m_Polygon->Points->GetNumberOfPoints(), ((vtkDoubleArray *)this->m_Polygon->Points->GetData())->GetPointer(0), (double*)const_cast(this)->m_ProjectedContourBounds, polygonNormal) == 1 ) return true; else return false; } mitk::ScalarType mitk::ExtrudedContour::GetVolume() { return -1.0; } void mitk::ExtrudedContour::UpdateOutputInformation() { if ( this->GetSource() ) { this->GetSource()->UpdateOutputInformation(); } if(GetMTime() > m_LastCalculateExtrusionTime) { BuildGeometry(); BuildSurface(); } //if ( ( m_CalculateBoundingBox ) && ( m_PolyDataSeries.size() > 0 ) ) // CalculateBoundingBox(); } void mitk::ExtrudedContour::BuildSurface() { if(m_Contour.IsNull()) { SetVtkPolyData(NULL); return; } // set extrusion contour vtkPolyData *polyData = vtkPolyData::New(); vtkCellArray *polys = vtkCellArray::New(); polys->InsertNextCell(m_Polygon->GetPointIds()); polyData->SetPoints(m_Polygon->GetPoints()); //float vtkpoint[3]; //unsigned int i, numPts = m_Polygon->GetNumberOfPoints(); //for(i=0; im_Polygon->Points->GetPoint(i); // pointids[i]=loopPoints->InsertNextPoint(vtkpoint); //} //polys->InsertNextCell( i, pointids ); //delete [] pointids; //polyData->SetPoints( loopPoints ); polyData->SetPolys( polys ); polys->Delete(); m_ExtrusionFilter->SetInputData(polyData); polyData->Delete(); // set extrusion scale factor m_ExtrusionFilter->SetScaleFactor(GetGeometry()->GetExtentInMM(2)); SetVtkPolyData(m_SubdivisionFilter->GetOutput()); //if(m_ClippingGeometry.IsNull()) //{ // SetVtkPolyData(m_SubdivisionFilter->GetOutput()); //} //else //{ // m_ClipPolyDataFilter->SetInput(m_SubdivisionFilter->GetOutput()); // mitk::BoundingBox::BoundsArrayType bounds=m_ClippingGeometry->GetBounds(); // m_ClippingBox->SetBounds(bounds[0], bounds[1], bounds[2], bounds[3], bounds[4], bounds[5]); // m_ClippingBox->SetTransform(GetGeometry()->GetVtkTransform()); // m_ClipPolyDataFilter->SetClipFunction(m_ClippingBox); // m_ClipPolyDataFilter->SetValue(0); // SetVtkPolyData(m_ClipPolyDataFilter->GetOutput()); //} m_LastCalculateExtrusionTime.Modified(); } void mitk::ExtrudedContour::BuildGeometry() { if(m_Contour.IsNull()) return; // Initialize(1); Vector3D nullvector; nullvector.Fill(0.0); float xProj[3]; unsigned int i; unsigned int numPts = 20; //m_Contour->GetNumberOfPoints(); mitk::Contour::PathPointer path = m_Contour->GetContourPath(); mitk::Contour::PathType::InputType cstart = path->StartOfInput(); mitk::Contour::PathType::InputType cend = path->EndOfInput(); mitk::Contour::PathType::InputType cstep = (cend-cstart)/numPts; mitk::Contour::PathType::InputType ccur; // Part I: guarantee/calculate legal vectors m_Vector.Normalize(); itk2vtk(m_Vector, m_Normal); // check m_Vector if(mitk::Equal(m_Vector, nullvector) || m_AutomaticVectorGeneration) { if ( m_AutomaticVectorGeneration == false) itkWarningMacro("Extrusion vector is 0 ("<< m_Vector << "); trying to use normal of polygon"); vtkPoints *loopPoints = vtkPoints::New(); //mitk::Contour::PointsContainerIterator pointsIt = m_Contour->GetPoints()->Begin(); double vtkpoint[3]; unsigned int i=0; for(i=0, ccur=cstart; iEvaluate(ccur), vtkpoint); loopPoints->InsertNextPoint(vtkpoint); } // Make sure points define a loop with a m_Normal vtkPolygon::ComputeNormal(loopPoints, m_Normal); loopPoints->Delete(); vtk2itk(m_Normal, m_Vector); if(mitk::Equal(m_Vector, nullvector)) { itkExceptionMacro("Cannot calculate normal of polygon"); } } // check m_RightVector if((mitk::Equal(m_RightVector, nullvector)) || (mitk::Equal(m_RightVector*m_Vector, 0.0)==false)) { if(mitk::Equal(m_RightVector, nullvector)) { itkDebugMacro("Right vector is 0. Calculating."); } else { itkWarningMacro("Right vector ("<InitializeStandardPlane(rightDV, downDV); // create vtkPolygon from contour and simultaneously determine 2D bounds of // contour projected on m_ProjectionPlane //mitk::Contour::PointsContainerIterator pointsIt = m_Contour->GetPoints()->Begin(); m_Polygon->Points->Reset(); m_Polygon->Points->SetNumberOfPoints(numPts); m_Polygon->PointIds->Reset(); m_Polygon->PointIds->SetNumberOfIds(numPts); mitk::Point2D pt2d; mitk::Point3D pt3d; mitk::Point2D min, max; min.Fill(ScalarTypeNumericTraits::max()); max.Fill(ScalarTypeNumericTraits::min()); xProj[2]=0.0; for(i=0, ccur=cstart; iEvaluate(ccur)); m_ProjectionPlane->Map(pt3d, pt2d); xProj[0]=pt2d[0]; if(pt2d[0]max[0]) max[0]=pt2d[0]; xProj[1]=pt2d[1]; if(pt2d[1]max[1]) max[1]=pt2d[1]; m_Polygon->Points->SetPoint(i, xProj); m_Polygon->PointIds->SetId(i, i); } // shift parametric origin to (0,0) for(i=0; im_Polygon->Points->GetPoint(i); pt[0]-=min[0]; pt[1]-=min[1]; itkDebugMacro( << i << ": (" << pt[0] << "," << pt[1] << "," << pt[2] << ")" ); } this->m_Polygon->GetBounds(m_ProjectedContourBounds); //m_ProjectedContourBounds[4]=-1.0; m_ProjectedContourBounds[5]=1.0; // calculate origin (except translation along the normal) and bounds // of m_ProjectionPlane: // origin is composed of the minimum x-/y-coordinates of the polygon, // bounds from the extent of the polygon, both after projecting on the plane mitk::Point3D origin; m_ProjectionPlane->Map(min, origin); ScalarType bounds[6]={0, max[0]-min[0], 0, max[1]-min[1], 0, 1}; m_ProjectionPlane->SetBounds(bounds); m_ProjectionPlane->SetOrigin(origin); // Part III: initialize geometry if(m_ClippingGeometry.IsNotNull()) { ScalarType min_dist=ScalarTypeNumericTraits::max(), max_dist=ScalarTypeNumericTraits::min(), dist; unsigned char i; for(i=0; i<8; ++i) { dist = m_ProjectionPlane->SignedDistance( m_ClippingGeometry->GetCornerPoint(i) ); if(distmax_dist) max_dist=dist; } //incorporate translation along the normal into origin origin = origin+m_Vector*min_dist; m_ProjectionPlane->SetOrigin(origin); bounds[5]=max_dist-min_dist; } else bounds[5]=20; itk2vtk(origin, m_Origin); - mitk::Geometry3D::Pointer g3d = GetGeometry( 0 ); + mitk::BaseGeometry::Pointer g3d = GetGeometry( 0 ); assert( g3d.IsNotNull() ); g3d->SetBounds(bounds); g3d->SetIndexToWorldTransform(m_ProjectionPlane->GetIndexToWorldTransform()); - g3d->TransferItkToVtkTransform(); ProportionalTimeGeometry::Pointer timeGeometry = ProportionalTimeGeometry::New(); timeGeometry->Initialize(g3d,1); SetTimeGeometry(timeGeometry); } unsigned long mitk::ExtrudedContour::GetMTime() const { unsigned long latestTime = Superclass::GetMTime(); if(m_Contour.IsNotNull()) { unsigned long localTime; localTime = m_Contour->GetMTime(); if(localTime > latestTime) latestTime = localTime; } return latestTime; } diff --git a/Modules/Segmentation/DataManagement/mitkExtrudedContour.h b/Modules/Segmentation/DataManagement/mitkExtrudedContour.h index d0ac18d482..06396245dd 100644 --- a/Modules/Segmentation/DataManagement/mitkExtrudedContour.h +++ b/Modules/Segmentation/DataManagement/mitkExtrudedContour.h @@ -1,128 +1,128 @@ /*=================================================================== 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 MITKEXTRUDEDCONTOUR_H_HEADER_INCLUDED #define MITKEXTRUDEDCONTOUR_H_HEADER_INCLUDED #include "mitkBoundingObject.h" #include #include #include #include #include class vtkLinearExtrusionFilter; class vtkPlanes; class vtkClipPolyData; class vtkLinearSubdivisionFilter; class vtkTriangleFilter; class vtkDecimatePro; class vtkPolygon; namespace mitk { //##Documentation //## @brief Data class containing a bounding-object created by //## extruding a Contour along a vector //## //## The m_Contour is extruded in the direction m_Vector until //## reaching m_ClippingGeometry. //## @ingroup Data class MitkSegmentation_EXPORT ExtrudedContour : public BoundingObject { public: mitkClassMacro(ExtrudedContour, BoundingObject); itkFactorylessNewMacro(Self) itkCloneMacro(Self) virtual mitk::ScalarType GetVolume(); virtual bool IsInside(const Point3D& p) const; virtual void UpdateOutputInformation(); //##Documentation //## @brief Contour to extrude itkGetConstObjectMacro(Contour, mitk::Contour); itkSetObjectMacro(Contour, mitk::Contour); //##Documentation //## @brief Vector to specify the direction of the extrusion mitkGetVectorMacro(Vector, mitk::Vector3D); mitkSetVectorMacro(Vector, mitk::Vector3D); itkGetConstMacro(AutomaticVectorGeneration, bool); itkSetMacro(AutomaticVectorGeneration, bool); itkBooleanMacro(AutomaticVectorGeneration); //##Documentation //## @brief Optional vector to specify the orientation of the bounding-box mitkGetVectorMacro(RightVector, mitk::Vector3D); mitkSetVectorMacro(RightVector, mitk::Vector3D); //##Documentation //## @brief Optional geometry for clipping the extruded contour - itkGetConstObjectMacro(ClippingGeometry, mitk::Geometry3D); - itkSetObjectMacro(ClippingGeometry, mitk::Geometry3D); + itkGetConstObjectMacro(ClippingGeometry, mitk::BaseGeometry); + itkSetObjectMacro(ClippingGeometry, mitk::BaseGeometry); virtual unsigned long GetMTime() const; protected: ExtrudedContour(); virtual ~ExtrudedContour(); void BuildSurface(); void BuildGeometry(); mitk::Contour::Pointer m_Contour; mitk::Vector3D m_Vector; mitk::Vector3D m_RightVector; - mitk::Geometry3D::Pointer m_ClippingGeometry; + mitk::BaseGeometry::Pointer m_ClippingGeometry; bool m_AutomaticVectorGeneration; vtkPolygon* m_Polygon; #if ((VTK_MAJOR_VERSION > 4) || ((VTK_MAJOR_VERSION==4) && (VTK_MINOR_VERSION>=4) )) double m_ProjectedContourBounds[6]; #else float m_ProjectedContourBounds[6]; #endif mitk::PlaneGeometry::Pointer m_ProjectionPlane; //##Documentation //## @brief For fast projection on plane float m_Right[3]; float m_Down[3]; #if ((VTK_MAJOR_VERSION > 4) || ((VTK_MAJOR_VERSION==4) && (VTK_MINOR_VERSION>=4) )) double m_Normal[3]; #else float m_Normal[3]; #endif float m_Origin[3]; vtkLinearExtrusionFilter* m_ExtrusionFilter; vtkTriangleFilter *m_TriangleFilter; vtkDecimatePro* m_Decimate; vtkLinearSubdivisionFilter* m_SubdivisionFilter; vtkPlanes* m_ClippingBox; vtkClipPolyData* m_ClipPolyDataFilter; itk::TimeStamp m_LastCalculateExtrusionTime; }; } #endif /* MITKEXTRUDEDCONTOUR_H_HEADER_INCLUDED */ diff --git a/Modules/Segmentation/Interactions/mitkFeedbackContourTool.cpp b/Modules/Segmentation/Interactions/mitkFeedbackContourTool.cpp index 5b9f6016e5..be7a8af186 100644 --- a/Modules/Segmentation/Interactions/mitkFeedbackContourTool.cpp +++ b/Modules/Segmentation/Interactions/mitkFeedbackContourTool.cpp @@ -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. ===================================================================*/ #include "mitkFeedbackContourTool.h" #include "mitkToolManager.h" #include "mitkProperties.h" #include "mitkStringProperty.h" #include "mitkColorProperty.h" #include "mitkDataStorage.h" #include "mitkBaseRenderer.h" #include "mitkRenderingManager.h" mitk::FeedbackContourTool::FeedbackContourTool(const char* type) :SegTool2D(type), m_FeedbackContourVisible(false) { m_FeedbackContour = ContourModel::New(); m_FeedbackContour->SetClosed(true); m_FeedbackContourNode = DataNode::New(); m_FeedbackContourNode->SetData( m_FeedbackContour ); m_FeedbackContourNode->SetProperty("name", StringProperty::New("One of FeedbackContourTool's feedback nodes")); m_FeedbackContourNode->SetProperty("visible", BoolProperty::New(true)); m_FeedbackContourNode->SetProperty("helper object", BoolProperty::New(true)); m_FeedbackContourNode->SetProperty("layer", IntProperty::New(1000)); m_FeedbackContourNode->SetProperty("contour.project-onto-plane", BoolProperty::New(false)); m_FeedbackContourNode->SetProperty("contour.width", FloatProperty::New(1.0)); this->Disable3dRendering(); SetFeedbackContourColorDefault(); } mitk::FeedbackContourTool::~FeedbackContourTool() { } void mitk::FeedbackContourTool::SetFeedbackContourColor( float r, float g, float b ) { m_FeedbackContourNode->SetProperty("contour.color", ColorProperty::New(r, g, b)); } void mitk::FeedbackContourTool::SetFeedbackContourColorDefault() { m_FeedbackContourNode->SetProperty("contour.color", ColorProperty::New(0.0/255.0, 255.0/255.0, 0.0/255.0)); } mitk::ContourModel* mitk::FeedbackContourTool::GetFeedbackContour() { return m_FeedbackContour; } void mitk::FeedbackContourTool::SetFeedbackContour(ContourModel& contour) { // begin of temporary fix for 3m3 release this->Disable3dRendering(); //end of temporary fix for 3m3 release m_FeedbackContour = &contour; m_FeedbackContourNode->SetData( m_FeedbackContour ); } void mitk::FeedbackContourTool::SetFeedbackContourVisible(bool visible) { // begin of temporary fix for 3m3 release this->Disable3dRendering(); //end of temporary fix for 3m3 release if ( m_FeedbackContourVisible == visible ) return; // nothing changed if ( DataStorage* storage = m_ToolManager->GetDataStorage() ) { if (visible) { storage->Add( m_FeedbackContourNode ); } else { storage->Remove( m_FeedbackContourNode ); } } m_FeedbackContourVisible = visible; } mitk::ContourModel::Pointer mitk::FeedbackContourTool::ProjectContourTo2DSlice(Image* slice, ContourModel* contourIn3D, bool correctionForIpSegmentation, bool constrainToInside) { return mitk::ContourModelUtils::ProjectContourTo2DSlice(slice, contourIn3D, correctionForIpSegmentation, constrainToInside); } -mitk::ContourModel::Pointer mitk::FeedbackContourTool::BackProjectContourFrom2DSlice(const Geometry3D* sliceGeometry, ContourModel* contourIn2D, bool correctionForIpSegmentation) +mitk::ContourModel::Pointer mitk::FeedbackContourTool::BackProjectContourFrom2DSlice(const BaseGeometry* sliceGeometry, ContourModel* contourIn2D, bool correctionForIpSegmentation) { return mitk::ContourModelUtils::BackProjectContourFrom2DSlice(sliceGeometry, contourIn2D, correctionForIpSegmentation); } void mitk::FeedbackContourTool::FillContourInSlice( ContourModel* projectedContour, Image* sliceImage, int paintingPixelValue ) { this->FillContourInSlice(projectedContour, 0, sliceImage, paintingPixelValue); } void mitk::FeedbackContourTool::FillContourInSlice( ContourModel* projectedContour, unsigned int timeStep, Image* sliceImage, int paintingPixelValue ) { mitk::ContourModelUtils::FillContourInSlice(projectedContour, timeStep, sliceImage, paintingPixelValue); } void mitk::FeedbackContourTool::Disable3dRendering() { // set explicitly visible=false for all 3D renderer (that exist already ...) const RenderingManager::RenderWindowVector& renderWindows = RenderingManager::GetInstance()->GetAllRegisteredRenderWindows(); for (RenderingManager::RenderWindowVector::const_iterator iter = renderWindows.begin(); iter != renderWindows.end(); ++iter) { if ( mitk::BaseRenderer::GetInstance((*iter))->GetMapperID() == BaseRenderer::Standard3D ) //if ( (*iter)->GetRenderer()->GetMapperID() == BaseRenderer::Standard3D ) { m_FeedbackContourNode->SetProperty("visible", BoolProperty::New(false), mitk::BaseRenderer::GetInstance((*iter))); } } } diff --git a/Modules/Segmentation/Interactions/mitkFeedbackContourTool.h b/Modules/Segmentation/Interactions/mitkFeedbackContourTool.h index 999c0488e2..b33fbe4ac6 100644 --- a/Modules/Segmentation/Interactions/mitkFeedbackContourTool.h +++ b/Modules/Segmentation/Interactions/mitkFeedbackContourTool.h @@ -1,110 +1,110 @@ /*=================================================================== 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 mitkFeedbackContourTool_h_Included #define mitkFeedbackContourTool_h_Included #include "mitkCommon.h" #include #include "mitkSegTool2D.h" #include "mitkContourModelUtils.h" #include "mitkContourUtils.h" //TODO remove legacy support #include "mitkImage.h" #include "mitkDataNode.h" #include "mitkImageCast.h" namespace mitk { /** \brief Base class for tools that use a contour for feedback \sa Tool \sa ContourModel \ingroup Interaction \ingroup ToolManagerEtAl Implements helper methods, that might be of use to all kind of 2D segmentation tools that use a contour for user feedback. - Providing a feedback contour that might be added or removed from the visible scene (SetFeedbackContourVisible). - Filling of a contour into a 2D slice These helper methods are actually implemented in ContourUtils now. FeedbackContourTool only forwards such requests. \warning Only to be instantiated by mitk::ToolManager. $Author: nolden $ */ class MitkSegmentation_EXPORT FeedbackContourTool : public SegTool2D { public: mitkClassMacro(FeedbackContourTool, SegTool2D); protected: FeedbackContourTool(); // purposely hidden FeedbackContourTool(const char*); // purposely hidden virtual ~FeedbackContourTool(); ContourModel* GetFeedbackContour(); void SetFeedbackContour(ContourModel&); void Disable3dRendering(); void SetFeedbackContourVisible(bool); /// Provide values from 0.0 (black) to 1.0 (full color) void SetFeedbackContourColor( float r, float g, float b ); void SetFeedbackContourColorDefault(); /** \brief Projects a contour onto an image point by point. Converts from world to index coordinates. \param correctionForIpSegmentation adds 0.5 to x and y index coordinates (difference between ipSegmentation and MITK contours) */ ContourModel::Pointer ProjectContourTo2DSlice(Image* slice, ContourModel* contourIn3D, bool correctionForIpSegmentation = false, bool constrainToInside = true); /** \brief Projects a slice index coordinates of a contour back into world coordinates. \param correctionForIpSegmentation subtracts 0.5 to x and y index coordinates (difference between ipSegmentation and MITK contours) */ - ContourModel::Pointer BackProjectContourFrom2DSlice(const Geometry3D* sliceGeometry, ContourModel* contourIn2D, bool correctionForIpSegmentation = false); + ContourModel::Pointer BackProjectContourFrom2DSlice(const BaseGeometry* sliceGeometry, ContourModel* contourIn2D, bool correctionForIpSegmentation = false); /** \brief Fill a contour in a 2D slice with a specified pixel value. */ void FillContourInSlice( ContourModel* projectedContour, Image* sliceImage, int paintingPixelValue = 1 ); /** \brief Fill a contour in a 2D slice with a specified pixel value at a given time step. */ void FillContourInSlice( ContourModel* projectedContour, unsigned int timeStep, Image* sliceImage, int paintingPixelValue = 1 ); private: ContourModel::Pointer m_FeedbackContour; DataNode::Pointer m_FeedbackContourNode; bool m_FeedbackContourVisible; }; } // namespace #endif diff --git a/Modules/Segmentation/Interactions/mitkPickingTool.cpp b/Modules/Segmentation/Interactions/mitkPickingTool.cpp index 92928d6feb..e4cd87be2d 100644 --- a/Modules/Segmentation/Interactions/mitkPickingTool.cpp +++ b/Modules/Segmentation/Interactions/mitkPickingTool.cpp @@ -1,228 +1,228 @@ /*=================================================================== 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 "mitkPickingTool.h" #include "mitkToolManager.h" #include "mitkProperties.h" #include #include "mitkGlobalInteraction.h" // us #include #include #include #include #include "mitkImageCast.h" #include "mitkImageTimeSelector.h" #include "mitkPointSetInteractor.h" #include "mitkITKImageImport.h" #include "mitkImageAccessByItk.h" #include "mitkImageTimeSelector.h" #include namespace mitk { MITK_TOOL_MACRO(MitkSegmentation_EXPORT, PickingTool, "PickingTool"); } mitk::PickingTool::PickingTool() { m_PointSetNode = mitk::DataNode::New(); m_PointSetNode->GetPropertyList()->SetProperty("name", mitk::StringProperty::New("Picking_Seedpoint")); m_PointSetNode->GetPropertyList()->SetProperty("helper object", mitk::BoolProperty::New(true)); m_PointSet = mitk::PointSet::New(); m_PointSetNode->SetData(m_PointSet); m_SeedPointInteractor = mitk::PointSetInteractor::New("singlepointinteractor", m_PointSetNode); //Watch for point added or modified itk::SimpleMemberCommand::Pointer pointAddedCommand = itk::SimpleMemberCommand::New(); pointAddedCommand->SetCallbackFunction(this, &mitk::PickingTool::OnPointAdded); m_PointSetAddObserverTag = m_PointSet->AddObserver( mitk::PointSetAddEvent(), pointAddedCommand); //create new node for picked region m_ResultNode = mitk::DataNode::New(); // set some properties m_ResultNode->SetProperty("name", mitk::StringProperty::New("result")); m_ResultNode->SetProperty("helper object", mitk::BoolProperty::New(true)); m_ResultNode->SetProperty("color", mitk::ColorProperty::New(0.0,1.0,0.0)); m_ResultNode->SetProperty("layer", mitk::IntProperty::New(1)); m_ResultNode->SetProperty("opacity", mitk::FloatProperty::New(0.7)); } mitk::PickingTool::~PickingTool() { m_PointSet->RemoveObserver(m_PointSetAddObserverTag); } const char** mitk::PickingTool::GetXPM() const { return NULL; } const char* mitk::PickingTool::GetName() const { return "Picking"; } us::ModuleResource mitk::PickingTool::GetIconResource() const { us::Module* module = us::GetModuleContext()->GetModule(); us::ModuleResource resource = module->GetResource("Pick_48x48.png"); return resource; } void mitk::PickingTool::Activated() { //add to datastorage and enable interaction if (!GetDataStorage()->Exists(m_PointSetNode)) GetDataStorage()->Add(m_PointSetNode, GetWorkingData()); mitk::GlobalInteraction::GetInstance()->AddInteractor(m_SeedPointInteractor); // now add result to data tree GetDataStorage()->Add( m_ResultNode, this->GetWorkingData() ); } void mitk::PickingTool::Deactivated() { if (m_PointSet->GetPointSet()->GetNumberOfPoints() != 0) { mitk::Point3D point = m_PointSet->GetPoint(0); mitk::PointOperation* doOp = new mitk::PointOperation(mitk::OpREMOVE, point, 0); m_PointSet->ExecuteOperation(doOp); } //remove from data storage and disable interaction mitk::GlobalInteraction::GetInstance()->RemoveInteractor(m_SeedPointInteractor); GetDataStorage()->Remove(m_PointSetNode); GetDataStorage()->Remove( m_ResultNode); } mitk::DataNode* mitk::PickingTool::GetReferenceData(){ return this->m_ToolManager->GetReferenceData(0); } mitk::DataStorage* mitk::PickingTool::GetDataStorage(){ return this->m_ToolManager->GetDataStorage(); } mitk::DataNode* mitk::PickingTool::GetWorkingData(){ return this->m_ToolManager->GetWorkingData(0); } mitk::DataNode::Pointer mitk::PickingTool::GetPointSetNode() { return m_PointSetNode; } void mitk::PickingTool::OnPointAdded() { //Perform region growing/picking int timeStep = mitk::BaseRenderer::GetInstance( mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget1") )->GetTimeStep(); mitk::PointSet::PointType seedPoint = m_PointSet->GetPointSet(timeStep)->GetPoints()->Begin().Value(); //as we want to pick a region from our segmentation image use the working data from ToolManager mitk::Image::Pointer orgImage = dynamic_cast (m_ToolManager->GetWorkingData(0)->GetData()); if (orgImage.IsNotNull()) { if (orgImage->GetDimension() == 4) { //there may be 4D segmentation data even though we currently don't support that mitk::ImageTimeSelector::Pointer timeSelector = mitk::ImageTimeSelector::New(); timeSelector->SetInput(orgImage); timeSelector->SetTimeNr( timeStep ); timeSelector->UpdateLargestPossibleRegion(); mitk::Image* timedImage = timeSelector->GetOutput(); AccessByItk_2( timedImage , StartRegionGrowing, timedImage->GetGeometry(), seedPoint); } else if (orgImage->GetDimension() == 3) { AccessByItk_2(orgImage, StartRegionGrowing, orgImage->GetGeometry(), seedPoint); } this->m_PointSet->Clear(); } } template - void mitk::PickingTool::StartRegionGrowing(itk::Image* itkImage, mitk::Geometry3D* imageGeometry, mitk::PointSet::PointType seedPoint) + void mitk::PickingTool::StartRegionGrowing(itk::Image* itkImage, mitk::BaseGeometry* imageGeometry, mitk::PointSet::PointType seedPoint) { typedef itk::Image InputImageType; typedef typename InputImageType::IndexType IndexType; typedef itk::ConnectedThresholdImageFilter RegionGrowingFilterType; typename RegionGrowingFilterType::Pointer regionGrower = RegionGrowingFilterType::New(); // convert world coordinates to image indices IndexType seedIndex; imageGeometry->WorldToIndex( seedPoint, seedIndex); //perform region growing in desired segmented region regionGrower->SetInput( itkImage ); regionGrower->AddSeed( seedIndex ); regionGrower->SetLower( 1 ); regionGrower->SetUpper( 255 ); try { regionGrower->Update(); } catch(itk::ExceptionObject &exc) { return; // can't work } catch( ... ) { return; } //Store result and preview mitk::Image::Pointer resultImage = mitk::ImportItkImage(regionGrower->GetOutput(),imageGeometry)->Clone(); m_ResultNode->SetData( resultImage ); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void mitk::PickingTool::ConfirmSegmentation() { //create a new node and store the image from the result node mitk::DataNode::Pointer newNode = mitk::DataNode::New(); newNode->SetProperty("name", mitk::StringProperty::New("Picking_result")); newNode->SetProperty("helper object", mitk::BoolProperty::New(false)); newNode->SetProperty("color", mitk::ColorProperty::New(1.0,0.0,0.0)); newNode->SetProperty("opacity", mitk::FloatProperty::New(1.0)); newNode->SetData(m_ResultNode->GetData()); GetDataStorage()->Add(newNode); //reset result node m_ResultNode->SetData(NULL); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } diff --git a/Modules/Segmentation/Interactions/mitkPickingTool.h b/Modules/Segmentation/Interactions/mitkPickingTool.h index f89e166a86..f094eaa60d 100644 --- a/Modules/Segmentation/Interactions/mitkPickingTool.h +++ b/Modules/Segmentation/Interactions/mitkPickingTool.h @@ -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. ===================================================================*/ #ifndef mitkPickingTool_h_Included #define mitkPickingTool_h_Included #include "mitkCommon.h" #include #include "mitkAutoSegmentationTool.h" #include "mitkDataStorage.h" #include "mitkPointSetInteractor.h" #include "mitkPointSet.h" namespace us { class ModuleResource; } namespace mitk { /** \brief Extracts a single region from a segmentation image and creates a new image with same geometry of the input image. The region is extracted in 3D space. This is done by performing region growing within the desired region. Use shift click to add the seed point. \ingroup ToolManagerEtAl \sa mitk::Tool \sa QmitkInteractiveSegmentation */ class MitkSegmentation_EXPORT PickingTool : public AutoSegmentationTool { public: mitkClassMacro(PickingTool, AutoSegmentationTool); itkFactorylessNewMacro(Self) itkCloneMacro(Self) virtual const char** GetXPM() const; virtual const char* GetName() const; us::ModuleResource GetIconResource() const; virtual void Activated(); virtual void Deactivated(); virtual DataNode::Pointer GetPointSetNode(); mitk::DataNode* GetReferenceData(); mitk::DataNode* GetWorkingData(); mitk::DataStorage* GetDataStorage(); void ConfirmSegmentation(); protected: PickingTool(); // purposely hidden virtual ~PickingTool(); //Callback for point add event of PointSet void OnPointAdded(); //Observer id long m_PointSetAddObserverTag; mitk::DataNode::Pointer m_ResultNode; //itk regrowing template < typename TPixel, unsigned int VImageDimension > - void StartRegionGrowing( itk::Image< TPixel, VImageDimension >* itkImage, mitk::Geometry3D* imageGeometry, mitk::PointSet::PointType seedPoint ); + void StartRegionGrowing( itk::Image< TPixel, VImageDimension >* itkImage, mitk::BaseGeometry* imageGeometry, mitk::PointSet::PointType seedPoint ); //seed point PointSet::Pointer m_PointSet; PointSetInteractor::Pointer m_SeedPointInteractor; DataNode::Pointer m_PointSetNode; }; } // namespace #endif diff --git a/Modules/Segmentation/Interactions/mitkRegionGrow3DTool.cpp b/Modules/Segmentation/Interactions/mitkRegionGrow3DTool.cpp index 8ed1d441af..6ceeaacc45 100644 --- a/Modules/Segmentation/Interactions/mitkRegionGrow3DTool.cpp +++ b/Modules/Segmentation/Interactions/mitkRegionGrow3DTool.cpp @@ -1,439 +1,439 @@ /*=================================================================== 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 "mitkRegionGrow3DTool.h" #include "mitkToolManager.h" #include "mitkRenderingManager.h" #include "mitkLevelWindowProperty.h" #include "mitkPointSetInteractor.h" #include "mitkGlobalInteraction.h" #include "mitkITKImageImport.h" #include "itkImage.h" #include "itkBinaryThresholdImageFilter.h" #include "itkConnectedAdaptiveThresholdImageFilter.h" #include "mitkImageCast.h" #include "mitkImageAccessByItk.h" #include "mitkMaskAndCutRoiImageFilter.h" #include "mitkPadImageFilter.h" #include "mitkRegionGrow3DTool.xpm" namespace mitk { MITK_TOOL_MACRO(MitkSegmentation_EXPORT, RegionGrow3DTool, "RegionGrower 3D"); } mitk::RegionGrow3DTool::RegionGrow3DTool(): Tool("PressMoveRelease"), m_LowerThreshold(-5000), m_UpperThreshold(5000), m_CurrentRGDirectionIsUpwards(false) { CONNECT_FUNCTION( "Release", OnMouseReleased); m_FeedbackNode = DataNode::New(); m_FeedbackNode->SetProperty( "color", ColorProperty::New(1.0, 0.0, 0.0) ); m_FeedbackNode->SetProperty( "texture interpolation", BoolProperty::New(false) ); m_FeedbackNode->SetProperty( "layer", IntProperty::New( 100 ) ); m_FeedbackNode->SetProperty( "levelwindow", LevelWindowProperty::New( LevelWindow(100, 1) ) ); m_FeedbackNode->SetProperty( "name", StringProperty::New("failsafe region grow feedback") ); m_FeedbackNode->SetProperty( "opacity", FloatProperty::New(0.3) ); m_FeedbackNode->SetProperty( "helper object", BoolProperty::New(true) ); m_FeedbackNode->SetVisibility(false); m_PointSetNode = mitk::DataNode::New(); m_PointSetNode->SetName("regiongrow3d pointset"); m_PointSetNode->SetProperty("helper object", mitk::BoolProperty::New(true)); m_PointSetNode->SetProperty("layer", mitk::IntProperty::New(2)); mitk::PointSet::Pointer pointSet = mitk::PointSet::New(); m_PointSetNode->SetData(pointSet); } mitk::RegionGrow3DTool::~RegionGrow3DTool() { } bool mitk::RegionGrow3DTool::OnMouseReleased( StateMachineAction*, InteractionEvent* /*interactionEvent*/ ) { mitk::PointSetInteractor::Pointer interactor = dynamic_cast (m_PointSetNode->GetInteractor()); if (interactor.IsNotNull()) { mitk::PointSet::Pointer pointSet = dynamic_cast (m_PointSetNode->GetData()); if (pointSet.IsNotNull()) { if (pointSet->GetSize() == 1) { //check if we have a valid picture this->RunSegmentation(); SeedButtonToggled.Send(false); } } } return true; } void mitk::RegionGrow3DTool::Activated() { if (m_ToolManager) { m_ToolManager->RoiDataChanged += mitk::MessageDelegate(this, &mitk::RegionGrow3DTool::UpdatePreview); m_OriginalImageNode = m_ToolManager->GetReferenceData(0); m_NodeToProceed = m_OriginalImageNode; if (m_NodeToProceed.IsNotNull()) { SetupPreviewNodeFor(m_NodeToProceed); mitk::Image::Pointer image = dynamic_cast (m_OriginalImageNode->GetData()); if (image.IsNotNull()) { m_RoiMin = image->GetScalarValueMin(); m_RoiMax = image->GetScalarValueMax(); } } else m_ToolManager->ActivateTool(-1); } } void mitk::RegionGrow3DTool::Deactivated() { m_ToolManager->RoiDataChanged -= mitk::MessageDelegate(this, &mitk::RegionGrow3DTool::UpdatePreview); if (mitk::DataStorage* ds = m_ToolManager->GetDataStorage()) { ds->Remove(m_PointSetNode); ds->Remove(m_FeedbackNode); } m_FeedbackNode->SetData(NULL); m_FeedbackNode->SetLevelWindow(NULL); m_FeedbackNode->SetVisibility(false); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } const char* mitk::RegionGrow3DTool::GetName() const { return "RegionGrower 3D"; } const char** mitk::RegionGrow3DTool::GetXPM() const { return mitkRegionGrow3DTool_xpm; } void mitk::RegionGrow3DTool::SetSeedPoint(bool toggled) { if (!m_ToolManager->GetDataStorage()->Exists(m_PointSetNode)) //no pointSet present m_ToolManager->GetDataStorage()->Add(m_PointSetNode, m_OriginalImageNode); //add Interactor if there is none and add it to GlobalInteraction if (toggled == true) // button is down { mitk::PointSetInteractor::Pointer interactor = dynamic_cast (m_PointSetNode->GetInteractor()); if (interactor.IsNull()) { //create a new interactor and add it to node interactor = mitk::PointSetInteractor::New("singlepointinteractorwithoutshiftclick", m_PointSetNode, 1); } mitk::GlobalInteraction::GetInstance()->AddInteractor(interactor); } else { mitk::PointSetInteractor::Pointer interactor = dynamic_cast (m_PointSetNode->GetInteractor()); if (interactor.IsNotNull()) { m_PointSetNode->SetInteractor(NULL); mitk::GlobalInteraction::GetInstance()->RemoveInteractor(interactor); } } } void mitk::RegionGrow3DTool::RunSegmentation() { //safety if no pointSet or pointSet empty mitk::PointSet::Pointer seedPointSet = dynamic_cast (m_PointSetNode->GetData()); if (seedPointSet.IsNull()) { return; } if (!(seedPointSet->GetSize() > 0)) { return; } mitk::PointSet::PointType seedPoint = seedPointSet->GetPointSet()->GetPoints()->Begin().Value(); mitk::Image::Pointer image = dynamic_cast (m_NodeToProceed->GetData()); if (image.IsNotNull()) { m_LowerThreshold = static_cast (m_RoiMin); m_UpperThreshold = static_cast (m_RoiMax); AccessByItk_2(image, StartRegionGrowing, image->GetGeometry(), seedPoint); } } template -void mitk::RegionGrow3DTool::StartRegionGrowing(itk::Image* itkImage, mitk::Geometry3D* imageGeometry, mitk::PointSet::PointType seedPoint) +void mitk::RegionGrow3DTool::StartRegionGrowing(itk::Image* itkImage, mitk::BaseGeometry* imageGeometry, mitk::PointSet::PointType seedPoint) { typedef itk::Image InputImageType; typedef typename InputImageType::IndexType IndexType; typedef itk::ConnectedAdaptiveThresholdImageFilter RegionGrowingFilterType; typename RegionGrowingFilterType::Pointer regionGrower = RegionGrowingFilterType::New(); if ( !imageGeometry->IsInside(seedPoint) ) { return; } IndexType seedIndex; imageGeometry->WorldToIndex( seedPoint, seedIndex);// convert world coordinates to image indices //int seedValue = itkImage->GetPixel(seedIndex); regionGrower->SetInput( itkImage ); regionGrower->AddSeed( seedIndex ); regionGrower->SetLower( m_LowerThreshold ); regionGrower->SetUpper( m_UpperThreshold ); regionGrower->SetGrowingDirectionIsUpwards( m_CurrentRGDirectionIsUpwards ); try { regionGrower->Update(); } catch( ... ) { MITK_ERROR << "Something went wrong!" << endl; return; } m_SeedpointValue = regionGrower->GetSeedpointValue(); //initialize slider if(m_CurrentRGDirectionIsUpwards) { UpperThresholdValueChanged.Send(m_UpperThreshold); LowerThresholdValueChanged.Send(m_SeedpointValue); } else { UpperThresholdValueChanged.Send(m_SeedpointValue); LowerThresholdValueChanged.Send(m_LowerThreshold); } m_DetectedLeakagePoint = regionGrower->GetLeakagePoint(); mitk::Image::Pointer resultImage = mitk::ImportItkImage(regionGrower->GetOutput())->Clone(); m_FeedbackNode->SetData( resultImage ); m_FeedbackNode->SetVisibility(true); InitializeLevelWindow(); } void mitk::RegionGrow3DTool::InitializeLevelWindow() { mitk::LevelWindow tempLevelWindow; m_FeedbackNode->GetLevelWindow(tempLevelWindow, NULL, "levelWindow"); mitk::ScalarType* level = new mitk::ScalarType(0.5); mitk::ScalarType* window = new mitk::ScalarType(1); int upper; if (m_CurrentRGDirectionIsUpwards) { upper = m_UpperThreshold - m_SeedpointValue; } else { upper = m_SeedpointValue - m_LowerThreshold; } tempLevelWindow.SetRangeMinMax(mitk::ScalarType(0), mitk::ScalarType(upper)); tempLevelWindow.SetLevelWindow(*level, *window); m_FeedbackNode->SetLevelWindow(tempLevelWindow, NULL, "levelWindow"); //get the suggested threshold from the detected leakage-point and adjust the slider if (m_CurrentRGDirectionIsUpwards) { SliderValueChanged.Send(m_SeedpointValue + m_DetectedLeakagePoint -1); } else { SliderValueChanged.Send(m_SeedpointValue - m_DetectedLeakagePoint +1); } mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void mitk::RegionGrow3DTool::ChangeLevelWindow(int value) { if (m_FeedbackNode.IsNull()) return; mitk::LevelWindow tempLevelWindow; m_FeedbackNode->GetLevelWindow(tempLevelWindow, NULL, "levelWindow"); //get the levelWindow associated with the preview mitk::ScalarType level;// = this->m_UPPERTHRESHOLD - newValue + 0.5; mitk::ScalarType* window = new mitk::ScalarType(1); //adjust the levelwindow according to the position of the slider (newvalue) if (m_CurrentRGDirectionIsUpwards) { level = m_UpperThreshold - value + 0.5; tempLevelWindow.SetLevelWindow(level, *window); } else { level = value - m_LowerThreshold +0.5; tempLevelWindow.SetLevelWindow(level, *window); } m_FeedbackNode->SetLevelWindow(tempLevelWindow, NULL, "levelWindow"); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void mitk::RegionGrow3DTool::ConfirmSegmentation( std::string name, mitk::Color color) { mitk::DataNode::Pointer new_node = mitk::DataNode::New(); new_node->SetColor(color); new_node->SetName(name); new_node->SetProperty("binary", mitk::BoolProperty::New("true")); mitk::Image* image = dynamic_cast (m_FeedbackNode->GetData()); mitk::LevelWindow tempLevelWindow; m_FeedbackNode->GetLevelWindow( tempLevelWindow, NULL, "levelWindow"); int upperThresholdLabeledImage = (short int) tempLevelWindow.GetRangeMax(); int lowerThresholdLabeledImage = (short int) tempLevelWindow.GetLowerWindowBound() + 1; typedef itk::Image InputImageType; typedef itk::Image SegmentationType; typedef itk::BinaryThresholdImageFilter ThresholdFilterType; ThresholdFilterType::Pointer filter = ThresholdFilterType::New(); InputImageType::Pointer itkImage; mitk::CastToItkImage(image, itkImage); filter->SetInput(itkImage); filter->SetInsideValue(1); filter->SetOutsideValue(0); filter->SetUpperThreshold(upperThresholdLabeledImage); filter->SetLowerThreshold(lowerThresholdLabeledImage); filter->Update(); mitk::Image::Pointer new_image = mitk::Image::New(); mitk::CastToMitkImage(filter->GetOutput(), new_image); //pad to original size if (m_OriginalImageNode.GetPointer() != m_NodeToProceed.GetPointer()) { mitk::PadImageFilter::Pointer padFilter = mitk::PadImageFilter::New(); padFilter->SetInput(0, new_image); padFilter->SetInput(1, dynamic_cast (m_OriginalImageNode->GetData())); padFilter->SetBinaryFilter(true); padFilter->SetUpperThreshold(1); padFilter->SetLowerThreshold(1); padFilter->Update(); new_image = padFilter->GetOutput(); } new_node->SetData(new_image); m_ToolManager->GetDataStorage()->Add(new_node); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); m_ToolManager->ActivateTool(-1); } void mitk::RegionGrow3DTool::CancelSegmentation() { m_ToolManager->ActivateTool(-1); } void mitk::RegionGrow3DTool::SetupPreviewNodeFor( DataNode* nodeToProceed) { if (nodeToProceed) { Image::Pointer image = dynamic_cast( nodeToProceed->GetData() ); if (image.IsNotNull()) { m_FeedbackNode->SetData( image ); int layer(50); nodeToProceed->GetIntProperty("layer", layer); m_FeedbackNode->SetIntProperty("layer", layer+1); m_FeedbackNode->SetLevelWindow(NULL); if (DataStorage* storage = m_ToolManager->GetDataStorage()) { if (storage->Exists(m_FeedbackNode)) storage->Remove(m_FeedbackNode); storage->Add( m_FeedbackNode, nodeToProceed ); } } } mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void mitk::RegionGrow3DTool::UpdatePreview() { typedef itk::Image ItkImageType; typedef itk::Image ItkMaskType; mitk::DataNode::Pointer node = m_ToolManager->GetRoiData(0); if (node.IsNull()) { this->SetupPreviewNodeFor(m_OriginalImageNode); m_NodeToProceed = m_OriginalImageNode; mitk::Image::Pointer image = dynamic_cast (m_OriginalImageNode->GetData()); if (image.IsNotNull()) { m_RoiMin = image->GetScalarValueMin(); m_RoiMax = image->GetScalarValueMax(); } return; } mitk::MaskAndCutRoiImageFilter::Pointer roiFilter = mitk::MaskAndCutRoiImageFilter::New(); roiFilter->SetInput(dynamic_cast (m_NodeToProceed->GetData())); roiFilter->SetRegionOfInterest(node->GetData()); roiFilter->Update(); mitk::DataNode::Pointer new_node = mitk::DataNode::New(); mitk::Image::Pointer tmpImage = roiFilter->GetOutput(); new_node->SetData(tmpImage); m_RoiMax = roiFilter->GetMaxValue(); m_RoiMin = roiFilter->GetMinValue(); this->SetupPreviewNodeFor(new_node); m_NodeToProceed = new_node; //this->RunSegmentation(); } void mitk::RegionGrow3DTool::SetCurrentRGDirectionIsUpwards(bool flag) { m_CurrentRGDirectionIsUpwards = flag; } diff --git a/Modules/Segmentation/Interactions/mitkRegionGrow3DTool.h b/Modules/Segmentation/Interactions/mitkRegionGrow3DTool.h index 26d79fc4a6..190f536cef 100644 --- a/Modules/Segmentation/Interactions/mitkRegionGrow3DTool.h +++ b/Modules/Segmentation/Interactions/mitkRegionGrow3DTool.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 MITKREGIONGROW3DTOOL_H #define MITKREGIONGROW3DTOOL_H #include "mitkTool.h" #include "mitkPointSet.h" #include #include "mitkStateEvent.h" #include "itkImage.h" namespace mitk{ class StateMachineAction; class InteractionEvent; class MitkSegmentation_EXPORT RegionGrow3DTool : public Tool { public: mitkClassMacro(RegionGrow3DTool, Tool); itkFactorylessNewMacro(Self) itkCloneMacro(Self) mitk::Message1 UpperThresholdValueChanged; mitk::Message1 LowerThresholdValueChanged; mitk::Message1 SliderValueChanged; mitk::Message1 SeedButtonToggled; virtual const char* GetName() const; virtual const char** GetXPM() const; virtual void Activated(); virtual void Deactivated(); void RunSegmentation(); void ConfirmSegmentation(std::string name, mitk::Color color); void CancelSegmentation(); void InitializeLevelWindow(); void ChangeLevelWindow(int); void SetSeedPoint(bool); void SetCurrentRGDirectionIsUpwards(bool); protected: RegionGrow3DTool(); virtual ~RegionGrow3DTool(); void SetupPreviewNodeFor(mitk::DataNode* nodeToProceed); void UpdatePreview(); template < typename TPixel, unsigned int VImageDimension > - void StartRegionGrowing( itk::Image< TPixel, VImageDimension >* itkImage, mitk::Geometry3D* imageGeometry, mitk::PointSet::PointType seedPoint ); + void StartRegionGrowing( itk::Image< TPixel, VImageDimension >* itkImage, mitk::BaseGeometry* imageGeometry, mitk::PointSet::PointType seedPoint ); bool OnMouseReleased( StateMachineAction*, InteractionEvent* ); int m_SeedpointValue; mitk::ScalarType m_RoiMax; mitk::ScalarType m_RoiMin; int m_LowerThreshold; int m_UpperThreshold; int m_DetectedLeakagePoint; bool m_CurrentRGDirectionIsUpwards; mitk::DataNode::Pointer m_PointSetNode; mitk::DataNode::Pointer m_FeedbackNode; mitk::DataNode::Pointer m_NodeToProceed; mitk::DataNode::Pointer m_OriginalImageNode; };//class }//namespace #endif diff --git a/Modules/Segmentation/Interactions/mitkRegionGrowingTool.cpp b/Modules/Segmentation/Interactions/mitkRegionGrowingTool.cpp index 05e0dc5fa3..3397d71efe 100644 --- a/Modules/Segmentation/Interactions/mitkRegionGrowingTool.cpp +++ b/Modules/Segmentation/Interactions/mitkRegionGrowingTool.cpp @@ -1,702 +1,702 @@ /*=================================================================== 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 "mitkRegionGrowingTool.h" #include "mitkToolManager.h" #include "mitkOverwriteSliceImageFilter.h" #include "mitkImageDataItem.h" #include "mitkBaseRenderer.h" #include "mitkRenderingManager.h" #include "mitkApplicationCursor.h" #include "ipSegmentation.h" #include "mitkRegionGrowingTool.xpm" #include "mitkOverwriteDirectedPlaneImageFilter.h" #include "mitkExtractDirectedPlaneImageFilterNew.h" // us #include #include #include #include namespace mitk { MITK_TOOL_MACRO(MitkSegmentation_EXPORT, RegionGrowingTool, "Region growing tool"); } #define ROUND(a) ((a)>0 ? (int)((a)+0.5) : -(int)(0.5-(a))) mitk::RegionGrowingTool::RegionGrowingTool() :FeedbackContourTool("PressMoveRelease"), m_LowerThreshold(200), m_UpperThreshold(200), m_InitialLowerThreshold(200), m_InitialUpperThreshold(200), m_ScreenYDifference(0), m_OriginalPicSlice(NULL), m_SeedPointMemoryOffset(0), m_VisibleWindow(0), m_DefaultWindow(0), m_MouseDistanceScaleFactor(0.5), m_LastWorkingSeed(-1), m_FillFeedbackContour(true) { } mitk::RegionGrowingTool::~RegionGrowingTool() { } void mitk::RegionGrowingTool::ConnectActionsAndFunctions() { CONNECT_FUNCTION( "PrimaryButtonPressed", OnMousePressed); CONNECT_FUNCTION( "Move", OnMouseMoved); CONNECT_FUNCTION( "Release", OnMouseReleased); } const char** mitk::RegionGrowingTool::GetXPM() const { return mitkRegionGrowingTool_xpm; } us::ModuleResource mitk::RegionGrowingTool::GetIconResource() const { us::Module* module = us::GetModuleContext()->GetModule(); us::ModuleResource resource = module->GetResource("RegionGrowing_48x48.png"); return resource; } us::ModuleResource mitk::RegionGrowingTool::GetCursorIconResource() const { us::Module* module = us::GetModuleContext()->GetModule(); us::ModuleResource resource = module->GetResource("RegionGrowing_Cursor_32x32.png"); return resource; } const char* mitk::RegionGrowingTool::GetName() const { return "Region Growing"; } void mitk::RegionGrowingTool::Activated() { Superclass::Activated(); } void mitk::RegionGrowingTool::Deactivated() { Superclass::Deactivated(); } /** 1 Determine which slice is clicked into 2 Determine if the user clicked inside or outside of the segmentation 3 Depending on the pixel value under the mouse click position, two different things happen: (separated out into OnMousePressedInside and OnMousePressedOutside) 3.1 Create a skeletonization of the segmentation and try to find a nice cut 3.1.1 Call a ipSegmentation algorithm to create a nice cut 3.1.2 Set the result of this algorithm as the feedback contour 3.2 Initialize region growing 3.2.1 Determine memory offset inside the original image 3.2.2 Determine initial region growing parameters from the level window settings of the image 3.2.3 Perform a region growing (which generates a new feedback contour) */ bool mitk::RegionGrowingTool::OnMousePressed ( StateMachineAction*, InteractionEvent* interactionEvent ) { mitk::InteractionPositionEvent* positionEvent = dynamic_cast( interactionEvent ); //const PositionEvent* positionEvent = dynamic_cast(stateEvent->GetEvent()); if (!positionEvent) return false; m_LastEventSender = positionEvent->GetSender(); m_LastEventSlice = m_LastEventSender->GetSlice(); //ToolLogger::SetVerboseness(3); MITK_DEBUG << "OnMousePressed" << std::endl; if ( FeedbackContourTool::CanHandleEvent(interactionEvent) > 0.0 ) { MITK_DEBUG << "OnMousePressed: FeedbackContourTool says ok" << std::endl; // 1. Find out which slice the user clicked, find out which slice of the toolmanager's reference and working image corresponds to that if (positionEvent) { MITK_DEBUG << "OnMousePressed: got positionEvent" << std::endl; m_ReferenceSlice = FeedbackContourTool::GetAffectedReferenceSlice( positionEvent ); m_WorkingSlice = FeedbackContourTool::GetAffectedWorkingSlice( positionEvent ); if ( m_WorkingSlice.IsNotNull() ) // can't do anything without the segmentation { MITK_DEBUG << "OnMousePressed: got working slice" << std::endl; // 2. Determine if the user clicked inside or outside of the segmentation - const Geometry3D* workingSliceGeometry = m_WorkingSlice->GetGeometry(); + const BaseGeometry* workingSliceGeometry = m_WorkingSlice->GetGeometry(); Point3D mprojectedPointIn2D; workingSliceGeometry->WorldToIndex( positionEvent->GetPositionInWorld(), mprojectedPointIn2D); itk::Index<2> projectedPointInWorkingSlice2D; projectedPointInWorkingSlice2D[0] = static_cast( mprojectedPointIn2D[0] - 0.5 ); projectedPointInWorkingSlice2D[1] = static_cast( mprojectedPointIn2D[1] - 0.5 ); if ( workingSliceGeometry->IsIndexInside( projectedPointInWorkingSlice2D ) ) { MITK_DEBUG << "OnMousePressed: point " << positionEvent->GetPositionInWorld() << " (index coordinates " << projectedPointInWorkingSlice2D << ") IS in working slice" << std::endl; // Convert to ipMITKSegmentationTYPE (because getting pixels relys on that data type) itk::Image< ipMITKSegmentationTYPE, 2 >::Pointer correctPixelTypeImage; CastToItkImage( m_WorkingSlice, correctPixelTypeImage ); assert (correctPixelTypeImage.IsNotNull() ); // possible bug in CastToItkImage ? // direction maxtrix is wrong/broken/not working after CastToItkImage, leading to a failed assertion in // mitk/Core/DataStructures/mitkSlicedGeometry3D.cpp, 479: // virtual void mitk::SlicedGeometry3D::SetSpacing(const mitk::Vector3D&): Assertion `aSpacing[0]>0 && aSpacing[1]>0 && aSpacing[2]>0' failed // solution here: we overwrite it with an unity matrix itk::Image< ipMITKSegmentationTYPE, 2 >::DirectionType imageDirection; imageDirection.SetIdentity(); correctPixelTypeImage->SetDirection(imageDirection); Image::Pointer temporarySlice = Image::New(); // temporarySlice = ImportItkImage( correctPixelTypeImage ); CastToMitkImage( correctPixelTypeImage, temporarySlice ); mitkIpPicDescriptor* workingPicSlice = mitkIpPicNew(); CastToIpPicDescriptor(temporarySlice, workingPicSlice); int initialWorkingOffset = projectedPointInWorkingSlice2D[1] * workingPicSlice->n[0] + projectedPointInWorkingSlice2D[0]; if ( initialWorkingOffset < static_cast( workingPicSlice->n[0] * workingPicSlice->n[1] ) && initialWorkingOffset >= 0 ) { // 3. determine the pixel value under the last click bool inside = static_cast(workingPicSlice->data)[initialWorkingOffset] != 0; m_PaintingPixelValue = inside ? 0 : 1; // if inside, we want to remove a part, otherwise we want to add something if ( m_LastWorkingSeed >= static_cast( workingPicSlice->n[0] * workingPicSlice->n[1] ) || m_LastWorkingSeed < 0 ) { inside = false; } if ( m_ReferenceSlice.IsNotNull() ) { MITK_DEBUG << "OnMousePressed: got reference slice" << std::endl; m_OriginalPicSlice = mitkIpPicNew(); CastToIpPicDescriptor(m_ReferenceSlice, m_OriginalPicSlice); // 3.1. Switch depending on the pixel value if (inside) { OnMousePressedInside( NULL, interactionEvent, workingPicSlice, initialWorkingOffset); } else { OnMousePressedOutside( NULL, interactionEvent); } } } } } } } MITK_DEBUG << "end OnMousePressed" << std::endl; return true; } /** 3.1 Create a skeletonization of the segmentation and try to find a nice cut 3.1.1 Call a ipSegmentation algorithm to create a nice cut 3.1.2 Set the result of this algorithm as the feedback contour */ bool mitk::RegionGrowingTool::OnMousePressedInside( StateMachineAction*, InteractionEvent* interactionEvent, mitkIpPicDescriptor* workingPicSlice, int initialWorkingOffset) { mitk::InteractionPositionEvent* positionEvent = dynamic_cast( interactionEvent ); //const PositionEvent* positionEvent = dynamic_cast(stateEvent->GetEvent()); // checked in OnMousePressed // 3.1.1. Create a skeletonization of the segmentation and try to find a nice cut // apply the skeletonization-and-cut algorithm // generate contour to remove // set m_ReferenceSlice = NULL so nothing will happen during mouse move // remember to fill the contour with 0 in mouserelease mitkIpPicDescriptor* segmentationHistory = ipMITKSegmentationCreateGrowerHistory( workingPicSlice, m_LastWorkingSeed, NULL ); // free again if (segmentationHistory) { tCutResult cutContour = ipMITKSegmentationGetCutPoints( workingPicSlice, segmentationHistory, initialWorkingOffset ); // tCutResult is a ipSegmentation type mitkIpPicFree( segmentationHistory ); if (cutContour.cutIt) { int timestep = positionEvent->GetSender()->GetTimeStep(); // 3.1.2 copy point from float* to mitk::Contour ContourModel::Pointer contourInImageIndexCoordinates = ContourModel::New(); contourInImageIndexCoordinates->Expand(timestep + 1); contourInImageIndexCoordinates->SetClosed(true, timestep); Point3D newPoint; for (int index = 0; index < cutContour.deleteSize; ++index) { newPoint[0] = cutContour.deleteCurve[ 2 * index + 0 ] - 0.5;//correction is needed because the output of the algorithm is center based newPoint[1] = cutContour.deleteCurve[ 2 * index + 1 ] - 0.5;//and we want our contour displayed corner based. newPoint[2] = 0.0; contourInImageIndexCoordinates->AddVertex( newPoint, timestep ); } free(cutContour.traceline); free(cutContour.deleteCurve); // perhaps visualize this for fun? free(cutContour.onGradient); ContourModel::Pointer contourInWorldCoordinates = FeedbackContourTool::BackProjectContourFrom2DSlice( m_WorkingSlice->GetGeometry(), contourInImageIndexCoordinates, true ); // true: sub 0.5 for ipSegmentation correction FeedbackContourTool::SetFeedbackContour( *contourInWorldCoordinates ); FeedbackContourTool::SetFeedbackContourVisible(true); mitk::RenderingManager::GetInstance()->RequestUpdate( positionEvent->GetSender()->GetRenderWindow() ); m_FillFeedbackContour = true; } else { m_FillFeedbackContour = false; } } else { m_FillFeedbackContour = false; } m_ReferenceSlice = NULL; return true; } /** 3.2 Initialize region growing 3.2.1 Determine memory offset inside the original image 3.2.2 Determine initial region growing parameters from the level window settings of the image 3.2.3 Perform a region growing (which generates a new feedback contour) */ bool mitk::RegionGrowingTool::OnMousePressedOutside( StateMachineAction*, InteractionEvent* interactionEvent ) { mitk::InteractionPositionEvent* positionEvent = dynamic_cast( interactionEvent ); //const PositionEvent* positionEvent = dynamic_cast(stateEvent->GetEvent()); // checked in OnMousePressed // 3.2 If we have a reference image, then perform an initial region growing, considering the reference image's level window // if click was outside the image, don't continue - const Geometry3D* sliceGeometry = m_ReferenceSlice->GetGeometry(); + const BaseGeometry* sliceGeometry = m_ReferenceSlice->GetGeometry(); Point3D mprojectedPointIn2D; sliceGeometry->WorldToIndex( positionEvent->GetPositionInWorld(), mprojectedPointIn2D ); itk::Index<2> projectedPointIn2D; projectedPointIn2D[0] = static_cast( mprojectedPointIn2D[0] - 0.5 ); projectedPointIn2D[1] = static_cast( mprojectedPointIn2D[1] - 0.5 ); if ( sliceGeometry->IsIndexInside( mprojectedPointIn2D ) ) { MITK_DEBUG << "OnMousePressed: point " << positionEvent->GetPositionInWorld() << " (index coordinates " << mprojectedPointIn2D << ") IS in reference slice" << std::endl; // 3.2.1 Remember Y cursor position and initial seed point //m_ScreenYPositionAtStart = static_cast(positionEvent->GetDisplayPosition()[1]); m_LastScreenPosition = ApplicationCursor::GetInstance()->GetCursorPosition(); m_ScreenYDifference = 0; m_SeedPointMemoryOffset = projectedPointIn2D[1] * m_OriginalPicSlice->n[0] + projectedPointIn2D[0]; m_LastWorkingSeed = m_SeedPointMemoryOffset; // remember for skeletonization if ( m_SeedPointMemoryOffset < static_cast( m_OriginalPicSlice->n[0] * m_OriginalPicSlice->n[1] ) && m_SeedPointMemoryOffset >= 0 ) { // 3.2.2 Get level window from reference DataNode // Use some logic to determine initial gray value bounds LevelWindow lw(0, 500); m_ToolManager->GetReferenceData(0)->GetLevelWindow(lw); // will fill lw if levelwindow property is present, otherwise won't touch it. ScalarType currentVisibleWindow = lw.GetWindow(); if (!mitk::Equal(currentVisibleWindow, m_VisibleWindow)) { m_InitialLowerThreshold = currentVisibleWindow / 20.0; m_InitialUpperThreshold = currentVisibleWindow / 20.0; m_LowerThreshold = m_InitialLowerThreshold; m_UpperThreshold = m_InitialUpperThreshold; // 3.2.3. Actually perform region growing mitkIpPicDescriptor* result = PerformRegionGrowingAndUpdateContour(positionEvent->GetSender()->GetTimeStep()); ipMITKSegmentationFree( result); // display the contour FeedbackContourTool::SetFeedbackContourVisible(true); mitk::RenderingManager::GetInstance()->RequestUpdate(positionEvent->GetSender()->GetRenderWindow()); m_FillFeedbackContour = true; } } return true; } return false; } /** If in region growing mode (m_ReferenceSlice != NULL), then 1. Calculate the new thresholds from mouse position (relative to first position) 2. Perform a new region growing and update the feedback contour */ bool mitk::RegionGrowingTool::OnMouseMoved( StateMachineAction*, InteractionEvent* interactionEvent ) { if ( FeedbackContourTool::CanHandleEvent(interactionEvent) > 0.0 ) { if ( m_ReferenceSlice.IsNotNull() && m_OriginalPicSlice ) { mitk::InteractionPositionEvent* positionEvent = dynamic_cast( interactionEvent ); //const PositionEvent* positionEvent = dynamic_cast(stateEvent->GetEvent()); if (positionEvent) { ApplicationCursor* cursor = ApplicationCursor::GetInstance(); if (!cursor) return false; m_ScreenYDifference += cursor->GetCursorPosition()[1] - m_LastScreenPosition[1]; cursor->SetCursorPosition( m_LastScreenPosition ); m_LowerThreshold = std::max(0.0, m_InitialLowerThreshold - m_ScreenYDifference * m_MouseDistanceScaleFactor); m_UpperThreshold = std::max(0.0, m_InitialUpperThreshold - m_ScreenYDifference * m_MouseDistanceScaleFactor); // 2. Perform region growing again and show the result mitkIpPicDescriptor* result = PerformRegionGrowingAndUpdateContour(positionEvent->GetSender()->GetTimeStep()); ipMITKSegmentationFree( result ); // 3. Update the contour mitk::RenderingManager::GetInstance()->ForceImmediateUpdate(positionEvent->GetSender()->GetRenderWindow()); } } } return true; } /** If the feedback contour should be filled, then it is done here. (Contour is NOT filled, when skeletonization is done but no nice cut was found) */ bool mitk::RegionGrowingTool::OnMouseReleased( StateMachineAction*, InteractionEvent* interactionEvent ) { if ( FeedbackContourTool::CanHandleEvent(interactionEvent) > 0.0 ) { // 1. If we have a working slice, use the contour to fill a new piece on segmentation on it (or erase a piece that was selected by ipMITKSegmentationGetCutPoints) if ( m_WorkingSlice.IsNotNull() && m_OriginalPicSlice ) { mitk::InteractionPositionEvent* positionEvent = dynamic_cast( interactionEvent ); //const PositionEvent* positionEvent = dynamic_cast(stateEvent->GetEvent()); if (positionEvent) { // remember parameters for next time m_InitialLowerThreshold = m_LowerThreshold; m_InitialUpperThreshold = m_UpperThreshold; int timestep = positionEvent->GetSender()->GetTimeStep(); if (m_FillFeedbackContour) { // 3. use contour to fill a region in our working slice ContourModel* feedbackContour( FeedbackContourTool::GetFeedbackContour() ); if (feedbackContour) { ContourModel::Pointer projectedContour = FeedbackContourTool::ProjectContourTo2DSlice( m_WorkingSlice, feedbackContour, false, false ); // false: don't add any 0.5 // false: don't constrain the contour to the image's inside if (projectedContour.IsNotNull()) { FeedbackContourTool::FillContourInSlice( projectedContour, timestep, m_WorkingSlice, m_PaintingPixelValue ); const PlaneGeometry* planeGeometry( dynamic_cast (positionEvent->GetSender()->GetCurrentWorldGeometry2D() ) ); //MITK_DEBUG << "OnMouseReleased: writing back to dimension " << affectedDimension << ", slice " << affectedSlice << " in working image" << std::endl; // 4. write working slice back into image volume this->WriteBackSegmentationResult(positionEvent, m_WorkingSlice); } } } FeedbackContourTool::SetFeedbackContourVisible(false); mitk::RenderingManager::GetInstance()->RequestUpdate( positionEvent->GetSender()->GetRenderWindow() ); } } } m_ReferenceSlice = NULL; // don't leak m_WorkingSlice = NULL; m_OriginalPicSlice = NULL; return true; } /** Uses ipSegmentation algorithms to do the actual region growing. The result (binary image) is first smoothed by a 5x5 circle mask, then its contour is extracted and converted to MITK coordinates. */ mitkIpPicDescriptor* mitk::RegionGrowingTool::PerformRegionGrowingAndUpdateContour(int timestep) { // 1. m_OriginalPicSlice and m_SeedPointMemoryOffset are set to sensitive values, as well as m_LowerThreshold and m_UpperThreshold assert (m_OriginalPicSlice); if (m_OriginalPicSlice->n[0] != 256 || m_OriginalPicSlice->n[1] != 256) // ??? assert( (m_SeedPointMemoryOffset < static_cast( m_OriginalPicSlice->n[0] * m_OriginalPicSlice->n[1] )) && (m_SeedPointMemoryOffset >= 0) ); // inside the image // 2. ipSegmentation is used to perform region growing float ignored; int oneContourOffset( 0 ); mitkIpPicDescriptor* regionGrowerResult = ipMITKSegmentationGrowRegion4N( m_OriginalPicSlice, m_SeedPointMemoryOffset, // seed point true, // grayvalue interval relative to seed point gray value? m_LowerThreshold, m_UpperThreshold, 0, // continue until done (maxIterations == 0) NULL, // allocate new memory (only this time, on mouse move we'll reuse the old buffer) oneContourOffset, // a pixel that is near the resulting contour ignored // ignored by us ); if (!regionGrowerResult || oneContourOffset == -1) { ContourModel::Pointer dummyContour = ContourModel::New(); dummyContour->Initialize(); FeedbackContourTool::SetFeedbackContour( *dummyContour ); if (regionGrowerResult) ipMITKSegmentationFree(regionGrowerResult); return NULL; } // 3. We smooth the result a little to reduce contour complexity bool smoothResult( true ); // currently fixed, perhaps remove else block mitkIpPicDescriptor* smoothedRegionGrowerResult; if (smoothResult) { // Smooth the result (otherwise very detailed contour) smoothedRegionGrowerResult = SmoothIPPicBinaryImage( regionGrowerResult, oneContourOffset ); ipMITKSegmentationFree( regionGrowerResult ); } else { smoothedRegionGrowerResult = regionGrowerResult; } // 4. convert the result of region growing into a mitk::Contour // At this point oneContourOffset could be useless, if smoothing destroyed a thin bridge. In these // cases, we have two or more unconnected segmentation regions, and we don't know, which one is touched by oneContourOffset. // In the bad case, the contour is not the one around our seedpoint, so the result looks very strange to the user. // -> we remove the point where the contour started so far. Then we look from the bottom of the image for the first segmentation pixel // and start another contour extraction from there. This is done, until the seedpoint is inside the contour int numberOfContourPoints( 0 ); int newBufferSize( 0 ); float* contourPoints = ipMITKSegmentationGetContour8N( smoothedRegionGrowerResult, oneContourOffset, numberOfContourPoints, newBufferSize ); // memory allocated with malloc if (contourPoints) { while ( !ipMITKSegmentationIsInsideContour( contourPoints, // contour numberOfContourPoints, // points in contour m_SeedPointMemoryOffset % smoothedRegionGrowerResult->n[0], // test point x m_SeedPointMemoryOffset / smoothedRegionGrowerResult->n[0] // test point y ) ) { // we decide that this cannot be part of the segmentation because the seedpoint is not contained in the contour (fill the 4-neighborhood with 0) ipMITKSegmentationReplaceRegion4N( smoothedRegionGrowerResult, oneContourOffset, 0 ); // move the contour offset to the last row (x position of the seed point) int rowLength = smoothedRegionGrowerResult->n[0]; // number of pixels in a row oneContourOffset = m_SeedPointMemoryOffset % smoothedRegionGrowerResult->n[0] // x of seed point + rowLength*(smoothedRegionGrowerResult->n[1]-1); // y of last row while ( oneContourOffset >=0 && (*(static_cast(smoothedRegionGrowerResult->data) + oneContourOffset) == 0) ) { oneContourOffset -= rowLength; // if pixel at data+oneContourOffset is 0, then move up one row } if ( oneContourOffset < 0 ) { break; // just use the last contour we found } free(contourPoints); // release contour memory contourPoints = ipMITKSegmentationGetContour8N( smoothedRegionGrowerResult, oneContourOffset, numberOfContourPoints, newBufferSize ); // memory allocated with malloc } // copy point from float* to mitk::Contour ContourModel::Pointer contourInImageIndexCoordinates = ContourModel::New(); contourInImageIndexCoordinates->Expand(timestep + 1); contourInImageIndexCoordinates->SetClosed(true, timestep); Point3D newPoint; for (int index = 0; index < numberOfContourPoints; ++index) { newPoint[0] = contourPoints[ 2 * index + 0 ] - 0.5;//correction is needed because the output of the algorithm is center based newPoint[1] = contourPoints[ 2 * index + 1 ] - 0.5;//and we want our contour displayed corner based. newPoint[2] = 0; contourInImageIndexCoordinates->AddVertex( newPoint, timestep ); } free(contourPoints); ContourModel::Pointer contourInWorldCoordinates = FeedbackContourTool::BackProjectContourFrom2DSlice( m_ReferenceSlice->GetGeometry(), contourInImageIndexCoordinates, true ); // true: sub 0.5 for ipSegmentation correctio FeedbackContourTool::SetFeedbackContour( *contourInWorldCoordinates ); } // 5. Result HAS TO BE freed by caller, contains the binary region growing result return smoothedRegionGrowerResult; } /** Helper method for SmoothIPPicBinaryImage. Smoothes a given part of and image. \param sourceImage The original binary image. \param dest The smoothed image (will be written without bounds checking). \param contourOfs One offset of the contour. Is updated if a pixel is changed (which might change the contour). \param maskOffsets Memory offsets that describe the smoothing mask. \param maskSize Entries of the mask. \param startOffset First pixel that should be smoothed using this mask. \param endOffset Last pixel that should be smoothed using this mask. */ void mitk::RegionGrowingTool::SmoothIPPicBinaryImageHelperForRows( mitkIpPicDescriptor* sourceImage, mitkIpPicDescriptor* dest, int &contourOfs, int* maskOffsets, int maskSize, int startOffset, int endOffset ) { // work on the very first row ipMITKSegmentationTYPE* current; ipMITKSegmentationTYPE* source = ((ipMITKSegmentationTYPE*)sourceImage->data) + startOffset; // + 1! don't read at start-1 ipMITKSegmentationTYPE* end = ((ipMITKSegmentationTYPE*)dest->data) + endOffset; int ofs = startOffset; int minority = (maskSize - 1) / 2; for (current = ((ipMITKSegmentationTYPE*)dest->data) + startOffset; current minority) { *current = 1; contourOfs = ofs; } else { *current = 0; } ++source; ++ofs; } } /** Smoothes a binary ipPic image with a 5x5 mask. The image borders (some first and last rows) are treated differently. */ mitkIpPicDescriptor* mitk::RegionGrowingTool::SmoothIPPicBinaryImage( mitkIpPicDescriptor* image, int &contourOfs, mitkIpPicDescriptor* dest ) { if (!image) return NULL; // Original code from /trunk/mbi-qm/Qmitk/Qmitk2DSegTools/RegionGrowerTool.cpp (first version by T. Boettger?). Reformatted and documented and restructured. #define MSK_SIZE5x5 21 #define MSK_SIZE3x3 5 #define MSK_SIZE3x1 3 // mask is an array of coordinates that form a rastered circle like this // // OOO // OOOOO // OOOOO // OOOOO // OOO // // int mask5x5[MSK_SIZE5x5][2] = { /******/ {-1,-2}, {0,-2}, {1,-2}, /*****/ {-2,-1}, {-1,-1}, {0,-1}, {1,-1}, {2,-1}, {-2, 0}, {-1, 0}, {0, 0}, {1, 0}, {2, 0}, {-2, 1}, {-1, 1}, {0, 1}, {1, 1}, {2, 1}, /******/ {-1, 2}, {0, 2}, {1, 2} /*****/ }; int mask3x3[MSK_SIZE3x3][2] = { /******/ {0,-1}, /*****/ {-1, 0}, {0, 0}, {1, 0}, /******/ {0, 1} /*****/ }; int mask3x1[MSK_SIZE3x1][2] = { {-1, 0}, {0, 0}, {1, 0} }; // The following lines iterate over all the pixels of a (sliced) image (except the first and last three rows). // For each pixel, all the coordinates around it (according to mask) are evaluated (this means 21 pixels). // If more than 10 of the evaluated pixels are non-zero, then the central pixel is set to 1, else to 0. // This is determining a majority. If there is no clear majority, then the central pixel itself "decides". int maskOffset5x5[MSK_SIZE5x5]; int line = image->n[0]; for (int i=0; in[0]; int spareOut1Rows = 1*image->n[0]; if ( image->n[1] > 0 ) SmoothIPPicBinaryImageHelperForRows( image, dest, contourOfs, maskOffset3x1, MSK_SIZE3x1, 1, dest->n[0] ); if ( image->n[1] > 3 ) SmoothIPPicBinaryImageHelperForRows( image, dest, contourOfs, maskOffset3x3, MSK_SIZE3x3, spareOut1Rows, dest->n[0]*3 ); if ( image->n[1] > 6 ) SmoothIPPicBinaryImageHelperForRows( image, dest, contourOfs, maskOffset5x5, MSK_SIZE5x5, spareOut3Rows, dest->n[0]*dest->n[1] - spareOut3Rows ); if ( image->n[1] > 8 ) SmoothIPPicBinaryImageHelperForRows( image, dest, contourOfs, maskOffset3x3, MSK_SIZE3x3, dest->n[0]*dest->n[1] -spareOut3Rows, dest->n[0]*dest->n[1] - spareOut1Rows ); if ( image->n[1] > 10) SmoothIPPicBinaryImageHelperForRows( image, dest, contourOfs, maskOffset3x1, MSK_SIZE3x1, dest->n[0]*dest->n[1] -spareOut1Rows, dest->n[0]*dest->n[1] - 1 ); // correction for first pixel (sorry for the ugliness) if ( *((ipMITKSegmentationTYPE*)(dest->data)+1) == 1 ) { *((ipMITKSegmentationTYPE*)(dest->data)+0) = 1; } if (dest->n[0] * dest->n[1] > 2) { // correction for last pixel if ( *((ipMITKSegmentationTYPE*)(dest->data)+dest->n[0]*dest->n[1]-2) == 1 ) { *((ipMITKSegmentationTYPE*)(dest->data)+dest->n[0]*dest->n[1]-1) = 1; } } return dest; } diff --git a/Modules/Segmentation/Interactions/mitkSegTool2D.cpp b/Modules/Segmentation/Interactions/mitkSegTool2D.cpp index 5ef3294046..51dcba5d8f 100644 --- a/Modules/Segmentation/Interactions/mitkSegTool2D.cpp +++ b/Modules/Segmentation/Interactions/mitkSegTool2D.cpp @@ -1,407 +1,407 @@ /*=================================================================== 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( InteractionEvent const *stateEvent) const { const InteractionPositionEvent* positionEvent = dynamic_cast( stateEvent ); if (!positionEvent) return 0.0; if ( positionEvent->GetSender()->GetMapperID() != BaseRenderer::Standard2D ) return 0.0; // we don't want anything but 2D return 1.0; // //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 - Geometry3D* imageGeometry = image->GetGeometry(0); + 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 InteractionPositionEvent* 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)); + m_undoOperation = new DiffSliceOperation(const_cast(image), extractor->GetVtkOutput(), dynamic_cast(slice->GetGeometry()), timeStep, const_cast(planeGeometry)); /*============= END undo feature block ========================*/ return slice; } mitk::Image::Pointer mitk::SegTool2D::GetAffectedWorkingSlice(const InteractionPositionEvent* 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 InteractionPositionEvent* 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 InteractionPositionEvent* 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)); + m_doOperation = new DiffSliceOperation(image, extractor->GetVtkOutput(),dynamic_cast(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 InteractionPositionEvent* 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; } diff --git a/Modules/Segmentation/Interactions/mitkSetRegionTool.cpp b/Modules/Segmentation/Interactions/mitkSetRegionTool.cpp index adb20b9dba..5a5fec1b95 100644 --- a/Modules/Segmentation/Interactions/mitkSetRegionTool.cpp +++ b/Modules/Segmentation/Interactions/mitkSetRegionTool.cpp @@ -1,330 +1,330 @@ /*=================================================================== 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 "mitkSetRegionTool.h" #include "mitkToolManager.h" #include "mitkOverwriteSliceImageFilter.h" #include "ipSegmentation.h" #include "mitkBaseRenderer.h" #include "mitkImageDataItem.h" #include "mitkLegacyAdaptors.h" #include "mitkOverwriteDirectedPlaneImageFilter.h" mitk::SetRegionTool::SetRegionTool(int paintingPixelValue) :FeedbackContourTool("PressMoveReleaseWithCTRLInversion"), m_PaintingPixelValue(paintingPixelValue), m_FillContour(false), m_StatusFillWholeSlice(false) { } mitk::SetRegionTool::~SetRegionTool() { } void mitk::SetRegionTool::ConnectActionsAndFunctions() { CONNECT_FUNCTION( "PrimaryButtonPressed", OnMousePressed); CONNECT_FUNCTION( "Release", OnMouseReleased); CONNECT_FUNCTION( "InvertLogic", OnInvertLogic); } void mitk::SetRegionTool::Activated() { Superclass::Activated(); } void mitk::SetRegionTool::Deactivated() { Superclass::Deactivated(); } bool mitk::SetRegionTool::OnMousePressed ( StateMachineAction*, InteractionEvent* interactionEvent ) { mitk::InteractionPositionEvent* positionEvent = dynamic_cast( interactionEvent ); //const PositionEvent* positionEvent = dynamic_cast(stateEvent->GetEvent()); if (!positionEvent) return false; m_LastEventSender = positionEvent->GetSender(); m_LastEventSlice = m_LastEventSender->GetSlice(); int timeStep = positionEvent->GetSender()->GetTimeStep(); if ( FeedbackContourTool::CanHandleEvent(interactionEvent) < 1.0 ) return false; // 1. Get the working image Image::Pointer workingSlice = FeedbackContourTool::GetAffectedWorkingSlice( positionEvent ); if ( workingSlice.IsNull() ) return false; // can't do anything without the segmentation // if click was outside the image, don't continue - const Geometry3D* sliceGeometry = workingSlice->GetGeometry(); + const BaseGeometry* sliceGeometry = workingSlice->GetGeometry(); itk::Index<2> projectedPointIn2D; sliceGeometry->WorldToIndex( positionEvent->GetPositionInWorld(), projectedPointIn2D ); if ( !sliceGeometry->IsIndexInside( projectedPointIn2D ) ) { MITK_ERROR << "point apparently not inside segmentation slice" << std::endl; return false; // can't use that as a seed point } // Convert to ipMITKSegmentationTYPE (because ipMITKSegmentationGetContour8N relys on that data type) itk::Image< ipMITKSegmentationTYPE, 2 >::Pointer correctPixelTypeImage; CastToItkImage( workingSlice, correctPixelTypeImage ); assert (correctPixelTypeImage.IsNotNull() ); // possible bug in CastToItkImage ? // direction maxtrix is wrong/broken/not working after CastToItkImage, leading to a failed assertion in // mitk/Core/DataStructures/mitkSlicedGeometry3D.cpp, 479: // virtual void mitk::SlicedGeometry3D::SetSpacing(const mitk::Vector3D&): Assertion `aSpacing[0]>0 && aSpacing[1]>0 && aSpacing[2]>0' failed // solution here: we overwrite it with an unity matrix itk::Image< ipMITKSegmentationTYPE, 2 >::DirectionType imageDirection; imageDirection.SetIdentity(); correctPixelTypeImage->SetDirection(imageDirection); Image::Pointer temporarySlice = Image::New(); // temporarySlice = ImportItkImage( correctPixelTypeImage ); CastToMitkImage( correctPixelTypeImage, temporarySlice ); // check index positions mitkIpPicDescriptor* originalPicSlice = mitkIpPicNew(); CastToIpPicDescriptor( temporarySlice, originalPicSlice ); int m_SeedPointMemoryOffset = projectedPointIn2D[1] * originalPicSlice->n[0] + projectedPointIn2D[0]; if ( m_SeedPointMemoryOffset >= static_cast( originalPicSlice->n[0] * originalPicSlice->n[1] ) || m_SeedPointMemoryOffset < 0 ) { MITK_ERROR << "Memory offset calculation if mitk::SetRegionTool has some serious flaw! Aborting.." << std::endl; return false; } // 2. Determine the contour that surronds the selected "piece of the image" // find a contour seed point unsigned int oneContourOffset = static_cast( m_SeedPointMemoryOffset ); // safe because of earlier check if m_SeedPointMemoryOffset < 0 /** * The logic of finding a starting point for the contour is the following: * * - If the initial seed point is 0, we are either inside a hole or outside of every segmentation. * We move to the right until we hit a 1, which must be part of a contour. * * - If the initial seed point is 1, then ... * we now do the same (running to the right) until we hit a 1 * * In both cases the found contour point is used to extract a contour and * then a test is applied to find out if the initial seed point is contained * in the contour. If this is the case, filling should be applied, otherwise * nothing is done. */ unsigned int size = originalPicSlice->n[0] * originalPicSlice->n[1]; /* unsigned int rowSize = originalPicSlice->n[0]; */ ipMITKSegmentationTYPE* data = static_cast(originalPicSlice->data); if ( data[oneContourOffset] == 0 ) // initial seed 0 { for ( ; oneContourOffset < size; ++oneContourOffset ) { if ( data[oneContourOffset] > 0 ) break; } } else if ( data[oneContourOffset] == 1 ) // initial seed 1 { unsigned int lastValidPixel = size-1; // initialization, will be changed lateron bool inSeg = true; // inside segmentation? for ( ; oneContourOffset < size; ++oneContourOffset ) { if ( ( data[oneContourOffset] == 0 ) && inSeg ) // pixel 0 and inside-flag set: this happens at the first pixel outside a filled region { inSeg = false; lastValidPixel = oneContourOffset - 1; // store the last pixel position inside a filled region break; } else // pixel 1, inside-flag doesn't matter: this happens while we are inside a filled region { inSeg = true; // first iteration lands here } } oneContourOffset = lastValidPixel; } else { MITK_ERROR << "Fill/Erase was never intended to work with other than binary images." << std::endl; m_FillContour = false; return false; } if (oneContourOffset == size) // nothing found until end of slice { m_FillContour = false; return false; } int numberOfContourPoints( 0 ); int newBufferSize( 0 ); //MITK_INFO << "getting contour from offset " << oneContourOffset << " ("<n[0]<<","<n[0]<<")"< 0); bool cursorInsideContour = ipMITKSegmentationIsInsideContour( contourPoints, numberOfContourPoints, projectedPointIn2D[0], projectedPointIn2D[1]); // decide if contour should be filled or not m_FillContour = cursorInsideContour; if (m_FillContour) { // copy point from float* to mitk::Contour ContourModel::Pointer contourInImageIndexCoordinates = ContourModel::New(); contourInImageIndexCoordinates->Expand(timeStep + 1); contourInImageIndexCoordinates->SetClosed(true, timeStep); Point3D newPoint; for (int index = 0; index < numberOfContourPoints; ++index) { newPoint[0] = contourPoints[ 2 * index + 0 ] - 0.5; newPoint[1] = contourPoints[ 2 * index + 1] - 0.5; newPoint[2] = 0; contourInImageIndexCoordinates->AddVertex(newPoint, timeStep); } m_SegmentationContourInWorldCoordinates = FeedbackContourTool::BackProjectContourFrom2DSlice( workingSlice->GetGeometry(), contourInImageIndexCoordinates, true ); // true, correct the result from ipMITKSegmentationGetContour8N // 3. Show the contour FeedbackContourTool::SetFeedbackContour( *m_SegmentationContourInWorldCoordinates ); FeedbackContourTool::SetFeedbackContourVisible(true); mitk::RenderingManager::GetInstance()->RequestUpdate(positionEvent->GetSender()->GetRenderWindow()); } // always generate a second contour, containing the whole image (used when CTRL is pressed) { // copy point from float* to mitk::Contour ContourModel::Pointer contourInImageIndexCoordinates = ContourModel::New(); contourInImageIndexCoordinates->Expand(timeStep + 1); contourInImageIndexCoordinates->SetClosed(true, timeStep); Point3D newPoint; newPoint[0] = 0; newPoint[1] = 0; newPoint[2] = 0.0; contourInImageIndexCoordinates->AddVertex( newPoint, timeStep ); newPoint[0] = originalPicSlice->n[0]; newPoint[1] = 0; newPoint[2] = 0.0; contourInImageIndexCoordinates->AddVertex( newPoint, timeStep ); newPoint[0] = originalPicSlice->n[0]; newPoint[1] = originalPicSlice->n[1]; newPoint[2] = 0.0; contourInImageIndexCoordinates->AddVertex( newPoint, timeStep ); newPoint[0] = 0; newPoint[1] = originalPicSlice->n[1]; newPoint[2] = 0.0; contourInImageIndexCoordinates->AddVertex( newPoint, timeStep ); m_WholeImageContourInWorldCoordinates = FeedbackContourTool::BackProjectContourFrom2DSlice( workingSlice->GetGeometry(), contourInImageIndexCoordinates, true ); // true, correct the result from ipMITKSegmentationGetContour8N // 3. Show the contour FeedbackContourTool::SetFeedbackContour( *m_SegmentationContourInWorldCoordinates ); FeedbackContourTool::SetFeedbackContourVisible(true); mitk::RenderingManager::GetInstance()->RequestUpdate(positionEvent->GetSender()->GetRenderWindow()); } free(contourPoints); return true; } bool mitk::SetRegionTool::OnMouseReleased( StateMachineAction*, InteractionEvent* interactionEvent ) { // 1. Hide the feedback contour, find out which slice the user clicked, find out which slice of the toolmanager's working image corresponds to that FeedbackContourTool::SetFeedbackContourVisible(false); mitk::InteractionPositionEvent* positionEvent = dynamic_cast( interactionEvent ); //const PositionEvent* positionEvent = dynamic_cast(stateEvent->GetEvent()); if (!positionEvent) return false; assert( positionEvent->GetSender()->GetRenderWindow() ); mitk::RenderingManager::GetInstance()->RequestUpdate(positionEvent->GetSender()->GetRenderWindow()); int timeStep = positionEvent->GetSender()->GetTimeStep(); if (!m_FillContour && !m_StatusFillWholeSlice) return true; if ( FeedbackContourTool::CanHandleEvent(interactionEvent) < 1.0 ) return false; DataNode* workingNode( m_ToolManager->GetWorkingData(0) ); if (!workingNode) return false; Image* image = dynamic_cast(workingNode->GetData()); const PlaneGeometry* planeGeometry( dynamic_cast (positionEvent->GetSender()->GetCurrentWorldGeometry2D() ) ); if ( !image || !planeGeometry ) return false; Image::Pointer slice = FeedbackContourTool::GetAffectedImageSliceAs2DImage( positionEvent, image ); if ( slice.IsNull() ) { MITK_ERROR << "Unable to extract slice." << std::endl; return false; } ContourModel* feedbackContour( FeedbackContourTool::GetFeedbackContour() ); ContourModel::Pointer projectedContour = FeedbackContourTool::ProjectContourTo2DSlice( slice, feedbackContour, false, false ); // false: don't add 0.5 (done by FillContourInSlice) // false: don't constrain the contour to the image's inside if (projectedContour.IsNull()) return false; FeedbackContourTool::FillContourInSlice( projectedContour, timeStep, slice, m_PaintingPixelValue ); this->WriteBackSegmentationResult(positionEvent, slice); m_WholeImageContourInWorldCoordinates = NULL; m_SegmentationContourInWorldCoordinates = NULL; return true; } /** Called when the CTRL key is pressed. Will change the painting pixel value from 0 to 1 or from 1 to 0. */ bool mitk::SetRegionTool::OnInvertLogic( StateMachineAction*, InteractionEvent* interactionEvent ) { if ( FeedbackContourTool::CanHandleEvent(interactionEvent) < 1.0 ) return false; mitk::InteractionPositionEvent* positionEvent = dynamic_cast( interactionEvent ); //const PositionEvent* positionEvent = dynamic_cast(stateEvent->GetEvent()); if (!positionEvent) return false; if (m_StatusFillWholeSlice) { // use contour extracted from image data if (m_SegmentationContourInWorldCoordinates.IsNotNull()) FeedbackContourTool::SetFeedbackContour( *m_SegmentationContourInWorldCoordinates ); mitk::RenderingManager::GetInstance()->RequestUpdate(positionEvent->GetSender()->GetRenderWindow()); } else { // use some artificial contour if (m_WholeImageContourInWorldCoordinates.IsNotNull()) FeedbackContourTool::SetFeedbackContour( *m_WholeImageContourInWorldCoordinates ); mitk::RenderingManager::GetInstance()->RequestUpdate(positionEvent->GetSender()->GetRenderWindow()); } m_StatusFillWholeSlice = !m_StatusFillWholeSlice; return true; } diff --git a/Modules/SegmentationUI/Qmitk/QmitkAdaptiveRegionGrowingToolGUI.cpp b/Modules/SegmentationUI/Qmitk/QmitkAdaptiveRegionGrowingToolGUI.cpp index 93c4af6042..cb50c0513c 100644 --- a/Modules/SegmentationUI/Qmitk/QmitkAdaptiveRegionGrowingToolGUI.cpp +++ b/Modules/SegmentationUI/Qmitk/QmitkAdaptiveRegionGrowingToolGUI.cpp @@ -1,848 +1,848 @@ /*=================================================================== 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 "QmitkAdaptiveRegionGrowingToolGUI.h" #include "QmitkStdMultiWidget.h" #include #include "mitkNodePredicateDataType.h" #include "mitkGlobalInteraction.h" #include "mitkPointSetInteractor.h" #include "mitkProperties.h" #include "mitkITKImageImport.h" #include "mitkImageAccessByItk.h" #include "mitkTransferFunctionProperty.h" #include "mitkImageTimeSelector.h" #include "mitkImageStatisticsHolder.h" #include #include #include #include #include "itkOrImageFilter.h" #include "mitkImageCast.h" #include "QmitkConfirmSegmentationDialog.h" #include "mitkPixelTypeMultiplex.h" #include "mitkImagePixelReadAccessor.h" MITK_TOOL_GUI_MACRO( , QmitkAdaptiveRegionGrowingToolGUI, "") QmitkAdaptiveRegionGrowingToolGUI::QmitkAdaptiveRegionGrowingToolGUI(QWidget* parent) : QmitkToolGUI(), m_MultiWidget(NULL), m_UseVolumeRendering(false), m_UpdateSuggestedThreshold(true), m_SuggestedThValue(0.0), m_DataStorage(NULL) { this->setParent(parent); m_Controls.setupUi(this); m_Controls.m_ThresholdSlider->setDecimals(1); m_Controls.m_ThresholdSlider->setSpinBoxAlignment(Qt::AlignVCenter); m_Controls.m_PreviewSlider->setEnabled(false); m_Controls.m_PreviewSlider->setSingleStep(0.5); //Not yet available //m_Controls.m_PreviewSlider->InvertedAppearance(true); this->CreateConnections(); this->SetDataNodeNames("labeledRGSegmentation","RGResult","RGFeedbackSurface"); connect( this, SIGNAL(NewToolAssociated(mitk::Tool*)), this, SLOT(OnNewToolAssociated(mitk::Tool*)) ); } QmitkAdaptiveRegionGrowingToolGUI::~QmitkAdaptiveRegionGrowingToolGUI() { //Removing the observer of the PointSet node if (m_RegionGrow3DTool->GetPointSetNode().IsNotNull()) { m_RegionGrow3DTool->GetPointSetNode()->GetData()->RemoveObserver(m_PointSetAddObserverTag); } this->RemoveHelperNodes(); } void QmitkAdaptiveRegionGrowingToolGUI::OnNewToolAssociated(mitk::Tool* tool) { m_RegionGrow3DTool = dynamic_cast (tool); if(m_RegionGrow3DTool.IsNotNull()) { SetInputImageNode( this->m_RegionGrow3DTool->GetReferenceData() ); this->m_DataStorage = this->m_RegionGrow3DTool->GetDataStorage(); this->EnableControls(true); //Watch for point added or modified itk::SimpleMemberCommand::Pointer pointAddedCommand = itk::SimpleMemberCommand::New(); pointAddedCommand->SetCallbackFunction(this, &QmitkAdaptiveRegionGrowingToolGUI::OnPointAdded); m_PointSetAddObserverTag = m_RegionGrow3DTool->GetPointSetNode()->GetData()->AddObserver( mitk::PointSetAddEvent(), pointAddedCommand); } else { this->EnableControls(false); } } void QmitkAdaptiveRegionGrowingToolGUI::RemoveHelperNodes() { mitk::DataNode::Pointer imageNode = m_DataStorage->GetNamedNode( m_NAMEFORLABLEDSEGMENTATIONIMAGE); if( imageNode.IsNotNull() ) { m_DataStorage->Remove(imageNode); } } void QmitkAdaptiveRegionGrowingToolGUI::CreateConnections() { //Connecting GUI components connect( (QObject*) (m_Controls.m_pbRunSegmentation), SIGNAL(clicked()), this, SLOT(RunSegmentation())); connect( m_Controls.m_PreviewSlider, SIGNAL(valueChanged(double)), this, SLOT(ChangeLevelWindow(double))); connect( (QObject*) (m_Controls.m_pbConfirmSegementation), SIGNAL(clicked()), this, SLOT(ConfirmSegmentation())); connect( (QObject*) (m_Controls.m_cbVolumeRendering), SIGNAL(toggled(bool)), this, SLOT(UseVolumeRendering(bool) )); connect( m_Controls.m_ThresholdSlider, SIGNAL(maximumValueChanged(double)), this, SLOT(SetUpperThresholdValue(double))); connect( m_Controls.m_ThresholdSlider, SIGNAL(minimumValueChanged(double)), this, SLOT(SetLowerThresholdValue(double))); } void QmitkAdaptiveRegionGrowingToolGUI::SetDataNodeNames(std::string labledSegmentation, std::string binaryImage, std::string surface) { m_NAMEFORLABLEDSEGMENTATIONIMAGE = labledSegmentation; m_NAMEFORBINARYIMAGE = binaryImage; m_NAMEFORSURFACE = surface; } void QmitkAdaptiveRegionGrowingToolGUI::SetDataStorage(mitk::DataStorage* dataStorage) { m_DataStorage = dataStorage; } void QmitkAdaptiveRegionGrowingToolGUI::SetMultiWidget(QmitkStdMultiWidget* multiWidget) { m_MultiWidget = multiWidget; } void QmitkAdaptiveRegionGrowingToolGUI::SetInputImageNode(mitk::DataNode* node) { m_InputImageNode = node; mitk::Image* inputImage = dynamic_cast(m_InputImageNode->GetData()); if (inputImage) { mitk::ScalarType max = inputImage->GetStatistics()->GetScalarValueMax(); mitk::ScalarType min = inputImage->GetStatistics()->GetScalarValueMin(); m_Controls.m_ThresholdSlider->setMaximum(max); m_Controls.m_ThresholdSlider->setMinimum(min); // Just for initialization m_Controls.m_ThresholdSlider->setMaximumValue(max); m_Controls.m_ThresholdSlider->setMinimumValue(min); } } template static void AccessPixel(mitk::PixelType ptype, const mitk::Image::Pointer im, mitk::Point3D p, int & val) { mitk::ImagePixelReadAccessor access(im); val = access.GetPixelByWorldCoordinates(p); } void QmitkAdaptiveRegionGrowingToolGUI::OnPointAdded() { if (m_RegionGrow3DTool.IsNull()) return; mitk::DataNode* node = m_RegionGrow3DTool->GetPointSetNode(); if (node != NULL) { mitk::PointSet::Pointer pointSet = dynamic_cast(node->GetData()); if (pointSet.IsNull()) { QMessageBox::critical(NULL, "QmitkAdaptiveRegionGrowingToolGUI", "PointSetNode does not contain a pointset"); return; } m_Controls.m_lblSetSeedpoint->setText(""); mitk::Image* image = dynamic_cast(m_InputImageNode->GetData()); mitk::Point3D seedPoint = pointSet->GetPointSet(mitk::BaseRenderer::GetInstance( mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget1") )->GetTimeStep())->GetPoints()->ElementAt(0); mitkPixelTypeMultiplex3(AccessPixel,image->GetChannelDescriptor().GetPixelType(),image,seedPoint,m_SeedpointValue); /* In this case the seedpoint is placed e.g. in the lung or bronchialtree * The lowerFactor sets the windowsize depending on the regiongrowing direction */ m_CurrentRGDirectionIsUpwards = true; if (m_SeedpointValue < -500) { m_CurrentRGDirectionIsUpwards = false; } // Initializing the region by the area around the seedpoint m_SeedPointValueMean = 0; mitk::Index3D currentIndex, runningIndex; mitk::ScalarType pixelValues[125]; unsigned int pos (0); image->GetGeometry(0)->WorldToIndex(seedPoint, currentIndex); runningIndex = currentIndex; for(int i = runningIndex[0]-2; i <= runningIndex[0]+2; i++) { for(int j = runningIndex[1]-2; j <= runningIndex[1]+2; j++) { for(int k = runningIndex[2]-2; k <= runningIndex[2]+2; k++) { currentIndex[0] = i; currentIndex[1] = j; currentIndex[2] = k; if(image->GetGeometry()->IsIndexInside(currentIndex)) { pixelValues[pos] = image->GetPixelValueByIndex(currentIndex); pos++; } else { pixelValues[pos] = -10000000; pos++; } } } } //Now calculation mean of the pixelValues unsigned int numberOfValues(0); for (unsigned int i = 0; i < 125; i++) { if(pixelValues[i] > -10000000) { m_SeedPointValueMean += pixelValues[i]; numberOfValues++; } } m_SeedPointValueMean = m_SeedPointValueMean/numberOfValues; /* * Here the upper- and lower threshold is calculated: * The windowSize is 20% of the maximum range of the intensity values existing in the current image * If the RG direction is upwards the lower TH is meanSeedValue-0.15*windowSize and upper TH is meanSeedValue+0.85*windowsSize * if the RG direction is downwards the lower TH is meanSeedValue-0.85*windowSize and upper TH is meanSeedValue+0.15*windowsSize */ mitk::ScalarType min = image->GetStatistics()->GetScalarValueMin(); mitk::ScalarType max = image->GetStatistics()->GetScalarValueMax(); mitk::ScalarType windowSize = max - min; windowSize = 0.15*windowSize; if (m_CurrentRGDirectionIsUpwards) { m_LOWERTHRESHOLD = m_SeedPointValueMean; if (m_SeedpointValue < m_SeedPointValueMean) m_LOWERTHRESHOLD = m_SeedpointValue; m_UPPERTHRESHOLD = m_SeedpointValue + windowSize; if (m_UPPERTHRESHOLD > max) m_UPPERTHRESHOLD = max; m_Controls.m_ThresholdSlider->setMaximumValue(m_UPPERTHRESHOLD); m_Controls.m_ThresholdSlider->setMinimumValue(m_LOWERTHRESHOLD); } else { m_UPPERTHRESHOLD = m_SeedPointValueMean; if (m_SeedpointValue > m_SeedPointValueMean) m_UPPERTHRESHOLD = m_SeedpointValue; m_LOWERTHRESHOLD = m_SeedpointValue - windowSize; if (m_LOWERTHRESHOLD < min) m_LOWERTHRESHOLD = min; m_Controls.m_ThresholdSlider->setMinimumValue(m_LOWERTHRESHOLD); m_Controls.m_ThresholdSlider->setMaximumValue(m_UPPERTHRESHOLD); } } } void QmitkAdaptiveRegionGrowingToolGUI::RunSegmentation() { if (m_InputImageNode.IsNull()) { QMessageBox::information( NULL, "Adaptive Region Growing functionality", "Please specify the image in Datamanager!"); return; } mitk::DataNode::Pointer node = m_RegionGrow3DTool->GetPointSetNode(); if (node.IsNull()) { QMessageBox::information( NULL, "Adaptive Region Growing functionality", "Please insert a seed point inside the image.\n\nFirst press the \"Define Seed Point\" button,\nthen click left mouse button inside the image."); return; } //safety if no pointSet or pointSet empty mitk::PointSet::Pointer seedPointSet = dynamic_cast (node->GetData()); if (seedPointSet.IsNull()) { m_Controls.m_pbRunSegmentation->setEnabled(true); QMessageBox::information( NULL, "Adaptive Region Growing functionality", "The seed point is empty! Please choose a new seed point."); return; } int timeStep = mitk::BaseRenderer::GetInstance( mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget1") )->GetTimeStep(); if (!(seedPointSet->GetSize(timeStep))) { m_Controls.m_pbRunSegmentation->setEnabled(true); QMessageBox::information( NULL, "Adaptive Region Growing functionality", "The seed point is empty! Please choose a new seed point."); return; } QApplication::setOverrideCursor( QCursor(Qt::WaitCursor) ); mitk::PointSet::PointType seedPoint = seedPointSet->GetPointSet(timeStep)->GetPoints()->Begin().Value(); mitk::Image::Pointer orgImage = dynamic_cast (m_InputImageNode->GetData()); if (orgImage.IsNotNull()) { if (orgImage->GetDimension() == 4) { mitk::ImageTimeSelector::Pointer timeSelector = mitk::ImageTimeSelector::New(); timeSelector->SetInput(orgImage); timeSelector->SetTimeNr( timeStep ); timeSelector->UpdateLargestPossibleRegion(); mitk::Image* timedImage = timeSelector->GetOutput(); AccessByItk_2( timedImage , StartRegionGrowing, timedImage->GetGeometry(), seedPoint); } else if (orgImage->GetDimension() == 3) { //QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); //set the cursor to waiting AccessByItk_2(orgImage, StartRegionGrowing, orgImage->GetGeometry(), seedPoint); //QApplication::restoreOverrideCursor();//reset cursor } else { QApplication::restoreOverrideCursor();//reset cursor QMessageBox::information( NULL, "Adaptive Region Growing functionality", "Only images of dimension 3 or 4 can be processed!"); return; } } EnableControls(true); // Segmentation ran successfully, so enable all controls. node->SetVisibility(true); QApplication::restoreOverrideCursor();//reset cursor } template -void QmitkAdaptiveRegionGrowingToolGUI::StartRegionGrowing(itk::Image* itkImage, mitk::Geometry3D* imageGeometry, mitk::PointSet::PointType seedPoint) +void QmitkAdaptiveRegionGrowingToolGUI::StartRegionGrowing(itk::Image* itkImage, mitk::BaseGeometry* imageGeometry, mitk::PointSet::PointType seedPoint) { typedef itk::Image InputImageType; typedef typename InputImageType::IndexType IndexType; typedef itk::ConnectedAdaptiveThresholdImageFilter RegionGrowingFilterType; typename RegionGrowingFilterType::Pointer regionGrower = RegionGrowingFilterType::New(); typedef itk::MinimumMaximumImageCalculator MinMaxValueFilterType; if ( !imageGeometry->IsInside(seedPoint) ) { QApplication::restoreOverrideCursor();//reset cursor to be able to click ok with the regular mouse cursor QMessageBox::information( NULL, "Segmentation functionality", "The seed point is outside of the image! Please choose a position inside the image!"); return; } IndexType seedIndex; imageGeometry->WorldToIndex( seedPoint, seedIndex);// convert world coordinates to image indices if (m_SeedpointValue>m_UPPERTHRESHOLD || m_SeedpointValueSetGrowingDirectionIsUpwards( m_CurrentRGDirectionIsUpwards ); regionGrower->SetInput( itkImage ); regionGrower->AddSeed( seedIndex ); //In some cases we have to subtract 1 for the lower threshold and add 1 to the upper. //Otherwise no region growing is done. Maybe a bug in the ConnectiveAdaptiveThresholdFilter regionGrower->SetLower( m_LOWERTHRESHOLD-1 ); regionGrower->SetUpper( m_UPPERTHRESHOLD+1); try { regionGrower->Update(); } catch(itk::ExceptionObject &exc) { QMessageBox errorInfo; errorInfo.setWindowTitle("Adaptive RG Segmentation Functionality"); errorInfo.setIcon(QMessageBox::Critical); errorInfo.setText("An error occurred during region growing!"); errorInfo.setDetailedText(exc.what()); errorInfo.exec(); return; // can't work } catch( ... ) { QMessageBox::critical( NULL, "Adaptive RG Segmentation Functionality", "An error occurred during region growing!"); return; } mitk::Image::Pointer resultImage = mitk::ImportItkImage(regionGrower->GetOutput())->Clone(); //initialize slider m_Controls.m_PreviewSlider->setMinimum(m_LOWERTHRESHOLD); mitk::ScalarType max = m_LOWERTHRESHOLD+resultImage->GetStatistics()->GetScalarValueMax(); if (max < m_UPPERTHRESHOLD) m_Controls.m_PreviewSlider->setMaximum(max); else m_Controls.m_PreviewSlider->setMaximum(m_UPPERTHRESHOLD); this->m_DetectedLeakagePoint = regionGrower->GetLeakagePoint(); if(m_CurrentRGDirectionIsUpwards) { m_Controls.m_PreviewSlider->setValue(m_SeedPointValueMean-1); } else { m_Controls.m_PreviewSlider->setValue(m_SeedPointValueMean+1); } this->m_SliderInitialized = true; //create new node and then delete the old one if there is one mitk::DataNode::Pointer newNode = mitk::DataNode::New(); newNode->SetData( resultImage ); // set some properties newNode->SetProperty("name", mitk::StringProperty::New(m_NAMEFORLABLEDSEGMENTATIONIMAGE)); newNode->SetProperty("helper object", mitk::BoolProperty::New(true)); newNode->SetProperty("color", mitk::ColorProperty::New(0.0,1.0,0.0)); newNode->SetProperty("layer", mitk::IntProperty::New(1)); newNode->SetProperty("opacity", mitk::FloatProperty::New(0.7)); //delete the old image, if there was one: mitk::DataNode::Pointer binaryNode = m_DataStorage->GetNamedNode(m_NAMEFORLABLEDSEGMENTATIONIMAGE); m_DataStorage->Remove(binaryNode); // now add result to data tree m_DataStorage->Add( newNode, m_InputImageNode ); this->InitializeLevelWindow(); if(m_UseVolumeRendering) this->EnableVolumeRendering(true); m_UpdateSuggestedThreshold = true;// reset first stored threshold value //Setting progress to finished mitk::ProgressBar::GetInstance()->Progress(357); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkAdaptiveRegionGrowingToolGUI::InitializeLevelWindow() { //get the preview from the datatree mitk::DataNode::Pointer newNode = m_DataStorage->GetNamedNode( m_NAMEFORLABLEDSEGMENTATIONIMAGE); mitk::LevelWindow tempLevelWindow; newNode->GetLevelWindow(tempLevelWindow, NULL, "levelwindow"); mitk::ScalarType* level = new mitk::ScalarType(0.0); mitk::ScalarType* window = new mitk::ScalarType(1.0); int upper; if (m_CurrentRGDirectionIsUpwards) { upper = m_UPPERTHRESHOLD - m_SeedpointValue; } else { upper = m_SeedpointValue - m_LOWERTHRESHOLD; } tempLevelWindow.SetRangeMinMax(mitk::ScalarType(0), mitk::ScalarType(upper)); //get the suggested threshold from the detected leakage-point and adjust the slider if (m_CurrentRGDirectionIsUpwards) { this->m_Controls.m_PreviewSlider->setValue(m_SeedpointValue); *level = m_UPPERTHRESHOLD - (m_SeedpointValue) + 0.5; } else { this->m_Controls.m_PreviewSlider->setValue(m_SeedpointValue); *level = (m_SeedpointValue) - m_LOWERTHRESHOLD + 0.5; } tempLevelWindow.SetLevelWindow(*level, *window); newNode->SetLevelWindow(tempLevelWindow, NULL, "levelwindow"); //update the widgets mitk::RenderingManager::GetInstance()->RequestUpdateAll(); m_SliderInitialized = true; //inquiry need to fix bug#1828 static int lastSliderPosition = 0; if ((this->m_SeedpointValue + this->m_DetectedLeakagePoint - 1) == lastSliderPosition) { this->ChangeLevelWindow(lastSliderPosition); } lastSliderPosition = this->m_SeedpointValue + this->m_DetectedLeakagePoint-1; if(m_MultiWidget) { this->m_MultiWidget->levelWindowWidget->GetManager()->SetAutoTopMostImage(false); this->m_MultiWidget->levelWindowWidget->GetManager()->SetLevelWindowProperty(static_cast(newNode->GetProperty("levelwindow"))); } if (m_UseVolumeRendering) this->UpdateVolumeRenderingThreshold((int) (*level + 0.5));//lower threshold for labeled image } void QmitkAdaptiveRegionGrowingToolGUI::ChangeLevelWindow(double newValue) { if (m_SliderInitialized) { //do nothing, if no preview exists mitk::DataNode::Pointer newNode = m_DataStorage->GetNamedNode( m_NAMEFORLABLEDSEGMENTATIONIMAGE); if (newNode.IsNull()) return; mitk::LevelWindow tempLevelWindow; newNode->GetLevelWindow(tempLevelWindow, NULL, "levelwindow"); //get the levelWindow associated with the preview mitk::ScalarType level;// = this->m_UPPERTHRESHOLD - newValue + 0.5; mitk::ScalarType* window = new mitk::ScalarType(1); //adjust the levelwindow according to the position of the slider (newvalue) if (m_CurrentRGDirectionIsUpwards) { level = m_UPPERTHRESHOLD - newValue + 0.5; tempLevelWindow.SetLevelWindow(level, *window); } else { level = newValue - m_LOWERTHRESHOLD +0.5; tempLevelWindow.SetLevelWindow(level, *window); } newNode->SetLevelWindow(tempLevelWindow, NULL, "levelwindow"); if (m_UseVolumeRendering) this->UpdateVolumeRenderingThreshold((int) (level - 0.5));//lower threshold for labeled image newNode->SetVisibility(true); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } void QmitkAdaptiveRegionGrowingToolGUI::DecreaseSlider() { //moves the slider one step to the left, when the "-"-button is pressed if (this->m_Controls.m_PreviewSlider->value() != this->m_Controls.m_PreviewSlider->minimum()) { int newValue = this->m_Controls.m_PreviewSlider->value() - 1; this->ChangeLevelWindow(newValue); this->m_Controls.m_PreviewSlider->setValue(newValue); } } void QmitkAdaptiveRegionGrowingToolGUI::IncreaseSlider() { //moves the slider one step to the right, when the "+"-button is pressed if (this->m_Controls.m_PreviewSlider->value() != this->m_Controls.m_PreviewSlider->maximum()) { int newValue = this->m_Controls.m_PreviewSlider->value() + 1; this->ChangeLevelWindow(newValue); this->m_Controls.m_PreviewSlider->setValue(newValue); } } void QmitkAdaptiveRegionGrowingToolGUI::ConfirmSegmentation() { //get image node if(m_InputImageNode.IsNull()) { QMessageBox::critical( NULL, "Adaptive region growing functionality", "Please specify the image in Datamanager!"); return; } //get image data mitk::Image::Pointer orgImage = dynamic_cast (m_InputImageNode->GetData()); if(orgImage.IsNull()) { QMessageBox::critical( NULL, "Adaptive region growing functionality", "No Image found!"); return; } //get labeled segmentation mitk::Image::Pointer labeledSeg = (mitk::Image*)m_DataStorage->GetNamedObject(m_NAMEFORLABLEDSEGMENTATIONIMAGE); if(labeledSeg.IsNull()) { QMessageBox::critical( NULL, "Adaptive region growing functionality", "No Segmentation Preview found!"); return; } mitk::DataNode::Pointer newNode = m_DataStorage->GetNamedNode( m_NAMEFORLABLEDSEGMENTATIONIMAGE); if (newNode.IsNull()) return; QmitkConfirmSegmentationDialog dialog; QString segName = QString::fromStdString(m_RegionGrow3DTool->GetCurrentSegmentationName()); dialog.SetSegmentationName(segName); int result = dialog.exec(); switch(result) { case QmitkConfirmSegmentationDialog::CREATE_NEW_SEGMENTATION: m_RegionGrow3DTool->SetOverwriteExistingSegmentation(false); break; case QmitkConfirmSegmentationDialog::OVERWRITE_SEGMENTATION: m_RegionGrow3DTool->SetOverwriteExistingSegmentation(true); break; case QmitkConfirmSegmentationDialog::CANCEL_SEGMENTATION: return; } mitk::Image* img = dynamic_cast(newNode->GetData()); AccessByItk(img, ITKThresholding); // disable volume rendering preview after the segmentation node was created this->EnableVolumeRendering(false); newNode->SetVisibility(false); m_Controls.m_cbVolumeRendering->setChecked(false); //TODO disable slider etc... } template void QmitkAdaptiveRegionGrowingToolGUI::ITKThresholding(itk::Image* itkImage) { mitk::Image::Pointer originalSegmentation = dynamic_cast(this->m_RegionGrow3DTool-> GetTargetSegmentationNode()->GetData()); int timeStep = mitk::BaseRenderer::GetInstance( mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget1") )->GetTimeStep(); if (originalSegmentation) { typedef itk::Image InputImageType; typedef itk::Image SegmentationType; //select single 3D volume if we have more than one time step typename SegmentationType::Pointer originalSegmentationInITK = SegmentationType::New(); if(originalSegmentation->GetTimeGeometry()->CountTimeSteps() > 1) { mitk::ImageTimeSelector::Pointer timeSelector = mitk::ImageTimeSelector::New(); timeSelector->SetInput( originalSegmentation ); timeSelector->SetTimeNr( timeStep ); timeSelector->UpdateLargestPossibleRegion(); CastToItkImage( timeSelector->GetOutput(), originalSegmentationInITK ); } else //use original { CastToItkImage( originalSegmentation, originalSegmentationInITK ); } //Fill current preiview image in segmentation image originalSegmentationInITK->FillBuffer(0); itk::ImageRegionIterator itOutput( originalSegmentationInITK, originalSegmentationInITK->GetLargestPossibleRegion() ); itk::ImageRegionIterator itInput( itkImage, itkImage->GetLargestPossibleRegion() ); itOutput.GoToBegin(); itInput.GoToBegin(); //calculate threhold from slider value int currentTreshold = 0; if (m_CurrentRGDirectionIsUpwards) { currentTreshold = m_UPPERTHRESHOLD - m_Controls.m_PreviewSlider->value() + 1; } else { currentTreshold = m_Controls.m_PreviewSlider->value() - m_LOWERTHRESHOLD; } //iterate over image and set pixel in segmentation according to thresholded labeled image while( !itOutput.IsAtEnd() && !itInput.IsAtEnd() ) { //Use threshold slider to determine if pixel is set to 1 if( itInput.Value() != 0 && itInput.Value() > currentTreshold ) { itOutput.Set( 1 ); } ++itOutput; ++itInput; } //combine current working segmentation image with our region growing result originalSegmentation->SetVolume( (void*)(originalSegmentationInITK->GetPixelContainer()->GetBufferPointer()), timeStep); originalSegmentation->Modified(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } void QmitkAdaptiveRegionGrowingToolGUI::EnableControls(bool enable) { if (m_RegionGrow3DTool.IsNull()) return; // Check if seed point is already set, if not leave RunSegmentation disabled //if even m_DataStorage is NULL leave node NULL mitk::DataNode::Pointer node = m_RegionGrow3DTool->GetPointSetNode(); if (node.IsNull()) { this->m_Controls.m_pbRunSegmentation->setEnabled(false); } else { this->m_Controls.m_pbRunSegmentation->setEnabled(enable); } // Check if a segmentation exists, if not leave segmentation dependent disabled. //if even m_DataStorage is NULL leave node NULL node = m_DataStorage?m_DataStorage->GetNamedNode(m_NAMEFORLABLEDSEGMENTATIONIMAGE):NULL; if (node.IsNull()) { this->m_Controls.m_PreviewSlider->setEnabled(false); this->m_Controls.m_pbConfirmSegementation->setEnabled(false); } else { this->m_Controls.m_PreviewSlider->setEnabled(enable); this->m_Controls.m_pbConfirmSegementation->setEnabled(enable); } this->m_Controls.m_cbVolumeRendering->setEnabled(enable); } void QmitkAdaptiveRegionGrowingToolGUI::EnableVolumeRendering(bool enable) { mitk::DataNode::Pointer node = m_DataStorage->GetNamedNode( m_NAMEFORLABLEDSEGMENTATIONIMAGE); if(node.IsNull()) return; if(m_MultiWidget) m_MultiWidget->SetWidgetPlanesVisibility(!enable); if (enable) { node->SetBoolProperty("volumerendering", enable); node->SetBoolProperty("volumerendering.uselod", true); } else { node->SetBoolProperty("volumerendering", enable); } mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkAdaptiveRegionGrowingToolGUI::UpdateVolumeRenderingThreshold(int thValue) { mitk::DataNode::Pointer node = m_DataStorage->GetNamedNode( m_NAMEFORLABLEDSEGMENTATIONIMAGE); mitk::TransferFunction::Pointer tf = mitk::TransferFunction::New(); if (m_UpdateSuggestedThreshold) { m_SuggestedThValue = thValue; m_UpdateSuggestedThreshold = false; } // grayvalue->opacity { vtkPiecewiseFunction *f = tf->GetScalarOpacityFunction(); f->RemoveAllPoints(); f->AddPoint(0, 0); f->AddPoint(thValue+0.5, 0); f->AddPoint(thValue+1.5, 1); f->AddPoint(1000, 1); f->ClampingOn(); f->Modified(); } // grayvalue->color { float a = 255.0; vtkColorTransferFunction *ctf = tf->GetColorTransferFunction(); ctf->RemoveAllPoints(); //ctf->AddRGBPoint(-1000, 0.0, 0.0, 0.0); ctf->AddRGBPoint(m_SuggestedThValue+1, 203/a, 104/a, 102/a); ctf->AddRGBPoint(m_SuggestedThValue, 255/a, 0/a, 0/a); ctf->ClampingOn(); ctf->Modified(); } // GradientOpacityFunction { vtkPiecewiseFunction *gof = tf->GetGradientOpacityFunction(); gof->RemoveAllPoints(); gof->AddPoint(-10000, 1); gof->AddPoint(10000, 1); gof->ClampingOn(); gof->Modified(); } mitk::TransferFunctionProperty::Pointer tfp = mitk::TransferFunctionProperty::New(); tfp->SetValue(tf); node->SetProperty("TransferFunction", tfp); } void QmitkAdaptiveRegionGrowingToolGUI::UseVolumeRendering(bool on) { m_UseVolumeRendering = on; this->EnableVolumeRendering(on); } void QmitkAdaptiveRegionGrowingToolGUI::SetLowerThresholdValue( double lowerThreshold ) { m_LOWERTHRESHOLD = lowerThreshold; } void QmitkAdaptiveRegionGrowingToolGUI::SetUpperThresholdValue( double upperThreshold) { m_UPPERTHRESHOLD = upperThreshold; } void QmitkAdaptiveRegionGrowingToolGUI::Deactivated() { // make the segmentation preview node invisible mitk::DataNode::Pointer node = m_DataStorage->GetNamedNode( m_NAMEFORLABLEDSEGMENTATIONIMAGE); if( node.IsNotNull() ) { node->SetVisibility(false); } // disable volume rendering preview after the segmentation node was created this->EnableVolumeRendering(false); m_Controls.m_cbVolumeRendering->setChecked(false); } void QmitkAdaptiveRegionGrowingToolGUI::Activated() { } diff --git a/Modules/SegmentationUI/Qmitk/QmitkAdaptiveRegionGrowingToolGUI.h b/Modules/SegmentationUI/Qmitk/QmitkAdaptiveRegionGrowingToolGUI.h index f069283ebe..c5bdcd03df 100644 --- a/Modules/SegmentationUI/Qmitk/QmitkAdaptiveRegionGrowingToolGUI.h +++ b/Modules/SegmentationUI/Qmitk/QmitkAdaptiveRegionGrowingToolGUI.h @@ -1,162 +1,162 @@ /*=================================================================== 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 QMITK_QmitkAdaptiveRegionGrowingToolGUI_H #define QMITK_QmitkAdaptiveRegionGrowingToolGUI_H #include "mitkDataStorage.h" #include "itkImage.h" #include "mitkGeometry3D.h" #include "mitkPointSet.h" #include "qwidget.h" #include "ui_QmitkAdaptiveRegionGrowingToolGUIControls.h" #include #include "QmitkToolGUI.h" #include "mitkAdaptiveRegionGrowingTool.h" class QmitkStdMultiWidget; class DataNode; class QmitkAdaptiveRegionGrowingToolGUIControls; /*! * * \brief QmitkAdaptiveRegionGrowingToolGUI * * Adaptive Region Growing View class of the segmentation. * */ class MitkSegmentationUI_EXPORT QmitkAdaptiveRegionGrowingToolGUI : public QmitkToolGUI { Q_OBJECT public: mitkClassMacro(QmitkAdaptiveRegionGrowingToolGUI, QmitkToolGUI); itkFactorylessNewMacro(Self) itkCloneMacro(Self) QmitkAdaptiveRegionGrowingToolGUI(QWidget* parent=0); /** \brief Method to create the connections for the component. This Method is obligatory even if no connections is needed*/ virtual void CreateConnections(); ///** \brief Method to set the default data storage.*/ virtual void SetDataStorage(mitk::DataStorage* dataStorage); void SetMultiWidget(QmitkStdMultiWidget* multiWidget); void SetDataNodeNames(std::string labledSegmentation, std::string binaryImage, /*std::string vesselTree,*/ std::string surface); void EnableControls(bool enable); void SetInputImageNode(mitk::DataNode* node); void Deactivated(); void Activated(); /** * @brief The created GUI from the .ui-File. This Attribute is obligatory */ Ui::QmitkAdaptiveRegionGrowingToolGUIControls m_Controls; protected slots: void RunSegmentation(); void ChangeLevelWindow(double newValue);//called, when the Level Window is changed via the slider in the ControlWidget //****called, when the slider-position is modified via the +/- buttons void IncreaseSlider(); void DecreaseSlider(); //*** void ConfirmSegmentation(); void UseVolumeRendering(bool on); void SetLowerThresholdValue(double lowerThreshold); void SetUpperThresholdValue(double upperThreshold); void OnNewToolAssociated(mitk::Tool*); protected: mitk::AdaptiveRegionGrowingTool::Pointer m_RegionGrow3DTool; /** \brief Destructor. */ virtual ~QmitkAdaptiveRegionGrowingToolGUI(); //Pointer to the main widget to be able to reach the renderer QmitkStdMultiWidget* m_MultiWidget; mitk::DataStorage* m_DataStorage; mitk::DataNode::Pointer m_InputImageNode; void OnPointAdded(); private: std::string m_NAMEFORORGIMAGE; std::string m_NAMEFORLABLEDSEGMENTATIONIMAGE; std::string m_NAMEFORBINARYIMAGE; std::string m_NAMEFORSURFACE; mitk::ScalarType m_LOWERTHRESHOLD; //Hounsfield value mitk::ScalarType m_UPPERTHRESHOLD; //Hounsfield value mitk::ScalarType m_SeedPointValueMean; void RemoveHelperNodes(); int m_DetectedLeakagePoint; bool m_CurrentRGDirectionIsUpwards; // defines fixed threshold (true = LOWERTHRESHOLD fixed, false = UPPERTHRESHOLD fixed) int m_SeedpointValue; bool m_SliderInitialized; bool m_UseVolumeRendering; bool m_UpdateSuggestedThreshold; float m_SuggestedThValue; long m_PointSetAddObserverTag; template < typename TPixel, unsigned int VImageDimension > - void StartRegionGrowing( itk::Image< TPixel, VImageDimension >* itkImage, mitk::Geometry3D* imageGeometry, mitk::PointSet::PointType seedPoint ); + void StartRegionGrowing( itk::Image< TPixel, VImageDimension >* itkImage, mitk::BaseGeometry* imageGeometry, mitk::PointSet::PointType seedPoint ); template < typename TPixel, unsigned int VImageDimension > void ITKThresholding( itk::Image< TPixel, VImageDimension >* inputImage ); void InitializeLevelWindow(); void EnableVolumeRendering(bool enable); void UpdateVolumeRenderingThreshold(int thValue); }; #endif diff --git a/Plugins/org.mitk.gui.common/src/mitkIRenderingManager.cpp b/Plugins/org.mitk.gui.common/src/mitkIRenderingManager.cpp index fd824d9dec..e61c6e070f 100644 --- a/Plugins/org.mitk.gui.common/src/mitkIRenderingManager.cpp +++ b/Plugins/org.mitk.gui.common/src/mitkIRenderingManager.cpp @@ -1,183 +1,183 @@ /*=================================================================== 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 "mitkIRenderingManager.h" namespace mitk { struct RenderingManagerInterface : public IRenderingManager { RenderingManagerInterface(RenderingManager::Pointer manager) : m_RM(manager) {} QList GetAllRegisteredVtkRenderWindows() const { RenderingManager::RenderWindowVector vec(m_RM->GetAllRegisteredRenderWindows()); QList result; for (RenderingManager::RenderWindowVector::iterator i = vec.begin(); i != vec.end(); ++i) { result.append(*i); } return result; } void RequestUpdate( vtkRenderWindow *renderWindow ) { m_RM->RequestUpdate(renderWindow); } void ForceImmediateUpdate( vtkRenderWindow *renderWindow ) { m_RM->ForceImmediateUpdate(renderWindow); } void RequestUpdateAll( RenderingManager::RequestType type ) { m_RM->RequestUpdateAll(type); } void ForceImmediateUpdateAll( RenderingManager::RequestType type ) { m_RM->ForceImmediateUpdateAll(type); } - bool InitializeViews( const Geometry3D *geometry, + bool InitializeViews( const BaseGeometry *geometry, RenderingManager::RequestType type = RenderingManager::REQUEST_UPDATE_ALL, bool preserveRoughOrientationInWorldSpace = false ) { return m_RM->InitializeViews( geometry, type, preserveRoughOrientationInWorldSpace); } bool InitializeViews( const TimeGeometry *geometry, RenderingManager::RequestType type = RenderingManager::REQUEST_UPDATE_ALL, bool preserveRoughOrientationInWorldSpace = false ) { return m_RM->InitializeViews( geometry, type, preserveRoughOrientationInWorldSpace); } bool InitializeViews( RenderingManager::RequestType type ) { return m_RM->InitializeViews(type); } - bool InitializeView( vtkRenderWindow *renderWindow, const Geometry3D *geometry, + bool InitializeView( vtkRenderWindow *renderWindow, const BaseGeometry *geometry, bool initializeGlobalTimeSNC = false) { return m_RM->InitializeView(renderWindow, geometry, initializeGlobalTimeSNC); } bool InitializeView( vtkRenderWindow *renderWindow ) { return m_RM->InitializeView(renderWindow); } const SliceNavigationController *GetTimeNavigationController() const { return m_RM->GetTimeNavigationController(); } SliceNavigationController *GetTimeNavigationController() { return m_RM->GetTimeNavigationController(); } bool IsRendering() const { return m_RM->IsRendering(); } void AbortRendering() { m_RM->AbortRendering(); } void SetLODIncreaseBlocked(bool blocked) { m_RM->SetLODIncreaseBlocked(blocked); } bool GetLODIncreaseBlocked() const { return m_RM->GetLODIncreaseBlocked(); } void SetLODAbortMechanismEnabled(bool abort) { m_RM->SetLODAbortMechanismEnabled(abort); } bool GetLODAbortMechanismEnabled() const { return m_RM->GetLODAbortMechanismEnabled(); } int GetNextLOD( BaseRenderer* renderer ) const { return m_RM->GetNextLOD(renderer); } void SetMaximumLOD( unsigned int max ) { m_RM->SetMaximumLOD(max); } void SetShading( bool state, unsigned int lod ) { m_RM->SetShading(state, lod); } bool GetShading( unsigned int lod ) { return m_RM->GetShading(lod); } void SetClippingPlaneStatus( bool status ) { m_RM->SetClippingPlaneStatus(status); } bool GetClippingPlaneStatus() { return m_RM->GetClippingPlaneStatus(); } void SetShadingValues( float ambient, float diffuse, float specular, float specpower ) { m_RM->SetShadingValues(ambient, diffuse, specular, specpower); } QList GetShadingValues() const { RenderingManager::FloatVector vec(m_RM->GetShadingValues()); QList result; for (RenderingManager::FloatVector::iterator i = vec.begin(); i != vec.end(); ++i) { result.push_back(*i); } return result; } const RenderingManager::Pointer m_RM; }; IRenderingManager* MakeRenderingManagerInterface(RenderingManager::Pointer manager) { return new RenderingManagerInterface(manager); } } \ No newline at end of file diff --git a/Plugins/org.mitk.gui.common/src/mitkIRenderingManager.h b/Plugins/org.mitk.gui.common/src/mitkIRenderingManager.h index a47e010ff3..26ab0d918b 100644 --- a/Plugins/org.mitk.gui.common/src/mitkIRenderingManager.h +++ b/Plugins/org.mitk.gui.common/src/mitkIRenderingManager.h @@ -1,148 +1,148 @@ /*=================================================================== 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 MITKIRENDERINGMANAGER_H #define MITKIRENDERINGMANAGER_H #include #include #include #include namespace mitk { /** * \ingroup org_mitk_gui_common * * \brief An interface for accessing a mitk::RenderingManager instance. * * This interface acts as a wrapper to a mitk::RenderingManager instance, hiding some * methods from the user. * * \see MakeRenderingManagerInterface */ struct IRenderingManager { virtual ~IRenderingManager() {} /** Get a list of all registered RenderWindows */ virtual QList GetAllRegisteredVtkRenderWindows() const = 0; /** * Requests an update for the specified RenderWindow, to be executed as * soon as the main loop is ready for rendering. */ virtual void RequestUpdate( vtkRenderWindow *renderWindow ) = 0; /** Immediately executes an update of the specified RenderWindow. */ virtual void ForceImmediateUpdate( vtkRenderWindow *renderWindow ) = 0; /** * Requests all currently registered RenderWindows to be updated. * If only 2D or 3D windows should be updated, this can be specified * via the parameter requestType. */ virtual void RequestUpdateAll( RenderingManager::RequestType type = RenderingManager::REQUEST_UPDATE_ALL ) = 0; /** * Immediately executes an update of all registered RenderWindows. * If only 2D or 3D windows should be updated, this can be specified * via the parameter requestType. */ virtual void ForceImmediateUpdateAll( RenderingManager::RequestType type = RenderingManager::REQUEST_UPDATE_ALL ) = 0; /** Initializes the windows specified by requestType to the given geometry. */ - virtual bool InitializeViews( const Geometry3D *geometry, + virtual bool InitializeViews( const BaseGeometry *geometry, RenderingManager::RequestType type = RenderingManager::REQUEST_UPDATE_ALL, bool preserveRoughOrientationInWorldSpace = false ) = 0; virtual bool InitializeViews( const TimeGeometry *geometry, RenderingManager::RequestType type = RenderingManager::REQUEST_UPDATE_ALL, bool preserveRoughOrientationInWorldSpace = false ) = 0; /** * Initializes the windows to the default viewing direction * (geomtry information is NOT changed). */ virtual bool InitializeViews( RenderingManager::RequestType type = RenderingManager::REQUEST_UPDATE_ALL ) = 0; /** * Initializes the specified window to the given geometry. Set * "initializeGlobalTimeSNC" to true in order to use this geometry as * global TimeGeometry. */ - virtual bool InitializeView( vtkRenderWindow *renderWindow, const Geometry3D *geometry, + virtual bool InitializeView( vtkRenderWindow *renderWindow, const BaseGeometry *geometry, bool initializeGlobalTimeSNC = false) = 0; /** * Initializes the specified window to the default viewing direction * (geomtry information is NOT changed). */ virtual bool InitializeView( vtkRenderWindow *renderWindow ) = 0; /** Gets the SliceNavigationController responsible for time-slicing. */ virtual const SliceNavigationController *GetTimeNavigationController() const = 0; /** Gets the SliceNavigationController responsible for time-slicing. */ virtual SliceNavigationController *GetTimeNavigationController() = 0; virtual bool IsRendering() const = 0; virtual void AbortRendering() = 0; /** En-/Disable LOD increase globally. */ virtual void SetLODIncreaseBlocked(bool blocked) = 0; /** Get LOD blocked status. */ virtual bool GetLODIncreaseBlocked() const = 0; /** En-/Disable LOD abort mechanism. */ virtual void SetLODAbortMechanismEnabled(bool abort) = 0; /** Get LOD abort mechanism status. */ virtual bool GetLODAbortMechanismEnabled() const = 0; virtual int GetNextLOD( BaseRenderer* renderer ) const = 0; /** Set current LOD (NULL means all renderers)*/ virtual void SetMaximumLOD( unsigned int max ) = 0; virtual void SetShading( bool state, unsigned int lod ) = 0; virtual bool GetShading( unsigned int lod ) = 0; virtual void SetClippingPlaneStatus( bool status ) = 0; virtual bool GetClippingPlaneStatus() = 0; virtual void SetShadingValues( float ambient, float diffuse, float specular, float specpower ) = 0; virtual QList GetShadingValues() const = 0; }; } Q_DECLARE_INTERFACE(mitk::IRenderingManager, "org.mitk.ui.IRenderingManager") namespace mitk { /** * Create a IRenderManager interface for a given RenderingManager. Ownership of the * returned pointer is transferred to the caller of this function. * * \param manager The RenderingManager instance for which to create a interface. * \return A pointer to the interface object. The caller is responsible for deleting the pointer. */ MITK_GUI_COMMON_PLUGIN IRenderingManager* MakeRenderingManagerInterface(RenderingManager::Pointer manager); } #endif // MITKIRENDERINGMANAGER_H \ No newline at end of file diff --git a/Plugins/org.mitk.gui.qt.imagenavigator/src/internal/QmitkImageNavigatorView.cpp b/Plugins/org.mitk.gui.qt.imagenavigator/src/internal/QmitkImageNavigatorView.cpp index bfbcf73b5e..9ebd0e6cf3 100644 --- a/Plugins/org.mitk.gui.qt.imagenavigator/src/internal/QmitkImageNavigatorView.cpp +++ b/Plugins/org.mitk.gui.qt.imagenavigator/src/internal/QmitkImageNavigatorView.cpp @@ -1,407 +1,407 @@ /*=================================================================== 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 "QmitkImageNavigatorView.h" #include #include #include #include const std::string QmitkImageNavigatorView::VIEW_ID = "org.mitk.views.imagenavigator"; QmitkImageNavigatorView::QmitkImageNavigatorView() : m_AxialStepper(0) , m_SagittalStepper(0) , m_FrontalStepper(0) , m_TimeStepper(0) , m_Parent(0) , m_IRenderWindowPart(0) { } QmitkImageNavigatorView::~QmitkImageNavigatorView() { } void QmitkImageNavigatorView::CreateQtPartControl(QWidget *parent) { // create GUI widgets m_Parent = parent; m_Controls.setupUi(parent); m_Controls.m_SliceNavigatorAxial->SetInverseDirection(true); connect(m_Controls.m_XWorldCoordinateSpinBox, SIGNAL(valueChanged(double)), this, SLOT(OnMillimetreCoordinateValueChanged())); connect(m_Controls.m_YWorldCoordinateSpinBox, SIGNAL(valueChanged(double)), this, SLOT(OnMillimetreCoordinateValueChanged())); connect(m_Controls.m_ZWorldCoordinateSpinBox, SIGNAL(valueChanged(double)), this, SLOT(OnMillimetreCoordinateValueChanged())); m_Parent->setEnabled(false); mitk::IRenderWindowPart* renderPart = this->GetRenderWindowPart(); this->RenderWindowPartActivated(renderPart); } void QmitkImageNavigatorView::SetFocus () { m_Controls.m_XWorldCoordinateSpinBox->setFocus(); } void QmitkImageNavigatorView::RenderWindowPartActivated(mitk::IRenderWindowPart* renderWindowPart) { if (this->m_IRenderWindowPart != renderWindowPart) { this->m_IRenderWindowPart = renderWindowPart; this->m_Parent->setEnabled(true); QmitkRenderWindow* renderWindow = renderWindowPart->GetQmitkRenderWindow("axial"); if (renderWindow) { if (m_AxialStepper) m_AxialStepper->deleteLater(); m_AxialStepper = new QmitkStepperAdapter(m_Controls.m_SliceNavigatorAxial, renderWindow->GetSliceNavigationController()->GetSlice(), "sliceNavigatorAxialFromSimpleExample"); m_Controls.m_SliceNavigatorAxial->setEnabled(true); m_Controls.m_AxialLabel->setEnabled(true); m_Controls.m_ZWorldCoordinateSpinBox->setEnabled(true); connect(m_AxialStepper, SIGNAL(Refetch()), this, SLOT(OnRefetch())); } else { m_Controls.m_SliceNavigatorAxial->setEnabled(false); m_Controls.m_AxialLabel->setEnabled(false); m_Controls.m_ZWorldCoordinateSpinBox->setEnabled(false); } renderWindow = renderWindowPart->GetQmitkRenderWindow("sagittal"); if (renderWindow) { if (m_SagittalStepper) m_SagittalStepper->deleteLater(); m_SagittalStepper = new QmitkStepperAdapter(m_Controls.m_SliceNavigatorSagittal, renderWindow->GetSliceNavigationController()->GetSlice(), "sliceNavigatorSagittalFromSimpleExample"); m_Controls.m_SliceNavigatorSagittal->setEnabled(true); m_Controls.m_SagittalLabel->setEnabled(true); m_Controls.m_YWorldCoordinateSpinBox->setEnabled(true); connect(m_SagittalStepper, SIGNAL(Refetch()), this, SLOT(OnRefetch())); } else { m_Controls.m_SliceNavigatorSagittal->setEnabled(false); m_Controls.m_SagittalLabel->setEnabled(false); m_Controls.m_YWorldCoordinateSpinBox->setEnabled(false); } renderWindow = renderWindowPart->GetQmitkRenderWindow("coronal"); if (renderWindow) { if (m_FrontalStepper) m_FrontalStepper->deleteLater(); m_FrontalStepper = new QmitkStepperAdapter(m_Controls.m_SliceNavigatorFrontal, renderWindow->GetSliceNavigationController()->GetSlice(), "sliceNavigatorFrontalFromSimpleExample"); m_Controls.m_SliceNavigatorFrontal->setEnabled(true); m_Controls.m_CoronalLabel->setEnabled(true); m_Controls.m_XWorldCoordinateSpinBox->setEnabled(true); connect(m_FrontalStepper, SIGNAL(Refetch()), this, SLOT(OnRefetch())); } else { m_Controls.m_SliceNavigatorFrontal->setEnabled(false); m_Controls.m_CoronalLabel->setEnabled(false); m_Controls.m_XWorldCoordinateSpinBox->setEnabled(false); } mitk::SliceNavigationController* timeController = renderWindowPart->GetTimeNavigationController(); if (timeController) { if (m_TimeStepper) m_TimeStepper->deleteLater(); m_TimeStepper = new QmitkStepperAdapter(m_Controls.m_SliceNavigatorTime, timeController->GetTime(), "sliceNavigatorTimeFromSimpleExample"); m_Controls.m_SliceNavigatorTime->setEnabled(true); m_Controls.m_TimeLabel->setEnabled(true); } else { m_Controls.m_SliceNavigatorTime->setEnabled(false); m_Controls.m_TimeLabel->setEnabled(false); } } } void QmitkImageNavigatorView::RenderWindowPartDeactivated(mitk::IRenderWindowPart* /*renderWindowPart*/) { m_IRenderWindowPart = 0; m_Parent->setEnabled(false); } int QmitkImageNavigatorView::GetSizeFlags(bool width) { if(!width) { return berry::Constants::MIN | berry::Constants::MAX | berry::Constants::FILL; } else { return 0; } } int QmitkImageNavigatorView::ComputePreferredSize(bool width, int /*availableParallel*/, int /*availablePerpendicular*/, int preferredResult) { if(width==false) { return 200; } else { return preferredResult; } } int QmitkImageNavigatorView::GetClosestAxisIndex(mitk::Vector3D normal) { // cos(theta) = normal . axis // cos(theta) = (a, b, c) . (d, e, f) // cos(theta) = (a, b, c) . (1, 0, 0) = a // cos(theta) = (a, b, c) . (0, 1, 0) = b // cos(theta) = (a, b, c) . (0, 0, 1) = c double absCosThetaWithAxis[3]; for (int i = 0; i < 3; i++) { absCosThetaWithAxis[i] = fabs(normal[i]); } int largestIndex = 0; double largestValue = absCosThetaWithAxis[0]; for (int i = 1; i < 3; i++) { if (absCosThetaWithAxis[i] > largestValue) { largestValue = absCosThetaWithAxis[i]; largestIndex = i; } } return largestIndex; } void QmitkImageNavigatorView::SetBorderColors() { if (m_IRenderWindowPart) { QmitkRenderWindow* renderWindow = m_IRenderWindowPart->GetQmitkRenderWindow("axial"); if (renderWindow) { mitk::PlaneGeometry::ConstPointer geometry = renderWindow->GetSliceNavigationController()->GetCurrentPlaneGeometry(); if (geometry.IsNotNull()) { mitk::Vector3D normal = geometry->GetNormal(); int axis = this->GetClosestAxisIndex(normal); this->SetBorderColor(axis, QString("red")); } } renderWindow = m_IRenderWindowPart->GetQmitkRenderWindow("sagittal"); if (renderWindow) { mitk::PlaneGeometry::ConstPointer geometry = renderWindow->GetSliceNavigationController()->GetCurrentPlaneGeometry(); if (geometry.IsNotNull()) { mitk::Vector3D normal = geometry->GetNormal(); int axis = this->GetClosestAxisIndex(normal); this->SetBorderColor(axis, QString("green")); } } renderWindow = m_IRenderWindowPart->GetQmitkRenderWindow("coronal"); if (renderWindow) { mitk::PlaneGeometry::ConstPointer geometry = renderWindow->GetSliceNavigationController()->GetCurrentPlaneGeometry(); if (geometry.IsNotNull()) { mitk::Vector3D normal = geometry->GetNormal(); int axis = this->GetClosestAxisIndex(normal); this->SetBorderColor(axis, QString("blue")); } } } } void QmitkImageNavigatorView::SetBorderColor(int axis, QString colorAsStyleSheetString) { if (axis == 0) { this->SetBorderColor(m_Controls.m_XWorldCoordinateSpinBox, colorAsStyleSheetString); } else if (axis == 1) { this->SetBorderColor(m_Controls.m_YWorldCoordinateSpinBox, colorAsStyleSheetString); } else if (axis == 2) { this->SetBorderColor(m_Controls.m_ZWorldCoordinateSpinBox, colorAsStyleSheetString); } } void QmitkImageNavigatorView::SetBorderColor(QDoubleSpinBox *spinBox, QString colorAsStyleSheetString) { assert(spinBox); spinBox->setStyleSheet(QString("border: 2px solid ") + colorAsStyleSheetString + ";"); } void QmitkImageNavigatorView::SetStepSizes() { this->SetStepSize(0); this->SetStepSize(1); this->SetStepSize(2); } void QmitkImageNavigatorView::SetStepSize(int axis) { if (m_IRenderWindowPart) { - mitk::Geometry3D::ConstPointer geometry = m_IRenderWindowPart->GetActiveQmitkRenderWindow()->GetSliceNavigationController()->GetInputWorldGeometry3D(); + mitk::BaseGeometry::ConstPointer geometry = m_IRenderWindowPart->GetActiveQmitkRenderWindow()->GetSliceNavigationController()->GetInputWorldGeometry3D(); if (geometry.IsNotNull()) { mitk::Point3D crossPositionInIndexCoordinates; mitk::Point3D crossPositionInIndexCoordinatesPlus1; mitk::Point3D crossPositionInMillimetresPlus1; mitk::Vector3D transformedAxisDirection; mitk::Point3D crossPositionInMillimetres = m_IRenderWindowPart->GetSelectedPosition(); geometry->WorldToIndex(crossPositionInMillimetres, crossPositionInIndexCoordinates); crossPositionInIndexCoordinatesPlus1 = crossPositionInIndexCoordinates; crossPositionInIndexCoordinatesPlus1[axis] += 1; geometry->IndexToWorld(crossPositionInIndexCoordinatesPlus1, crossPositionInMillimetresPlus1); transformedAxisDirection = crossPositionInMillimetresPlus1 - crossPositionInMillimetres; int closestAxisInMillimetreSpace = this->GetClosestAxisIndex(transformedAxisDirection); double stepSize = transformedAxisDirection.GetNorm(); this->SetStepSize(closestAxisInMillimetreSpace, stepSize); } } } void QmitkImageNavigatorView::SetStepSize(int axis, double stepSize) { if (axis == 0) { m_Controls.m_XWorldCoordinateSpinBox->setSingleStep(stepSize); } else if (axis == 1) { m_Controls.m_YWorldCoordinateSpinBox->setSingleStep(stepSize); } else if (axis == 2) { m_Controls.m_ZWorldCoordinateSpinBox->setSingleStep(stepSize); } } void QmitkImageNavigatorView::OnMillimetreCoordinateValueChanged() { if (m_IRenderWindowPart) { mitk::TimeGeometry::ConstPointer geometry = m_IRenderWindowPart->GetActiveQmitkRenderWindow()->GetSliceNavigationController()->GetInputWorldTimeGeometry(); if (geometry.IsNotNull()) { mitk::Point3D positionInWorldCoordinates; positionInWorldCoordinates[0] = m_Controls.m_XWorldCoordinateSpinBox->value(); positionInWorldCoordinates[1] = m_Controls.m_YWorldCoordinateSpinBox->value(); positionInWorldCoordinates[2] = m_Controls.m_ZWorldCoordinateSpinBox->value(); m_IRenderWindowPart->SetSelectedPosition(positionInWorldCoordinates); } } } void QmitkImageNavigatorView::OnRefetch() { if (m_IRenderWindowPart) { - mitk::Geometry3D::ConstPointer geometry = m_IRenderWindowPart->GetActiveQmitkRenderWindow()->GetSliceNavigationController()->GetInputWorldGeometry3D(); + mitk::BaseGeometry::ConstPointer geometry = m_IRenderWindowPart->GetActiveQmitkRenderWindow()->GetSliceNavigationController()->GetInputWorldGeometry3D(); mitk::TimeGeometry::ConstPointer timeGeometry = m_IRenderWindowPart->GetActiveQmitkRenderWindow()->GetSliceNavigationController()->GetInputWorldTimeGeometry(); if (geometry.IsNull() && timeGeometry.IsNotNull()) { mitk::TimeStepType timeStep = m_IRenderWindowPart->GetActiveQmitkRenderWindow()->GetSliceNavigationController()->GetTime()->GetPos(); geometry = timeGeometry->GetGeometryForTimeStep(timeStep); } if (geometry.IsNotNull()) { mitk::BoundingBox::BoundsArrayType bounds = geometry->GetBounds(); mitk::Point3D cornerPoint1InIndexCoordinates; cornerPoint1InIndexCoordinates[0] = bounds[0]; cornerPoint1InIndexCoordinates[1] = bounds[2]; cornerPoint1InIndexCoordinates[2] = bounds[4]; mitk::Point3D cornerPoint2InIndexCoordinates; cornerPoint2InIndexCoordinates[0] = bounds[1]; cornerPoint2InIndexCoordinates[1] = bounds[3]; cornerPoint2InIndexCoordinates[2] = bounds[5]; if (!geometry->GetImageGeometry()) { cornerPoint1InIndexCoordinates[0] += 0.5; cornerPoint1InIndexCoordinates[1] += 0.5; cornerPoint1InIndexCoordinates[2] += 0.5; cornerPoint2InIndexCoordinates[0] -= 0.5; cornerPoint2InIndexCoordinates[1] -= 0.5; cornerPoint2InIndexCoordinates[2] -= 0.5; } mitk::Point3D crossPositionInWorldCoordinates = m_IRenderWindowPart->GetSelectedPosition(); mitk::Point3D cornerPoint1InWorldCoordinates; mitk::Point3D cornerPoint2InWorldCoordinates; geometry->IndexToWorld(cornerPoint1InIndexCoordinates, cornerPoint1InWorldCoordinates); geometry->IndexToWorld(cornerPoint2InIndexCoordinates, cornerPoint2InWorldCoordinates); m_Controls.m_XWorldCoordinateSpinBox->blockSignals(true); m_Controls.m_YWorldCoordinateSpinBox->blockSignals(true); m_Controls.m_ZWorldCoordinateSpinBox->blockSignals(true); m_Controls.m_XWorldCoordinateSpinBox->setMinimum(std::min(cornerPoint1InWorldCoordinates[0], cornerPoint2InWorldCoordinates[0])); m_Controls.m_YWorldCoordinateSpinBox->setMinimum(std::min(cornerPoint1InWorldCoordinates[1], cornerPoint2InWorldCoordinates[1])); m_Controls.m_ZWorldCoordinateSpinBox->setMinimum(std::min(cornerPoint1InWorldCoordinates[2], cornerPoint2InWorldCoordinates[2])); m_Controls.m_XWorldCoordinateSpinBox->setMaximum(std::max(cornerPoint1InWorldCoordinates[0], cornerPoint2InWorldCoordinates[0])); m_Controls.m_YWorldCoordinateSpinBox->setMaximum(std::max(cornerPoint1InWorldCoordinates[1], cornerPoint2InWorldCoordinates[1])); m_Controls.m_ZWorldCoordinateSpinBox->setMaximum(std::max(cornerPoint1InWorldCoordinates[2], cornerPoint2InWorldCoordinates[2])); m_Controls.m_XWorldCoordinateSpinBox->setValue(crossPositionInWorldCoordinates[0]); m_Controls.m_YWorldCoordinateSpinBox->setValue(crossPositionInWorldCoordinates[1]); m_Controls.m_ZWorldCoordinateSpinBox->setValue(crossPositionInWorldCoordinates[2]); m_Controls.m_XWorldCoordinateSpinBox->blockSignals(false); m_Controls.m_YWorldCoordinateSpinBox->blockSignals(false); m_Controls.m_ZWorldCoordinateSpinBox->blockSignals(false); } this->SetBorderColors(); } } diff --git a/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSegmentationView.cpp b/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSegmentationView.cpp index ce7494dd13..5dcdc18953 100644 --- a/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSegmentationView.cpp +++ b/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSegmentationView.cpp @@ -1,1226 +1,1226 @@ /*=================================================================== 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 "mitkSegTool2D.h" #include "mitkStatusBar.h" #include "QmitkStdMultiWidget.h" #include "QmitkNewSegmentationDialog.h" #include #include #include "QmitkSegmentationView.h" #include "QmitkSegmentationOrganNamesHandling.cpp" #include #include "mitkVtkResliceInterpolationProperty.h" #include "mitkApplicationCursor.h" #include "mitkSegmentationObjectFactory.h" #include "mitkPluginActivator.h" #include "usModuleResource.h" #include "usModuleResourceStream.h" //micro service to get the ToolManager instance #include "mitkToolManagerProvider.h" const std::string QmitkSegmentationView::VIEW_ID = "org.mitk.views.segmentation"; // public methods QmitkSegmentationView::QmitkSegmentationView() :m_Parent(NULL) ,m_Controls(NULL) ,m_MultiWidget(NULL) ,m_DataSelectionChanged(false) ,m_MouseCursorSet(false) { mitk::NodePredicateDataType::Pointer isDwi = mitk::NodePredicateDataType::New("DiffusionImage"); mitk::NodePredicateDataType::Pointer isDti = mitk::NodePredicateDataType::New("TensorImage"); mitk::NodePredicateDataType::Pointer isQbi = mitk::NodePredicateDataType::New("QBallImage"); mitk::NodePredicateOr::Pointer isDiffusionImage = mitk::NodePredicateOr::New(isDwi, isDti); isDiffusionImage = mitk::NodePredicateOr::New(isDiffusionImage, isQbi); m_IsOfTypeImagePredicate = mitk::NodePredicateOr::New(isDiffusionImage, mitk::TNodePredicateDataType::New()); m_IsBinaryPredicate = mitk::NodePredicateProperty::New("binary", mitk::BoolProperty::New(true)); m_IsNotBinaryPredicate = mitk::NodePredicateNot::New( m_IsBinaryPredicate ); m_IsNotABinaryImagePredicate = mitk::NodePredicateAnd::New( m_IsOfTypeImagePredicate, m_IsNotBinaryPredicate ); m_IsABinaryImagePredicate = mitk::NodePredicateAnd::New( m_IsOfTypeImagePredicate, m_IsBinaryPredicate); } QmitkSegmentationView::~QmitkSegmentationView() { delete m_Controls; } void QmitkSegmentationView::NewNodesGenerated() { MITK_WARN<<"Use of deprecated function: NewNodesGenerated!! This function is empty and will be removed in the next time!"; } void QmitkSegmentationView::NewNodeObjectsGenerated(mitk::ToolManager::DataVectorType* nodes) { if (!nodes) return; mitk::ToolManager* toolManager = mitk::ToolManagerProvider::GetInstance()->GetToolManager(); if (!toolManager) return; for (mitk::ToolManager::DataVectorType::iterator iter = nodes->begin(); iter != nodes->end(); ++iter) { this->FireNodeSelected( *iter ); // only last iteration meaningful, multiple generated objects are not taken into account here } } void QmitkSegmentationView::Visible() { if (m_DataSelectionChanged) { this->OnSelectionChanged(this->GetDataManagerSelection()); } } void QmitkSegmentationView::Activated() { // should be moved to ::BecomesVisible() or similar if( m_Controls ) { m_Controls->m_ManualToolSelectionBox2D->setEnabled( true ); m_Controls->m_ManualToolSelectionBox3D->setEnabled( true ); // m_Controls->m_OrganToolSelectionBox->setEnabled( true ); // m_Controls->m_LesionToolSelectionBox->setEnabled( true ); // m_Controls->m_SlicesInterpolator->Enable3DInterpolation( m_Controls->widgetStack->currentWidget() == m_Controls->pageManual ); mitk::DataStorage::SetOfObjects::ConstPointer segmentations = this->GetDefaultDataStorage()->GetSubset( m_IsABinaryImagePredicate ); mitk::DataStorage::SetOfObjects::ConstPointer image = this->GetDefaultDataStorage()->GetSubset( m_IsNotABinaryImagePredicate ); if (!image->empty()) { OnSelectionChanged(*image->begin()); } for ( mitk::DataStorage::SetOfObjects::const_iterator iter = segmentations->begin(); iter != segmentations->end(); ++iter) { mitk::DataNode* node = *iter; itk::SimpleMemberCommand::Pointer command = itk::SimpleMemberCommand::New(); command->SetCallbackFunction(this, &QmitkSegmentationView::OnWorkingNodeVisibilityChanged); m_WorkingDataObserverTags.insert( std::pair( node, node->GetProperty("visible")->AddObserver( itk::ModifiedEvent(), command ) ) ); itk::SimpleMemberCommand::Pointer command2 = itk::SimpleMemberCommand::New(); command2->SetCallbackFunction(this, &QmitkSegmentationView::OnBinaryPropertyChanged); m_BinaryPropertyObserverTags.insert( std::pair( node, node->GetProperty("binary")->AddObserver( itk::ModifiedEvent(), command2 ) ) ); } } itk::SimpleMemberCommand::Pointer command3 = itk::SimpleMemberCommand::New(); command3->SetCallbackFunction( this, &QmitkSegmentationView::RenderingManagerReinitialized ); m_RenderingManagerObserverTag = mitk::RenderingManager::GetInstance()->AddObserver( mitk::RenderingManagerViewsInitializedEvent(), command3 ); this->SetToolManagerSelection(m_Controls->patImageSelector->GetSelectedNode(), m_Controls->segImageSelector->GetSelectedNode()); } void QmitkSegmentationView::Deactivated() { if( m_Controls ) { this->SetToolSelectionBoxesEnabled( false ); //deactivate all tools mitk::ToolManagerProvider::GetInstance()->GetToolManager()->ActivateTool(-1); //Removing all observers for ( NodeTagMapType::iterator dataIter = m_WorkingDataObserverTags.begin(); dataIter != m_WorkingDataObserverTags.end(); ++dataIter ) { (*dataIter).first->GetProperty("visible")->RemoveObserver( (*dataIter).second ); } m_WorkingDataObserverTags.clear(); for ( NodeTagMapType::iterator dataIter = m_BinaryPropertyObserverTags.begin(); dataIter != m_BinaryPropertyObserverTags.end(); ++dataIter ) { (*dataIter).first->GetProperty("binary")->RemoveObserver( (*dataIter).second ); } m_BinaryPropertyObserverTags.clear(); mitk::RenderingManager::GetInstance()->RemoveObserver(m_RenderingManagerObserverTag); ctkPluginContext* context = mitk::PluginActivator::getContext(); ctkServiceReference ppmRef = context->getServiceReference(); mitk::PlanePositionManagerService* service = context->getService(ppmRef); service->RemoveAllPlanePositions(); context->ungetService(ppmRef); this->SetToolManagerSelection(0,0); } } void QmitkSegmentationView::StdMultiWidgetAvailable( QmitkStdMultiWidget& stdMultiWidget ) { SetMultiWidget(&stdMultiWidget); } void QmitkSegmentationView::StdMultiWidgetNotAvailable() { SetMultiWidget(NULL); } void QmitkSegmentationView::StdMultiWidgetClosed( QmitkStdMultiWidget& /*stdMultiWidget*/ ) { SetMultiWidget(NULL); } void QmitkSegmentationView::SetMultiWidget(QmitkStdMultiWidget* multiWidget) { // save the current multiwidget as the working widget m_MultiWidget = multiWidget; if (m_Parent) { m_Parent->setEnabled(m_MultiWidget); } // tell the interpolation about toolmanager and multiwidget (and data storage) if (m_Controls && m_MultiWidget) { mitk::ToolManager* toolManager = mitk::ToolManagerProvider::GetInstance()->GetToolManager(); m_Controls->m_SlicesInterpolator->SetDataStorage( this->GetDefaultDataStorage()); QList controllers; controllers.push_back(m_MultiWidget->GetRenderWindow1()->GetSliceNavigationController()); controllers.push_back(m_MultiWidget->GetRenderWindow2()->GetSliceNavigationController()); controllers.push_back(m_MultiWidget->GetRenderWindow3()->GetSliceNavigationController()); m_Controls->m_SlicesInterpolator->Initialize( toolManager, controllers ); } } void QmitkSegmentationView::OnPreferencesChanged(const berry::IBerryPreferences* prefs) { if (m_Controls != NULL) { bool slimView = prefs->GetBool("slim view", false); m_Controls->m_ManualToolSelectionBox2D->SetShowNames(!slimView); m_Controls->m_ManualToolSelectionBox3D->SetShowNames(!slimView); } m_AutoSelectionEnabled = prefs->GetBool("auto selection", false); this->ForceDisplayPreferencesUponAllImages(); } void QmitkSegmentationView::CreateNewSegmentation() { mitk::DataNode::Pointer node = mitk::ToolManagerProvider::GetInstance()->GetToolManager()->GetReferenceData(0); if (node.IsNotNull()) { mitk::Image::Pointer image = dynamic_cast( node->GetData() ); if (image.IsNotNull()) { if (image->GetDimension()>1) { // ask about the name and organ type of the new segmentation QmitkNewSegmentationDialog* dialog = new QmitkNewSegmentationDialog( m_Parent ); // needs a QWidget as parent, "this" is not QWidget QString storedList = QString::fromStdString( this->GetPreferences()->GetByteArray("Organ-Color-List","") ); QStringList organColors; if (storedList.isEmpty()) { organColors = GetDefaultOrganColorString(); } else { /* a couple of examples of how organ names are stored: a simple item is built up like 'name#AABBCC' where #AABBCC is the hexadecimal notation of a color as known from HTML items are stored separated by ';' this makes it necessary to escape occurrences of ';' in name. otherwise the string "hugo;ypsilon#AABBCC;eugen#AABBCC" could not be parsed as two organs but we would get "hugo" and "ypsilon#AABBCC" and "eugen#AABBCC" so the organ name "hugo;ypsilon" is stored as "hugo\;ypsilon" and must be unescaped after loading the following lines could be one split with Perl's negative lookbehind */ // recover string list from BlueBerry view's preferences QString storedString = QString::fromStdString( this->GetPreferences()->GetByteArray("Organ-Color-List","") ); MITK_DEBUG << "storedString: " << storedString.toStdString(); // match a string consisting of any number of repetitions of either "anything but ;" or "\;". This matches everything until the next unescaped ';' QRegExp onePart("(?:[^;]|\\\\;)*"); MITK_DEBUG << "matching " << onePart.pattern().toStdString(); int count = 0; int pos = 0; while( (pos = onePart.indexIn( storedString, pos )) != -1 ) { ++count; int length = onePart.matchedLength(); if (length == 0) break; QString matchedString = storedString.mid(pos, length); MITK_DEBUG << " Captured length " << length << ": " << matchedString.toStdString(); pos += length + 1; // skip separating ';' // unescape possible occurrences of '\;' in the string matchedString.replace("\\;", ";"); // add matched string part to output list organColors << matchedString; } MITK_DEBUG << "Captured " << count << " organ name/colors"; } dialog->SetSuggestionList( organColors ); int dialogReturnValue = dialog->exec(); if ( dialogReturnValue == QDialog::Rejected ) return; // user clicked cancel or pressed Esc or something similar // ask the user about an organ type and name, add this information to the image's (!) propertylist // create a new image of the same dimensions and smallest possible pixel type mitk::ToolManager* toolManager = mitk::ToolManagerProvider::GetInstance()->GetToolManager(); mitk::Tool* firstTool = toolManager->GetToolById(0); if (firstTool) { try { std::string newNodeName = dialog->GetSegmentationName().toStdString(); if(newNodeName.empty()) newNodeName = "no_name"; mitk::DataNode::Pointer emptySegmentation = firstTool->CreateEmptySegmentationNode( image, newNodeName, dialog->GetColor() ); // initialize showVolume to false to prevent recalculating the volume while working on the segmentation emptySegmentation->SetProperty( "showVolume", mitk::BoolProperty::New( false ) ); if (!emptySegmentation) return; // could be aborted by user UpdateOrganList( organColors, dialog->GetSegmentationName(), dialog->GetColor() ); /* escape ';' here (replace by '\;'), see longer comment above */ std::string stringForStorage = organColors.replaceInStrings(";","\\;").join(";").toStdString(); MITK_DEBUG << "Will store: " << stringForStorage; this->GetPreferences()->PutByteArray("Organ-Color-List", stringForStorage ); this->GetPreferences()->Flush(); if(mitk::ToolManagerProvider::GetInstance()->GetToolManager()->GetWorkingData(0)) { mitk::ToolManagerProvider::GetInstance()->GetToolManager()->GetWorkingData(0)->SetSelected(false); } emptySegmentation->SetSelected(true); this->GetDefaultDataStorage()->Add( emptySegmentation, node ); // add as a child, because the segmentation "derives" from the original this->ApplyDisplayOptions( emptySegmentation ); this->FireNodeSelected( emptySegmentation ); this->OnSelectionChanged( emptySegmentation ); m_Controls->segImageSelector->SetSelectedNode(emptySegmentation); } catch (std::bad_alloc) { QMessageBox::warning(NULL,"Create new segmentation","Could not allocate memory for new segmentation"); } } } else { QMessageBox::information(NULL,"Segmentation","Segmentation is currently not supported for 2D images"); } } } else { MITK_ERROR << "'Create new segmentation' button should never be clickable unless a patient image is selected..."; } } void QmitkSegmentationView::OnWorkingNodeVisibilityChanged() { mitk::DataNode* selectedNode = m_Controls->segImageSelector->GetSelectedNode(); bool selectedNodeIsVisible = selectedNode->IsVisible(mitk::BaseRenderer::GetInstance( mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget1"))); if (!selectedNodeIsVisible) { this->SetToolSelectionBoxesEnabled(false); this->UpdateWarningLabel("The selected segmentation is currently not visible!"); } else { this->SetToolSelectionBoxesEnabled(true); this->UpdateWarningLabel(""); } } void QmitkSegmentationView::OnBinaryPropertyChanged() { mitk::DataStorage::SetOfObjects::ConstPointer patImages = m_Controls->patImageSelector->GetNodes(); bool isBinary(false); for (mitk::DataStorage::SetOfObjects::ConstIterator it = patImages->Begin(); it != patImages->End(); ++it) { const mitk::DataNode* node = it->Value(); node->GetBoolProperty("binary", isBinary); if(isBinary) { m_Controls->patImageSelector->RemoveNode(node); m_Controls->segImageSelector->AddNode(node); this->SetToolManagerSelection(NULL,NULL); return; } } mitk::DataStorage::SetOfObjects::ConstPointer segImages = m_Controls->segImageSelector->GetNodes(); isBinary = true; for (mitk::DataStorage::SetOfObjects::ConstIterator it = segImages->Begin(); it != segImages->End(); ++it) { const mitk::DataNode* node = it->Value(); node->GetBoolProperty("binary", isBinary); if(!isBinary) { m_Controls->segImageSelector->RemoveNode(node); m_Controls->patImageSelector->AddNode(node); if (mitk::ToolManagerProvider::GetInstance()->GetToolManager()->GetWorkingData(0) == node) mitk::ToolManagerProvider::GetInstance()->GetToolManager()->SetWorkingData(NULL); return; } } } void QmitkSegmentationView::NodeAdded(const mitk::DataNode *node) { bool isBinary (false); bool isHelperObject (false); node->GetBoolProperty("binary", isBinary); node->GetBoolProperty("helper object", isHelperObject); if (m_AutoSelectionEnabled) { if (!isBinary && dynamic_cast(node->GetData())) { FireNodeSelected(const_cast(node)); } } if (isBinary && !isHelperObject) { itk::SimpleMemberCommand::Pointer command = itk::SimpleMemberCommand::New(); command->SetCallbackFunction(this, &QmitkSegmentationView::OnWorkingNodeVisibilityChanged); m_WorkingDataObserverTags.insert( std::pair( const_cast(node), node->GetProperty("visible")->AddObserver( itk::ModifiedEvent(), command ) ) ); itk::SimpleMemberCommand::Pointer command2 = itk::SimpleMemberCommand::New(); command2->SetCallbackFunction(this, &QmitkSegmentationView::OnBinaryPropertyChanged); m_BinaryPropertyObserverTags.insert( std::pair( const_cast(node), node->GetProperty("binary")->AddObserver( itk::ModifiedEvent(), command2 ) ) ); this->ApplyDisplayOptions( const_cast(node) ); m_Controls->segImageSelector->setCurrentIndex( m_Controls->segImageSelector->Find(node) ); } } void QmitkSegmentationView::NodeRemoved(const mitk::DataNode* node) { bool isSeg(false); bool isHelperObject(false); node->GetBoolProperty("helper object", isHelperObject); node->GetBoolProperty("binary", isSeg); mitk::Image* image = dynamic_cast(node->GetData()); if(isSeg && !isHelperObject && image) { //First of all remove all possible contour markers of the segmentation mitk::DataStorage::SetOfObjects::ConstPointer allContourMarkers = this->GetDataStorage()->GetDerivations(node, mitk::NodePredicateProperty::New("isContourMarker" , mitk::BoolProperty::New(true))); ctkPluginContext* context = mitk::PluginActivator::getContext(); ctkServiceReference ppmRef = context->getServiceReference(); mitk::PlanePositionManagerService* service = context->getService(ppmRef); for (mitk::DataStorage::SetOfObjects::ConstIterator it = allContourMarkers->Begin(); it != allContourMarkers->End(); ++it) { std::string nodeName = node->GetName(); unsigned int t = nodeName.find_last_of(" "); unsigned int id = atof(nodeName.substr(t+1).c_str())-1; service->RemovePlanePosition(id); this->GetDataStorage()->Remove(it->Value()); } context->ungetService(ppmRef); service = NULL; if ((mitk::ToolManagerProvider::GetInstance()->GetToolManager()->GetWorkingData(0) == node) && m_Controls->patImageSelector->GetSelectedNode().IsNotNull()) { this->SetToolManagerSelection(mitk::ToolManagerProvider::GetInstance()->GetToolManager()->GetReferenceData(0), NULL); this->UpdateWarningLabel("Select or create a segmentation"); } mitk::SurfaceInterpolationController::GetInstance()->RemoveSegmentationFromContourList(image); } mitk::DataNode* tempNode = const_cast(node); //Since the binary property could be changed during runtime by the user if (image && !isHelperObject) { node->GetProperty("visible")->RemoveObserver( m_WorkingDataObserverTags[tempNode] ); m_WorkingDataObserverTags.erase(tempNode); node->GetProperty("binary")->RemoveObserver( m_BinaryPropertyObserverTags[tempNode] ); m_BinaryPropertyObserverTags.erase(tempNode); } if((mitk::ToolManagerProvider::GetInstance()->GetToolManager()->GetReferenceData(0) == node)) { //as we don't know which node was actually removed e.g. our reference node, disable 'New Segmentation' button. //consider the case that there is no more image in the datastorage this->SetToolManagerSelection(NULL, NULL); this->SetToolSelectionBoxesEnabled( false ); } } //void QmitkSegmentationView::CreateSegmentationFromSurface() //{ // mitk::DataNode::Pointer surfaceNode = // m_Controls->MaskSurfaces->GetSelectedNode(); // mitk::Surface::Pointer surface(0); // if(surfaceNode.IsNotNull()) // surface = dynamic_cast ( surfaceNode->GetData() ); // if(surface.IsNull()) // { // this->HandleException( "No surface selected.", m_Parent, true); // return; // } // mitk::DataNode::Pointer imageNode // = mitk::ToolManagerProvider::GetInstance()->GetToolManager()->GetReferenceData(0); // mitk::Image::Pointer image(0); // if (imageNode.IsNotNull()) // image = dynamic_cast( imageNode->GetData() ); // if(image.IsNull()) // { // this->HandleException( "No image selected.", m_Parent, true); // return; // } // mitk::SurfaceToImageFilter::Pointer s2iFilter // = mitk::SurfaceToImageFilter::New(); // s2iFilter->MakeOutputBinaryOn(); // s2iFilter->SetInput(surface); // s2iFilter->SetImage(image); // s2iFilter->Update(); // mitk::DataNode::Pointer resultNode = mitk::DataNode::New(); // std::string nameOfResultImage = imageNode->GetName(); // nameOfResultImage.append(surfaceNode->GetName()); // resultNode->SetProperty("name", mitk::StringProperty::New(nameOfResultImage) ); // resultNode->SetProperty("binary", mitk::BoolProperty::New(true) ); // resultNode->SetData( s2iFilter->GetOutput() ); // this->GetDataStorage()->Add(resultNode, imageNode); //} //void QmitkSegmentationView::ToolboxStackPageChanged(int id) //{ // // interpolation only with manual tools visible // m_Controls->m_SlicesInterpolator->EnableInterpolation( id == 0 ); // if( id == 0 ) // { // mitk::DataNode::Pointer workingData = mitk::ToolManagerProvider::GetInstance()->GetToolManager()->GetWorkingData(0); // if( workingData.IsNotNull() ) // { // m_Controls->segImageSelector->setCurrentIndex( m_Controls->segImageSelector->Find(workingData) ); // } // } // // this is just a workaround, should be removed when all tools support 3D+t // if (id==2) // lesions // { // mitk::DataNode::Pointer node = mitk::ToolManagerProvider::GetInstance()->GetToolManager()->GetReferenceData(0); // if (node.IsNotNull()) // { // mitk::Image::Pointer image = dynamic_cast( node->GetData() ); // if (image.IsNotNull()) // { // if (image->GetDimension()>3) // { // m_Controls->widgetStack->setCurrentIndex(0); // QMessageBox::information(NULL,"Segmentation","Lesion segmentation is currently not supported for 4D images"); // } // } // } // } //} // protected void QmitkSegmentationView::OnPatientComboBoxSelectionChanged( const mitk::DataNode* node ) { //mitk::DataNode* selectedNode = const_cast(node); if( node != NULL ) { this->UpdateWarningLabel(""); mitk::DataNode* segNode = m_Controls->segImageSelector->GetSelectedNode(); if (segNode) { mitk::DataStorage::SetOfObjects::ConstPointer possibleParents = this->GetDefaultDataStorage()->GetSources( segNode, m_IsNotABinaryImagePredicate ); bool isSourceNode(false); for (mitk::DataStorage::SetOfObjects::ConstIterator it = possibleParents->Begin(); it != possibleParents->End(); it++) { if (it.Value() == node) isSourceNode = true; } if ( !isSourceNode && (!this->CheckForSameGeometry(segNode, node) || possibleParents->Size() > 0 )) { this->SetToolManagerSelection(node, NULL); this->SetToolSelectionBoxesEnabled( false ); this->UpdateWarningLabel("The selected patient image does not match with the selected segmentation!"); } else if ((!isSourceNode && this->CheckForSameGeometry(segNode, node)) || isSourceNode ) { this->SetToolManagerSelection(node, segNode); //Doing this we can assure that the segmenation is always visible if the segmentation and the patient image are //loaded separately int layer(10); node->GetIntProperty("layer", layer); layer++; segNode->SetProperty("layer", mitk::IntProperty::New(layer)); //this->UpdateWarningLabel(""); RenderingManagerReinitialized(); } } else { this->SetToolManagerSelection(node, NULL); this->SetToolSelectionBoxesEnabled( false ); this->UpdateWarningLabel("Select or create a segmentation"); } } else { this->UpdateWarningLabel("Please load an image!"); this->SetToolSelectionBoxesEnabled( false ); } } void QmitkSegmentationView::OnSegmentationComboBoxSelectionChanged(const mitk::DataNode *node) { if (node == NULL) { this->UpdateWarningLabel("Select or create a segmentation"); this->SetToolSelectionBoxesEnabled( false ); return; } mitk::DataNode* refNode = m_Controls->patImageSelector->GetSelectedNode(); RenderingManagerReinitialized(); if ( m_Controls->lblSegmentationWarnings->isVisible()) // "RenderingManagerReinitialized()" caused a warning. we do not nede to go any further return; if (m_AutoSelectionEnabled) { this->OnSelectionChanged(const_cast(node)); } else { mitk::DataStorage::SetOfObjects::ConstPointer possibleParents = this->GetDefaultDataStorage()->GetSources( node, m_IsNotABinaryImagePredicate ); if ( possibleParents->Size() == 1 ) { mitk::DataNode* parentNode = possibleParents->ElementAt(0); if (parentNode != refNode) { this->UpdateWarningLabel("The selected segmentation does not match with the selected patient image!"); this->SetToolSelectionBoxesEnabled( false ); this->SetToolManagerSelection(NULL, node); } else { this->UpdateWarningLabel(""); this->SetToolManagerSelection(refNode, node); } } else if (refNode && this->CheckForSameGeometry(node, refNode)) { this->UpdateWarningLabel(""); this->SetToolManagerSelection(refNode, node); } else if (!refNode || !this->CheckForSameGeometry(node, refNode)) { this->UpdateWarningLabel("Please select or load the according patient image!"); } } if (!node->IsVisible(mitk::BaseRenderer::GetInstance( mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget1")))) { this->UpdateWarningLabel("The selected segmentation is currently not visible!"); this->SetToolSelectionBoxesEnabled( false ); } } void QmitkSegmentationView::OnShowMarkerNodes (bool state) { mitk::SegTool2D::Pointer manualSegmentationTool; unsigned int numberOfExistingTools = mitk::ToolManagerProvider::GetInstance()->GetToolManager()->GetTools().size(); for(unsigned int i = 0; i < numberOfExistingTools; i++) { manualSegmentationTool = dynamic_cast(mitk::ToolManagerProvider::GetInstance()->GetToolManager()->GetToolById(i)); if (manualSegmentationTool) { if(state == true) { manualSegmentationTool->SetShowMarkerNodes( true ); } else { manualSegmentationTool->SetShowMarkerNodes( false ); } } } } void QmitkSegmentationView::OnSelectionChanged(mitk::DataNode* node) { std::vector nodes; nodes.push_back( node ); this->OnSelectionChanged( nodes ); } void QmitkSegmentationView::OnSelectionChanged(std::vector nodes) { if (nodes.size() != 0) { std::string markerName = "Position"; unsigned int numberOfNodes = nodes.size(); std::string nodeName = nodes.at( 0 )->GetName(); if ( ( numberOfNodes == 1 ) && ( nodeName.find( markerName ) == 0) ) { this->OnContourMarkerSelected( nodes.at( 0 ) ); return; } } if (m_AutoSelectionEnabled && this->IsActivated()) { if (nodes.size() == 0 && m_Controls->patImageSelector->GetSelectedNode().IsNull()) { SetToolManagerSelection(NULL,NULL); } else if (nodes.size() == 1) { mitk::DataNode::Pointer selectedNode = nodes.at(0); if(selectedNode.IsNull()) { return; } mitk::Image::Pointer selectedImage = dynamic_cast(selectedNode->GetData()); if (selectedImage.IsNull()) { SetToolManagerSelection(NULL,NULL); return; } else { bool isASegmentation(false); selectedNode->GetBoolProperty("binary", isASegmentation); if (isASegmentation) { //If a segmentation is selected find a possible reference image: mitk::DataStorage::SetOfObjects::ConstPointer sources = this->GetDataStorage()->GetSources(selectedNode, m_IsNotABinaryImagePredicate); mitk::DataNode::Pointer refNode; if (sources->Size() != 0) { refNode = sources->ElementAt(0); refNode->SetVisibility(true); selectedNode->SetVisibility(true); SetToolManagerSelection(refNode,selectedNode); mitk::DataStorage::SetOfObjects::ConstPointer otherSegmentations = this->GetDataStorage()->GetSubset(m_IsABinaryImagePredicate); for(mitk::DataStorage::SetOfObjects::const_iterator iter = otherSegmentations->begin(); iter != otherSegmentations->end(); ++iter) { mitk::DataNode* node = *iter; if (dynamic_cast(node->GetData()) != selectedImage.GetPointer()) node->SetVisibility(false); } mitk::DataStorage::SetOfObjects::ConstPointer otherPatientImages = this->GetDataStorage()->GetSubset(m_IsNotABinaryImagePredicate); for(mitk::DataStorage::SetOfObjects::const_iterator iter = otherPatientImages->begin(); iter != otherPatientImages->end(); ++iter) { mitk::DataNode* node = *iter; if (dynamic_cast(node->GetData()) != dynamic_cast(refNode->GetData())) node->SetVisibility(false); } } else { mitk::DataStorage::SetOfObjects::ConstPointer possiblePatientImages = this->GetDataStorage()->GetSubset(m_IsNotABinaryImagePredicate); for (mitk::DataStorage::SetOfObjects::ConstIterator it = possiblePatientImages->Begin(); it != possiblePatientImages->End(); it++) { refNode = it->Value(); if (this->CheckForSameGeometry(selectedNode, it->Value())) { refNode->SetVisibility(true); selectedNode->SetVisibility(true); mitk::DataStorage::SetOfObjects::ConstPointer otherSegmentations = this->GetDataStorage()->GetSubset(m_IsABinaryImagePredicate); for(mitk::DataStorage::SetOfObjects::const_iterator iter = otherSegmentations->begin(); iter != otherSegmentations->end(); ++iter) { mitk::DataNode* node = *iter; if (dynamic_cast(node->GetData()) != selectedImage.GetPointer()) node->SetVisibility(false); } mitk::DataStorage::SetOfObjects::ConstPointer otherPatientImages = this->GetDataStorage()->GetSubset(m_IsNotABinaryImagePredicate); for(mitk::DataStorage::SetOfObjects::const_iterator iter = otherPatientImages->begin(); iter != otherPatientImages->end(); ++iter) { mitk::DataNode* node = *iter; if (dynamic_cast(node->GetData()) != dynamic_cast(refNode->GetData())) node->SetVisibility(false); } this->SetToolManagerSelection(refNode, selectedNode); //Doing this we can assure that the segmenation is always visible if the segmentation and the patient image are at the //same level in the datamanager int layer(10); refNode->GetIntProperty("layer", layer); layer++; selectedNode->SetProperty("layer", mitk::IntProperty::New(layer)); return; } } this->SetToolManagerSelection(NULL, selectedNode); } } else { if (mitk::ToolManagerProvider::GetInstance()->GetToolManager()->GetReferenceData(0) != selectedNode) { SetToolManagerSelection(selectedNode, NULL); //May be a bug in the selection services. A node which is deselected will be passed as selected node to the OnSelectionChanged function if (!selectedNode->IsVisible(mitk::BaseRenderer::GetInstance( mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget1")))) selectedNode->SetVisibility(true); this->UpdateWarningLabel("The selected patient image does not\nmatchwith the selected segmentation!"); this->SetToolSelectionBoxesEnabled( false ); } } } } mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } void QmitkSegmentationView::OnContourMarkerSelected(const mitk::DataNode *node) { QmitkRenderWindow* selectedRenderWindow = 0; QmitkRenderWindow* RenderWindow1 = this->GetActiveStdMultiWidget()->GetRenderWindow1(); QmitkRenderWindow* RenderWindow2 = this->GetActiveStdMultiWidget()->GetRenderWindow2(); QmitkRenderWindow* RenderWindow3 = this->GetActiveStdMultiWidget()->GetRenderWindow3(); QmitkRenderWindow* RenderWindow4 = this->GetActiveStdMultiWidget()->GetRenderWindow4(); bool PlanarFigureInitializedWindow = false; // find initialized renderwindow if (node->GetBoolProperty("PlanarFigureInitializedWindow", PlanarFigureInitializedWindow, RenderWindow1->GetRenderer())) { selectedRenderWindow = RenderWindow1; } if (!selectedRenderWindow && node->GetBoolProperty( "PlanarFigureInitializedWindow", PlanarFigureInitializedWindow, RenderWindow2->GetRenderer())) { selectedRenderWindow = RenderWindow2; } if (!selectedRenderWindow && node->GetBoolProperty( "PlanarFigureInitializedWindow", PlanarFigureInitializedWindow, RenderWindow3->GetRenderer())) { selectedRenderWindow = RenderWindow3; } if (!selectedRenderWindow && node->GetBoolProperty( "PlanarFigureInitializedWindow", PlanarFigureInitializedWindow, RenderWindow4->GetRenderer())) { selectedRenderWindow = RenderWindow4; } // make node visible if (selectedRenderWindow) { std::string nodeName = node->GetName(); unsigned int t = nodeName.find_last_of(" "); unsigned int id = atof(nodeName.substr(t+1).c_str())-1; { ctkPluginContext* context = mitk::PluginActivator::getContext(); ctkServiceReference ppmRef = context->getServiceReference(); mitk::PlanePositionManagerService* service = context->getService(ppmRef); selectedRenderWindow->GetSliceNavigationController()->ExecuteOperation(service->GetPlanePosition(id)); context->ungetService(ppmRef); } selectedRenderWindow->GetRenderer()->GetDisplayGeometry()->Fit(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } void QmitkSegmentationView::OnTabWidgetChanged(int id) { //always disable tools on tab changed mitk::ToolManagerProvider::GetInstance()->GetToolManager()->ActivateTool(-1); //2D Tab ID = 0 //3D Tab ID = 1 if (id == 0) { //Hide 3D selection box, show 2D selection box m_Controls->m_ManualToolSelectionBox3D->hide(); m_Controls->m_ManualToolSelectionBox2D->show(); //Deactivate possible active tool //TODO Remove possible visible interpolations -> Maybe changes in SlicesInterpolator } else { //Hide 3D selection box, show 2D selection box m_Controls->m_ManualToolSelectionBox2D->hide(); m_Controls->m_ManualToolSelectionBox3D->show(); //Deactivate possible active tool } } void QmitkSegmentationView::SetToolManagerSelection(const mitk::DataNode* referenceData, const mitk::DataNode* workingData) { // called as a result of new BlueBerry selections // tells the ToolManager for manual segmentation about new selections // updates GUI information about what the user should select mitk::ToolManager* toolManager = mitk::ToolManagerProvider::GetInstance()->GetToolManager(); toolManager->SetReferenceData(const_cast(referenceData)); toolManager->SetWorkingData( const_cast(workingData)); // check original image m_Controls->btnNewSegmentation->setEnabled(referenceData != NULL); if (referenceData) { this->UpdateWarningLabel(""); disconnect( m_Controls->patImageSelector, SIGNAL( OnSelectionChanged( const mitk::DataNode* ) ), this, SLOT( OnPatientComboBoxSelectionChanged( const mitk::DataNode* ) ) ); m_Controls->patImageSelector->setCurrentIndex( m_Controls->patImageSelector->Find(referenceData) ); connect( m_Controls->patImageSelector, SIGNAL( OnSelectionChanged( const mitk::DataNode* ) ), this, SLOT( OnPatientComboBoxSelectionChanged( const mitk::DataNode* ) ) ); } // check segmentation if (referenceData) { if (workingData) { this->FireNodeSelected(const_cast(workingData)); // if( m_Controls->widgetStack->currentIndex() == 0 ) // { disconnect( m_Controls->segImageSelector, SIGNAL( OnSelectionChanged( const mitk::DataNode* ) ), this, SLOT( OnSegmentationComboBoxSelectionChanged( const mitk::DataNode* ) ) ); m_Controls->segImageSelector->setCurrentIndex(m_Controls->segImageSelector->Find(workingData)); connect( m_Controls->segImageSelector, SIGNAL( OnSelectionChanged( const mitk::DataNode* ) ), this, SLOT( OnSegmentationComboBoxSelectionChanged(const mitk::DataNode*)) ); // } } } } void QmitkSegmentationView::ForceDisplayPreferencesUponAllImages() { if (!m_Parent || !m_Parent->isVisible()) return; // check all images and segmentations in DataStorage: // (items in brackets are implicitly done by previous steps) // 1. // if a reference image is selected, // show the reference image // and hide all other images (orignal and segmentation), // (and hide all segmentations of the other original images) // and show all the reference's segmentations // if no reference image is selected, do do nothing // // 2. // if a segmentation is selected, // show it // (and hide all all its siblings (childs of the same parent, incl, NULL parent)) // if no segmentation is selected, do nothing if (!m_Controls) return; // might happen on initialization (preferences loaded) mitk::ToolManager* toolManager = mitk::ToolManagerProvider::GetInstance()->GetToolManager(); mitk::DataNode::Pointer referenceData = toolManager->GetReferenceData(0); mitk::DataNode::Pointer workingData = toolManager->GetWorkingData(0); // 1. if (referenceData.IsNotNull()) { // iterate all images mitk::DataStorage::SetOfObjects::ConstPointer allImages = this->GetDefaultDataStorage()->GetSubset( m_IsABinaryImagePredicate ); for ( mitk::DataStorage::SetOfObjects::const_iterator iter = allImages->begin(); iter != allImages->end(); ++iter) { mitk::DataNode* node = *iter; // apply display preferences ApplyDisplayOptions(node); // set visibility node->SetVisibility(node == referenceData); } } // 2. if (workingData.IsNotNull()) workingData->SetVisibility(true); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkSegmentationView::ApplyDisplayOptions(mitk::DataNode* node) { if (!node) return; bool isBinary(false); node->GetPropertyValue("binary", isBinary); if (isBinary) { node->SetProperty( "outline binary", mitk::BoolProperty::New( this->GetPreferences()->GetBool("draw outline", true)) ); node->SetProperty( "outline width", mitk::FloatProperty::New( 2.0 ) ); node->SetProperty( "opacity", mitk::FloatProperty::New( this->GetPreferences()->GetBool("draw outline", true) ? 1.0 : 0.3 ) ); node->SetProperty( "volumerendering", mitk::BoolProperty::New( this->GetPreferences()->GetBool("volume rendering", false) ) ); } } void QmitkSegmentationView::RenderingManagerReinitialized() { if ( ! m_MultiWidget ) { return; } /* * Here we check whether the geometry of the selected segmentation image if aligned with the worldgeometry * At the moment it is not supported to use a geometry different from the selected image for reslicing. * For further information see Bug 16063 */ mitk::DataNode* workingNode = m_Controls->segImageSelector->GetSelectedNode(); - const mitk::Geometry3D* worldGeo = m_MultiWidget->GetRenderWindow4()->GetSliceNavigationController()->GetCurrentGeometry3D(); + const mitk::BaseGeometry* worldGeo = m_MultiWidget->GetRenderWindow4()->GetSliceNavigationController()->GetCurrentGeometry3D(); if (workingNode && worldGeo) { - const mitk::Geometry3D* workingNodeGeo = workingNode->GetData()->GetGeometry(); + const mitk::BaseGeometry* workingNodeGeo = workingNode->GetData()->GetGeometry(); if (mitk::Equal(workingNodeGeo->GetBoundingBox(), worldGeo->GetBoundingBox(), mitk::eps, true)) { this->SetToolManagerSelection(m_Controls->patImageSelector->GetSelectedNode(), workingNode); this->SetToolSelectionBoxesEnabled(true); this->UpdateWarningLabel(""); } else { this->SetToolManagerSelection(m_Controls->patImageSelector->GetSelectedNode(), NULL); this->SetToolSelectionBoxesEnabled(false); this->UpdateWarningLabel("Please perform a reinit on the segmentation image!"); } } } bool QmitkSegmentationView::CheckForSameGeometry(const mitk::DataNode *node1, const mitk::DataNode *node2) const { bool isSameGeometry(true); mitk::Image* image1 = dynamic_cast(node1->GetData()); mitk::Image* image2 = dynamic_cast(node2->GetData()); if (image1 && image2) { - mitk::Geometry3D* geo1 = image1->GetGeometry(); - mitk::Geometry3D* geo2 = image2->GetGeometry(); + mitk::BaseGeometry* geo1 = image1->GetGeometry(); + mitk::BaseGeometry* geo2 = image2->GetGeometry(); isSameGeometry = isSameGeometry && mitk::Equal(geo1->GetOrigin(), geo2->GetOrigin()); isSameGeometry = isSameGeometry && mitk::Equal(geo1->GetExtent(0), geo2->GetExtent(0)); isSameGeometry = isSameGeometry && mitk::Equal(geo1->GetExtent(1), geo2->GetExtent(1)); isSameGeometry = isSameGeometry && mitk::Equal(geo1->GetExtent(2), geo2->GetExtent(2)); isSameGeometry = isSameGeometry && mitk::Equal(geo1->GetSpacing(), geo2->GetSpacing()); isSameGeometry = isSameGeometry && mitk::MatrixEqualElementWise(geo1->GetIndexToWorldTransform()->GetMatrix(), geo2->GetIndexToWorldTransform()->GetMatrix()); return isSameGeometry; } else { return false; } } void QmitkSegmentationView::UpdateWarningLabel(QString text) { if (text.size() == 0) m_Controls->lblSegmentationWarnings->hide(); else m_Controls->lblSegmentationWarnings->show(); m_Controls->lblSegmentationWarnings->setText(text); } void QmitkSegmentationView::CreateQtPartControl(QWidget* parent) { // setup the basic GUI of this view m_Parent = parent; m_Controls = new Ui::QmitkSegmentationControls; m_Controls->setupUi(parent); m_Controls->patImageSelector->SetDataStorage(this->GetDefaultDataStorage()); m_Controls->patImageSelector->SetPredicate(m_IsNotABinaryImagePredicate); this->UpdateWarningLabel("Please load an image"); if( m_Controls->patImageSelector->GetSelectedNode().IsNotNull() ) this->UpdateWarningLabel("Select or create a new segmentation"); m_Controls->segImageSelector->SetDataStorage(this->GetDefaultDataStorage()); m_Controls->segImageSelector->SetPredicate(m_IsABinaryImagePredicate); if( m_Controls->segImageSelector->GetSelectedNode().IsNotNull() ) this->UpdateWarningLabel(""); mitk::ToolManager* toolManager = mitk::ToolManagerProvider::GetInstance()->GetToolManager(); assert ( toolManager ); toolManager->SetDataStorage( *(this->GetDefaultDataStorage()) ); toolManager->InitializeTools(); // all part of open source MITK m_Controls->m_ManualToolSelectionBox2D->SetGenerateAccelerators(true); m_Controls->m_ManualToolSelectionBox2D->SetToolGUIArea( m_Controls->m_ManualToolGUIContainer2D ); m_Controls->m_ManualToolSelectionBox2D->SetDisplayedToolGroups("Add Subtract Correction Paint Wipe 'Region Growing' Fill Erase 'Live Wire' '2D Fast Marching'"); m_Controls->m_ManualToolSelectionBox2D->SetLayoutColumns(3); m_Controls->m_ManualToolSelectionBox2D->SetEnabledMode( QmitkToolSelectionBox::EnabledWithReferenceAndWorkingDataVisible ); connect( m_Controls->m_ManualToolSelectionBox2D, SIGNAL(ToolSelected(int)), this, SLOT(OnManualTool2DSelected(int)) ); //setup 3D Tools m_Controls->m_ManualToolSelectionBox3D->SetGenerateAccelerators(true); m_Controls->m_ManualToolSelectionBox3D->SetToolGUIArea( m_Controls->m_ManualToolGUIContainer3D ); //specify tools to be added to 3D Tool area m_Controls->m_ManualToolSelectionBox3D->SetDisplayedToolGroups("Threshold 'UL Threshold' Otsu 'Fast Marching 3D' 'Region Growing 3D' Watershed Picking"); m_Controls->m_ManualToolSelectionBox3D->SetLayoutColumns(3); m_Controls->m_ManualToolSelectionBox3D->SetEnabledMode( QmitkToolSelectionBox::EnabledWithReferenceAndWorkingDataVisible ); //Hide 3D selection box, show 2D selection box m_Controls->m_ManualToolSelectionBox3D->hide(); m_Controls->m_ManualToolSelectionBox2D->show(); toolManager->NewNodesGenerated += mitk::MessageDelegate( this, &QmitkSegmentationView::NewNodesGenerated ); // update the list of segmentations toolManager->NewNodeObjectsGenerated += mitk::MessageDelegate1( this, &QmitkSegmentationView::NewNodeObjectsGenerated ); // update the list of segmentations // create signal/slot connections connect( m_Controls->patImageSelector, SIGNAL( OnSelectionChanged( const mitk::DataNode* ) ), this, SLOT( OnPatientComboBoxSelectionChanged( const mitk::DataNode* ) ) ); connect( m_Controls->segImageSelector, SIGNAL( OnSelectionChanged( const mitk::DataNode* ) ), this, SLOT( OnSegmentationComboBoxSelectionChanged( const mitk::DataNode* ) ) ); connect( m_Controls->btnNewSegmentation, SIGNAL(clicked()), this, SLOT(CreateNewSegmentation()) ); // connect( m_Controls->CreateSegmentationFromSurface, SIGNAL(clicked()), this, SLOT(CreateSegmentationFromSurface()) ); // connect( m_Controls->widgetStack, SIGNAL(currentChanged(int)), this, SLOT(ToolboxStackPageChanged(int)) ); connect( m_Controls->tabWidgetSegmentationTools, SIGNAL(currentChanged(int)), this, SLOT(OnTabWidgetChanged(int))); // connect(m_Controls->MaskSurfaces, SIGNAL( OnSelectionChanged( const mitk::DataNode* ) ), // this, SLOT( OnSurfaceSelectionChanged( ) ) ); connect(m_Controls->m_SlicesInterpolator, SIGNAL(SignalShowMarkerNodes(bool)), this, SLOT(OnShowMarkerNodes(bool))); // m_Controls->MaskSurfaces->SetDataStorage(this->GetDefaultDataStorage()); // m_Controls->MaskSurfaces->SetPredicate(mitk::NodePredicateDataType::New("Surface")); } void QmitkSegmentationView::OnManualTool2DSelected(int id) { if (id >= 0) { std::string text = "Active Tool: \""; mitk::ToolManager* toolManager = mitk::ToolManagerProvider::GetInstance()->GetToolManager(); text += toolManager->GetToolById(id)->GetName(); text += "\""; mitk::StatusBar::GetInstance()->DisplayText(text.c_str()); us::ModuleResource resource = toolManager->GetToolById(id)->GetCursorIconResource(); this->SetMouseCursor(resource, 0, 0); } else { this->ResetMouseCursor(); mitk::StatusBar::GetInstance()->DisplayText(""); } } void QmitkSegmentationView::ResetMouseCursor() { if ( m_MouseCursorSet ) { mitk::ApplicationCursor::GetInstance()->PopCursor(); m_MouseCursorSet = false; } } void QmitkSegmentationView::SetMouseCursor( const us::ModuleResource& resource, int hotspotX, int hotspotY ) { if (!resource) return; // Remove previously set mouse cursor if ( m_MouseCursorSet ) { mitk::ApplicationCursor::GetInstance()->PopCursor(); } us::ModuleResourceStream cursor(resource, std::ios::binary); mitk::ApplicationCursor::GetInstance()->PushCursor( cursor, hotspotX, hotspotY ); m_MouseCursorSet = true; } void QmitkSegmentationView::SetToolSelectionBoxesEnabled(bool status) { m_Controls->m_ManualToolSelectionBox2D->setEnabled(status); m_Controls->m_ManualToolSelectionBox3D->setEnabled(status); m_Controls->m_SlicesInterpolator->setEnabled(status); } // ATTENTION some methods for handling the known list of (organ names, colors) are defined in QmitkSegmentationOrganNamesHandling.cpp