diff --git a/Core/Code/DataManagement/mitkBaseGeometry.cpp b/Core/Code/DataManagement/mitkBaseGeometry.cpp index a0ff6a9e1d..5c22b6f05a 100644 --- a/Core/Code/DataManagement/mitkBaseGeometry.cpp +++ b/Core/Code/DataManagement/mitkBaseGeometry.cpp @@ -1,930 +1,1008 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY 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 "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" mitk::BaseGeometry::BaseGeometry(): Superclass(), mitk::OperationActor(), - m_FrameOfReferenceID(0), m_IndexToWorldTransformLastModified(0) + 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::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_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()); m_VtkMatrix = vtkMatrix4x4::New(); m_VtkMatrix->DeepCopy(other.m_VtkMatrix); 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::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(); } } void mitk::BaseGeometry::TransferItkToVtkTransform() { // copy m_IndexToWorldTransform into m_VtkIndexToWorldTransform TransferItkTransformToVtkMatrix(m_IndexToWorldTransform.GetPointer(), m_VtkMatrix); m_VtkIndexToWorldTransform->Modified(); } void mitk::BaseGeometry::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::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); vtk2itk(m_IndexToWorldTransform->GetOffset(), m_Origin); m_VtkMatrix->Identity(); m_TimeBounds[0]=ScalarTypeNumericTraits::NonpositiveMin(); m_TimeBounds[1]=ScalarTypeNumericTraits::max(); m_FrameOfReferenceID = 0; this->InternPostInitialize(); } +void mitk::BaseGeometry::InternPostInitializeGeometry(BaseGeometry * newGeometry) const +{ + newGeometry->m_ImageGeometry = m_ImageGeometry; +} + 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::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); } /** 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->InternPostInitializeGeometry(newGeometry); } +void mitk::BaseGeometry::InternPostInitialize() +{ + m_ImageGeometry = false; +} /** Set the bounds */ void mitk::BaseGeometry::SetBounds(const BoundsArrayType& bounds) { InternPreSetBounds(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(); } void mitk::BaseGeometry::InternPreSetBounds(const BoundsArrayType& bounds){}; void mitk::BaseGeometry::SetIndexToWorldTransform(mitk::AffineTransform3D* transform) { InternPreSetIndexToWorldTransform(transform); if(m_IndexToWorldTransform.GetPointer() != transform) { m_IndexToWorldTransform = transform; CopySpacingFromTransform(m_IndexToWorldTransform, m_Spacing, m_FloatSpacing); vtk2itk(m_IndexToWorldTransform->GetOffset(), m_Origin); TransferItkToVtkTransform(); Modified(); } InternPostSetIndexToWorldTransform(transform); } void mitk::BaseGeometry::InternPreSetIndexToWorldTransform(mitk::AffineTransform3D* transform) {} void mitk::BaseGeometry::InternPostSetIndexToWorldTransform(mitk::AffineTransform3D* transform) {} const mitk::BaseGeometry::BoundsArrayType mitk::BaseGeometry::GetBounds() const { assert(m_BoundingBox.IsNotNull()); return m_BoundingBox->GetBounds(); } bool mitk::BaseGeometry::IsValid() const { return true; } const float* mitk::BaseGeometry::GetFloatSpacing() const { return m_FloatSpacing; } 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() ) ) { if(verbose) { MITK_INFO << "[( BaseGeometry::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; } bool mitk::Equal( const mitk::BaseGeometry::BoundingBoxType *leftHandSide, const mitk::BaseGeometry::BoundingBoxType *rightHandSide, ScalarType eps, bool verbose ) { bool result = true; if( rightHandSide == NULL ) { if(verbose) MITK_INFO << "[( BaseGeometry::BoundingBoxType )] rightHandSide NULL."; return false; } if( leftHandSide == NULL ) { if(verbose) MITK_INFO << "[( BaseGeometry::BoundingBoxType )] leftHandSide NULL."; return false; } BaseGeometry::BoundsArrayType rightBounds = rightHandSide->GetBounds(); BaseGeometry::BoundsArrayType leftBounds = leftHandSide->GetBounds(); BaseGeometry::BoundsArrayType::Iterator itLeft = leftBounds.Begin(); for( BaseGeometry::BoundsArrayType::Iterator itRight = rightBounds.Begin(); itRight != rightBounds.End(); ++itRight) { if(( !mitk::Equal( *itLeft, *itRight, eps )) ) { if(verbose) { MITK_INFO << "[( BaseGeometry::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::BaseGeometry *leftHandSide, const mitk::BaseGeometry *rightHandSide, ScalarType eps, bool verbose) { bool result = true; if( rightHandSide == NULL ) { if(verbose) - MITK_INFO << "[( BaseGeometry )] rightHandSide NULL."; + MITK_INFO << "[( Geometry3D )] rightHandSide NULL."; return false; } if( leftHandSide == NULL) { if(verbose) - MITK_INFO << "[( BaseGeometry )] leftHandSide NULL."; + MITK_INFO << "[( Geometry3D )] leftHandSide NULL."; return false; } //Compare spacings if( !mitk::Equal( leftHandSide->GetSpacing(), rightHandSide->GetSpacing(), eps ) ) { if(verbose) { - MITK_INFO << "[( BaseGeometry )] Spacing differs."; + 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 << "[( BaseGeometry )] Origin differs."; + 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 << "[( BaseGeometry )] AxisVector #" << i << " differ"; + 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 << "[( BaseGeometry )] Extent #" << i << " differ"; + 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; } void mitk::BaseGeometry::SetSpacing(const mitk::Vector3D& aSpacing, bool enforceSetSpacing ) { InternPreSetSpacing(aSpacing); InternSetSpacing(aSpacing, enforceSetSpacing); } void mitk::BaseGeometry::InternPreSetSpacing(const mitk::Vector3D& aSpacing) {} void mitk::BaseGeometry::InternSetSpacing(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()); itk2vtk(m_Spacing, m_FloatSpacing); } } mitk::Vector3D mitk::BaseGeometry::GetAxisVector(unsigned int direction) const { Vector3D frontToBack; frontToBack.SetVnlVector(m_IndexToWorldTransform->GetMatrix().GetVnlMatrix().get_column(direction)); frontToBack *= GetExtent(direction); return frontToBack; } mitk::ScalarType mitk::BaseGeometry::GetExtent(unsigned int direction) const { 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]; } bool mitk::BaseGeometry::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; } mitk::Point3D mitk::BaseGeometry::GetCenter() const { assert(m_BoundingBox.IsNotNull()); return m_IndexToWorldTransform->TransformPoint(m_BoundingBox->GetCenter()); } double mitk::BaseGeometry::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 mitk::BaseGeometry::GetDiagonalLength() const { return sqrt(GetDiagonalLength2()); } mitk::Point3D mitk::BaseGeometry::GetCornerPoint(int id) const { assert(id >= 0); - assert(m_BoundingBox.IsNotNull()); + assert(this->IsBoundingBoxNull()==false); - BoundingBox::BoundsArrayType bounds = m_BoundingBox->GetBounds(); + 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."); } } - return m_IndexToWorldTransform->TransformPoint(cornerpoint); + 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::Point3D mitk::BaseGeometry::GetCornerPoint(bool xFront, bool yFront, bool zFront) const { - assert(m_BoundingBox.IsNotNull()); - BoundingBox::BoundsArrayType bounds = m_BoundingBox->GetBounds(); + 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 m_IndexToWorldTransform->TransformPoint(cornerpoint); + 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(); } InternPostSetExtentInMM(direction,extentInMM); } void mitk::BaseGeometry::InternPostSetExtentInMM(int direction, ScalarType extentInMM){}; bool mitk::BaseGeometry::IsInside(const mitk::Point3D& p) const { mitk::Point3D index; WorldToIndex(p, index); return IsIndexInside(index); } 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) - inside = m_BoundingBox->IsInside(index); + 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 = 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; } -//##Documentation -//## @brief Convenience method for working with ITK indices -template -bool mitk::BaseGeometry::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); -} + 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()) { 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]; } } } 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, m_FloatSpacing); //NEW!!! Modified(); TransferItkToVtkTransform(); } void mitk::BaseGeometry::TransferVtkToItkTransform() { TransferVtkMatrixToItkTransform(m_VtkMatrix, m_IndexToWorldTransform.GetPointer()); CopySpacingFromTransform(m_IndexToWorldTransform, m_Spacing, m_FloatSpacing); 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, m_FloatSpacing); 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::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; } default: vtktransform->Delete(); return; } m_VtkMatrix->DeepCopy(vtktransform->GetMatrix()); TransferVtkToItkTransform(); Modified(); vtktransform->Delete(); } mitk::VnlVector mitk::BaseGeometry::GetMatrixColumn(unsigned int direction) const { return m_IndexToWorldTransform->GetMatrix().GetVnlMatrix().get_column(direction); } 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) { 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; } void mitk::BaseGeometry::SetTimeBounds(const TimeBounds& timebounds) { if(m_TimeBounds != timebounds) { m_TimeBounds = timebounds; Modified(); } InternPostSetTimeBounds(timebounds); } void mitk::BaseGeometry::InternPostSetTimeBounds(const TimeBounds& timebounds) {} const std::string mitk::BaseGeometry::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::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::BaseGeometry::BackTransform(const mitk::Point3D &/*at*/, const mitk::Vector3D &in, mitk::Vector3D& out) const { 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); } vtkMatrix4x4* mitk::BaseGeometry::GetVtkMatrix(){ return m_VtkMatrix; } bool mitk::BaseGeometry::IsBoundingBoxNull() const{ return m_BoundingBox.IsNull(); } bool mitk::BaseGeometry::IsIndexToWorldTransformNull() const{ return m_IndexToWorldTransform.IsNull(); } + +void + mitk::BaseGeometry::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); +} diff --git a/Core/Code/DataManagement/mitkBaseGeometry.h b/Core/Code/DataManagement/mitkBaseGeometry.h index 33874a3964..b24b9cd502 100644 --- a/Core/Code/DataManagement/mitkBaseGeometry.h +++ b/Core/Code/DataManagement/mitkBaseGeometry.h @@ -1,584 +1,625 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY 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 xxxxxxxxxxxxxx //## //## xxxxxxxxxxx //## //## 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); // ********************************** TypeDef ********************************** typedef itk::QuaternionRigidTransform< ScalarType > QuaternionTransformType; typedef QuaternionTransformType::VnlQuaternionType VnlQuaternionType; 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 Get the spacing as a float[3] array. const float* GetFloatSpacing() const; //##Documentation //## @brief Set the spacing (m_Spacing) 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 Geometry3D in a state that is valid? virtual bool IsValid() const; + virtual void PrintSelf(std::ostream& os, itk::Indent indent) const = 0; + // ********************************** Initialize ********************************** //##Documentation //## @brief Initialize the Geometry3D void Initialize(); void InitializeGeometry(Self * newGeometry) const; static void CopySpacingFromTransform(mitk::AffineTransform3D* transform, mitk::Vector3D& spacing, float floatSpacing[3]); // ********************************** 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 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); /** Set/Get the IndexToWorldTransform */ itkGetConstObjectMacro(IndexToWorldTransform, AffineTransform3D); itkGetObjectMacro(IndexToWorldTransform, AffineTransform3D); vtkMatrix4x4* GetVtkMatrix(); //##Documentation //## @brief Get the m_IndexToWorldTransform as a vtkLinearTransform vtkLinearTransform* GetVtkTransform() const; //##Documentation //## @brief Set the transform to identity and origin to 0 //## virtual void SetIdentity(); // ********************************** Transformations ********************************** //##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 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. 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 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); } // ********************************** 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. - virtual Point3D GetCornerPoint(int id) const; + 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. - virtual Point3D GetCornerPoint(bool xFront=true, bool yFront=true, bool zFront=true) const; + 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 - virtual bool IsInside(const mitk::Point3D& p) const; + 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 - virtual bool IsIndexInside(const mitk::Point3D& index) const; + bool IsIndexInside(const mitk::Point3D& index) const; //##Documentation //## @brief Convenience method for working with ITK indices template - bool IsIndexInside(const itk::Index &index) const; + 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 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); protected: // ********************************** Constructor ********************************** BaseGeometry(); BaseGeometry(const BaseGeometry& other); virtual ~BaseGeometry(); 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; static const std::string GetTransformAsString( TransformType* transformType ); - virtual void InternPostInitialize() {}; - virtual void InternPostInitializeGeometry(Self * newGeometry) const{}; + virtual void InternPostInitialize(); + virtual void InternPostInitializeGeometry(Self * newGeometry) const; virtual void InternPreSetBounds(const BoundsArrayType& bounds); virtual void InternPostSetExtentInMM(int direction, ScalarType extentInMM); virtual void InternPostSetTimeBounds(const TimeBounds& timebounds); virtual void InternPreSetIndexToWorldTransform(mitk::AffineTransform3D* transform); virtual void InternPostSetIndexToWorldTransform(mitk::AffineTransform3D* transform); virtual void InternPreSetSpacing(const mitk::Vector3D& aSpacing); void InternSetSpacing(const mitk::Vector3D& aSpacing, bool enforceSetSpacing = false); itkGetConstMacro(NDimensions, unsigned int); bool IsBoundingBoxNull() const; bool IsIndexToWorldTransformNull() const; private: // ********************************** Variables ********************************** mitk::Vector3D m_Spacing; AffineTransform3D::Pointer m_IndexToWorldTransform; mutable BoundingBoxPointer m_BoundingBox; vtkMatrixToLinearTransform* m_VtkIndexToWorldTransform; vtkMatrix4x4* m_VtkMatrix; unsigned int m_FrameOfReferenceID; mutable 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; float m_FloatSpacing[3]; //this was private + bool m_ImageGeometry; + // DEPRECATED(VnlQuaternionType m_RotationQuaternion); }; // ********************************** Equal Functions ********************************** // // Static compare functions mainly for testing // /** * @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, mitk::ScalarType eps, bool verbose); //ToDo // // Static compare functions mainly for testing // /** * @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, mitk::ScalarType eps, bool verbose); //ToDo /** * @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, mitk::ScalarType eps, bool verbose); //ToDo } // namespace mitk #endif /* BaseGeometry_H_HEADER_INCLUDED */ diff --git a/Core/Code/DataManagement/mitkGeometry3D.cpp b/Core/Code/DataManagement/mitkGeometry3D.cpp index 3af6c96e8f..013216d61d 100644 --- a/Core/Code/DataManagement/mitkGeometry3D.cpp +++ b/Core/Code/DataManagement/mitkGeometry3D.cpp @@ -1,300 +1,112 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY 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 "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_ImageGeometry(false) +mitk::Geometry3D::Geometry3D() : BaseGeometry() { } -mitk::Geometry3D::Geometry3D(const Geometry3D& other) : BaseGeometry(other), m_ImageGeometry(other.m_ImageGeometry) +mitk::Geometry3D::Geometry3D(const Geometry3D& other) : BaseGeometry(other) { } mitk::Geometry3D::~Geometry3D() { } itk::LightObject::Pointer mitk::Geometry3D::InternalClone() const { Self::Pointer newGeometry = new Self(*this); newGeometry->UnRegister(); return newGeometry.GetPointer(); } -void mitk::Geometry3D::InternPostInitializeGeometry(Geometry3D * newGeometry) const -{ - newGeometry->m_ImageGeometry = m_ImageGeometry; -} - void mitk::Geometry3D::PrintSelf(std::ostream& os, itk::Indent indent) const { os << indent << " IndexToWorldTransform: "; 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 << this->GetIndexToWorldTransform()->GetMatrix()[i][j] << " "; } os << 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 << this->GetIndexToWorldTransform()->GetInverseMatrix()[i][j] << " "; } os << std::endl; } // from itk::ScalableAffineTransform os << indent << "Scale : "; for (i = 0; i < 3; i++) { os << this->GetIndexToWorldTransform()->GetScale()[i] << " "; } os << std::endl; } os << indent << " BoundingBox: "; if(this->IsBoundingBoxNull()) os << "NULL" << std::endl; else { os << indent << "( "; for (unsigned int i=0; i<3; i++) { os << this->GetBoundingBox()->GetBounds()[2*i] << "," << this->GetBoundingBox()->GetBounds()[2*i+1] << " "; } os << " )" << std::endl; } os << indent << " Origin: " << this->GetOrigin() << std::endl; - os << indent << " ImageGeometry: " << m_ImageGeometry << std::endl; + os << indent << " ImageGeometry: " << this->GetImageGeometry() << std::endl; os << indent << " Spacing: " << this->GetSpacing() << std::endl; os << indent << " TimeBounds: " << this->GetTimeBounds() << std::endl; } - -mitk::Point3D mitk::Geometry3D::GetCornerPoint(int id) const -{ - assert(id >= 0); - assert(this->IsBoundingBoxNull()==false); - - 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); -} - -mitk::Point3D mitk::Geometry3D::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); -} - -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::Equal(const mitk::Geometry3D *leftHandSide, const mitk::Geometry3D *rightHandSide, ScalarType eps, bool verbose) -{ - bool result = true; - - if( rightHandSide == NULL ) - { - if(verbose) - MITK_INFO << "[( Geometry3D )] rightHandSide NULL."; - return false; - } - if( leftHandSide == NULL) - { - if(verbose) - MITK_INFO << "[( Geometry3D )] leftHandSide NULL."; - return false; - } - - //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; -} - -void mitk::Geometry3D::InternPostInitialize() -{ - m_ImageGeometry = false; -} diff --git a/Core/Code/DataManagement/mitkGeometry3D.h b/Core/Code/DataManagement/mitkGeometry3D.h index 8bf083e259..f15e30a075 100644 --- a/Core/Code/DataManagement/mitkGeometry3D.h +++ b/Core/Code/DataManagement/mitkGeometry3D.h @@ -1,247 +1,127 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY 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 "itkScalableAffineTransform.h" #include #include "mitkBaseGeometry.h" class vtkLinearTransform; namespace mitk { //##Documentation //## @brief Standard typedef for time-bounds typedef itk::FixedArray TimeBounds; typedef itk::FixedArray FixedArrayType; //##Documentation //## @brief Standard 3D-BoundingBox typedef //## //## Standard 3D-BoundingBox typedef to get rid of template arguments (3D, type). typedef itk::BoundingBox BoundingBox; //##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 BaseGeometry { public: mitkClassMacro(Geometry3D, mitk::BaseGeometry); typedef itk::QuaternionRigidTransform< ScalarType > QuaternionTransformType; typedef QuaternionTransformType::VnlQuaternionType VnlQuaternionType; /** Method for creation through the object factory. */ itkNewMacro(Self); mitkNewMacro1Param(Self,Self); - //##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 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 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 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 = 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; - } - - //##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 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; - - - - - - protected: Geometry3D(); Geometry3D(const Geometry3D& other); virtual ~Geometry3D(); virtual void PrintSelf(std::ostream& os, itk::Indent indent) const; - - - bool m_ImageGeometry; - - virtual void InternPostInitialize(); - virtual void InternPostInitializeGeometry(Geometry3D* newGeometry) const; - 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; }; - - // - // Static compare functions mainly for testing - // - /** - * @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); } // namespace mitk #endif /* GEOMETRY3D_H_HEADER_INCLUDED_C1EBD0AD */ diff --git a/Core/Code/Testing/mitkBaseGeometryTest.cpp b/Core/Code/Testing/mitkBaseGeometryTest.cpp index f2b23fbbf0..174d092930 100644 --- a/Core/Code/Testing/mitkBaseGeometryTest.cpp +++ b/Core/Code/Testing/mitkBaseGeometryTest.cpp @@ -1,1213 +1,1232 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY 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); const float *dummy1FloatSpacing = dummy1->GetFloatSpacing(); CPPUNIT_ASSERT(dummy1FloatSpacing[0]==aFloatSpacing[0]); CPPUNIT_ASSERT(dummy1FloatSpacing[1]==aFloatSpacing[1]); CPPUNIT_ASSERT(dummy1FloatSpacing[2]==aFloatSpacing[2]); 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)