diff --git a/Core/Code/DataManagement/mitkSlicedGeometry3D.cpp b/Core/Code/DataManagement/mitkSlicedGeometry3D.cpp index 86081e442e..e4e33ded59 100644 --- a/Core/Code/DataManagement/mitkSlicedGeometry3D.cpp +++ b/Core/Code/DataManagement/mitkSlicedGeometry3D.cpp @@ -1,840 +1,835 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision$ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #include "mitkSlicedGeometry3D.h" #include "mitkPlaneGeometry.h" #include "mitkRotationOperation.h" #include "mitkPlaneOperation.h" #include "mitkInteractionConst.h" #include "mitkSliceNavigationController.h" mitk::SlicedGeometry3D::SlicedGeometry3D() : m_EvenlySpaced( true ), m_Slices( 0 ), m_ReferenceGeometry( NULL ), m_SliceNavigationController( NULL ) { this->InitializeSlicedGeometry( m_Slices ); } +mitk::SlicedGeometry3D::SlicedGeometry3D(const SlicedGeometry3D& other) +: Superclass(other), + m_EvenlySpaced( other.m_EvenlySpaced ), + m_Slices( other.m_Slices ), + m_ReferenceGeometry( other.m_ReferenceGeometry ), + m_SliceNavigationController( other.m_SliceNavigationController ) +{ + + SetSpacing( other.GetSpacing() ); + SetDirectionVector( other.GetDirectionVector() ); + + if ( m_EvenlySpaced ) + { + AffineGeometryFrame3D::Pointer geometry = other.m_Geometry2Ds[0]->Clone(); + Geometry2D* geometry2D = dynamic_cast(geometry.GetPointer()); + assert(geometry2D!=NULL); + SetGeometry2D(geometry2D, 0); + } + else + { + unsigned int s; + for ( s = 0; s < other.m_Slices; ++s ) + { + if ( other.m_Geometry2Ds[s].IsNull() ) + { + assert(other.m_EvenlySpaced); + m_Geometry2Ds[s] = NULL; + } + else + { + AffineGeometryFrame3D::Pointer geometry = other.m_Geometry2Ds[s]->Clone(); + Geometry2D* geometry2D = dynamic_cast(geometry.GetPointer()); + assert(geometry2D!=NULL); + SetGeometry2D(geometry2D, s); + } + } + } +} mitk::SlicedGeometry3D::~SlicedGeometry3D() { } mitk::Geometry2D * mitk::SlicedGeometry3D::GetGeometry2D( int s ) const { mitk::Geometry2D::Pointer geometry2D = NULL; if ( this->IsValidSlice(s) ) { geometry2D = m_Geometry2Ds[s]; // If (a) m_EvenlySpaced==true, (b) we don't have a Geometry2D stored // for the requested slice, and (c) the first slice (s=0) // is a PlaneGeometry instance, then we calculate the geometry of the // requested as the plane of the first slice shifted by m_Spacing[2]*s // in the direction of m_DirectionVector. if ( (m_EvenlySpaced) && (geometry2D.IsNull()) ) { PlaneGeometry *firstSlice = dynamic_cast< PlaneGeometry * > ( m_Geometry2Ds[0].GetPointer() ); if ( firstSlice != NULL ) { if ( (m_DirectionVector[0] == 0.0) && (m_DirectionVector[1] == 0.0) && (m_DirectionVector[2] == 0.0) ) { m_DirectionVector = firstSlice->GetNormal(); m_DirectionVector.Normalize(); } Vector3D direction; direction = m_DirectionVector * m_Spacing[2]; mitk::PlaneGeometry::Pointer requestedslice; requestedslice = static_cast< mitk::PlaneGeometry * >( firstSlice->Clone().GetPointer() ); requestedslice->SetOrigin( requestedslice->GetOrigin() + direction * s ); geometry2D = requestedslice; m_Geometry2Ds[s] = geometry2D; } } return geometry2D; } else { return NULL; } } const mitk::BoundingBox * mitk::SlicedGeometry3D::GetBoundingBox() const { assert(m_BoundingBox.IsNotNull()); return m_BoundingBox.GetPointer(); } bool mitk::SlicedGeometry3D::SetGeometry2D( mitk::Geometry2D *geometry2D, int s ) { if ( this->IsValidSlice(s) ) { m_Geometry2Ds[s] = geometry2D; m_Geometry2Ds[s]->SetReferenceGeometry( m_ReferenceGeometry ); return true; } return false; } void mitk::SlicedGeometry3D::InitializeSlicedGeometry( unsigned int slices ) { Superclass::Initialize(); m_Slices = slices; Geometry2D::Pointer gnull = NULL; m_Geometry2Ds.assign( m_Slices, gnull ); Vector3D spacing; spacing.Fill( 1.0 ); this->SetSpacing( spacing ); m_DirectionVector.Fill( 0 ); } void mitk::SlicedGeometry3D::InitializeEvenlySpaced( mitk::Geometry2D* geometry2D, unsigned int slices, bool flipped ) { assert( geometry2D != NULL ); this->InitializeEvenlySpaced( geometry2D, geometry2D->GetExtentInMM(2)/geometry2D->GetExtent(2), slices, flipped ); } void mitk::SlicedGeometry3D::InitializeEvenlySpaced( mitk::Geometry2D* geometry2D, mitk::ScalarType zSpacing, unsigned int slices, bool flipped ) { assert( geometry2D != NULL ); assert( geometry2D->GetExtent(0) > 0 ); assert( geometry2D->GetExtent(1) > 0 ); geometry2D->Register(); Superclass::Initialize(); m_Slices = slices; BoundingBox::BoundsArrayType bounds = geometry2D->GetBounds(); bounds[4] = 0; bounds[5] = slices; // clear and reserve Geometry2D::Pointer gnull = NULL; m_Geometry2Ds.assign( m_Slices, gnull ); Vector3D directionVector = geometry2D->GetAxisVector(2); directionVector.Normalize(); directionVector *= zSpacing; if ( flipped == false ) { // Normally we should use the following four lines to create a copy of // the transform contrained in geometry2D, because it may not be changed // by us. But we know that SetSpacing creates a new transform without // changing the old (coming from geometry2D), so we can use the fifth // line instead. We check this at (**). // // AffineTransform3D::Pointer transform = AffineTransform3D::New(); // transform->SetMatrix(geometry2D->GetIndexToWorldTransform()->GetMatrix()); // transform->SetOffset(geometry2D->GetIndexToWorldTransform()->GetOffset()); // SetIndexToWorldTransform(transform); m_IndexToWorldTransform = const_cast< AffineTransform3D * >( geometry2D->GetIndexToWorldTransform() ); } else { directionVector *= -1.0; m_IndexToWorldTransform = AffineTransform3D::New(); m_IndexToWorldTransform->SetMatrix( geometry2D->GetIndexToWorldTransform()->GetMatrix() ); AffineTransform3D::OutputVectorType scaleVector; FillVector3D(scaleVector, 1.0, 1.0, -1.0); m_IndexToWorldTransform->Scale(scaleVector, true); m_IndexToWorldTransform->SetOffset( geometry2D->GetIndexToWorldTransform()->GetOffset() ); } mitk::Vector3D spacing; FillVector3D( spacing, geometry2D->GetExtentInMM(0) / bounds[1], geometry2D->GetExtentInMM(1) / bounds[3], zSpacing ); // Ensure that spacing differs from m_Spacing to make SetSpacing change the // matrix. m_Spacing[2] = zSpacing - 1; this->SetDirectionVector( directionVector ); this->SetBounds( bounds ); this->SetGeometry2D( geometry2D, 0 ); this->SetSpacing( spacing ); this->SetEvenlySpaced(); this->SetTimeBounds( geometry2D->GetTimeBounds() ); assert(m_IndexToWorldTransform.GetPointer() != geometry2D->GetIndexToWorldTransform()); // (**) see above. this->SetFrameOfReferenceID( geometry2D->GetFrameOfReferenceID() ); this->SetImageGeometry( geometry2D->GetImageGeometry() ); geometry2D->UnRegister(); } void mitk::SlicedGeometry3D::InitializePlanes( const mitk::Geometry3D *geometry3D, mitk::PlaneGeometry::PlaneOrientation planeorientation, bool top, bool frontside, bool rotated ) { m_ReferenceGeometry = const_cast< Geometry3D * >( geometry3D ); PlaneGeometry::Pointer planeGeometry = mitk::PlaneGeometry::New(); planeGeometry->InitializeStandardPlane( geometry3D, top, planeorientation, frontside, rotated ); ScalarType viewSpacing = 1; unsigned int slices = 1; switch ( planeorientation ) { case PlaneGeometry::Transversal: viewSpacing = geometry3D->GetSpacing()[2]; slices = (unsigned int) geometry3D->GetExtent( 2 ); break; case PlaneGeometry::Frontal: viewSpacing = geometry3D->GetSpacing()[1]; slices = (unsigned int) geometry3D->GetExtent( 1 ); break; case PlaneGeometry::Sagittal: viewSpacing = geometry3D->GetSpacing()[0]; slices = (unsigned int) geometry3D->GetExtent( 0 ); break; default: itkExceptionMacro("unknown PlaneOrientation"); } mitk::Vector3D normal = this->AdjustNormal( planeGeometry->GetNormal() ); ScalarType directedExtent = fabs( m_ReferenceGeometry->GetExtentInMM( 0 ) * normal[0] ) + fabs( m_ReferenceGeometry->GetExtentInMM( 1 ) * normal[1] ) + fabs( m_ReferenceGeometry->GetExtentInMM( 2 ) * normal[2] ); if ( directedExtent >= viewSpacing ) { slices = static_cast< int >(directedExtent / viewSpacing + 0.5); } else { slices = 1; } bool flipped = (top == false); if ( frontside == false ) { flipped = !flipped; } if ( planeorientation == PlaneGeometry::Frontal ) { flipped = !flipped; } this->InitializeEvenlySpaced( planeGeometry, viewSpacing, slices, flipped ); } void mitk::SlicedGeometry3D ::ReinitializePlanes( const Point3D ¢er, const Point3D &referencePoint ) { // Need a reference frame to align the rotated planes if ( !m_ReferenceGeometry ) { return; } // Get first plane of plane stack PlaneGeometry *firstPlane = dynamic_cast< PlaneGeometry * >( m_Geometry2Ds[0].GetPointer() ); // If plane stack is empty, exit if ( firstPlane == NULL ) { return; } // Calculate the "directed" spacing when taking the plane (defined by its axes // vectors and normal) as the reference coordinate frame. // // This is done by calculating the radius of the ellipsoid defined by the // original volume spacing axes, in the direction of the respective axis of the // reference frame. mitk::Vector3D axis0 = firstPlane->GetAxisVector(0); mitk::Vector3D axis1 = firstPlane->GetAxisVector(1); mitk::Vector3D normal = firstPlane->GetNormal(); normal.Normalize(); Vector3D spacing; spacing[0] = this->CalculateSpacing( axis0 ); spacing[1] = this->CalculateSpacing( axis1 ); spacing[2] = this->CalculateSpacing( normal ); Superclass::SetSpacing( spacing ); // Now we need to calculate the number of slices in the plane's normal // direction, so that the entire volume is covered. This is done by first // calculating the dot product between the volume diagonal (the maximum // distance inside the volume) and the normal, and dividing this value by // the directed spacing calculated above. ScalarType directedExtent = fabs( m_ReferenceGeometry->GetExtentInMM( 0 ) * normal[0] ) + fabs( m_ReferenceGeometry->GetExtentInMM( 1 ) * normal[1] ) + fabs( m_ReferenceGeometry->GetExtentInMM( 2 ) * normal[2] ); if ( directedExtent >= spacing[2] ) { m_Slices = static_cast< unsigned int >(directedExtent / spacing[2] + 0.5); } else { m_Slices = 1; } // The origin of our "first plane" needs to be adapted to this new extent. // To achieve this, we first calculate the current distance to the volume's // center, and then shift the origin in the direction of the normal by the // difference between this distance and half of the new extent. double centerOfRotationDistance = firstPlane->SignedDistanceFromPlane( center ); if ( centerOfRotationDistance > 0 ) { firstPlane->SetOrigin( firstPlane->GetOrigin() + normal * (centerOfRotationDistance - directedExtent / 2.0) ); m_DirectionVector = normal; } else { firstPlane->SetOrigin( firstPlane->GetOrigin() + normal * (directedExtent / 2.0 + centerOfRotationDistance) ); m_DirectionVector = -normal; } // Now we adjust this distance according with respect to the given reference // point: we need to make sure that the point is touched by one slice of the // new slice stack. double referencePointDistance = firstPlane->SignedDistanceFromPlane( referencePoint ); int referencePointSlice = static_cast< int >( referencePointDistance / spacing[2]); double alignmentValue = referencePointDistance / spacing[2] - referencePointSlice; firstPlane->SetOrigin( firstPlane->GetOrigin() + normal * alignmentValue * spacing[2] ); // Finally, we can clear the previous geometry stack and initialize it with // our re-initialized "first plane". m_Geometry2Ds.assign( m_Slices, Geometry2D::Pointer( NULL ) ); if ( m_Slices > 0 ) { m_Geometry2Ds[0] = firstPlane; } // Reinitialize SNC with new number of slices m_SliceNavigationController->GetSlice()->SetSteps( m_Slices ); this->Modified(); } double mitk::SlicedGeometry3D::CalculateSpacing( const mitk::Vector3D &d ) const { // Need the spacing of the underlying dataset / geometry if ( !m_ReferenceGeometry ) { return 1.0; } // The following can be derived from the ellipsoid equation // // 1 = x^2/a^2 + y^2/b^2 + z^2/c^2 // // where (a,b,c) = spacing of original volume (ellipsoid radii) // and (x,y,z) = scaled coordinates of vector d (according to ellipsoid) // const mitk::Vector3D &spacing = m_ReferenceGeometry->GetSpacing(); double scaling = d[0]*d[0] / (spacing[0] * spacing[0]) + d[1]*d[1] / (spacing[1] * spacing[1]) + d[2]*d[2] / (spacing[2] * spacing[2]); scaling = sqrt( scaling ); return ( sqrt( d[0]*d[0] + d[1]*d[1] + d[2]*d[2] ) / scaling ); } mitk::Vector3D mitk::SlicedGeometry3D::AdjustNormal( const mitk::Vector3D &normal ) const { Geometry3D::TransformType::Pointer inverse = Geometry3D::TransformType::New(); m_ReferenceGeometry->GetIndexToWorldTransform()->GetInverse( inverse ); Vector3D transformedNormal = inverse->TransformVector( normal ); transformedNormal.Normalize(); return transformedNormal; } void mitk::SlicedGeometry3D::SetImageGeometry( const bool isAnImageGeometry ) { Superclass::SetImageGeometry( isAnImageGeometry ); mitk::Geometry3D* geometry; unsigned int s; for ( s = 0; s < m_Slices; ++s ) { geometry = m_Geometry2Ds[s]; if ( geometry!=NULL ) { geometry->SetImageGeometry( isAnImageGeometry ); } } } void mitk::SlicedGeometry3D::ChangeImageGeometryConsideringOriginOffset( const bool isAnImageGeometry ) { mitk::Geometry3D* geometry; unsigned int s; for ( s = 0; s < m_Slices; ++s ) { geometry = m_Geometry2Ds[s]; if ( geometry!=NULL ) { geometry->ChangeImageGeometryConsideringOriginOffset( isAnImageGeometry ); } } Superclass::ChangeImageGeometryConsideringOriginOffset( isAnImageGeometry ); } bool mitk::SlicedGeometry3D::IsValidSlice( int s ) const { return ((s >= 0) && (s < (int)m_Slices)); } void mitk::SlicedGeometry3D::SetReferenceGeometry( Geometry3D *referenceGeometry ) { m_ReferenceGeometry = referenceGeometry; std::vector::iterator it; for ( it = m_Geometry2Ds.begin(); it != m_Geometry2Ds.end(); ++it ) { (*it)->SetReferenceGeometry( referenceGeometry ); } } void mitk::SlicedGeometry3D::SetSpacing( const mitk::Vector3D &aSpacing ) { bool hasEvenlySpacedPlaneGeometry = false; mitk::Point3D origin; mitk::Vector3D rightDV, bottomDV; BoundingBox::BoundsArrayType bounds; assert(aSpacing[0]>0 && aSpacing[1]>0 && aSpacing[2]>0); // In case of evenly-spaced data: re-initialize instances of Geometry2D, // since the spacing influences them if ((m_EvenlySpaced) && (m_Geometry2Ds.size() > 0)) { mitk::Geometry2D::ConstPointer firstGeometry = m_Geometry2Ds[0].GetPointer(); const PlaneGeometry *planeGeometry = dynamic_cast< const PlaneGeometry * >( firstGeometry.GetPointer() ); if (planeGeometry != NULL ) { this->WorldToIndex( planeGeometry->GetOrigin(), origin ); this->WorldToIndex( planeGeometry->GetAxisVector(0), rightDV ); this->WorldToIndex( planeGeometry->GetAxisVector(1), bottomDV ); bounds = planeGeometry->GetBounds(); hasEvenlySpacedPlaneGeometry = true; } } Superclass::SetSpacing(aSpacing); mitk::Geometry2D::Pointer firstGeometry; // In case of evenly-spaced data: re-initialize instances of Geometry2D, // since the spacing influences them if ( hasEvenlySpacedPlaneGeometry ) { //create planeGeometry according to new spacing this->IndexToWorld( origin, origin ); this->IndexToWorld( rightDV, rightDV ); this->IndexToWorld( bottomDV, bottomDV ); mitk::PlaneGeometry::Pointer planeGeometry = mitk::PlaneGeometry::New(); planeGeometry->SetImageGeometry( this->GetImageGeometry() ); planeGeometry->SetReferenceGeometry( m_ReferenceGeometry ); planeGeometry->InitializeStandardPlane( rightDV.Get_vnl_vector(), bottomDV.Get_vnl_vector(), &m_Spacing ); planeGeometry->SetOrigin(origin); planeGeometry->SetBounds(bounds); firstGeometry = planeGeometry; } else if ( (m_EvenlySpaced) && (m_Geometry2Ds.size() > 0) ) { firstGeometry = m_Geometry2Ds[0].GetPointer(); } //clear and reserve Geometry2D::Pointer gnull=NULL; m_Geometry2Ds.assign(m_Slices, gnull); if ( m_Slices > 0 ) { m_Geometry2Ds[0] = firstGeometry; } this->Modified(); } void mitk::SlicedGeometry3D ::SetSliceNavigationController( SliceNavigationController *snc ) { m_SliceNavigationController = snc; } mitk::SliceNavigationController * mitk::SlicedGeometry3D::GetSliceNavigationController() { return m_SliceNavigationController; } void mitk::SlicedGeometry3D::SetEvenlySpaced(bool on) { if(m_EvenlySpaced!=on) { m_EvenlySpaced=on; this->Modified(); } } void mitk::SlicedGeometry3D ::SetDirectionVector( const mitk::Vector3D& directionVector ) { Vector3D diff = m_DirectionVector - directionVector; if ( (m_DirectionVector.GetSquaredNorm() == 0.0) || (diff.GetNorm() >= vnl_math::float_eps) ) { m_DirectionVector = directionVector; m_DirectionVector.Normalize(); this->Modified(); } } void mitk::SlicedGeometry3D::SetTimeBounds( const mitk::TimeBounds& timebounds ) { Superclass::SetTimeBounds( timebounds ); unsigned int s; for ( s = 0; s < m_Slices; ++s ) { if(m_Geometry2Ds[s].IsNotNull()) { m_Geometry2Ds[s]->SetTimeBounds( timebounds ); } } m_TimeBounds = timebounds; } mitk::AffineGeometryFrame3D::Pointer mitk::SlicedGeometry3D::Clone() const { - Self::Pointer newGeometry = Self::New(); - newGeometry->Initialize(m_Slices); - InitializeGeometry(newGeometry); + Self::Pointer newGeometry = new SlicedGeometry3D(*this); return newGeometry.GetPointer(); } - -void -mitk::SlicedGeometry3D::InitializeGeometry( Self *newGeometry ) const -{ - Superclass::InitializeGeometry( newGeometry ); - - newGeometry->SetEvenlySpaced( m_EvenlySpaced ); - newGeometry->SetSpacing( this->GetSpacing() ); - newGeometry->SetDirectionVector( this->GetDirectionVector() ); - - newGeometry->SetSliceNavigationController( m_SliceNavigationController ); - newGeometry->m_ReferenceGeometry = m_ReferenceGeometry; - - if ( m_EvenlySpaced ) - { - AffineGeometryFrame3D::Pointer geometry = m_Geometry2Ds[0]->Clone(); - Geometry2D* geometry2D = dynamic_cast(geometry.GetPointer()); - assert(geometry2D!=NULL); - newGeometry->SetGeometry2D(geometry2D, 0); - } - else - { - unsigned int s; - for ( s = 0; s < m_Slices; ++s ) - { - if ( m_Geometry2Ds[s].IsNull() ) - { - assert(m_EvenlySpaced); - } - else - { - AffineGeometryFrame3D::Pointer geometry = m_Geometry2Ds[s]->Clone(); - Geometry2D* geometry2D = dynamic_cast(geometry.GetPointer()); - assert(geometry2D!=NULL); - newGeometry->SetGeometry2D(geometry2D, s); - } - } - } -} - - void mitk::SlicedGeometry3D::PrintSelf( std::ostream& os, itk::Indent indent ) const { Superclass::PrintSelf(os,indent); os << indent << " EvenlySpaced: " << m_EvenlySpaced << std::endl; if ( m_EvenlySpaced ) { os << indent << " DirectionVector: " << m_DirectionVector << std::endl; } os << indent << " Slices: " << m_Slices << std::endl; os << std::endl; os << indent << " GetGeometry2D(0): "; if ( this->GetGeometry2D(0) == NULL ) { os << "NULL" << std::endl; } else { this->GetGeometry2D(0)->Print(os, indent); } } void mitk::SlicedGeometry3D::ExecuteOperation(Operation* operation) { switch ( operation->GetOperationType() ) { case OpNOTHING: break; case OpROTATE: if ( m_EvenlySpaced ) { // Need a reference frame to align the rotation if ( m_ReferenceGeometry ) { // Clear all generated geometries and then rotate only the first slice. // The other slices will be re-generated on demand // Save first slice Geometry2D::Pointer geometry2D = m_Geometry2Ds[0]; RotationOperation *rotOp = dynamic_cast< RotationOperation * >( operation ); // Generate a RotationOperation using the dataset center instead of // the supplied rotation center. This is necessary so that the rotated // zero-plane does not shift away. The supplied center is instead used // to adjust the slice stack afterwards. Point3D center = m_ReferenceGeometry->GetCenter(); RotationOperation centeredRotation( rotOp->GetOperationType(), center, rotOp->GetVectorOfRotation(), rotOp->GetAngleOfRotation() ); // Rotate first slice geometry2D->ExecuteOperation( ¢eredRotation ); // Clear the slice stack and adjust it according to the center of // the dataset and the supplied rotation center (see documentation of // ReinitializePlanes) this->ReinitializePlanes( center, rotOp->GetCenterOfRotation() ); if ( m_SliceNavigationController ) { m_SliceNavigationController->SelectSliceByPoint( rotOp->GetCenterOfRotation() ); m_SliceNavigationController->AdjustSliceStepperRange(); } Geometry3D::ExecuteOperation( ¢eredRotation ); } } else { // Reach through to all slices for (std::vector::iterator iter = m_Geometry2Ds.begin(); iter != m_Geometry2Ds.end(); ++iter) { (*iter)->ExecuteOperation(operation); } } break; case OpORIENT: if ( m_EvenlySpaced ) { // Save first slice Geometry2D::Pointer geometry2D = m_Geometry2Ds[0]; PlaneGeometry *planeGeometry = dynamic_cast< PlaneGeometry * >( geometry2D.GetPointer() ); PlaneOperation *planeOp = dynamic_cast< PlaneOperation * >( operation ); // Need a PlaneGeometry, a PlaneOperation and a reference frame to // carry out the re-orientation if ( m_ReferenceGeometry && planeGeometry && planeOp ) { // Clear all generated geometries and then rotate only the first slice. // The other slices will be re-generated on demand // Generate a RotationOperation by calculating the angle between // the current and the requested slice orientation Point3D center = m_ReferenceGeometry->GetCenter(); const mitk::Vector3D ¤tNormal = planeGeometry->GetNormal(); const mitk::Vector3D &newNormal = planeOp->GetNormal(); Vector3D rotationAxis = itk::CrossProduct( newNormal, currentNormal ); vtkFloatingPointType rotationAngle = - atan2( (double) rotationAxis.GetNorm(), (double) (newNormal * currentNormal) ); rotationAngle *= 180.0 / vnl_math::pi; RotationOperation centeredRotation( mitk::OpROTATE, center, rotationAxis, rotationAngle ); // Rotate first slice geometry2D->ExecuteOperation( ¢eredRotation ); // Clear the slice stack and adjust it according to the center of // rotation and plane position (see documentation of ReinitializePlanes) this->ReinitializePlanes( center, planeOp->GetPoint() ); if ( m_SliceNavigationController ) { m_SliceNavigationController->SelectSliceByPoint( planeOp->GetPoint() ); m_SliceNavigationController->AdjustSliceStepperRange(); } Geometry3D::ExecuteOperation( ¢eredRotation ); } } else { // Reach through to all slices for (std::vector::iterator iter = m_Geometry2Ds.begin(); iter != m_Geometry2Ds.end(); ++iter) { (*iter)->ExecuteOperation(operation); } } break; } this->Modified(); } diff --git a/Core/Code/DataManagement/mitkSlicedGeometry3D.h b/Core/Code/DataManagement/mitkSlicedGeometry3D.h index 1e27d28b80..d3c5ae5580 100644 --- a/Core/Code/DataManagement/mitkSlicedGeometry3D.h +++ b/Core/Code/DataManagement/mitkSlicedGeometry3D.h @@ -1,324 +1,323 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision$ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #ifndef MITKSLICEDGEOMETRY3D_H_HEADER_INCLUDED_C1EBD0AD #define MITKSLICEDGEOMETRY3D_H_HEADER_INCLUDED_C1EBD0AD #include "mitkGeometry3D.h" #include "mitkPlaneGeometry.h" namespace mitk { class SliceNavigationController; class NavigationController; /** \brief Describes the geometry of a data object consisting of slices. * * A Geometry2D can be requested for each slice. In the case of * \em evenly-spaced, \em plane geometries (m_EvenlySpaced==true), * only the 2D-geometry of the first slice has to be set (to an instance of * PlaneGeometry). The 2D geometries of the other slices are calculated * by shifting the first slice in the direction m_DirectionVector by * m_Spacing.z * sliceNumber. The m_Spacing member (which is only * relevant in the case m_EvenlySpaced==true) descibes the size of a voxel * (in mm), i.e., m_Spacing.x is the voxel width in the x-direction of the * plane. It is derived from the reference geometry of this SlicedGeometry3D, * which usually would be the global geometry describing how datasets are to * be resliced. * * By default, slices are oriented in the direction of one of the main axes * (x, y, z). However, by means of rotation, it is possible to realign the * slices in any possible direction. In case of an inclined plane, the spacing * is derived as a product of the (regular) geometry spacing and the direction * vector of the plane. * * SlicedGeometry3D and the associated Geometry2Ds have to be initialized in * the method GenerateOutputInformation() of BaseProcess (or CopyInformation / * UpdateOutputInformation of BaseData, if possible, e.g., by analyzing pic * tags in Image) subclasses. See also * * \sa itk::ProcessObject::GenerateOutputInformation(), * \sa itk::DataObject::CopyInformation() and * \a itk::DataObject::UpdateOutputInformation(). * * Rule: everything is in mm (or ms for temporal information) if not * stated otherwise. * * \warning The hull (i.e., transform, bounding-box and * time-bounds) is only guaranteed to be up-to-date after calling * UpdateInformation(). * * \ingroup Geometry */ class MITK_CORE_EXPORT SlicedGeometry3D : public mitk::Geometry3D { public: mitkClassMacro(SlicedGeometry3D, Geometry3D); /** Method for creation through the object factory. */ itkNewMacro(Self); /** * \brief Returns the Geometry2D of the slice (\a s). * * If (a) m_EvenlySpaced==true, (b) we don't have a Geometry2D stored * for the requested slice, and (c) the first slice (s=0) * is a PlaneGeometry instance, then we calculate the geometry of the * requested as the plane of the first slice shifted by m_Spacing[3]*s * in the direction of m_DirectionVector. * * \warning The Geometry2Ds are not necessarily up-to-date and not even * initialized. * * The Geometry2Ds have to be initialized in the method * GenerateOutputInformation() of BaseProcess (or CopyInformation / * UpdateOutputInformation of BaseData, if possible, e.g., by analyzing * pic tags in Image) subclasses. See also * * \sa itk::ProcessObject::GenerateOutputInformation(), * \sa itk::DataObject::CopyInformation() and * \sa itk::DataObject::UpdateOutputInformation(). */ virtual mitk::Geometry2D* GetGeometry2D( int s ) const; /** * \brief Set Geometry2D of slice \a s. */ virtual bool SetGeometry2D( mitk::Geometry2D *geometry2D, int s ); //##Documentation //## @brief When switching from an Image Geometry to a normal Geometry (and the other way around), you have to change the origin as well (See Geometry Documentation)! This function will change the "isImageGeometry" bool flag and changes the origin respectively. virtual void ChangeImageGeometryConsideringOriginOffset( const bool isAnImageGeometry ); virtual void SetTimeBounds( const mitk::TimeBounds& timebounds ); virtual const mitk::BoundingBox* GetBoundingBox() const; /** * \brief Get the number of slices */ itkGetConstMacro( Slices, unsigned int ); /** * \brief Check whether a slice exists */ virtual bool IsValidSlice( int s = 0 ) const; virtual void SetReferenceGeometry( Geometry3D *referenceGeometry ); /** * \brief Set the spacing (m_Spacing), in direction of the plane normal. * * INTERNAL METHOD. */ virtual void SetSpacing( const mitk::Vector3D &aSpacing ); /** * \brief Set the SliceNavigationController corresponding to this sliced * geometry. * * The SNC needs to be informed when the number of slices in the geometry * changes, which can occur whenthe slices are re-oriented by rotation. */ virtual void SetSliceNavigationController( mitk::SliceNavigationController *snc ); mitk::SliceNavigationController *GetSliceNavigationController(); /** * \brief Set/Get whether the SlicedGeometry3D is evenly-spaced * (m_EvenlySpaced) * * If (a) m_EvenlySpaced==true, (b) we don't have a Geometry2D stored for * the requested slice, and (c) the first slice (s=0) is a PlaneGeometry * instance, then we calculate the geometry of the requested as the plane * of the first slice shifted by m_Spacing.z * s in the direction of * m_DirectionVector. * * \sa GetGeometry2D */ itkGetConstMacro(EvenlySpaced, bool); virtual void SetEvenlySpaced(bool on = true); /** * \brief Set/Get the vector between slices for the evenly-spaced case * (m_EvenlySpaced==true). * * If the direction-vector is (0,0,0) (the default) and the first * 2D geometry is a PlaneGeometry, then the direction-vector will be * calculated from the plane normal. * * \sa m_DirectionVector */ virtual void SetDirectionVector(const mitk::Vector3D& directionVector); itkGetConstMacro(DirectionVector, const mitk::Vector3D&); virtual AffineGeometryFrame3D::Pointer Clone() const; static const std::string SLICES; const static std::string DIRECTION_VECTOR; const static std::string EVENLY_SPACED; /** * \brief Tell this instance how many Geometry2Ds it shall manage. Bounding * box and the Geometry2Ds must be set additionally by calling the respective * methods! * * \warning Bounding box and the 2D-geometries must be set additionally: use * SetBounds(), SetGeometry(). */ virtual void InitializeSlicedGeometry( unsigned int slices ); /** * \brief Completely initialize this instance as evenly-spaced with slices * parallel to the provided Geometry2D that is used as the first slice and * for spacing calculation. * * Initializes the bounding box according to the width/height of the * Geometry2D and \a slices. The spacing is calculated from the Geometry2D. */ virtual void InitializeEvenlySpaced( mitk::Geometry2D *geometry2D, unsigned int slices, bool flipped=false ); /** * \brief Completely initialize this instance as evenly-spaced with slices * parallel to the provided Geometry2D that is used as the first slice and * for spacing calculation (except z-spacing). * * Initializes the bounding box according to the width/height of the * Geometry2D and \a slices. The x-/y-spacing is calculated from the * Geometry2D. */ virtual void InitializeEvenlySpaced( mitk::Geometry2D *geometry2D, mitk::ScalarType zSpacing, unsigned int slices, bool flipped=false ); /** * \brief Completely initialize this instance as evenly-spaced plane slices * parallel to a side of the provided Geometry3D and using its spacing * information. * * Initializes the bounding box according to the width/height of the * Geometry3D and the number of slices according to * Geometry3D::GetExtent(2). * * \param planeorientation side parallel to which the slices will be oriented * \param top if \a true, create plane at top, otherwise at bottom * (for PlaneOrientation Transversal, for other plane locations respectively) * \param frontside defines the side of the plane (the definition of * front/back is somewhat arbitrary) * * \param rotate rotates the plane by 180 degree around its normal (the * definition of rotated vs not rotated is somewhat arbitrary) */ virtual void InitializePlanes( const mitk::Geometry3D *geometry3D, mitk::PlaneGeometry::PlaneOrientation planeorientation, bool top=true, bool frontside=true, bool rotated=false ); virtual void SetImageGeometry(const bool isAnImageGeometry); virtual void ExecuteOperation(Operation* operation); protected: SlicedGeometry3D(); + SlicedGeometry3D(const SlicedGeometry3D& other); + virtual ~SlicedGeometry3D(); /** * Reinitialize plane stack after rotation. More precisely, the first plane * of the stack needs to spatially aligned, in two respects: * * 1. Re-alignment with respect to the dataset center; this is necessary * since the distance from the first palne to the center could otherwise * continuously decrease or increase. * 2. Re-alignment with respect to a given reference point; the reference * point is a location which the user wants to be exactly touched by one * plane of the plane stack. The first plane is minimally shifted to * ensure this touching. Usually, the reference point would be the * point around which the geometry is rotated. */ virtual void ReinitializePlanes( const Point3D ¢er, const Point3D &referencePoint ); ScalarType GetLargestExtent( const Geometry3D *geometry ); - - virtual void InitializeGeometry(Self * newGeometry) const; - void PrintSelf(std::ostream& os, itk::Indent indent) const; /** Calculate "directed spacing", i.e. the spacing in directions * non-orthogonal to the coordinate axes. This is done via the * ellipsoid equation. */ double CalculateSpacing( const mitk::Vector3D &direction ) const; /** The extent of the slice stack, i.e. the number of slices, depends on the * plane normal. For rotated geometries, the geometry's transform needs to * be accounted in this calculation. */ mitk::Vector3D AdjustNormal( const mitk::Vector3D &normal ) const; /** * Container for the 2D-geometries contained within this SliceGeometry3D. */ mutable std::vector m_Geometry2Ds; /** * If (a) m_EvenlySpaced==true, (b) we don't have a Geometry2D stored * for the requested slice, and (c) the first slice (s=0) * is a PlaneGeometry instance, then we calculate the geometry of the * requested as the plane of the first slice shifted by m_Spacing.z*s * in the direction of m_DirectionVector. * * \sa GetGeometry2D */ bool m_EvenlySpaced; /** * Vector between slices for the evenly-spaced case (m_EvenlySpaced==true). * If the direction-vector is (0,0,0) (the default) and the first * 2D geometry is a PlaneGeometry, then the direction-vector will be * calculated from the plane normal. */ mutable mitk::Vector3D m_DirectionVector; /** Number of slices this SliceGeometry3D is descibing. */ unsigned int m_Slices; /** Underlying Geometry3D for this SlicedGeometry */ mitk::Geometry3D *m_ReferenceGeometry; /** SNC correcsponding to this geometry; used to reflect changes in the * number of slices due to rotation. */ //mitk::NavigationController *m_NavigationController; mitk::SliceNavigationController *m_SliceNavigationController; }; } // namespace mitk #endif /* MITKSLICEDGEOMETRY3D_H_HEADER_INCLUDED_C1EBD0AD */ diff --git a/Core/Code/DataManagement/mitkTimeSlicedGeometry.cpp b/Core/Code/DataManagement/mitkTimeSlicedGeometry.cpp index cad87de770..1193ee44a8 100644 --- a/Core/Code/DataManagement/mitkTimeSlicedGeometry.cpp +++ b/Core/Code/DataManagement/mitkTimeSlicedGeometry.cpp @@ -1,434 +1,416 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision$ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #include "mitkTimeSlicedGeometry.h" void mitk::TimeSlicedGeometry::UpdateInformation() { if(m_TimeSteps==0) return; unsigned long maxModifiedTime = 0, curModifiedTime; mitk::ScalarType stmin, stmax; stmin= ScalarTypeNumericTraits::NonpositiveMin(); stmax= ScalarTypeNumericTraits::max(); TimeBounds timeBounds; timeBounds[0]=stmax; timeBounds[1]=stmin; mitk::BoundingBox::Pointer boundingBox=mitk::BoundingBox::New(); mitk::BoundingBox::PointsContainer::Pointer pointscontainer=mitk::BoundingBox::PointsContainer::New(); unsigned int t; mitk::Geometry3D* geometry3d; mitk::BoundingBox::ConstPointer nextBoundingBox; mitk::BoundingBox::PointIdentifier pointid=0; // Need to check for zero bounding boxes mitk::ScalarType zeropoint[]={0,0,0,0,0,0}; BoundingBox::BoundsArrayType itkBoundsZero(zeropoint); for(t=0; t < m_TimeSteps; ++t) { geometry3d = GetGeometry3D(t); assert(geometry3d!=NULL); curModifiedTime = geometry3d->GetMTime(); if(maxModifiedTime < curModifiedTime) maxModifiedTime = curModifiedTime; const TimeBounds & curTimeBounds = geometry3d->GetTimeBounds(); if((curTimeBounds[0] > stmin) && (curTimeBounds[0] < timeBounds[0])) timeBounds[0] = curTimeBounds[0]; if((curTimeBounds[1] < stmax) && (curTimeBounds[1] > timeBounds[1])) timeBounds[1] = curTimeBounds[1]; nextBoundingBox = geometry3d->GetBoundingBox(); assert(nextBoundingBox.IsNotNull()); // Only respect non-zero BBes if (nextBoundingBox->GetBounds() == itkBoundsZero) { continue; } const mitk::BoundingBox::PointsContainer * nextPoints = nextBoundingBox->GetPoints(); if(nextPoints!=NULL) { mitk::BoundingBox::PointsContainer::ConstIterator pointsIt = nextPoints->Begin(); while (pointsIt != nextPoints->End() ) { pointscontainer->InsertElement( pointid++, pointsIt->Value()); ++pointsIt; } } } if(!(timeBounds[0] < stmax)) { timeBounds[0] = stmin; timeBounds[1] = stmax; } m_TimeBounds = timeBounds; assert(timeBounds[0]<=timeBounds[1]); boundingBox->SetPoints(pointscontainer); boundingBox->ComputeBoundingBox(); m_BoundingBox = boundingBox; SetIndexToWorldTransform(GetGeometry3D(0)->GetIndexToWorldTransform()); if(this->GetMTime() < maxModifiedTime) Modified(); } mitk::Geometry3D* mitk::TimeSlicedGeometry::GetGeometry3D(int t) const { mitk::Geometry3D::Pointer geometry3d = NULL; if(IsValidTime(t)) { geometry3d = m_Geometry3Ds[t]; //if (a) we don't have a Geometry3D stored for the requested time, //(b) m_EvenlyTimed is activated and (c) the first geometry (t=0) //is set, then we clone the geometry and set the m_TimeBounds accordingly. if((m_EvenlyTimed) && (geometry3d.IsNull())) { const Geometry3D* firstgeometry=m_Geometry3Ds[0].GetPointer(); assert(firstgeometry != NULL); mitk::Geometry3D::Pointer requestedgeometry; requestedgeometry = dynamic_cast(firstgeometry->Clone().GetPointer()); if ( requestedgeometry.IsNull() ) itkExceptionMacro("Geometry is NULL!"); TimeBounds timebounds = requestedgeometry->GetTimeBounds(); if(timebounds[1]SetTimeBounds(timebounds); } geometry3d = requestedgeometry; m_Geometry3Ds[t] = geometry3d; } } else return NULL; return geometry3d; } bool mitk::TimeSlicedGeometry::SetGeometry3D(mitk::Geometry3D* geometry3D, int t) { if(IsValidTime(t)) { m_Geometry3Ds[t]=geometry3D; return true; } return false; } int mitk::TimeSlicedGeometry::MSToTimeStep(mitk::ScalarType time_in_ms) const { if(time_in_ms < m_TimeBounds[0]) return -1; if(time_in_ms >= m_TimeBounds[1]) return m_TimeSteps; if(m_EvenlyTimed) { if(m_TimeBounds[0] == m_TimeBounds[1]) return 0; if((m_TimeBounds[0]>ScalarTypeNumericTraits::NonpositiveMin()) && (m_TimeBounds[1]GetTimeBounds(); if( (timeBounds[0] <= time_in_ms) && (time_in_ms <= timeBounds[1]) ) { return t; } } } return 0; } mitk::ScalarType mitk::TimeSlicedGeometry::TimeStepToMS(int timestep) const { if(IsValidTime(timestep)==false) return ScalarTypeNumericTraits::max(); if(m_EvenlyTimed) { if ( timestep == 0 ) return m_TimeBounds[0]; else { assert( ! (m_TimeBounds[0] == ScalarTypeNumericTraits::NonpositiveMin() && m_TimeBounds[1] == ScalarTypeNumericTraits::max() ) ); return ((mitk::ScalarType)timestep)/m_TimeSteps*(m_TimeBounds[1]-m_TimeBounds[0])+m_TimeBounds[0]; } } else { return GetGeometry3D(timestep)->GetTimeBounds()[0]; } } int mitk::TimeSlicedGeometry::TimeStepToTimeStep( const mitk::TimeSlicedGeometry *referenceGeometry, int t) const { int timeStep; if ( referenceGeometry->GetTimeSteps() > 1 ) { // referenceGeometry is nD+t timeStep = this->MSToTimeStep( referenceGeometry->TimeStepToMS( t ) ); } else { // referenceGEometry is nD (only one time step) timeStep = 0; } return timeStep; } void mitk::TimeSlicedGeometry::InitializeEvenlyTimed(unsigned int timeSteps) { Geometry3D::Pointer geometry3D = Geometry3D::New(); geometry3D->Initialize(); InitializeEvenlyTimed(geometry3D, timeSteps); } void mitk::TimeSlicedGeometry::InitializeEvenlyTimed(mitk::Geometry3D* geometry3D, unsigned int timeSteps) { assert(geometry3D!=NULL); geometry3D->Register(); InitializeEmpty(timeSteps); AffineTransform3D::Pointer transform = AffineTransform3D::New(); transform->SetMatrix(geometry3D->GetIndexToWorldTransform()->GetMatrix()); transform->SetOffset(geometry3D->GetIndexToWorldTransform()->GetOffset()); SetIndexToWorldTransform(transform); SetBounds(geometry3D->GetBounds()); SetGeometry3D(geometry3D, 0); SetEvenlyTimed(); UpdateInformation(); SetFrameOfReferenceID(geometry3D->GetFrameOfReferenceID()); SetImageGeometry(geometry3D->GetImageGeometry()); geometry3D->UnRegister(); } void mitk::TimeSlicedGeometry::InitializeEmpty(unsigned int timeSteps) { m_IndexToWorldTransform = NULL; Superclass::Initialize(); m_TimeSteps = timeSteps; // initialize with empty geometries Geometry3D::Pointer gnull=NULL; m_Geometry3Ds.assign(m_TimeSteps, gnull); } void mitk::TimeSlicedGeometry::ExpandToNumberOfTimeSteps( unsigned int timeSteps ) { if( timeSteps <= m_TimeSteps ) return; if(m_TimeSteps == 1) { Geometry3D* g3d = m_Geometry3Ds[0]; const TimeBounds & timeBounds = g3d->GetTimeBounds(); if( (timeBounds[0] == ScalarTypeNumericTraits::NonpositiveMin()) || (timeBounds[1]==ScalarTypeNumericTraits::max()) ) { mitk::ScalarType timeBounds[] = {0.0, 1.0}; m_Geometry3Ds[0]->SetTimeBounds( timeBounds ); } } // Expand to Number of time steps; initialize with empty geometries Geometry3D::Pointer gnull=NULL; m_Geometry3Ds.resize(timeSteps, gnull); m_TimeSteps = timeSteps; UpdateInformation(); } mitk::TimeSlicedGeometry::TimeSlicedGeometry() : m_TimeSteps(0), m_EvenlyTimed(false) { } mitk::TimeSlicedGeometry::TimeSlicedGeometry(const TimeSlicedGeometry& other) : Geometry3D(other), m_TimeSteps(other.m_TimeSteps), m_EvenlyTimed(other.m_EvenlyTimed) { m_Geometry3Ds.resize(m_TimeSteps); unsigned int t; for(t=0; t(other.m_Geometry3Ds[t]->Clone().GetPointer()), t); } } } mitk::TimeSlicedGeometry::~TimeSlicedGeometry() { } void mitk::TimeSlicedGeometry::SetImageGeometry(const bool isAnImageGeometry) { Superclass::SetImageGeometry(isAnImageGeometry); mitk::Geometry3D* geometry3d; unsigned int t; for(t=0; tSetImageGeometry(isAnImageGeometry); } } void mitk::TimeSlicedGeometry::ChangeImageGeometryConsideringOriginOffset(const bool isAnImageGeometry) { mitk::Geometry3D* geometry3d; unsigned int t; for(t=0; tChangeImageGeometryConsideringOriginOffset(isAnImageGeometry); } Superclass::ChangeImageGeometryConsideringOriginOffset(isAnImageGeometry); } void mitk::TimeSlicedGeometry::SetEvenlyTimed(bool on) { m_EvenlyTimed = on; Modified(); } bool mitk::TimeSlicedGeometry::IsValidTime(int t) const { return (t>=0) && (t< (int)m_TimeSteps); } void mitk::TimeSlicedGeometry::CopyTimes(const mitk::TimeSlicedGeometry* timeslicedgeometry, unsigned int t, unsigned int endtimeindex) { if(endtimeindex >= timeslicedgeometry->GetTimeSteps()) endtimeindex = timeslicedgeometry->GetTimeSteps()-1; if(endtimeindex >= this->GetTimeSteps()) endtimeindex = this->GetTimeSteps()-1; for(; t <= endtimeindex; ++t) { mitk::Geometry3D* geometry3d = GetGeometry3D(t); mitk::Geometry3D* othergeometry3d = timeslicedgeometry->GetGeometry3D(t); assert((geometry3d!=NULL) && (othergeometry3d!=NULL)); geometry3d->SetTimeBounds(othergeometry3d->GetTimeBounds()); } UpdateInformation(); } mitk::AffineGeometryFrame3D::Pointer mitk::TimeSlicedGeometry::Clone() const { Self::Pointer newGeometry = new TimeSlicedGeometry(*this); return newGeometry.GetPointer(); } -void mitk::TimeSlicedGeometry::InitializeGeometry(Self * newGeometry) const -{ - Superclass::InitializeGeometry(newGeometry); - - newGeometry->SetEvenlyTimed(m_EvenlyTimed); - unsigned int t; - for(t=0; tSetGeometry3D(dynamic_cast(m_Geometry3Ds[t]->Clone().GetPointer()), t); - } - } -} void mitk::TimeSlicedGeometry::PrintSelf(std::ostream& os, itk::Indent indent) const { //Superclass::PrintSelf(os,indent); os << indent << " EvenlyTimed: " << m_EvenlyTimed << std::endl; os << indent << " TimeSteps: " << m_TimeSteps << std::endl; os << std::endl; os << indent << " GetGeometry3D(0): "; if(GetGeometry3D(0)==NULL) os << "NULL" << std::endl; else GetGeometry3D(0)->Print(os, indent); } void mitk::TimeSlicedGeometry::ExecuteOperation(Operation* operation) { // reach through to all time steps for (std::vector::iterator iter = m_Geometry3Ds.begin(); iter != m_Geometry3Ds.end(); ++iter) { (*iter)->ExecuteOperation(operation); } Geometry3D::ExecuteOperation(operation); this->Modified(); } diff --git a/Core/Code/DataManagement/mitkTimeSlicedGeometry.h b/Core/Code/DataManagement/mitkTimeSlicedGeometry.h index f195ca512b..6f2e098320 100644 --- a/Core/Code/DataManagement/mitkTimeSlicedGeometry.h +++ b/Core/Code/DataManagement/mitkTimeSlicedGeometry.h @@ -1,180 +1,178 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision$ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #ifndef TIMESLICEDGEOMETRY_H_HEADER_INCLUDED_C1EBD0AD #define TIMESLICEDGEOMETRY_H_HEADER_INCLUDED_C1EBD0AD #include "mitkGeometry3D.h" namespace mitk { //##Documentation //## @brief Describes a geometry consisting of several geometries which //## exist at different times. //## //## The geometry contains m_TimeSteps geometries, which can be accessed //## using GetGeometry3D(int t). To convert between world-time in //## milliseconds and the integer timestep-number use MSToTimeStep. //## The hull (in space and time) of the TimeSlicedGeometry contains all //## contained geometries. //## @warning The hull (i.e., transform, bounding-box and //## time-bounds) is only guaranteed to be up-to-date after calling //## UpdateInformation(). //## //## TimeSlicedGeometry and the associated Geometry3Ds 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(). //## //## @ingroup Geometry class MITK_CORE_EXPORT TimeSlicedGeometry : public Geometry3D { public: mitkClassMacro(TimeSlicedGeometry, Geometry3D); itkNewMacro(Self); //##Documentation //## @brief Re-calculate the hull of the contained geometries. //## //## The transforms, bounding-box and time-bounds of this //## geometry (stored in members of the super-class Geometry3D) //## are re-calculated from the contained geometries. void UpdateInformation(); //##Documentation //## @brief Get the number of time-steps itkGetConstMacro(TimeSteps, unsigned int); //##Documentation //## @brief Set/Get whether the TimeSlicedGeometry is evenly-timed (m_EvenlyTimed) //## //## If (a) we don't have a Geometry3D stored for the requested time, //## (b) m_EvenlyTimed is activated and (c) the first geometry (t=0) //## is set, then we clone the geometry and set the m_TimeBounds accordingly. //## \sa GetGeometry3D itkGetConstMacro(EvenlyTimed, bool); virtual void SetEvenlyTimed(bool on = true); //##Documentation //## @brief Set the Geometry3D for time @a t virtual bool SetGeometry3D(mitk::Geometry3D* geometry3D, int t); //##Documentation //## @brief When switching from an Image Geometry to a normal Geometry (and the other way around), you have to change the origin as well (See Geometry Documentation)! This function will change the "isImageGeometry" bool flag and changes the origin respectively. virtual void ChangeImageGeometryConsideringOriginOffset( const bool isAnImageGeometry ); //##Documentation //## @brief Get the Geometry3D at time @a t virtual mitk::Geometry3D* GetGeometry3D(int t) const; //##Documentation //## @brief Test whether @a t is a valid time step virtual bool IsValidTime(int t) const; //##Documentation //## @brief Convert time in ms to a time step virtual int MSToTimeStep(mitk::ScalarType time_in_ms) const; //##Documentation //## @brief Convert time step to time in ms virtual mitk::ScalarType TimeStepToMS(int timestep) const; //##Documentation //## @brief Convert time step in the reference TimeSlicedGeometry to time step //## in this TimeSlicedGeometry. virtual int TimeStepToTimeStep(const mitk::TimeSlicedGeometry *referenceGeometry, int t) const; //##Documentation //## @brief Completely initialize this instance as evenly-timed with //## \a timeSteps geometries of type Geometry3D, each initialized by //## Geometry3D::Initialize(). virtual void InitializeEvenlyTimed(unsigned int timeSteps); //##Documentation //## @brief Completely initialize this instance as evenly-timed with //## \a timeSteps geometries identical to the provided Geometry3D //## except for the time bounds virtual void InitializeEvenlyTimed(mitk::Geometry3D* geometry3D, unsigned int timeSteps); //##Documentation //## @brief Initialize this instance to contain \a timeSteps //## geometries, but without setting them yet virtual void InitializeEmpty(unsigned int timeSteps); //##Documentation //## @brief Expand the number of time steps contained //## to \a timeSteps. //## //## New, additional time steps will be initialized empty. //## Only enlargement of the time steps vector is intended and possible. virtual void ExpandToNumberOfTimeSteps( unsigned int timeSteps ); virtual void SetImageGeometry(const bool isAnImageGeometry); //##Documentation //## @brief Copy the m_TimeBounds of the geometries contained //## in timeslicedgeometry into the geometries contained in this //## TimeSlicedGeometry object. //## //## Useful for initialization of the TimeSlicedGeometry of the //## output in GenerateOutputInformation() methods of process objects, //## see for example BoundingObjectCutter::GenerateOutputInformation(). //## @param t start time index //## @param endtimeindex (endtimeindex) is the time index of //## the last geometry whose time-bounds are copied. If //## timeslicedgeometry or this TimeSlicedGeometry object does //## not contain enough geometries, endtimeindex is reduced //## appropriately. void CopyTimes(const mitk::TimeSlicedGeometry* timeslicedgeometry, unsigned int t=0, unsigned int endtimeindex = itk::NumericTraits::max()); //##Documentation //## @brief duplicates the geometry virtual AffineGeometryFrame3D::Pointer Clone() const; virtual void ExecuteOperation(Operation* operation); protected: TimeSlicedGeometry(); TimeSlicedGeometry(const TimeSlicedGeometry& other); virtual ~TimeSlicedGeometry(); - void InitializeGeometry(Self * newGeometry) const; - virtual void PrintSelf(std::ostream& os, itk::Indent indent) const; mutable std::vector m_Geometry3Ds; //##Documentation //## @brief Number of time steps unsigned int m_TimeSteps; //##Documentation //## @brief \a true in case the time steps have equal length bool m_EvenlyTimed; static const std::string EVENLY_TIMED; static const std::string TIME_STEPS; }; } // namespace mitk #endif /* TIMESLICEDGEOMETRY_H_HEADER_INCLUDED_C1EBD0AD */