diff --git a/Core/Code/DataManagement/mitkBaseGeometry.cpp b/Core/Code/DataManagement/mitkBaseGeometry.cpp index 8ecbf527cd..df57c5c582 100644 --- a/Core/Code/DataManagement/mitkBaseGeometry.cpp +++ b/Core/Code/DataManagement/mitkBaseGeometry.cpp @@ -1,1095 +1,1095 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY 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" #include "mitkModifiedLock.h" mitk::BaseGeometry::BaseGeometry(): Superclass(), mitk::OperationActor(), m_FrameOfReferenceID(0), m_IndexToWorldTransformLastModified(0), m_ImageGeometry(false), m_ModifiedLockFlag(false), m_ModifiedCalledFlag(false) { 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_ImageGeometry(other.m_ImageGeometry), m_ModifiedLockFlag(false), m_ModifiedCalledFlag(false) { // DEPRECATED(m_RotationQuaternion = other.m_RotationQuaternion); // AffineGeometryFrame SetBounds(other.GetBounds()); m_VtkMatrix = vtkMatrix4x4::New(); m_VtkMatrix->DeepCopy(other.m_VtkMatrix); 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) { - mitk::ModifiedLock lock(this); + mitk::ModifiedLock lock(this); if(origin!=GetOrigin()) { m_Origin = origin; m_IndexToWorldTransform->SetOffset(m_Origin.GetVectorFromOrigin()); Modified(); TransferItkToVtkTransform(); } } void mitk::BaseGeometry::TransferItkToVtkTransform() { - mitk::ModifiedLock lock(this); + mitk::ModifiedLock lock(this); // copy m_IndexToWorldTransform into m_VtkIndexToWorldTransform TransferItkTransformToVtkMatrix(m_IndexToWorldTransform.GetPointer(), m_VtkMatrix); m_VtkIndexToWorldTransform->Modified(); } static void CopySpacingFromTransform(mitk::AffineTransform3D* transform, mitk::Vector3D& spacing) { mitk::AffineTransform3D::MatrixType::InternalMatrixType vnlmatrix; vnlmatrix = transform->GetMatrix().GetVnlMatrix(); spacing[0]=vnlmatrix.get_column(0).magnitude(); spacing[1]=vnlmatrix.get_column(1).magnitude(); spacing[2]=vnlmatrix.get_column(2).magnitude(); } 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); vtk2itk(m_IndexToWorldTransform->GetOffset(), m_Origin); m_VtkMatrix->Identity(); m_TimeBounds[0]=ScalarTypeNumericTraits::NonpositiveMin(); m_TimeBounds[1]=ScalarTypeNumericTraits::max(); m_FrameOfReferenceID = 0; m_ImageGeometry = false; this->PostInitialize(); } void mitk::BaseGeometry::PostInitializeGeometry(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->PostInitializeGeometry(newGeometry); } void mitk::BaseGeometry::PostInitialize() { } /** Set the bounds */ void mitk::BaseGeometry::SetBounds(const BoundsArrayType& bounds) { mitk::ModifiedLock lock(this); PreSetBounds(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::PreSetBounds(const BoundsArrayType& bounds){}; void mitk::BaseGeometry::SetIndexToWorldTransform(mitk::AffineTransform3D* transform) { mitk::ModifiedLock lock(this); PreSetIndexToWorldTransform(transform); if(m_IndexToWorldTransform.GetPointer() != transform) { m_IndexToWorldTransform = transform; CopySpacingFromTransform(m_IndexToWorldTransform, m_Spacing); vtk2itk(m_IndexToWorldTransform->GetOffset(), m_Origin); TransferItkToVtkTransform(); Modified(); } PostSetIndexToWorldTransform(transform); } void mitk::BaseGeometry::PreSetIndexToWorldTransform(mitk::AffineTransform3D* transform) {} void mitk::BaseGeometry::PostSetIndexToWorldTransform(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; } void mitk::BaseGeometry::SetSpacing(const mitk::Vector3D& aSpacing, bool enforceSetSpacing ) { PreSetSpacing(aSpacing); _SetSpacing(aSpacing, enforceSetSpacing); } void mitk::BaseGeometry::PreSetSpacing(const mitk::Vector3D& aSpacing) {} void mitk::BaseGeometry::_SetSpacing(const mitk::Vector3D& aSpacing, bool enforceSetSpacing){ if(mitk::Equal(m_Spacing, aSpacing) == false || enforceSetSpacing) { assert(aSpacing[0]>0 && aSpacing[1]>0 && aSpacing[2]>0); m_Spacing = aSpacing; AffineTransform3D::MatrixType::InternalMatrixType vnlmatrix; vnlmatrix = m_IndexToWorldTransform->GetMatrix().GetVnlMatrix(); mitk::VnlVector col; col = vnlmatrix.get_column(0); col.normalize(); col*=aSpacing[0]; vnlmatrix.set_column(0, col); col = vnlmatrix.get_column(1); col.normalize(); col*=aSpacing[1]; vnlmatrix.set_column(1, col); col = vnlmatrix.get_column(2); col.normalize(); col*=aSpacing[2]; vnlmatrix.set_column(2, col); Matrix3D matrix; matrix = vnlmatrix; AffineTransform3D::Pointer transform = AffineTransform3D::New(); transform->SetMatrix(matrix); transform->SetOffset(m_IndexToWorldTransform->GetOffset()); SetIndexToWorldTransform(transform.GetPointer()); } } 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(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::BaseGeometry::GetCornerPoint(bool xFront, bool yFront, bool zFront) const { assert(this->IsBoundingBoxNull()==false); BoundingBox::BoundsArrayType bounds = this->GetBoundingBox()->GetBounds(); Point3D cornerpoint; cornerpoint[0] = (xFront ? bounds[0] : bounds[1]); cornerpoint[1] = (yFront ? bounds[2] : bounds[3]); cornerpoint[2] = (zFront ? bounds[4] : bounds[5]); if(m_ImageGeometry) { // Here i have to adjust the 0.5 offset manually, because the cornerpoint is the corner of the // bounding box. The bounding box itself is no image, so it is corner-based FillVector3D(cornerpoint, cornerpoint[0]-0.5, cornerpoint[1]-0.5, cornerpoint[2]-0.5); } return this->GetIndexToWorldTransform()->TransformPoint(cornerpoint); } mitk::ScalarType mitk::BaseGeometry::GetExtentInMM(int direction) const { return m_IndexToWorldTransform->GetMatrix().GetVnlMatrix().get_column(direction).magnitude()*GetExtent(direction); } void mitk::BaseGeometry::SetExtentInMM(int direction, ScalarType extentInMM) { - mitk::ModifiedLock lock(this); + mitk::ModifiedLock lock(this); ScalarType len = GetExtentInMM(direction); if(fabs(len - extentInMM)>=mitk::eps) { AffineTransform3D::MatrixType::InternalMatrixType vnlmatrix; vnlmatrix = m_IndexToWorldTransform->GetMatrix().GetVnlMatrix(); if(len>extentInMM) vnlmatrix.set_column(direction, vnlmatrix.get_column(direction)/len*extentInMM); else vnlmatrix.set_column(direction, vnlmatrix.get_column(direction)*extentInMM/len); Matrix3D matrix; matrix = vnlmatrix; m_IndexToWorldTransform->SetMatrix(matrix); Modified(); } PostSetExtentInMM(direction,extentInMM); } void mitk::BaseGeometry::PostSetExtentInMM(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) 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; } 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() { - mitk::ModifiedLock lock(this); + mitk::ModifiedLock lock(this); m_IndexToWorldTransform->SetIdentity(); m_Origin.Fill(0); CopySpacingFromTransform(m_IndexToWorldTransform, m_Spacing); Modified(); TransferItkToVtkTransform(); } void mitk::BaseGeometry::TransferVtkToItkTransform() { TransferVtkMatrixToItkTransform(m_VtkMatrix, m_IndexToWorldTransform.GetPointer()); CopySpacingFromTransform(m_IndexToWorldTransform, m_Spacing); vtk2itk(m_IndexToWorldTransform->GetOffset(), m_Origin); } void mitk::BaseGeometry::Compose( const mitk::BaseGeometry::TransformType * other, bool pre ) { - mitk::ModifiedLock lock(this); + mitk::ModifiedLock lock(this); m_IndexToWorldTransform->Compose(other, pre); CopySpacingFromTransform(m_IndexToWorldTransform, m_Spacing); vtk2itk(m_IndexToWorldTransform->GetOffset(), m_Origin); Modified(); TransferItkToVtkTransform(); } void mitk::BaseGeometry::Compose( const vtkMatrix4x4 * vtkmatrix, bool pre ) { mitk::BaseGeometry::TransformType::Pointer itkTransform = mitk::BaseGeometry::TransformType::New(); TransferVtkMatrixToItkTransform(vtkmatrix, itkTransform.GetPointer()); Compose(itkTransform, pre); } void mitk::BaseGeometry::Translate(const Vector3D & vector) { if((vector[0] != 0) || (vector[1] != 0) || (vector[2] != 0)) { this->SetOrigin(m_Origin + vector); } } void mitk::BaseGeometry::IndexToWorld(const mitk::Point3D &pt_units, mitk::Point3D &pt_mm) const { pt_mm = m_IndexToWorldTransform->TransformPoint(pt_units); } void mitk::BaseGeometry::IndexToWorld(const mitk::Vector3D &vec_units, mitk::Vector3D &vec_mm) const { vec_mm = m_IndexToWorldTransform->TransformVector(vec_units); } #include void mitk::BaseGeometry::ExecuteOperation(Operation* operation) { - mitk::ModifiedLock lock(this); + mitk::ModifiedLock lock(this); 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) { - mitk::ModifiedLock lock(this); + mitk::ModifiedLock lock(this); if(m_TimeBounds != timebounds) { m_TimeBounds = timebounds; Modified(); } PostSetTimeBounds(timebounds); } void mitk::BaseGeometry::PostSetTimeBounds(const TimeBounds& timebounds) {} const std::string mitk::BaseGeometry::GetTransformAsString( TransformType* transformType ) { std::ostringstream out; out << '['; for( int i=0; i<3; ++i ) { out << '['; 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); } //itk::LightObject::Pointer mitk::BaseGeometry::InternalClone() const //{ // Self::Pointer newGeometry = new Self(*this); // newGeometry->UnRegister(); // return newGeometry.GetPointer(); //} void mitk::BaseGeometry::PrintSelf(std::ostream& os, itk::Indent indent) const { os << indent << " IndexToWorldTransform: "; if(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: " << this->GetImageGeometry() << std::endl; os << indent << " Spacing: " << this->GetSpacing() << std::endl; os << indent << " TimeBounds: " << this->GetTimeBounds() << std::endl; } void mitk::BaseGeometry::Modified() const{ if(!m_ModifiedLockFlag) Superclass::Modified(); else m_ModifiedCalledFlag = true; } bool mitk::Equal( const mitk::BaseGeometry::BoundingBoxType *leftHandSide, const mitk::BaseGeometry::BoundingBoxType *rightHandSide, ScalarType eps, bool verbose ) { if(( leftHandSide == NULL) || ( rightHandSide == NULL )) { MITK_ERROR << "mitk::Equal( const mitk::Geometry3D::BoundingBoxType *leftHandSide, const mitk::Geometry3D::BoundingBoxType *rightHandSide, ScalarType eps, bool verbose ) does not with NULL pointer input."; return false; } return Equal( *leftHandSide, *rightHandSide, eps, verbose); } bool mitk::Equal( const mitk::BaseGeometry::BoundingBoxType& leftHandSide, const mitk::BaseGeometry::BoundingBoxType& rightHandSide, ScalarType eps, bool verbose ) { bool result = true; 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 << "[( Geometry3D::BoundingBoxType )] bounds are not equal."; MITK_INFO << "rightHandSide is " << setprecision(12) << *itRight << " : leftHandSide is " << *itLeft << " and tolerance is " << eps; } result = false; } itLeft++; } return result; } bool mitk::Equal(const mitk::BaseGeometry *leftHandSide, const mitk::BaseGeometry *rightHandSide, ScalarType eps, bool verbose) { if(( leftHandSide == NULL) || ( rightHandSide == NULL )) { MITK_ERROR << "mitk::Equal(const mitk::Geometry3D *leftHandSide, const mitk::Geometry3D *rightHandSide, ScalarType eps, bool verbose) does not with NULL pointer input."; return false; } return Equal( *leftHandSide, *rightHandSide, eps, verbose); } bool mitk::Equal(const mitk::BaseGeometry& leftHandSide, const mitk::BaseGeometry& rightHandSide, ScalarType eps, bool verbose) { bool result = true; //Compare spacings if( !mitk::Equal( leftHandSide.GetSpacing(), rightHandSide.GetSpacing(), eps ) ) { if(verbose) { MITK_INFO << "[( Geometry3D )] Spacing differs."; MITK_INFO << "rightHandSide is " << setprecision(12) << rightHandSide.GetSpacing() << " : leftHandSide is " << leftHandSide.GetSpacing() << " and tolerance is " << eps; } result = false; } //Compare Origins if( !mitk::Equal( leftHandSide.GetOrigin(), rightHandSide.GetOrigin(), eps ) ) { if(verbose) { MITK_INFO << "[( Geometry3D )] Origin differs."; MITK_INFO << "rightHandSide is " << setprecision(12) << rightHandSide.GetOrigin() << " : leftHandSide is " << leftHandSide.GetOrigin() << " and tolerance is " << eps; } result = false; } //Compare Axis and Extents for( unsigned int i=0; i<3; ++i) { if( !mitk::Equal( leftHandSide.GetAxisVector(i), rightHandSide.GetAxisVector(i), eps)) { if(verbose) { MITK_INFO << "[( Geometry3D )] AxisVector #" << i << " differ"; MITK_INFO << "rightHandSide is " << setprecision(12) << rightHandSide.GetAxisVector(i) << " : leftHandSide is " << leftHandSide.GetAxisVector(i) << " and tolerance is " << eps; } result = false; } if( !mitk::Equal( leftHandSide.GetExtent(i), rightHandSide.GetExtent(i), eps) ) { if(verbose) { MITK_INFO << "[( Geometry3D )] Extent #" << i << " differ"; MITK_INFO << "rightHandSide is " << setprecision(12) << rightHandSide.GetExtent(i) << " : leftHandSide is " << leftHandSide.GetExtent(i) << " and tolerance is " << eps; } result = false; } } //Compare ImageGeometry Flag if( rightHandSide.GetImageGeometry() != leftHandSide.GetImageGeometry() ) { if(verbose) { MITK_INFO << "[( Geometry3D )] GetImageGeometry is different."; MITK_INFO << "rightHandSide is " << rightHandSide.GetImageGeometry() << " : leftHandSide is " << leftHandSide.GetImageGeometry(); } result = false; } //Compare BoundingBoxes if( !mitk::Equal( *leftHandSide.GetBoundingBox(), *rightHandSide.GetBoundingBox(), eps, verbose) ) { result = false; } //Compare IndexToWorldTransform Matrix if( !mitk::Equal( *leftHandSide.GetIndexToWorldTransform(), *rightHandSide.GetIndexToWorldTransform(), eps, verbose) ) { result = false; } return result; } bool mitk::Equal(const BaseGeometry::TransformType *leftHandSide, const BaseGeometry::TransformType *rightHandSide, ScalarType eps, bool verbose ) { if(( leftHandSide == NULL) || ( rightHandSide == NULL )) { MITK_ERROR << "mitk::Equal(const Geometry3D::TransformType *leftHandSide, const Geometry3D::TransformType *rightHandSide, ScalarType eps, bool verbose ) does not with NULL pointer input."; return false; } return Equal( *leftHandSide, *rightHandSide, eps, verbose); } bool mitk::Equal(const 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 << "[( Geometry3D::TransformType )] Index to World Transformation matrix differs."; MITK_INFO << "rightHandSide is " << setprecision(12) << rightHandSide.GetMatrix() << " : leftHandSide is " << leftHandSide.GetMatrix() << " and tolerance is " << eps; } return false; } return true; } diff --git a/Core/Code/DataManagement/mitkBaseGeometry.h b/Core/Code/DataManagement/mitkBaseGeometry.h index 2e37f58712..d68e3f8fab 100644 --- a/Core/Code/DataManagement/mitkBaseGeometry.h +++ b/Core/Code/DataManagement/mitkBaseGeometry.h @@ -1,757 +1,760 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef BaseGeometry_H_HEADER_INCLUDED #define BaseGeometry_H_HEADER_INCLUDED #include #include #include "mitkoperationactor.h" #include #include "mitkvector.h" #include #include #include "itkScalableAffineTransform.h" #include class vtkMatrix4x4; class vtkMatrixToLinearTransform; class vtkLinearTransform; namespace mitk { //##Documentation //## @brief Standard 3D-BoundingBox typedef //## //## Standard 3D-BoundingBox typedef to get rid of template arguments (3D, type). typedef itk::BoundingBox BoundingBox; //##Documentation //## @brief Standard typedef for time-bounds typedef itk::FixedArray TimeBounds; typedef itk::FixedArray FixedArrayType; //##Documentation //## @brief BaseGeometry Describes the geometry of a data object //## //## The class holds //## \li a bounding box which is axes-parallel in intrinsic coordinates //## (often integer indices of pixels), to be accessed by //## GetBoundingBox() //## \li a transform to convert intrinsic coordinates into a //## world-coordinate system with coordinates in millimeters //## and milliseconds (all are floating point values), to //## be accessed by GetIndexToWorldTransform() //## \li a life span, i.e. a bounding box in time in ms (with //## start and end time), to be accessed by GetTimeBounds(). //## The default is minus infinity to plus infinity. //## \li an origin and spacing to define the geometry //## //## BaseGeometry and its sub-classes allow converting between //## intrinsic coordinates (called index or unit coordinates) //## and world-coordinates (called world or mm coordinates), //## e.g. WorldToIndex. //## In case you need integer index coordinates, provide an //## mitk::Index3D (or itk::Index) as target variable to //## WorldToIndex, otherwise you will get a continuous index //## (floating point values). //## //## An important sub-class is SlicedGeometry3D, which descibes //## data objects consisting of slices, e.g., objects of type Image. //## Conversions between world coordinates (in mm) and unit coordinates //## (e.g., pixels in the case of an Image) can be performed. //## //## For more information on related classes, see \ref Geometry. //## //## BaseGeometry instances referring to an Image need a slightly //## different definition of corners, see SetImageGeometry. This //## is usualy automatically called by Image. //## //## BaseGeometry have to be initialized in the method GenerateOutputInformation() //## of BaseProcess (or CopyInformation/ UpdateOutputInformation of BaseData, //## if possible, e.g., by analyzing pic tags in Image) subclasses. See also //## itk::ProcessObject::GenerateOutputInformation(), //## itk::DataObject::CopyInformation() and //## itk::DataObject::UpdateOutputInformation(). //## //## At least, it can return the bounding box of the data object. //## //## The BaseGeometry class is an abstract class. The most simple implementation //## is the sublass Geometry3D. //## //## Rule: everything is in mm (ms) if not stated otherwise. //## @ingroup Geometry class MITK_CORE_EXPORT BaseGeometry : public itk::Object, public OperationActor { public: mitkClassMacro(BaseGeometry, itk::Object); // ********************************** TypeDef ********************************** typedef itk::ScalableAffineTransform TransformType; typedef itk::BoundingBox BoundingBoxType; typedef BoundingBoxType::BoundsArrayType BoundsArrayType; typedef BoundingBoxType::Pointer BoundingBoxPointer; // ********************************** Origin, Spacing ********************************** //##Documentation //## @brief Get the origin, e.g. the upper-left corner of the plane const Point3D& GetOrigin() const; //##Documentation //## @brief Set the origin, i.e. the upper-left corner of the plane //## void SetOrigin(const Point3D& origin); //##Documentation //## @brief Get the spacing (size of a pixel). //## itkGetConstReferenceMacro(Spacing, mitk::Vector3D); //##Documentation //## @brief Set the spacing (m_Spacing). //## //##The spacing is also changed in the IndexToWorldTransform. void SetSpacing(const mitk::Vector3D& aSpacing, bool enforceSetSpacing = false); //##Documentation //## @brief Get the origin as VnlVector //## //## \sa GetOrigin VnlVector GetOriginVnl() const; // ********************************** other functions ********************************** //##Documentation //## @brief Get the DICOM FrameOfReferenceID referring to the //## used world coordinate system itkGetConstMacro(FrameOfReferenceID, unsigned int); //##Documentation //## @brief Set the DICOM FrameOfReferenceID referring to the //## used world coordinate system itkSetMacro(FrameOfReferenceID, unsigned int); itkGetConstMacro(IndexToWorldTransformLastModified, unsigned long); - //##Documentation - //## @brief ModifiedLockFlag is used to prohibit the call of Modified() - //## - //## For the use of this Flag, see class ModifiedLock. This flag should only be set - //## by the ModifiedLock class! - bool m_ModifiedLockFlag; - - //##Documentation - //## @brief ModifiedcalledFlag is used to collect calls of Modified(). - //## - //## For the use of this Flag, see class ModifiedLock. This flag should only be set - //## by the Modified() function! - mutable bool m_ModifiedCalledFlag; - //##Documentation //## @brief Overload of function Modified() to prohibit several calls of Modified() using the ModifiedLock class. //## //## For the use of Modified(), see class ModifiedLock. void Modified() const; + + friend class ModifiedLock; + //##Documentation //## @brief Is this BaseGeometry in a state that is valid? //## //## This function returns always true in the BaseGeometry class. Other implementations are possible in subclasses. virtual bool IsValid() const; // ********************************** Initialize ********************************** //##Documentation //## @brief Initialize the BaseGeometry void Initialize(); void InitializeGeometry(Self * newGeometry) const; // ********************************** Transformations Set/Get ********************************** // a bit of a misuse, but we want only doxygen to see the following: #ifdef DOXYGEN_SKIP //##Documentation //## @brief Get the transformation used to convert from index //## to world coordinates itkGetObjectMacro(IndexToWorldTransform, AffineTransform3D); #endif //## @brief Set the transformation used to convert from index //## to world coordinates. The spacing of the new transform is //## copied to m_spacing. void SetIndexToWorldTransform(mitk::AffineTransform3D* transform); //##Documentation //## @brief Convenience method for setting the ITK transform //## (m_IndexToWorldTransform) via an vtkMatrix4x4.The spacing of //## the new transform is copied to m_spacing. //## \sa SetIndexToWorldTransform virtual void SetIndexToWorldTransformByVtkMatrix(vtkMatrix4x4* vtkmatrix); //## Get the IndexToWorldTransform itkGetConstObjectMacro(IndexToWorldTransform, AffineTransform3D); itkGetObjectMacro(IndexToWorldTransform, AffineTransform3D); //## Get the Vtk Matrix which describes the transform. vtkMatrix4x4* GetVtkMatrix(); //##Documentation //## @brief Get the m_IndexToWorldTransform as a vtkLinearTransform vtkLinearTransform* GetVtkTransform() const; //##Documentation //## @brief Set the transform to identity, the spacing to 1 and origin to 0 //## virtual void SetIdentity(); // ********************************** Transformations ********************************** //##Documentation //## @brief Compose new IndexToWorldTransform with a given transform. //## //## This method composes m_IndexToWorldTransform with another transform, //## modifying self to be the composition of self and other. //## If the argument pre is true, then other is precomposed with self; //## that is, the resulting transformation consists of first applying //## other to the source, followed by self. If pre is false or omitted, //## then other is post-composed with self; that is the resulting //## transformation consists of first applying self to the source, //## followed by other. //## This method also changes m_spacing. void Compose( const BaseGeometry::TransformType * other, bool pre = 0 ); //##Documentation //## @brief Compose new IndexToWorldTransform with a given vtkMatrix4x4. //## //## Converts the vtkMatrix4x4 into a itk-transform and calls the previous method. void Compose( const vtkMatrix4x4 * vtkmatrix, bool pre = 0 ); //##Documentation //## @brief Translate the origin by a vector //## void Translate(const Vector3D& vector); //##Documentation //##@brief executes affine operations (translate, rotate, scale) virtual void ExecuteOperation(Operation* operation); //##Documentation //## @brief Convert world coordinates (in mm) of a \em point to (continuous!) index coordinates //## \warning If you need (discrete) integer index coordinates (e.g., for iterating easily over an image), //## use WorldToIndex(const mitk::Point3D& pt_mm, itk::Index &index). //## For further information about coordinates types, please see the Geometry documentation void WorldToIndex(const mitk::Point3D& pt_mm, mitk::Point3D& pt_units) const; //##Documentation //## @brief Convert world coordinates (in mm) of a \em vector //## \a vec_mm to (continuous!) index coordinates. //## For further information about coordinates types, please see the Geometry documentation void WorldToIndex(const mitk::Vector3D& vec_mm, mitk::Vector3D& vec_units) const; //##Documentation //## @brief Convert world coordinates (in mm) of a \em point to (discrete!) index coordinates. //## This method rounds to integer indices! //## For further information about coordinates types, please see the Geometry documentation template void WorldToIndex(const mitk::Point3D& pt_mm, itk::Index &index) const { typedef itk::Index IndexType; mitk::Point3D pt_units; this->WorldToIndex(pt_mm, pt_units); int i, dim=index.GetIndexDimension(); if(dim>3) { index.Fill(0); dim=3; } for(i=0;i( pt_units[i] ); } } //##Documentation //## @brief Convert (continuous or discrete) index coordinates of a \em vector //## \a vec_units to world coordinates (in mm) //## For further information about coordinates types, please see the Geometry documentation void IndexToWorld(const mitk::Vector3D& vec_units, mitk::Vector3D& vec_mm) const; //##Documentation //## @brief Convert (continuous or discrete) index coordinates of a \em point to world coordinates (in mm) //## For further information about coordinates types, please see the Geometry documentation void IndexToWorld(const mitk::Point3D& pt_units, mitk::Point3D& pt_mm) const; //##Documentation //## @brief Convert (discrete) index coordinates of a \em point to world coordinates (in mm) //## For further information about coordinates types, please see the Geometry documentation template void IndexToWorld(const itk::Index &index, mitk::Point3D& pt_mm ) const { mitk::Point3D pt_units; pt_units.Fill(0); int i, dim=index.GetIndexDimension(); if(dim>3) { dim=3; } for(i=0;i void ItkPhysicalPointToWorld(const itk::Point& itkPhysicalPoint, mitk::Point3D& pt_mm) const { mitk::vtk2itk(itkPhysicalPoint, pt_mm); } //##Documentation //## @brief Deprecated for use with ITK version 3.10 or newer. //## Convert world coordinates (in mm) of a \em point to //## ITK physical coordinates (in mm, but without a possible rotation) //## //## This method is useful if you have want to access an mitk::Image //## via an itk::Image. ITK v3.8 and older did not support rotated (tilted) //## images, i.e., ITK images are always parallel to the coordinate axes. //## When accessing a (possibly rotated) mitk::Image via an itk::Image //## the rotational part of the transformation in the BaseGeometry is //## simply discarded; in other word: only the origin and spacing is //## used by ITK, not the complete matrix available in MITK. //## With WorldToItkPhysicalPoint you can convert an MITK world //## coordinate (including the rotation) into a coordinate that //## can be used with the ITK image as a ITK physical coordinate //## (excluding the rotation). template void WorldToItkPhysicalPoint(const mitk::Point3D& pt_mm, itk::Point& itkPhysicalPoint) const { mitk::vtk2itk(pt_mm, itkPhysicalPoint); } // ********************************** BoundingBox ********************************** /** Get the bounding box */ itkGetConstObjectMacro(BoundingBox, BoundingBoxType); //##Documentation //## @brief Get the time bounds (in ms) itkGetConstReferenceMacro(TimeBounds, TimeBounds); // a bit of a misuse, but we want only doxygen to see the following: #ifdef DOXYGEN_SKIP //##Documentation //## @brief Get bounding box (in index/unit coordinates) itkGetConstObjectMacro(BoundingBox, BoundingBoxType); //##Documentation //## @brief Get bounding box (in index/unit coordinates) as a BoundsArrayType const BoundsArrayType GetBounds() const; #endif const BoundsArrayType GetBounds() const; //##Documentation //## \brief Set the bounding box (in index/unit coordinates) //## //## Only possible via the BoundsArray to make clear that a //## copy of the bounding-box is stored, not a reference to it. void SetBounds(const BoundsArrayType& bounds); //##Documentation //## @brief Set the bounding box (in index/unit coordinates) via a float array void SetFloatBounds(const float bounds[6]); //##Documentation //## @brief Set the bounding box (in index/unit coordinates) via a double array void SetFloatBounds(const double bounds[6]); //##Documentation //## @brief Get a VnlVector along bounding-box in the specified //## @a direction, length is spacing //## //## \sa GetAxisVector VnlVector GetMatrixColumn(unsigned int direction) const; //##Documentation //## @brief Calculates a bounding-box around the geometry relative //## to a coordinate system defined by a transform //## mitk::BoundingBox::Pointer CalculateBoundingBoxRelativeToTransform(const mitk::AffineTransform3D* transform) const; //##Documentation //## @brief Set the time bounds (in ms) void SetTimeBounds(const TimeBounds& timebounds); // ********************************** Geometry ********************************** #ifdef DOXYGEN_SKIP //##Documentation //## @brief Get the extent of the bounding box (in index/unit coordinates) //## //## To access the extent in mm use GetExtentInMM ScalarType GetExtent(unsigned int direction) const; #endif /** Get the extent of the bounding box */ ScalarType GetExtent(unsigned int direction) const; //##Documentation //## @brief Get the extent of the bounding-box in the specified @a direction in mm //## //## Equals length of GetAxisVector(direction). ScalarType GetExtentInMM(int direction) const; //##Documentation //## @brief Get vector along bounding-box in the specified @a direction in mm //## //## The length of the vector is the size of the bounding-box in the //## specified @a direction in mm //## \sa GetMatrixColumn Vector3D GetAxisVector(unsigned int direction) const; //##Documentation //## @brief Checks, if the given geometry can be converted to 2D without information loss //## e.g. when a 2D image is saved, the matrix is usually cropped to 2x2, and when you load it back to MITK //## it will be filled with standard values. This function checks, if information would be lost during this //## procedure virtual bool Is2DConvertable(); //##Documentation //## @brief Get the center of the bounding-box in mm //## Point3D GetCenter() const; //##Documentation //## @brief Get the squared length of the diagonal of the bounding-box in mm //## double GetDiagonalLength2() const; //##Documentation //## @brief Get the length of the diagonal of the bounding-box in mm //## double GetDiagonalLength() const; //##Documentation //## @brief Get the position of the corner number \a id (in world coordinates) //## //## See SetImageGeometry for how a corner is defined on images. Point3D GetCornerPoint(int id) const; //##Documentation //## @brief Get the position of a corner (in world coordinates) //## //## See SetImageGeometry for how a corner is defined on images. Point3D GetCornerPoint(bool xFront=true, bool yFront=true, bool zFront=true) const; //##Documentation //## @brief Set the extent of the bounding-box in the specified @a direction in mm //## //## @note This changes the matrix in the transform, @a not the bounds, which are given in units! void SetExtentInMM(int direction, ScalarType extentInMM); //##Documentation //## @brief Test whether the point \a p (world coordinates in mm) is //## inside the bounding box bool IsInside(const mitk::Point3D& p) const; //##Documentation //## @brief Test whether the point \a p ((continous!)index coordinates in units) is //## inside the bounding box bool IsIndexInside(const mitk::Point3D& index) const; //##Documentation //## @brief Convenience method for working with ITK indices template bool IsIndexInside(const itk::Index &index) const { int i, dim=index.GetIndexDimension(); Point3D pt_index; pt_index.Fill(0); for ( i = 0; i < dim; ++i ) { pt_index[i] = index[i]; } return IsIndexInside(pt_index); } // ********************************* Image Geometry ******************************** //##Documentation //## @brief When switching from an Image Geometry to a normal Geometry (and the other way around), you have to change the origin as well (See Geometry Documentation)! This function will change the "isImageGeometry" bool flag and changes the origin respectively. virtual void ChangeImageGeometryConsideringOriginOffset( const bool isAnImageGeometry ); //##Documentation //## @brief Is this an ImageGeometry? //## //## For more information, see SetImageGeometry itkGetConstMacro(ImageGeometry, bool); //##Documentation //## @brief Define that this BaseGeometry is refering to an Image //## //## A geometry referring to an Image needs a slightly different //## definition of the position of the corners (see GetCornerPoint). //## The position of a voxel is defined by the position of its center. //## If we would use the origin (position of the (center of) the first //## voxel) as a corner and display this point, it would seem to be //## \em not at the corner but a bit within the image. Even worse for //## the opposite corner of the image: here the corner would appear //## outside the image (by half of the voxel diameter). Thus, we have //## to correct for this and to be able to do that, we need to know //## that the BaseGeometry is referring to an Image. itkSetMacro(ImageGeometry, bool); itkBooleanMacro(ImageGeometry); protected: // ********************************** Constructor ********************************** BaseGeometry(); BaseGeometry(const BaseGeometry& other); virtual ~BaseGeometry(); //##Documentation //## @brief clones the geometry //## //## Overwrite in all sub-classes. //## Normally looks like: //## \code //## Self::Pointer newGeometry = new Self(*this); //## newGeometry->UnRegister(); //## return newGeometry.GetPointer(); //## \endcode virtual itk::LightObject::Pointer InternalClone() const =0; virtual void PrintSelf(std::ostream& os, itk::Indent indent) const; void BackTransform(const mitk::Point3D& in, mitk::Point3D& out) const; //Without redundant parameter Point3D void BackTransform(const mitk::Vector3D& in, mitk::Vector3D& out) const; //##Documentation //## @brief Deprecated void BackTransform(const mitk::Point3D& at, const mitk::Vector3D& in, mitk::Vector3D& out) const; //##Documentation //## @brief Copy the ITK transform //## (m_IndexToWorldTransform) to the VTK transform //## \sa SetIndexToWorldTransform void TransferItkToVtkTransform(); //##Documentation //## @brief Copy the VTK transform //## to the ITK transform (m_IndexToWorldTransform) //## \sa SetIndexToWorldTransform void TransferVtkToItkTransform(); static const std::string GetTransformAsString( TransformType* transformType ); itkGetConstMacro(NDimensions, unsigned int); bool IsBoundingBoxNull() const; bool IsIndexToWorldTransformNull() const; //##Documentation //## @brief Intern functions to assure a consistent behaviour of SetSpacing. void _SetSpacing(const mitk::Vector3D& aSpacing, bool enforceSetSpacing = false); private: //##Documentation //## @brief Pre- and Post-functions are empty in BaseGeometry //## //## These virtual functions allow for a different beahiour in subclasses. virtual void PreSetBounds(const BoundsArrayType& bounds); virtual void PostInitialize(); virtual void PostInitializeGeometry(Self * newGeometry) const; virtual void PostSetExtentInMM(int direction, ScalarType extentInMM); virtual void PostSetTimeBounds(const TimeBounds& timebounds); virtual void PreSetIndexToWorldTransform(mitk::AffineTransform3D* transform); virtual void PostSetIndexToWorldTransform(mitk::AffineTransform3D* transform); virtual void PreSetSpacing(const mitk::Vector3D& aSpacing); // ********************************** Variables ********************************** //##Documentation //## @brief Spacing, measurement of the resolution //## mitk::Vector3D m_Spacing; //##Documentation //## @brief Index to World Transform, contains a transformation matrix to convert //## points from indes coordinates to world coordinates (mm). The Spacing is included in this variable. AffineTransform3D::Pointer m_IndexToWorldTransform; //##Documentation //## @brief Bounding Box, which is axes-parallel in intrinsic coordinates //## (often integer indices of pixels) BoundingBoxPointer m_BoundingBox; vtkMatrixToLinearTransform* m_VtkIndexToWorldTransform; vtkMatrix4x4* m_VtkMatrix; unsigned int m_FrameOfReferenceID; mitk::TimeBounds m_TimeBounds; //##Documentation //## @brief Origin, i.e. upper-left corner of the plane //## Point3D m_Origin; static const unsigned int m_NDimensions = 3; mutable TransformType::Pointer m_InvertedTransform; mutable unsigned long m_IndexToWorldTransformLastModified; bool m_ImageGeometry; + + //##Documentation + //## @brief ModifiedLockFlag is used to prohibit the call of Modified() + //## + //## For the use of this Flag, see class ModifiedLock. This flag should only be set + //## by the ModifiedLock class! + bool m_ModifiedLockFlag; + + //##Documentation + //## @brief ModifiedcalledFlag is used to collect calls of Modified(). + //## + //## For the use of this Flag, see class ModifiedLock. This flag should only be set + //## by the Modified() function! + mutable bool m_ModifiedCalledFlag; }; // ********************************** Equal Functions ********************************** // // Static compare functions mainly for testing // /** * @brief Equal A function comparing two geometries for beeing identical. * @warning This method is deprecated and will not be available in the future. Use the \a bool mitk::Equal(const mitk::mitk::BaseGeometry& g1, const mitk::BaseGeometry& g2) instead. * * @ingroup MITKTestingAPI * * The function compares the spacing, origin, axisvectors, extents, the matrix of the * IndexToWorldTransform (elementwise), the bounding (elementwise) and the ImageGeometry flag. * * The parameter eps is a tolarence value for all methods which are internally used for comparion. * If you want to use different tolarance values for different parts of the geometry, feel free to use * the other comparison methods and write your own implementation of Equal. * @param rightHandSide Compare this against leftHandSide. * @param leftHandSide Compare this against rightHandSide. * @param eps Tolarence for comparison. You can use mitk::eps in most cases. * @param verbose Flag indicating if the user wants detailed console output or not. * @return True, if all comparison are true. False in any other case. */ DEPRECATED( MITK_CORE_EXPORT bool Equal(const mitk::BaseGeometry* leftHandSide, const mitk::BaseGeometry* rightHandSide, ScalarType eps, bool verbose)); /** * @brief Equal A function comparing two geometries for beeing identical. * * @ingroup MITKTestingAPI * * The function compares the spacing, origin, axisvectors, extents, the matrix of the * IndexToWorldTransform (elementwise), the bounding (elementwise) and the ImageGeometry flag. * * The parameter eps is a tolarence value for all methods which are internally used for comparion. * If you want to use different tolarance values for different parts of the geometry, feel free to use * the other comparison methods and write your own implementation of Equal. * @param rightHandSide Compare this against leftHandSide. * @param leftHandSide Compare this against rightHandSide. * @param eps Tolarence for comparison. You can use mitk::eps in most cases. * @param verbose Flag indicating if the user wants detailed console output or not. * @return True, if all comparison are true. False in any other case. */ MITK_CORE_EXPORT bool Equal(const mitk::BaseGeometry& leftHandSide, const mitk::BaseGeometry& rightHandSide, ScalarType eps, bool verbose); /** * @brief Equal A function comparing two transforms (TransformType) for beeing identical. * @warning This method is deprecated and will not be available in the future. Use the \a bool mitk::Equal(const mitk::mitk::BaseGeometry::TransformType& t1, const mitk::BaseGeometry::TransformType& t2) instead. * * @ingroup MITKTestingAPI * * The function compares the IndexToWorldTransform (elementwise). * * The parameter eps is a tolarence value for all methods which are internally used for comparion. * @param rightHandSide Compare this against leftHandSide. * @param leftHandSide Compare this against rightHandSide. * @param eps Tolarence for comparison. You can use mitk::eps in most cases. * @param verbose Flag indicating if the user wants detailed console output or not. * @return True, if all comparison are true. False in any other case. */ DEPRECATED( MITK_CORE_EXPORT bool Equal(const mitk::BaseGeometry::TransformType *leftHandSide, const mitk::BaseGeometry::TransformType *rightHandSide, ScalarType eps, bool verbose)); /** * @brief Equal A function comparing two transforms (TransformType) for beeing identical. * * @ingroup MITKTestingAPI * * The function compares the IndexToWorldTransform (elementwise). * * The parameter eps is a tolarence value for all methods which are internally used for comparion. * @param rightHandSide Compare this against leftHandSide. * @param leftHandSide Compare this against rightHandSide. * @param eps Tolarence for comparison. You can use mitk::eps in most cases. * @param verbose Flag indicating if the user wants detailed console output or not. * @return True, if all comparison are true. False in any other case. */ MITK_CORE_EXPORT bool Equal(const mitk::BaseGeometry::TransformType& leftHandSide, const mitk::BaseGeometry::TransformType& rightHandSide, ScalarType eps, bool verbose); /** * @brief Equal A function comparing two bounding boxes (BoundingBoxType) for beeing identical. * @warning This method is deprecated and will not be available in the future. Use the \a bool mitk::Equal(const mitk::mitk::BaseGeometry::BoundingBoxType& b1, const mitk::BaseGeometry::BoundingBoxType& b2) instead. * * @ingroup MITKTestingAPI * * The function compares the bounds (elementwise). * * The parameter eps is a tolarence value for all methods which are internally used for comparion. * @param rightHandSide Compare this against leftHandSide. * @param leftHandSide Compare this against rightHandSide. * @param eps Tolarence for comparison. You can use mitk::eps in most cases. * @param verbose Flag indicating if the user wants detailed console output or not. * @return True, if all comparison are true. False in any other case. */ DEPRECATED( MITK_CORE_EXPORT bool Equal( const mitk::BaseGeometry::BoundingBoxType *leftHandSide, const mitk::BaseGeometry::BoundingBoxType *rightHandSide, ScalarType eps, bool verbose)); /** * @brief Equal A function comparing two bounding boxes (BoundingBoxType) for beeing identical. * * @ingroup MITKTestingAPI * * The function compares the bounds (elementwise). * * The parameter eps is a tolarence value for all methods which are internally used for comparion. * @param rightHandSide Compare this against leftHandSide. * @param leftHandSide Compare this against rightHandSide. * @param eps Tolarence for comparison. You can use mitk::eps in most cases. * @param verbose Flag indicating if the user wants detailed console output or not. * @return True, if all comparison are true. False in any other case. */ MITK_CORE_EXPORT bool Equal( const mitk::BaseGeometry::BoundingBoxType& leftHandSide, const mitk::BaseGeometry::BoundingBoxType& rightHandSide, ScalarType eps, bool verbose); } // namespace mitk #endif /* BaseGeometry_H_HEADER_INCLUDED */ diff --git a/Core/Code/DataManagement/mitkGeometry2D.cpp b/Core/Code/DataManagement/mitkGeometry2D.cpp index ecddbb1763..b7ba40ecb6 100644 --- a/Core/Code/DataManagement/mitkGeometry2D.cpp +++ b/Core/Code/DataManagement/mitkGeometry2D.cpp @@ -1,284 +1,30 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ - +//Deprecated Class #include "mitkGeometry2D.h" -#include - -mitk::Geometry2D::Geometry2D() - : m_ScaleFactorMMPerUnitX( 1.0 ), - m_ScaleFactorMMPerUnitY( 1.0 ), - m_ReferenceGeometry( NULL ) +// Standard constructor for the New() macro. Sets the geometry to 3 dimensions +mitk::Geometry2D::Geometry2D() : PlaneGeometry() { } - -mitk::Geometry2D::Geometry2D(const Geometry2D& other) - : Geometry3D(other), m_ScaleFactorMMPerUnitX( other.m_ScaleFactorMMPerUnitX), - m_ScaleFactorMMPerUnitY( other.m_ScaleFactorMMPerUnitY), - m_ReferenceGeometry( other.m_ReferenceGeometry ) +mitk::Geometry2D::Geometry2D(const Geometry2D& other) : PlaneGeometry(other) { } - - mitk::Geometry2D::~Geometry2D() { } - - -void -mitk::Geometry2D::SetIndexToWorldTransform( - mitk::AffineTransform3D* transform) -{ - Superclass::SetIndexToWorldTransform(transform); - - m_ScaleFactorMMPerUnitX=GetExtentInMM(0)/GetExtent(0); - m_ScaleFactorMMPerUnitY=GetExtentInMM(1)/GetExtent(1); - - assert(m_ScaleFactorMMPerUnitXIsBoundingBoxNull()==false); - - Point3D pt3d_units; - BackTransform(pt3d_mm, pt3d_units); - pt2d_mm[0]=pt3d_units[0]*m_ScaleFactorMMPerUnitX; - pt2d_mm[1]=pt3d_units[1]*m_ScaleFactorMMPerUnitY; - pt3d_units[2]=0; - return const_cast(this->GetBoundingBox())->IsInside(pt3d_units); -} - - -void -mitk::Geometry2D::Map(const mitk::Point2D &pt2d_mm, mitk::Point3D &pt3d_mm) const -{ - Point3D pt3d_units; - pt3d_units[0]=pt2d_mm[0]/m_ScaleFactorMMPerUnitX; - pt3d_units[1]=pt2d_mm[1]/m_ScaleFactorMMPerUnitY; - pt3d_units[2]=0; - pt3d_mm = GetIndexToWorldTransform()->TransformPoint(pt3d_units); -} - - -void -mitk::Geometry2D::IndexToWorld( - const mitk::Point2D &/*pt_units*/, mitk::Point2D &/*pt_mm*/) const -{ - itkExceptionMacro(<< "No general transform possible (only affine) ==> no general" \ - " IndexToWorld(const mitk::Point2D &pt_mm, mitk::Point2D &pt_units)" \ - " possible. Has to be implemented in sub-class."); -} - - -void -mitk::Geometry2D::WorldToIndex( - const mitk::Point2D &/*pt_mm*/, mitk::Point2D &/*pt_units*/) const -{ - itkExceptionMacro(<< "No general back transform possible (only affine) ==> no general" \ - " WorldToIndex(const mitk::Point2D &pt_mm, mitk::Point2D &pt_units)" \ - " possible. Has to be implemented in sub-class."); -} - - -void -mitk::Geometry2D::IndexToWorld(const mitk::Point2D &/*atPt2d_units*/, - const mitk::Vector2D &/*vec_units*/, mitk::Vector2D &/*vec_mm*/) const -{ - itkExceptionMacro(<< "No general transform possible (only affine) ==> no general" \ - " IndexToWorld(const mitk::Vector2D &vec_mm, mitk::Vector2D &vec_units)" \ - " possible. Has to be implemented in sub-class."); -} - - -void -mitk::Geometry2D::WorldToIndex(const mitk::Point2D &/*atPt2d_mm*/, - const mitk::Vector2D &/*vec_mm*/, mitk::Vector2D &/*vec_units*/) const -{ - itkExceptionMacro(<< "No general back transform possible (only affine) ==> no general" \ - " WorldToIndex(const mitk::Vector2D &vec_mm, mitk::Vector2D &vec_units)" \ - " possible. Has to be implemented in sub-class."); -} - -void -mitk::Geometry2D::SetSizeInUnits(mitk::ScalarType width, mitk::ScalarType height) -{ - ScalarType bounds[6]={0, width, 0, height, 0, 1}; - ScalarType extent, newextentInMM; - if(GetExtent(0)>0) - { - extent = GetExtent(0); - if(width>extent) - newextentInMM = GetExtentInMM(0)/width*extent; - else - newextentInMM = GetExtentInMM(0)*extent/width; - SetExtentInMM(0, newextentInMM); - } - if(GetExtent(1)>0) - { - extent = GetExtent(1); - if(width>extent) - newextentInMM = GetExtentInMM(1)/height*extent; - else - newextentInMM = GetExtentInMM(1)*extent/height; - SetExtentInMM(1, newextentInMM); - } - SetBounds(bounds); -} - - -bool -mitk::Geometry2D::Project( - const mitk::Point3D &pt3d_mm, mitk::Point3D &projectedPt3d_mm) const -{ - assert(this->IsBoundingBoxNull()==false); - - Point3D pt3d_units; - BackTransform(pt3d_mm, pt3d_units); - pt3d_units[2] = 0; - projectedPt3d_mm = GetIndexToWorldTransform()->TransformPoint(pt3d_units); - return const_cast(this->GetBoundingBox())->IsInside(pt3d_units); -} - -bool -mitk::Geometry2D::Project(const mitk::Vector3D &vec3d_mm, mitk::Vector3D &projectedVec3d_mm) const -{ - assert(this->IsBoundingBoxNull()==false); - - Vector3D vec3d_units; - BackTransform(vec3d_mm, vec3d_units); - vec3d_units[2] = 0; - projectedVec3d_mm = GetIndexToWorldTransform()->TransformVector(vec3d_units); - return true; -} - -bool -mitk::Geometry2D::Project(const mitk::Point3D & atPt3d_mm, - const mitk::Vector3D &vec3d_mm, mitk::Vector3D &projectedVec3d_mm) const -{ - MITK_WARN << "Deprecated function! Call Project(vec3D,vec3D) instead."; - assert(this->IsBoundingBoxNull()==false); - - Vector3D vec3d_units; - BackTransform(atPt3d_mm, vec3d_mm, vec3d_units); - vec3d_units[2] = 0; - projectedVec3d_mm = GetIndexToWorldTransform()->TransformVector(vec3d_units); - - Point3D pt3d_units; - BackTransform(atPt3d_mm, pt3d_units); - return const_cast(this->GetBoundingBox())->IsInside(pt3d_units); -} - - -bool -mitk::Geometry2D::Map(const mitk::Point3D & atPt3d_mm, - const mitk::Vector3D &vec3d_mm, mitk::Vector2D &vec2d_mm) const -{ - Point2D pt2d_mm_start, pt2d_mm_end; - Point3D pt3d_mm_end; - bool inside=Map(atPt3d_mm, pt2d_mm_start); - pt3d_mm_end = atPt3d_mm+vec3d_mm; - inside&=Map(pt3d_mm_end, pt2d_mm_end); - vec2d_mm=pt2d_mm_end-pt2d_mm_start; - return inside; -} - - -void -mitk::Geometry2D::Map(const mitk::Point2D &/*atPt2d_mm*/, - const mitk::Vector2D &/*vec2d_mm*/, mitk::Vector3D &/*vec3d_mm*/) const -{ - //@todo implement parallel to the other Map method! - assert(false); -} - - -mitk::ScalarType -mitk::Geometry2D::SignedDistance(const mitk::Point3D& pt3d_mm) const -{ - Point3D projectedPoint; - Project(pt3d_mm, projectedPoint); - Vector3D direction = pt3d_mm-projectedPoint; - ScalarType distance = direction.GetNorm(); - - if(IsAbove(pt3d_mm) == false) - distance*=-1.0; - - return distance; -} - -bool -mitk::Geometry2D::IsAbove(const mitk::Point3D& pt3d_mm) const -{ - Point3D pt3d_units; - BaseGeometry::WorldToIndex(pt3d_mm, pt3d_units); - return (pt3d_units[2] > this->GetBoundingBox()->GetBounds()[4]); -} - -itk::LightObject::Pointer -mitk::Geometry2D::InternalClone() const -{ - Self::Pointer newGeometry = new Geometry2D(*this); - newGeometry->UnRegister(); - return newGeometry.GetPointer(); -} - -void -mitk::Geometry2D::PrintSelf(std::ostream& os, itk::Indent indent) const -{ - Superclass::PrintSelf(os,indent); - os << indent << " ScaleFactorMMPerUnitX: " - << m_ScaleFactorMMPerUnitX << std::endl; - os << indent << " ScaleFactorMMPerUnitY: " - << m_ScaleFactorMMPerUnitY << std::endl; -} - -void -mitk::Geometry2D::SetReferenceGeometry( mitk::Geometry3D *geometry ) -{ - m_ReferenceGeometry = geometry; -} - -mitk::Geometry3D * -mitk::Geometry2D::GetReferenceGeometry() const -{ - return m_ReferenceGeometry; -} - -bool -mitk::Geometry2D::HasReferenceGeometry() const -{ - return ( m_ReferenceGeometry != NULL ); -} diff --git a/Core/Code/DataManagement/mitkGeometry2D.h b/Core/Code/DataManagement/mitkGeometry2D.h index f39bb6f0c0..4ffbd6c25f 100644 --- a/Core/Code/DataManagement/mitkGeometry2D.h +++ b/Core/Code/DataManagement/mitkGeometry2D.h @@ -1,276 +1,50 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ - #ifndef GEOMETRY2D_H_HEADER_INCLUDED_C1F4D8E0 #define GEOMETRY2D_H_HEADER_INCLUDED_C1F4D8E0 #include -#include "mitkGeometry3D.h" - -namespace mitk { - -/** - * \deprecated This class is deprecated. Please use PlaneGeometry instead. - * \brief Describes the geometry of a two-dimensional object - * - * Describes a two-dimensional manifold, i.e., to put it simply, - * an object that can be described using a 2D coordinate-system. - * - * Geometry2D can map points between 3D world coordinates - * (in mm) and the described 2D coordinate-system (in mm) by first projecting - * the 3D point onto the 2D manifold and then calculating the 2D-coordinates - * (in mm). These 2D-mm-coordinates can be further converted into - * 2D-unit-coordinates (e.g., pixels), giving a parameter representation of - * the object with parameter values inside a rectangle - * (e.g., [0,0]..[width, height]), which is the bounding box (bounding range - * in z-direction always [0]..[1]). - * - * A Geometry2D describes the 2D representation within a 3D object and is - * therefore itself a Geometry3D (derived from Geometry3D). For example, - * a single CT-image (slice) is 2D in the sense that you can access the - * pixels using 2D-coordinates, but is also 3D, as the pixels are really - * voxels, thus have an extension (thickness) in the 3rd dimension. - * - * Most often, instances of Geometry2D will be used to descibe a plane, - * which is represented by the sub-class PlaneGeometry, but curved - * surfaces are also possible. - * - * Optionally, a reference Geometry3D can be specified, which usually would - * be the geometry associated with the underlying dataset. This is currently - * used for calculating the intersection of inclined / rotated planes - * (represented as Geometry2D) with the bounding box of the associated - * Geometry3D. - * - * \warning The Geometry2Ds are not necessarily up-to-date and not even - * initialized. As described in the previous paragraph, one of the - * Generate-/Copy-/UpdateOutputInformation methods have to initialize it. - * mitk::BaseData::GetGeometry2D() makes sure, that the Geometry2D is - * up-to-date before returning it (by setting the update extent appropriately - * and calling UpdateOutputInformation). - * - * Rule: everything is in mm (or ms for temporal information) if not - * stated otherwise. - * \ingroup Geometry - */ - - class MITK_CORE_EXPORT Geometry2D : public mitk::Geometry3D -{ -public: - mitkClassMacro(Geometry2D, mitk::Geometry3D); - itkFactorylessNewMacro(Self) - itkCloneMacro(Self) - - /** - * \brief Project a 3D point given in mm (\a pt3d_mm) onto the 2D - * geometry. The result is a 2D point in mm (\a pt2d_mm). - * - * The result is a 2D point in mm (\a pt2d_mm) relative to the upper-left - * corner of the geometry. To convert this point into units (e.g., pixels - * in case of an image), use WorldToIndex. - * \return true projection was possible - * \sa Project(const mitk::Point3D &pt3d_mm, mitk::Point3D - * &projectedPt3d_mm) - */ - virtual bool Map(const mitk::Point3D &pt3d_mm, mitk::Point2D &pt2d_mm) const; - - /** - * \brief Converts a 2D point given in mm (\a pt2d_mm) relative to the - * upper-left corner of the geometry into the corresponding - * world-coordinate (a 3D point in mm, \a pt3d_mm). - * - * To convert a 2D point given in units (e.g., pixels in case of an - * image) into a 2D point given in mm (as required by this method), use - * IndexToWorld. - */ - virtual void Map(const mitk::Point2D &pt2d_mm, mitk::Point3D &pt3d_mm) const; - - /** - * \brief Convert a 2D point given in units (e.g., pixels in case of an - * image) into a 2D point given in mm - */ - virtual void IndexToWorld( - const mitk::Point2D &pt_units, mitk::Point2D &pt_mm) const; - - /** - * \brief Convert a 2D point given in mm into a 2D point given in mm - * (e.g., pixels in case of an image) - */ - virtual void WorldToIndex( - const mitk::Point2D &pt_mm, mitk::Point2D &pt_units) const; - - /** - * \brief Convert a 2D vector given in units (e.g., pixels in case of an - * image) into a 2D vector given in mm - * \warning strange: in contrast to vtkTransform the class itk::Transform - * does not have the parameter, \em where the vector that is to be - * transformed is located. This method here should also need this - * information for general transforms. - */ - virtual void IndexToWorld( - const mitk::Point2D &atPt2d_units, const mitk::Vector2D &vec_units, - mitk::Vector2D &vec_mm) const; - - /** - * \brief Convert a 2D vector given in mm into a 2D point vector in mm - * (e.g., pixels in case of an image) - * \warning strange: in contrast to vtkTransform the class itk::Transform - * does not have the parameter, \em where the vector that is to be - * transformed is located. This method here should also need this - * information for general transforms. - */ - virtual void WorldToIndex( - const mitk::Point2D &atPt2d_mm, const mitk::Vector2D &vec_mm, - mitk::Vector2D &vec_units) const; - - /** - * \brief Set the width and height of this 2D-geometry in units by calling - * SetBounds. This does \a not change the extent in mm! - * - * For an image, this is the number of pixels in x-/y-direction. - * \note In contrast to calling SetBounds directly, this does \a not change - * the extent in mm! - */ - virtual void SetSizeInUnits(mitk::ScalarType width, mitk::ScalarType height); - - /** - * \brief Project a 3D point given in mm (\a pt3d_mm) onto the 2D - * geometry. The result is a 3D point in mm (\a projectedPt3d_mm). - * - * \return true projection was possible - */ - virtual bool Project(const mitk::Point3D &pt3d_mm, - mitk::Point3D &projectedPt3d_mm) const; - - /** - * \brief Project a 3D vector given in mm (\a vec3d_mm) onto the 2D - * geometry. The result is a 2D vector in mm (\a vec2d_mm). - * - * The result is a 2D vector in mm (\a vec2d_mm) relative to the - * upper-left - * corner of the geometry. To convert this point into units (e.g., pixels - * in case of an image), use WorldToIndex. - * \return true projection was possible - * \sa Project(const mitk::Vector3D &vec3d_mm, mitk::Vector3D - * &projectedVec3d_mm) - */ - virtual bool Map(const mitk::Point3D & atPt3d_mm, - const mitk::Vector3D &vec3d_mm, mitk::Vector2D &vec2d_mm) const; - - /** - * \brief Converts a 2D vector given in mm (\a vec2d_mm) relative to the - * upper-left corner of the geometry into the corresponding - * world-coordinate (a 3D vector in mm, \a vec3d_mm). - * - * To convert a 2D vector given in units (e.g., pixels in case of an - * image) into a 2D vector given in mm (as required by this method), use - * IndexToWorld. - */ - virtual void Map(const mitk::Point2D & atPt2d_mm, - const mitk::Vector2D &vec2d_mm, mitk::Vector3D &vec3d_mm) const; +#include - /** - * \brief Project a 3D vector given in mm (\a vec3d_mm) onto the 2D - * geometry. The result is a 3D vector in mm (\a projectedVec3d_mm). - * - * DEPRECATED. Use Project(vector,vector) instead - * - * \return true projection was possible - */ - virtual bool Project(const mitk::Point3D & atPt3d_mm, - const mitk::Vector3D &vec3d_mm, mitk::Vector3D &projectedVec3d_mm) const; +#include "mitkPlaneGeometry.h" - /** - * \brief Project a 3D vector given in mm (\a vec3d_mm) onto the 2D - * geometry. The result is a 3D vector in mm (\a projectedVec3d_mm). - * - * \return true projection was possible - */ - virtual bool Project( const mitk::Vector3D &vec3d_mm, mitk::Vector3D &projectedVec3d_mm) const; +class vtkLinearTransform; - /** - * \brief Distance of the point from the geometry - * (bounding-box \em not considered) - * - */ - inline ScalarType Distance(const Point3D& pt3d_mm) const +namespace mitk { + //##Documentation + //## @brief Describes the geometry of a two-dimensional object + //## + //## @ingroup Geometry + /** \deprecatedSince{2014_06} This class is deprecated. Please use PlaneGeometry instead. */ + class MITK_CORE_EXPORT Geometry2D : public PlaneGeometry { - return fabs(SignedDistance(pt3d_mm)); - } - - /** - * \brief Signed distance of the point from the geometry - * (bounding-box \em not considered) - * - */ - virtual ScalarType SignedDistance(const Point3D& pt3d_mm) const; - - /** - * \brief Test if the point is above the geometry - * (bounding-box \em not considered) - * - */ - virtual bool IsAbove(const Point3D& pt3d_mm) const; - - virtual void SetIndexToWorldTransform(mitk::AffineTransform3D* transform); - - virtual void SetExtentInMM(int direction, ScalarType extentInMM); - - virtual itk::LightObject::Pointer InternalClone() const; - - /** - * \brief Set the geometrical frame of reference in which this Geometry2D - * is placed. - * - * This would usually be the Geometry3D of the underlying dataset, but - * setting it is optional. - */ - void SetReferenceGeometry( mitk::Geometry3D *geometry ); - - /** - * \brief Get the geometrical frame of reference for this Geometry2D. - */ - Geometry3D *GetReferenceGeometry() const; - bool HasReferenceGeometry() const; - - -protected: - Geometry2D(); - - Geometry2D(const Geometry2D& other); - - virtual ~Geometry2D(); - - virtual void PrintSelf(std::ostream& os, itk::Indent indent) const; - - /** - * \brief factor to convert x-coordinates from mm to units and vice versa - * - */ - mutable mitk::ScalarType m_ScaleFactorMMPerUnitX; + public: + mitkClassMacro(Geometry2D, mitk::PlaneGeometry); - /** - * \brief factor to convert y-coordinates from mm to units and vice versa - * - */ - mutable mitk::ScalarType m_ScaleFactorMMPerUnitY; + /** Method for creation through the object factory. */ + itkFactorylessNewMacro(Self) + itkCloneMacro(Self) - mitk::Geometry3D *m_ReferenceGeometry; -}; + protected: + Geometry2D(); + Geometry2D(const Geometry2D& other); + virtual ~Geometry2D(); + }; } // namespace mitk #endif /* GEOMETRY2D_H_HEADER_INCLUDED_C1F4D8E0 */ diff --git a/Core/Code/DataManagement/mitkModifiedLock.cpp b/Core/Code/DataManagement/mitkModifiedLock.cpp index fab1fb4122..dfc068756e 100644 --- a/Core/Code/DataManagement/mitkModifiedLock.cpp +++ b/Core/Code/DataManagement/mitkModifiedLock.cpp @@ -1,31 +1,32 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY 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 mitk::ModifiedLock::ModifiedLock(BaseGeometry* baseGeo){ m_baseGeometry = baseGeo; m_baseGeometry->m_ModifiedLockFlag = true; + m_baseGeometry->m_ModifiedCalledFlag = false; } mitk::ModifiedLock::~ModifiedLock(){ m_baseGeometry->m_ModifiedLockFlag = false; if(m_baseGeometry->m_ModifiedCalledFlag) m_baseGeometry->Modified(); m_baseGeometry->m_ModifiedCalledFlag = false; } diff --git a/Core/Code/DataManagement/mitkModifiedLock.h b/Core/Code/DataManagement/mitkModifiedLock.h index 6f1d649a44..a5f77139e9 100644 --- a/Core/Code/DataManagement/mitkModifiedLock.h +++ b/Core/Code/DataManagement/mitkModifiedLock.h @@ -1,37 +1,41 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY 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 ModifiedLock_H_HEADER_INCLUDED #define ModifiedLock_H_HEADER_INCLUDED #include #include namespace mitk { //##Documentation //## @brief ModifiedLock manages the calls of Modified() functions //## + //## If an object of ModifiedLock is created, the ModifiedLockFlag in class + //## BaseGeometry is set to true. Therefore, all following calls of Modified() + //## will be collected and only be carried out at the end of the function / section, + //## when the deconstructor of the ModifiedLock object is called. //## class MITK_CORE_EXPORT ModifiedLock { public: ModifiedLock(); ModifiedLock(BaseGeometry* baseGeo); ~ModifiedLock(); private: BaseGeometry* m_baseGeometry; }; } #endif //Header diff --git a/Core/Documentation/Doxygen/Concepts/GeometryOverview.dox b/Core/Documentation/Doxygen/Concepts/GeometryOverview.dox index e7876f2061..0de56b2e00 100644 --- a/Core/Documentation/Doxygen/Concepts/GeometryOverview.dox +++ b/Core/Documentation/Doxygen/Concepts/GeometryOverview.dox @@ -1,132 +1,134 @@ namespace mitk{ /** \page GeometryOverviewPage Geometry Overview \tableofcontents \section GeometryOverviewPage_Introduction Introduction to Geometries Geometries are used to describe the geometrical properties of data objects in space and time.\n To use the geometry classes in the right way you have to understand the three different coordinate types present in MITK:\n\n \image html CoordinateTypes.png
The different coordinate types\n\n
\n - -# World coordinates: - - World coordinates are describing the actual spacial position of all MITK objects regarding a global coordinate system, normally specified by the imaging modality - - World coordinates are represented by mitk::Point3D objects. - - The geometry defines the offset, orientation, and scale of the considered data objects in reference to the world coordinate systems. - - World coordinates are always measured in mm - - If you are dealing with an image geometry, the origin of an image is pointing to the CENTER of the bottom-left-back voxel.\n - - If you are NOT dealing with an image geometry (no defined discrete Voxels), the origin is pointing to the bottom-left-back CORNER - - Index coordinates can be converted to world coordinates by calling Geometry3D::IndexToWorld()\n\n + -# World coordinates: + - World coordinates are describing the actual spacial position of all MITK objects regarding a global coordinate system, normally specified by the imaging modality + - World coordinates are represented by mitk::Point3D objects. + - The geometry defines the offset, orientation, and scale of the considered data objects in reference to the world coordinate systems. + - World coordinates are always measured in mm + - If you are dealing with an image geometry, the origin of an image is pointing to the CENTER of the bottom-left-back voxel.\n + - If you are NOT dealing with an image geometry (no defined discrete Voxels), the origin is pointing to the bottom-left-back CORNER + - Index coordinates can be converted to world coordinates by calling BaseGeometry::IndexToWorld()\n\n \image html worldcoordinateSystem.png
Corner-based coordinates\n\n
\image html WorldcoordinateSystemCenterBased.png
Center-based image-coordinates\n\n
\n - -# Continuous index coordinates: - - Dividing world coordinates through the pixel spacing and simultanously taking the offset into account leads to continuous index coordinates inside your dataobject.\n - So continuous coordinates can be float values! - - Continuous index coordinates are represented by mitk::Point3D objects. - - They can be obtained by calling Geometry3D::WorldToIndex(), where &pt_mm is a point in worldcoordinates.\n - -# Index coordinate system: - - Index coordinates are discrete values that address voxels of a data object explicitly. - - Index coordinates are represented by mitk::Index3D objects. - - Basically they are continuous index coordinates which are rounded from half integer up. - - E.g. (0,0) specifies the very first pixel of a 2D image, (0,1) the pixel of the next column in the same row - - If you have world coordinates, they can be converted to discrete index coordinates by calling - Geometry3D::WorldToIndex()\n\n + -# Continuous index coordinates: + - Dividing world coordinates through the pixel spacing and simultanously taking the offset into account leads to continuous index coordinates inside your dataobject.\n + So continuous coordinates can be float values! + - Continuous index coordinates are represented by mitk::Point3D objects. + - They can be obtained by calling BaseGeometry::WorldToIndex(), where &pt_mm is a point in worldcoordinates.\n + -# Index coordinate system: + - Index coordinates are discrete values that address voxels of a data object explicitly. + - Index coordinates are represented by mitk::Index3D objects. + - Basically they are continuous index coordinates which are rounded from half integer up. + - E.g. (0,0) specifies the very first pixel of a 2D image, (0,1) the pixel of the next column in the same row + - If you have world coordinates, they can be converted to discrete index coordinates by calling + BaseGeometry::WorldToIndex()\n\n \section GeometryOverviewPage_PointsAndVector Difference between Points and Vectors -Like ITK, MITK differenciate between points and vectors. A point defines a position in a coordinate system while a +Like ITK, MITK differenciate between points and vectors. A point defines a position in a coordinate system while a vector is the distance between two points. Therefore points and vectors behave different if a coordinate transformation -is applied. An offest in a coordinate transformation will affect a transformed point but not a vector. +is applied. An offest in a coordinate transformation will affect a transformed point but not a vector. An Example:\n -If two systems are given, which differ by a offset of (1,0,0). The point A(2,2,2) in system one will correspont to point A'(3,2,2) in the second system. +If two systems are given, which differ by a offset of (1,0,0). The point A(2,2,2) in system one will correspont to point A'(3,2,2) in the second system. But a vector a(2,2,2) will correspond to the vector a'(2,2,2). - + \section GeometryOverviewPage_Concept The Geometry Concept -As the superclass of all MITK geometries Geometry3D holds: - - a spacial bounding box which is axes-parallel in index coordinates (often discrete indices of pixels), to be accessed by Geometry3D::GetBoundingBox() - - a time related bounding box which holds the temporal validity of the considered data object in milliseconds (start and end time), to be accessed by Geometry3D::GetTimeBounds().\n - The default for 3D geometries is minus infinity to plus infinity, meaning the object is always displayed independent of displayed time in MITK. - - position information in form of a Euclidean transform in respect to world coordinates (i.e. a linear transformation matrix and offset) to convert (discrete or continuous) index coordinates to world coordinates and vice versa,\n - to be accessed by Geometry3D::GetIndexToWorldTransform()\n - See also: \ref GeometryOverviewPage_Introduction "Introduction to Geometries" - - Many other properties (e.g. origin, extent, ...) which can be found in the \ref Geometry3D "class documentation" - - VERY IMPORTANT:\n A flag called isImageGeometry, which indicates whether the coordinates are center-based or not!\n - See also: \ref GeometryOverviewPage_Introduction "Introduction to Geometries" and \ref GeometryOverviewPage_Putting_Together "IMPORTANT: Putting it together for an Image"\n\n +As the superclass of all MITK geometries BaseGeometry holds: + - a spacial bounding box which is axes-parallel in index coordinates (often discrete indices of pixels), to be accessed by BaseGeometry::GetBoundingBox() + - a time related bounding box which holds the temporal validity of the considered data object in milliseconds (start and end time), to be accessed by BaseGeometry::GetTimeBounds().\n + The default for 3D geometries is minus infinity to plus infinity, meaning the object is always displayed independent of displayed time in MITK. + - position information in form of a Euclidean transform in respect to world coordinates (i.e. a linear transformation matrix and offset) to convert (discrete or continuous) index coordinates to world coordinates and vice versa,\n + to be accessed by BaseGeometry::GetIndexToWorldTransform()\n + See also: \ref GeometryOverviewPage_Introduction "Introduction to Geometries" + - Many other properties (e.g. origin, extent, ...) which can be found in the \ref BaseGeometry "class documentation" + - VERY IMPORTANT:\n A flag called isImageGeometry, which indicates whether the coordinates are center-based or not!\n + See also: \ref GeometryOverviewPage_Introduction "Introduction to Geometries" and \ref GeometryOverviewPage_Putting_Together "IMPORTANT: Putting it together for an Image"\n\n + +Every data object (sub-)class of BaseData has a TimeGeometry which is accessed by BaseData::GetTimeGeometry(). This TimeGeometry holds one or more BaseGeometry objects which describes the object at specific time points, e.g. provides conversion between world and index coordinates and contains bounding boxes covering the area in which the data are placed. There is the possibility of using different implementations of the abstract TimeGeometry class which may differ in how the time steps are saved and the times are calculated. -Every data object (sub-)class of BaseData has a TimeGeometry which is accessed by BaseData::GetTimeGeometry(). This TimeGeometry holds one or more Geometry3D objects which describes the object at specific time points, e.g. provides conversion between world and index coordinates and contains bounding boxes covering the area in which the data are placed. There is the possibility of using different implementations of the abstract TimeGeometry class which may differ in how the time steps are saved and the times are calculated. +There are two ways to represent a time, either by a TimePointType or a TimeStepType. The first is similar to the continous index coordinates and defines a Time Point in milliseconds from timepoint zero. The second type is similar to index coordinates. These are discrete values which specify the number of the current time step going from 0 to GetNumberOfTimeSteps(). The conversion between a time point and a time step is done by calling the method TimeGeometry::TimeStepToTimePoint() or TimeGeometry::TimePointToTimeStep(). Note that the duration of a time step may differ from object to object, so in general it is better to calculate the corresponding time steps by using time points. Also the distance of the time steps does not need to be equidistant over time, it depends on the used TimeGeometry implementation. -There are two ways to represent a time, either by a TimePointType or a TimeStepType. The first is similar to the continous index coordinates and defines a Time Point in milliseconds from timepoint zero. The second type is similar to index coordinates. These are discrete values which specify the number of the current time step going from 0 to GetNumberOfTimeSteps(). The conversion between a time point and a time step is done by calling the method TimeGeometry::TimeStepToTimePoint() or TimeGeometry::TimePointToTimeStep(). Note that the duration of a time step may differ from object to object, so in general it is better to calculate the corresponding time steps by using time points. Also the distance of the time steps does not need to be equidistant over time, it depends on the used TimeGeometry implementation. +Each TimeGeometry has a bounding box covering the whole area in which the corresponding object is situated during all time steps. This bounding box may be accessed by calling TimeGeometry::GetBoundingBoxInWorld() and is always in world coordinates. The bounding box is calculated from all time steps, to manually start this calculation process call TimeGeometry::Update(). The bounding box is not updated if the getter is called. -Each TimeGeometry has a bounding box covering the whole area in which the corresponding object is situated during all time steps. This bounding box may be accessed by calling TimeGeometry::GetBoundingBoxInWorld() and is always in world coordinates. The bounding box is calculated from all time steps, to manually start this calculation process call TimeGeometry::Update(). The bounding box is not updated if the getter is called. +The TimeGeometry does not provide a transformation of world coordinates into image coordinates since each time step may has a different transformation. If a conversion between image and world is needed, the BaseGeometry for a specific time step or time point must be fetched either by TimeGeometry::GetGeometryForTimeStep() or TimeGeometry::GetGeometryForTimePoint() and then the conversion is calculated by using this geometry. -The TimeGeometry does not provide a transformation of world coordinates into image coordinates since each time step may has a different transformation. If a conversion between image and world is needed, the Geometry3D for a specific time step or time point must be fetched either by TimeGeometry::GetGeometryForTimeStep() or TimeGeometry::GetGeometryForTimePoint() and then the conversion is calculated by using this geometry. +The TimeGeometry class is an abstract class therefore it is not possible to instantiate it. Instead a derived class must be used. Currently the only class that can be chosen is ProportionalTimeGeometry() which assumes that the time steps are ordered equidistant. To initialize an object with given geometries call ProportionalTimeGeometry::Initialize() with an existing BaseGeometry and the number of time steps. The given geometries will be copied and not referenced! -The TimeGeometry class is an abstract class therefore it is not possible to instantiate it. instead a derived class must be used. Currently the only class that can be chosen is ProportionalTimeGeometry() which assumes that the time steps are ordered equidistant. To initialize an object with given geometries call ProportionalTimeGeometry::Initialize() with an existing Geometry3D and the number of time steps. The given geometries will be copied and not referenced! +Also, the BaseGeometry is an abstract class and derived classes must be used. The most simple implementation, i.e. the one to one implementation of the BaseGeometry class, is the class Geometry3D. -SlicedGeometry3D is a sub-class of Geometry3D, which describes data objects consisting of slices, e.g., objects of type Image (or SlicedData, which is the super-class of Image). +SlicedGeometry3D is a sub-class of BaseGeometry, which describes data objects consisting of slices, e.g., objects of type Image (or SlicedData, which is the super-class of Image). Therefore, Image::GetTimeGeometry() will contain a list of SlicedGeometry3D instances. There is a special method SlicedData::GetSlicedGeometry(t) which directly returns\n a SlicedGeometry3D to avoid the need of casting. -The class SlicedGeometry3D contains a list of Geometry2D objects describing the slices in the image.We have here spatial steps from 0 to GetSlices(). -SlicedGeometry3D::InitializeEvenlySpaced (Geometry2D *geometry2D, unsigned int slices) initializes a stack of slices with the same thickness, one starting at the position where the previous one ends. +The class SlicedGeometry3D contains a list of PlaneGeometry objects describing the slices in the image.We have here spatial steps from 0 to GetSlices(). +SlicedGeometry3D::InitializeEvenlySpaced (PlaneGeometry *planeGeometry, unsigned int slices) initializes a stack of slices with the same thickness, one starting at the position where the previous one ends. -Geometry2D provides methods for working with 2D manifolds (i.e., simply spoken, an object that can be described using a 2D coordinate-system) in 3D space.\n -For example it allows mapping of a 3D point on the 2D manifold using Geometry2D::Map(). A subclass of Geometry2D called PlaneGeometry, explicitly describes a 2D rectangular plane.\n -Another important subclass of Geometry2D is the DisplayGeometry which describes the geometry of the display (the monitor screen). Basically it represents a rectangular view on a 2D world geometry\n +PlaneGeometry provides methods for working with 2D manifolds (i.e., simply spoken, an object that can be described using a 2D coordinate-system) in 3D space.\n +For example it allows mapping of a 3D point on the 2D manifold using PlaneGeometry::Map(). \n +An important subclass of PlaneGeometry is the DisplayGeometry which describes the geometry of the display (the monitor screen). Basically it represents a rectangular view on a 2D world geometry.\n The DisplayGeometry converts between screen and world coordinates, processes input events (e.g. mouse click) and provides methods for zooming and panning.\n \image html DisplayGeometry.png
Display Geometry\n\n
-Finally there is the AbstractTransformGeometry which describes a 2D manifold in 3D space, defined by a vtkAbstractTransform. It is a abstract superclass for arbitrary user defined geometries\n +Finally there is the AbstractTransformGeometry which describes a 2D manifold in 3D space, defined by a vtkAbstractTransform. It is a abstract superclass for arbitrary user defined geometries.\n An example is the ThinPlateSplineCurvedGeometry.\n \subsection GeometryOverviewPage_Putting_Together IMPORTANT: Putting it together for an Image Please read this section accurately if you are working with Images! The definition of the position of the corners of an image is different than the one of other data objects: As mentioned in the previous section, world coordinates of data objects (e.g. surfaces ) usually specify the bottom left back corner of an object. -In contrast to that a geometry of an Image is center-based, which means that the world coordinates of a voxel belonging to an image points to the center of that voxel. +In contrast to that a geometry of an Image is center-based, which means that the world coordinates of a voxel belonging to an image points to the center of that voxel. E.g: \image html PixelCenterBased.png
Center-based voxel\n\n
If the origin of e.g. a surface lies at (15,10,0) in world coordinates, the origin`s world coordinates for an image are internally calculated like the following: -
(15-0.5*X-Spacing\n - 10-0.5*Y-Spacing\n - 0-0.5*Z-Spacing)\n
- +
(15-0.5*X-Spacing\n + 10-0.5*Y-Spacing\n + 0-0.5*Z-Spacing)\n
+ If the image`s spacing is (x,y,z)=(1,1,3) then the corner coordinates are (14.5,9.5,-1.5). -If your geometry describes an image, the member variable isImageGeometry must be changed to true. This variable indicates also if your geometry is center-based or not.\n +If your geometry describes an image, the member variable isImageGeometry must be changed to true. This variable indicates also if your geometry is center-based or not.\n The change can be done in two ways:\n - -# You are sure that your origin is already center-based. Whether because you adjusted it manually or you copied it from another image.\n - In that case, you can call the function setImageGeometry(true) or imageGeometryOn() to set the bool variable to true. - -# You created a new geometry, did not manually adjust the origin to be center-based and have the bool value isImageGeometry set to false (default).\n - In that case, call the function ChangeImageGeometryConsideringOriginOffset(true). It will adjust your origin automatically and set the bool flag to true.\n + -# You are sure that your origin is already center-based. Whether because you adjusted it manually or you copied it from another image.\n + In that case, you can call the function setImageGeometry(true) or imageGeometryOn() to set the bool variable to true. + -# You created a new geometry, did not manually adjust the origin to be center-based and have the bool value isImageGeometry set to false (default).\n + In that case, call the function ChangeImageGeometryConsideringOriginOffset(true). It will adjust your origin automatically and set the bool flag to true.\n If you experience displaced contours, figures or other stuff, it is an indicator that you have not considered the origin offset mentioned above.\n\n -An image has a TimeGeometry, which contains one or more SlicedGeometry3D instances (one for each time step), all of which contain one or more instances of (sub-classes of) Geometry2D (usually PlaneGeometry).\n -As a reminder: Geometry instances referring to images need a slightly different definition of corners, see Geometry3D::SetImageGeometry. This is usualy automatically called by Image.\n\n +An image has a TimeGeometry, which contains one or more SlicedGeometry3D instances (one for each time step), all of which contain one or more instances of (sub-classes of) PlaneGeometry.\n +As a reminder: Geometry instances referring to images need a slightly different definition of corners, see BaseGeometry::SetImageGeometry. This is usualy automatically called by Image.\n\n \section GeometryOverviewPage_Connection Connection between MITK, ITK and VTK Geometries \image html ITK_VTK_MITK_Geometries.png \n\n - - VTK transformation for rendering - - ITK transformation for calculations - - Both automatically updated when one is changed\n + - VTK transformation for rendering + - ITK transformation for calculations + - Both automatically updated when one is changed\n Attention:Not automatically updated when changed hardcoded. Example: geometry->GetVtkMatrix()->Rotate(....) */ -} \ No newline at end of file +} diff --git a/Documentation/Doxygen/DeveloperManual/Toolkit/ModuleManuals/GeometryMigration.dox b/Documentation/Doxygen/DeveloperManual/Toolkit/ModuleManuals/GeometryMigration.dox new file mode 100644 index 0000000000..7dc384b670 --- /dev/null +++ b/Documentation/Doxygen/DeveloperManual/Toolkit/ModuleManuals/GeometryMigration.dox @@ -0,0 +1,133 @@ +/** + +\page GeometryMigration Migration Guide to new Geometry Concept + +\tableofcontents + + +\section GeneralChanges General Changes + +\subsection OldClasses Old class diagram +Until now, all geometry classes inherited from Geometry3D. This inheritance didn't make sense. For example, there is no reason, why we need a Geometry2D and a PlaneGeometry as both describe the same thing. Also, why does a two-dimensional class need to inherit from a three-dimensional class? +\image html oldClasses.png "Old class diagram." + +\subsection NewClasses New class diagram +Therefore, we inserted an abstract BaseGeometry class, from which all other geometry classes should inherit. The classes Geometry2D and PlaneGeometry are combined in the new PlaneGeometry class. Also, the LandmarkBasedCurvedGeometry is included in LandmarkProjectorBasedCurvedGeometry. +\image html currentClasses.png "New class diagram." + + +\section Howto How to adapt your code +Most content of the BaseGeometry class consists of functions and variables of the former Geometry3D. +Here are some guidelines, how to change your code to the new geometry scheme. + +\subsection privateVariables Variables are private. + All variables of BaseGeometry (former in Geometry3D) are private now. Hence, use the Set and Get methods to access the variables. + +\subsection geo2d Always use PlaneGeometry instead of Geometry2D. + The class Geometry2D does not exist any more. In most cases, you can just replace the Geometry2D by PlaneGeometry. Please pay attention if you use the function "IsAbove(Point3D point)". There were two different implementations in Geometry2D and PlaneGeometry. The default behavior is implemented according to the former function of PlaneGeometry. If you want to use the implementation of the former Geometry2D, please call "IsAbove(point,true)".\n + + Here are the different implementations: +\code + bool PlaneGeometry::IsAbove( const Point3D &pt3d_mm , bool considerBoundingBox = false) const + { + if(considerBoundingBox) + { + //This is the implementation of former Geometry2D + Point3D pt3d_units; + BaseGeometry::WorldToIndex(pt3d_mm, pt3d_units); + return (pt3d_units[2] > this->GetBoundingBox()->GetBounds()[4]); + } + else + { + //This is the implementation of former PlaneGeometry and the default behavior. + return SignedDistanceFromPlane(pt3d_mm) > 0; + } + } +\endcode + +\subsection geo2ddata Rename Geometry2D... classes. + All Geometry2D... classes and functions are renamed to PlaneGeometry. The classes are Geometry2DData, Geometry2DDataToSurfaceFilter, Geometry2DDataMapper2D, Geometry2DDataVTKMapper. The new names are PlaneGeometryData... . An example for functions is GetGeometry2D, which is now called GetPlaneGeometry. + + +\subsection geo3d In some cases, use BaseGeometry instead of Geometry3D. + As there are no classes any more, which inherit from Geometry3D, you cannot insert other classes (i.e. SlicedGeometry3D) for a Geometry3D. + If you have trouble, e.g. calling a function which expects a Geometry3D parameter, try one of the following steps: +
    +
  • Do you really need a Geometry3D? Maybe you always use e.g. a PlaneGeometry. Change your function to PlaneGeometry. +
  • If your function/object needs to be flexible for all geometry classes, change the Geometry3D to BaseGeometry. +
  • Try dynamic type casts to BaseGeometry. +
+ +\subsection clone Clones of BaseGeometry. + The BaseGeometry class is an abstract class. You cannot create an object of BaseGeometry. If you need a clone of BaseGeometry to call a function, use the following code: + \code + itk::LightObject::Pointer lopointer = geometry.Clone(); + Initialize(dynamic_cast(lopointer.GetPointer())); + \endcode + + instead of: + \code + Geometry3D::Pointer geometry3D = geometry.Clone(); + Initialize(geometry3D.GetPointer()); + \endcode + +\subsection object Create an object of BaseGeometry. + Again, you cannot create an object of BaseGeometry. However, there are cases, where we need a flexible Variable which can contain objects of any other geometry class later on. This might be the case for member variables, etc. In this case, try: + \code + mitk::Geometry3D::Pointer geo3D = Geometry3D::New(); + mitk::BaseGeometry::Pointer m_geometry = dynamic_cast(geo3D.GetPointer()); + \endcode + + instead of + + \code + mitk::Geometry3D::Pointer m_geometry = mitk::Geometry3D::New(); + \endcode + +\subsection virtual Virtual functions. + To ensure a reliable behavior of functions, most functions are not virtual any more. However, if a function needs a different behavior in subclasses, there are virtual Pre- and Post- functions, which allow for additional code. The pre-functions are called at the very beginning of a function, the post-functions at the end. In the BaseGeometry, all pre- and post-functions are empty.\n + An example:\n + The function "SetIndexToWorldTransform" is not virtual any more. + For a PlaneGeometry, we need a perpendicular normal before the transformation is set. Afterwards, we need to apply the transformation to the scaling factors.\n + Code of the BaseGeometry class: + +\code + void SetIndexToWorldTransform(mitk::AffineTransform3D* transform) + { + PreSetIndexToWorldTransform(transform); + if(m_IndexToWorldTransform.GetPointer() != transform) + { + m_IndexToWorldTransform = transform; + CopySpacingFromTransform(m_IndexToWorldTransform, m_Spacing); + vtk2itk(m_IndexToWorldTransform->GetOffset(), m_Origin); + TransferItkToVtkTransform(); + Modified(); + } + PostSetIndexToWorldTransform(transform); + } + virtual void PreSetIndexToWorldTransform(mitk::AffineTransform3D* transform){}; + virtual void PostSetIndexToWorldTransform(mitk::AffineTransform3D* transform){}; +\endcode + +Code of PlaneGeometry: +\code + void PlaneGeometry::PreSetIndexToWorldTransform(mitk::AffineTransform3D *transform) + { + EnsurePerpendicularNormal(transform); + } + void PlaneGeometry::PostSetIndexToWorldTransform(mitk::AffineTransform3D* transform) + { + m_ScaleFactorMMPerUnitX=GetExtentInMM(0)/GetExtent(0); + m_ScaleFactorMMPerUnitY=GetExtentInMM(1)/GetExtent(1); + } +\endcode + +\subsection parametric Parametric functions are not part of BaseGeometry. + In Geometry3D, there were several "Parametric" functions (e.g. GetParametricExtent), which only called the non-parametric function (e.g. GetExtent). These functions are removed, please use the non-parametric implementation instead. However, in the AbstractTransformGeometry (and all subclasses), these parametric functions behave different and are still available. + +\subsection floatspacing There is no float spacing any more. + Use GetSpacing instead of GetFloatSpacing. + +\subsection LandmarkBased Always use LandmarkProjectorBasedCurvedGeometry instead of LandmarkBasedCurvedGeometry. + The class LandmarkBasedCurvedGeometry does not exist any more. Please use LandmarkProjectorBasedCurvedGeometry. +*/ diff --git a/Documentation/Doxygen/DeveloperManual/Toolkit/ModuleManuals/MITKModuleManualsList.dox b/Documentation/Doxygen/DeveloperManual/Toolkit/ModuleManuals/MITKModuleManualsList.dox index a6cf1818b2..6e43c61bb7 100644 --- a/Documentation/Doxygen/DeveloperManual/Toolkit/ModuleManuals/MITKModuleManualsList.dox +++ b/Documentation/Doxygen/DeveloperManual/Toolkit/ModuleManuals/MITKModuleManualsList.dox @@ -1,25 +1,26 @@ /** \page MITKModuleManualsListPage MITK Module Manuals \section MITKModuleManualsListPageOverview Overview The modules are shared libraries that provide functionality that can be used by developers. \section MITKModuleManualsListPageModuleManualList List of Module Manuals \li \subpage IGTGeneralModulePage \li \subpage MitkOpenCL_Overview \li \subpage GeneratingDeviceModulesPage \li \subpage mitkPython_Overview \li \subpage USModulePage \section MITKModuleManualsListPageAdditionalInformation Additional Information on Certain Modules \li \ref PlanarPropertiesPage \li \subpage DiffusionImagingPropertiesPage \li \subpage ConnectomicsRenderingPropertiesPage \section MITKMigrationGuides Migration Guides \li \subpage InteractionMigration + \li \subpage GeometryMigration */ diff --git a/Documentation/Doxygen/DeveloperManual/Toolkit/ModuleManuals/currentClasses.png b/Documentation/Doxygen/DeveloperManual/Toolkit/ModuleManuals/currentClasses.png new file mode 100644 index 0000000000..f40fd3f7aa Binary files /dev/null and b/Documentation/Doxygen/DeveloperManual/Toolkit/ModuleManuals/currentClasses.png differ diff --git a/Documentation/Doxygen/DeveloperManual/Toolkit/ModuleManuals/oldClasses.png b/Documentation/Doxygen/DeveloperManual/Toolkit/ModuleManuals/oldClasses.png new file mode 100644 index 0000000000..a276cfc5f9 Binary files /dev/null and b/Documentation/Doxygen/DeveloperManual/Toolkit/ModuleManuals/oldClasses.png differ