diff --git a/Modules/Core/files.cmake b/Modules/Core/files.cmake index 30773d01f6..49db018347 100644 --- a/Modules/Core/files.cmake +++ b/Modules/Core/files.cmake @@ -1,322 +1,323 @@ file(GLOB_RECURSE H_FILES RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/include/*") set(CPP_FILES mitkCoreActivator.cpp mitkCoreObjectFactoryBase.cpp mitkCoreObjectFactory.cpp mitkCoreServices.cpp mitkException.cpp Algorithms/mitkBaseDataSource.cpp Algorithms/mitkClippedSurfaceBoundsCalculator.cpp Algorithms/mitkCompareImageDataFilter.cpp Algorithms/mitkCompositePixelValueToString.cpp Algorithms/mitkConvert2Dto3DImageFilter.cpp Algorithms/mitkDataNodeSource.cpp Algorithms/mitkExtractSliceFilter.cpp Algorithms/mitkExtractSliceFilter2.cpp Algorithms/mitkHistogramGenerator.cpp Algorithms/mitkImageChannelSelector.cpp Algorithms/mitkImageSliceSelector.cpp Algorithms/mitkImageSource.cpp Algorithms/mitkImageTimeSelector.cpp Algorithms/mitkImageToImageFilter.cpp Algorithms/mitkImageToSurfaceFilter.cpp Algorithms/mitkMultiComponentImageDataComparisonFilter.cpp Algorithms/mitkPlaneGeometryDataToSurfaceFilter.cpp Algorithms/mitkPointSetSource.cpp Algorithms/mitkPointSetToPointSetFilter.cpp Algorithms/mitkRGBToRGBACastImageFilter.cpp Algorithms/mitkSubImageSelector.cpp Algorithms/mitkSurfaceSource.cpp Algorithms/mitkSurfaceToImageFilter.cpp Algorithms/mitkSurfaceToSurfaceFilter.cpp Algorithms/mitkUIDGenerator.cpp Algorithms/mitkVolumeCalculator.cpp Algorithms/mitkTemporalJoinImagesFilter.cpp Controllers/mitkBaseController.cpp Controllers/mitkCallbackFromGUIThread.cpp Controllers/mitkCameraController.cpp Controllers/mitkCameraRotationController.cpp Controllers/mitkCrosshairManager.cpp Controllers/mitkLimitedLinearUndo.cpp Controllers/mitkOperationEvent.cpp Controllers/mitkPlanePositionManager.cpp Controllers/mitkProgressBar.cpp Controllers/mitkRenderingManager.cpp Controllers/mitkSliceNavigationController.cpp + Controllers/mitkSliceNavigationHelper.cpp Controllers/mitkStatusBar.cpp Controllers/mitkStepper.cpp Controllers/mitkTestManager.cpp Controllers/mitkUndoController.cpp Controllers/mitkVerboseLimitedLinearUndo.cpp Controllers/mitkVtkLayerController.cpp DataManagement/mitkAnatomicalStructureColorPresets.cpp DataManagement/mitkArbitraryTimeGeometry.cpp DataManagement/mitkAbstractTransformGeometry.cpp DataManagement/mitkAnnotationProperty.cpp DataManagement/mitkApplicationCursor.cpp DataManagement/mitkApplyTransformMatrixOperation.cpp DataManagement/mitkBaseData.cpp DataManagement/mitkBaseGeometry.cpp DataManagement/mitkBaseProperty.cpp DataManagement/mitkChannelDescriptor.cpp DataManagement/mitkClippingProperty.cpp DataManagement/mitkColorProperty.cpp DataManagement/mitkDataNode.cpp DataManagement/mitkDataStorage.cpp DataManagement/mitkEnumerationProperty.cpp DataManagement/mitkFloatPropertyExtension.cpp DataManagement/mitkGeometry3D.cpp DataManagement/mitkGeometryData.cpp DataManagement/mitkGeometryTransformHolder.cpp DataManagement/mitkGroupTagProperty.cpp DataManagement/mitkGenericIDRelationRule.cpp DataManagement/mitkIdentifiable.cpp DataManagement/mitkImageAccessorBase.cpp DataManagement/mitkImageCaster.cpp DataManagement/mitkImageCastPart1.cpp DataManagement/mitkImageCastPart2.cpp DataManagement/mitkImageCastPart3.cpp DataManagement/mitkImageCastPart4.cpp DataManagement/mitkImage.cpp DataManagement/mitkImageDataItem.cpp DataManagement/mitkImageDescriptor.cpp DataManagement/mitkImageReadAccessor.cpp DataManagement/mitkImageStatisticsHolder.cpp DataManagement/mitkImageVtkAccessor.cpp DataManagement/mitkImageVtkReadAccessor.cpp DataManagement/mitkImageVtkWriteAccessor.cpp DataManagement/mitkImageWriteAccessor.cpp DataManagement/mitkIntPropertyExtension.cpp DataManagement/mitkIPersistenceService.cpp DataManagement/mitkIPropertyAliases.cpp DataManagement/mitkIPropertyDescriptions.cpp DataManagement/mitkIPropertyExtensions.cpp DataManagement/mitkIPropertyFilters.cpp DataManagement/mitkIPropertyOwner.cpp DataManagement/mitkIPropertyPersistence.cpp DataManagement/mitkIPropertyProvider.cpp DataManagement/mitkLandmarkProjectorBasedCurvedGeometry.cpp DataManagement/mitkLandmarkProjector.cpp DataManagement/mitkLevelWindow.cpp DataManagement/mitkLevelWindowManager.cpp DataManagement/mitkLevelWindowPreset.cpp DataManagement/mitkLevelWindowProperty.cpp DataManagement/mitkLine.cpp DataManagement/mitkLookupTable.cpp DataManagement/mitkLookupTableProperty.cpp DataManagement/mitkLookupTables.cpp # specializations of GenericLookupTable DataManagement/mitkMaterial.cpp DataManagement/mitkMemoryUtilities.cpp DataManagement/mitkModalityProperty.cpp DataManagement/mitkModifiedLock.cpp DataManagement/mitkNodePredicateAnd.cpp DataManagement/mitkNodePredicateBase.cpp DataManagement/mitkNodePredicateCompositeBase.cpp DataManagement/mitkNodePredicateData.cpp DataManagement/mitkNodePredicateDataType.cpp DataManagement/mitkNodePredicateDataUID.cpp DataManagement/mitkNodePredicateDimension.cpp DataManagement/mitkNodePredicateFirstLevel.cpp DataManagement/mitkNodePredicateFunction.cpp DataManagement/mitkNodePredicateGeometry.cpp DataManagement/mitkNodePredicateNot.cpp DataManagement/mitkNodePredicateOr.cpp DataManagement/mitkNodePredicateProperty.cpp DataManagement/mitkNodePredicateDataProperty.cpp DataManagement/mitkNodePredicateSource.cpp DataManagement/mitkNodePredicateSubGeometry.cpp DataManagement/mitkNumericConstants.cpp DataManagement/mitkPlaneGeometry.cpp DataManagement/mitkPlaneGeometryData.cpp DataManagement/mitkPlaneOperation.cpp DataManagement/mitkPlaneOrientationProperty.cpp DataManagement/mitkPointOperation.cpp DataManagement/mitkPointSet.cpp DataManagement/mitkPointSetShapeProperty.cpp DataManagement/mitkProperties.cpp DataManagement/mitkPropertyAliases.cpp DataManagement/mitkPropertyDescriptions.cpp DataManagement/mitkPropertyExtension.cpp DataManagement/mitkPropertyExtensions.cpp DataManagement/mitkPropertyFilter.cpp DataManagement/mitkPropertyFilters.cpp DataManagement/mitkPropertyKeyPath.cpp DataManagement/mitkPropertyList.cpp DataManagement/mitkPropertyListReplacedObserver.cpp DataManagement/mitkPropertyNameHelper.cpp DataManagement/mitkPropertyObserver.cpp DataManagement/mitkPropertyPersistence.cpp DataManagement/mitkPropertyPersistenceInfo.cpp DataManagement/mitkPropertyRelationRuleBase.cpp DataManagement/mitkProportionalTimeGeometry.cpp DataManagement/mitkRenderingModeProperty.cpp DataManagement/mitkResliceMethodProperty.cpp DataManagement/mitkRestorePlanePositionOperation.cpp DataManagement/mitkRotationOperation.cpp DataManagement/mitkScaleOperation.cpp DataManagement/mitkSlicedData.cpp DataManagement/mitkSlicedGeometry3D.cpp DataManagement/mitkSmartPointerProperty.cpp DataManagement/mitkStandaloneDataStorage.cpp DataManagement/mitkStringProperty.cpp DataManagement/mitkSurface.cpp DataManagement/mitkSurfaceOperation.cpp DataManagement/mitkSourceImageRelationRule.cpp DataManagement/mitkThinPlateSplineCurvedGeometry.cpp DataManagement/mitkTimeGeometry.cpp DataManagement/mitkTransferFunction.cpp DataManagement/mitkTransferFunctionInitializer.cpp DataManagement/mitkTransferFunctionProperty.cpp DataManagement/mitkTemporoSpatialStringProperty.cpp DataManagement/mitkUIDManipulator.cpp DataManagement/mitkVector.cpp DataManagement/mitkVectorProperty.cpp DataManagement/mitkVtkInterpolationProperty.cpp DataManagement/mitkVtkRepresentationProperty.cpp DataManagement/mitkVtkResliceInterpolationProperty.cpp DataManagement/mitkVtkScalarModeProperty.cpp DataManagement/mitkWeakPointerProperty.cpp DataManagement/mitkIPropertyRelations.cpp DataManagement/mitkPropertyRelations.cpp Interactions/mitkAction.cpp Interactions/mitkBindDispatcherInteractor.cpp Interactions/mitkCrosshairPositionEvent.cpp Interactions/mitkDataInteractor.cpp Interactions/mitkDispatcher.cpp Interactions/mitkDisplayActionEventBroadcast.cpp Interactions/mitkDisplayActionEventFunctions.cpp Interactions/mitkDisplayActionEventHandler.cpp Interactions/mitkDisplayActionEventHandlerDesynchronized.cpp Interactions/mitkDisplayActionEventHandlerStd.cpp Interactions/mitkDisplayActionEventHandlerSynchronized.cpp Interactions/mitkDisplayCoordinateOperation.cpp Interactions/mitkEventConfig.cpp Interactions/mitkEventFactory.cpp Interactions/mitkEventRecorder.cpp Interactions/mitkEventStateMachine.cpp Interactions/mitkInteractionEventConst.cpp Interactions/mitkInteractionEvent.cpp Interactions/mitkInteractionEventHandler.cpp Interactions/mitkInteractionEventObserver.cpp Interactions/mitkInteractionKeyEvent.cpp Interactions/mitkInteractionPositionEvent.cpp Interactions/mitkInteractionSchemeSwitcher.cpp Interactions/mitkInternalEvent.cpp Interactions/mitkMouseDoubleClickEvent.cpp Interactions/mitkMouseMoveEvent.cpp Interactions/mitkMousePressEvent.cpp Interactions/mitkMouseReleaseEvent.cpp Interactions/mitkMouseWheelEvent.cpp Interactions/mitkPointSetDataInteractor.cpp Interactions/mitkSinglePointDataInteractor.cpp Interactions/mitkStateMachineAction.cpp Interactions/mitkStateMachineCondition.cpp Interactions/mitkStateMachineContainer.cpp Interactions/mitkStateMachineState.cpp Interactions/mitkStateMachineTransition.cpp Interactions/mitkVtkEventAdapter.cpp Interactions/mitkVtkInteractorStyle.cxx Interactions/mitkXML2EventParser.cpp IO/mitkAbstractFileIO.cpp IO/mitkAbstractFileReader.cpp IO/mitkAbstractFileWriter.cpp IO/mitkCustomMimeType.cpp IO/mitkFileReader.cpp IO/mitkFileReaderRegistry.cpp IO/mitkFileReaderSelector.cpp IO/mitkFileReaderWriterBase.cpp IO/mitkFileWriter.cpp IO/mitkFileWriterRegistry.cpp IO/mitkFileWriterSelector.cpp IO/mitkGeometry3DToXML.cpp IO/mitkIFileIO.cpp IO/mitkIFileReader.cpp IO/mitkIFileWriter.cpp IO/mitkGeometryDataReaderService.cpp IO/mitkGeometryDataWriterService.cpp IO/mitkImageGenerator.cpp IO/mitkImageVtkLegacyIO.cpp IO/mitkImageVtkXmlIO.cpp IO/mitkIMimeTypeProvider.cpp IO/mitkIOConstants.cpp IO/mitkIOMimeTypes.cpp IO/mitkIOUtil.cpp IO/mitkItkImageIO.cpp IO/mitkItkLoggingAdapter.cpp IO/mitkLegacyFileReaderService.cpp IO/mitkLegacyFileWriterService.cpp IO/mitkLocaleSwitch.cpp IO/mitkLog.cpp IO/mitkMimeType.cpp IO/mitkMimeTypeProvider.cpp IO/mitkOperation.cpp IO/mitkPixelType.cpp IO/mitkPointSetReaderService.cpp IO/mitkPointSetWriterService.cpp IO/mitkProportionalTimeGeometryToXML.cpp IO/mitkRawImageFileReader.cpp IO/mitkStandardFileLocations.cpp IO/mitkSurfaceStlIO.cpp IO/mitkSurfaceVtkIO.cpp IO/mitkSurfaceVtkLegacyIO.cpp IO/mitkSurfaceVtkXmlIO.cpp IO/mitkUtf8Util.cpp IO/mitkVtkLoggingAdapter.cpp IO/mitkPreferenceListReaderOptionsFunctor.cpp IO/mitkIOMetaInformationPropertyConstants.cpp Rendering/mitkAbstractAnnotationRenderer.cpp Rendering/mitkAnnotationUtils.cpp Rendering/mitkBaseRenderer.cpp #Rendering/mitkGLMapper.cpp Moved to deprecated LegacyGL Module Rendering/mitkGradientBackground.cpp Rendering/mitkImageVtkMapper2D.cpp Rendering/mitkMapper.cpp Rendering/mitkAnnotation.cpp Rendering/mitkPlaneGeometryDataMapper2D.cpp Rendering/mitkPlaneGeometryDataVtkMapper3D.cpp Rendering/mitkPointSetVtkMapper2D.cpp Rendering/mitkPointSetVtkMapper3D.cpp Rendering/mitkRenderWindowBase.cpp Rendering/mitkRenderWindow.cpp Rendering/mitkRenderWindowFrame.cpp #Rendering/mitkSurfaceGLMapper2D.cpp Moved to deprecated LegacyGL Module Rendering/mitkSurfaceVtkMapper2D.cpp Rendering/mitkSurfaceVtkMapper3D.cpp Rendering/mitkVtkEventProvider.cpp Rendering/mitkVtkMapper.cpp Rendering/mitkVtkPropRenderer.cpp Rendering/mitkVtkWidgetRendering.cpp Rendering/vtkMitkLevelWindowFilter.cpp Rendering/vtkMitkRectangleProp.cpp Rendering/vtkMitkRenderProp.cpp Rendering/vtkMitkThickSlicesFilter.cpp Rendering/vtkNeverTranslucentTexture.cpp ) set(RESOURCE_FILES Interactions/globalConfig.xml Interactions/DisplayInteraction.xml Interactions/DisplayConfigMITKBase.xml Interactions/DisplayConfigPACSBase.xml Interactions/DisplayConfigCrosshair.xml Interactions/DisplayConfigRotation.xml Interactions/DisplayConfigActivateCoupling.xml Interactions/DisplayConfigSwivel.xml Interactions/DisplayConfigPACSPan.xml Interactions/DisplayConfigPACSScroll.xml Interactions/DisplayConfigPACSZoom.xml Interactions/DisplayConfigPACSLevelWindow.xml Interactions/DisplayConfigBlockLMB.xml Interactions/PointSet.xml Interactions/PointSetConfig.xml mitkLevelWindowPresets.xml mitkAnatomicalStructureColorPresets.xml ) diff --git a/Modules/Core/include/mitkSliceNavigationHelper.h b/Modules/Core/include/mitkSliceNavigationHelper.h new file mode 100644 index 0000000000..2176f4585a --- /dev/null +++ b/Modules/Core/include/mitkSliceNavigationHelper.h @@ -0,0 +1,99 @@ +/*============================================================================ + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center (DKFZ) +All rights reserved. + +Use of this source code is governed by a 3-clause BSD license that can be +found in the LICENSE file. + +============================================================================*/ + +#ifndef MITKSLICENAVIGATIONHELPER_H +#define MITKSLICENAVIGATIONHELPER_H + +#include + +#include +#include + +namespace mitk +{ + namespace SliceNavigationHelper + { + + /** + * \brief Select a specific slice from the given time geometry given a 3D point. + * + * The function uses the currently selected time point to retrieve the current base geometry from + * the given time geometry. + * If this base geometry is a SlicedGeometry3D, the best fitting slice is computed by projecting + * the point onto the plane geometry of this SlicedGeometry. + * The slice with the shortest distance to the point is returned as the selected slice. + * + * \param timeGeometry The TimeGeometry of which the slice should be selected. + * \param point The Point3D which is used to select the specific slice. + * + * \pre The given TimeGeometry must cover the currently selected time point. If not, an exception is thrown. + * If the given TimeGeomety is a nullptr, -1 is returned. + * + * \return The selected slice as unsigned int. If the computed slice is negative, "0" (zero) is + * returned as the selected slice. + */ + MITKCORE_EXPORT unsigned int SelectSliceByPoint(const TimeGeometry* timeGeometry, const Point3D& point); + + /** + * \brief Create a new time geometry, which is oriented with the given plane orientation. + * + * The function uses the currently selected time point to retrieve the current base geometry from + * the given time geometry. + * A new SlicedGeometry3D is created and initialized with the given parameters and the extracted base geometry. + * This new SlicedGeometry3D is then used to replace the geometries of each time step of a cloned version + * of the given TimeGeometry. This allows to only replace the contained geometries for each time step, + * keeping the time steps (time point + time duration) from the original time geometry. + * + * \param timeGeometry The TimeGeometry which should be used for cloning. + * \param planeOrientation The PlaneOrientation that specifies the new orientation of the time geometry. + * \param top If true, create the plane at top, otherwise at bottom. + * \param frontside Defines the side of the plane. + * \param rotated Rotates the plane by 180 degree around its normal. + * s.a. PlaneGeometry::InitializeStandardPlane + * + * \pre The given TimeGeometry must cover the currently selected time point. If not, an exception is thrown. + * If the given TimeGeomety is a nullptr, a nullptr is returned immediately. + * + * \return The created geometry as TimeGeometry. + */ + MITKCORE_EXPORT TimeGeometry::Pointer CreateOrientedTimeGeometry(const TimeGeometry* timeGeometry, + PlaneGeometry::PlaneOrientation planeOrientation, + bool top, + bool frontside, + bool rotated); + + /** + * \brief Extracts the plane geometry for the given time step and slice position. + * + * The function uses the given time point to retrieve the current base geometry from + * the given time geometry. + * If this base geometry is a SlicedGeometry3D, the plane geometry of the given + * slice position is extracted and returned. + * If not, a nullptr is returned. + * + * \param timeGeometry The TimeGeometry of which the plane geometry should be extracted + * \param timePoint The time point to extract the current base geometry. + * \param slicePosition The slice position to extract the current plane geometry. + * + * \pre The given TimeGeometry must cover the currently selected time point. If not, an exception is thrown. + * If the given TimeGeomety is a nullptr, a nullptr is returned immediately. + * + * \return The extracted plane geometry as PlaneGeometry. + * Nullptr, if no SlicedGeometry3D was found for the given time step. + */ + MITKCORE_EXPORT PlaneGeometry* GetCurrentPlaneGeometry(const TimeGeometry* timeGeometry, + TimePointType timePoint, + unsigned int slicePosition); + } // namespace SliceNavigationHelper +} // namespace mitk + +#endif // MITKSLICENAVIGATIONHELPER_H diff --git a/Modules/Core/src/Controllers/mitkSliceNavigationController.cpp b/Modules/Core/src/Controllers/mitkSliceNavigationController.cpp index d99c58a6e6..6201016e0c 100644 --- a/Modules/Core/src/Controllers/mitkSliceNavigationController.cpp +++ b/Modules/Core/src/Controllers/mitkSliceNavigationController.cpp @@ -1,599 +1,562 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkSliceNavigationController.h" +#include #include "mitkBaseRenderer.h" #include "mitkCrosshairPositionEvent.h" #include "mitkOperation.h" #include "mitkOperationActor.h" #include "mitkPlaneGeometry.h" #include "mitkProportionalTimeGeometry.h" #include "mitkArbitraryTimeGeometry.h" #include "mitkSlicedGeometry3D.h" #include "mitkVtkPropRenderer.h" #include "mitkImage.h" #include "mitkImagePixelReadAccessor.h" #include "mitkInteractionConst.h" #include "mitkNodePredicateDataType.h" #include "mitkOperationEvent.h" #include "mitkPixelTypeMultiplex.h" #include "mitkPlaneOperation.h" #include "mitkPointOperation.h" #include "mitkStatusBar.h" #include "mitkApplyTransformMatrixOperation.h" namespace mitk { PlaneGeometry::PlaneOrientation ViewDirectionToPlaneOrientation(SliceNavigationController::ViewDirection viewDiretion) { switch (viewDiretion) { case SliceNavigationController::Axial: return PlaneGeometry::Axial; case SliceNavigationController::Sagittal: return PlaneGeometry::Sagittal; case SliceNavigationController::Coronal: return PlaneGeometry::Coronal; case SliceNavigationController::Original: default: return PlaneGeometry::None; } } SliceNavigationController::SliceNavigationController() : BaseController() , m_InputWorldTimeGeometry(TimeGeometry::ConstPointer()) , m_CreatedWorldGeometry(TimeGeometry::Pointer()) , m_ViewDirection(Axial) , m_DefaultViewDirection(Axial) , m_RenderingManager(RenderingManager::Pointer()) , m_Renderer(nullptr) , m_BlockUpdate(false) , m_SliceLocked(false) , m_SliceRotationLocked(false) { typedef itk::SimpleMemberCommand SNCCommandType; SNCCommandType::Pointer sliceStepperChangedCommand, timeStepperChangedCommand; sliceStepperChangedCommand = SNCCommandType::New(); timeStepperChangedCommand = SNCCommandType::New(); sliceStepperChangedCommand->SetCallbackFunction(this, &SliceNavigationController::SendSlice); timeStepperChangedCommand->SetCallbackFunction(this, &SliceNavigationController::SendTime); m_Slice->AddObserver(itk::ModifiedEvent(), sliceStepperChangedCommand); m_Time->AddObserver(itk::ModifiedEvent(), timeStepperChangedCommand); m_Slice->SetUnitName("mm"); m_Time->SetUnitName("ms"); } SliceNavigationController::~SliceNavigationController() { // nothing here } void SliceNavigationController::SetInputWorldTimeGeometry(const TimeGeometry* geometry) { if (nullptr != geometry) { if (geometry->GetBoundingBoxInWorld()->GetDiagonalLength2() < eps) { itkWarningMacro("setting an empty bounding-box"); geometry = nullptr; } } if (m_InputWorldTimeGeometry != geometry) { m_InputWorldTimeGeometry = geometry; this->Modified(); } } void SliceNavigationController::SetViewDirectionToDefault() { m_ViewDirection = m_DefaultViewDirection; } const char* SliceNavigationController::GetViewDirectionAsString() const { const char* viewDirectionString; switch (m_ViewDirection) { case SliceNavigationController::Axial: viewDirectionString = "Axial"; break; case SliceNavigationController::Sagittal: viewDirectionString = "Sagittal"; break; case SliceNavigationController::Coronal: viewDirectionString = "Coronal"; break; case SliceNavigationController::Original: viewDirectionString = "Original"; break; default: viewDirectionString = "No View Direction Available"; break; } return viewDirectionString; } void SliceNavigationController::Update() { if (!m_BlockUpdate) { if (m_ViewDirection == Sagittal) { this->Update(Sagittal, true, true, false); } else if (m_ViewDirection == Coronal) { this->Update(Coronal, false, true, false); } else if (m_ViewDirection == Axial) { this->Update(Axial, false, false, true); } else { this->Update(m_ViewDirection); } } } void SliceNavigationController::Update(SliceNavigationController::ViewDirection viewDirection, bool top, bool frontside, bool rotated) { if (m_BlockUpdate) { return; } if (m_InputWorldTimeGeometry.IsNull()) { return; } if (0 == m_InputWorldTimeGeometry->CountTimeSteps()) { return; } m_BlockUpdate = true; if (m_LastUpdateTime < m_InputWorldTimeGeometry->GetMTime()) { Modified(); } this->SetViewDirection(viewDirection); if (m_LastUpdateTime < GetMTime()) { m_LastUpdateTime = GetMTime(); this->CreateWorldGeometry(top, frontside, rotated); } // unblock update; we may do this now, because if m_BlockUpdate was already // true before this method was entered, then we will never come here. m_BlockUpdate = false; // Send the geometry. Do this even if nothing was changed, because maybe // Update() was only called to re-send the old geometry and time/slice data. this->SendCreatedWorldGeometry(); this->SendSlice(); this->SendTime(); // Adjust the stepper range of slice stepper according to geometry this->AdjustSliceStepperRange(); } void SliceNavigationController::SendCreatedWorldGeometry() { if (!m_BlockUpdate) { this->InvokeEvent(GeometrySendEvent(m_CreatedWorldGeometry, 0)); } } void SliceNavigationController::SendCreatedWorldGeometryUpdate() { if (!m_BlockUpdate) { this->InvokeEvent(GeometryUpdateEvent(m_CreatedWorldGeometry, m_Slice->GetPos())); } } void SliceNavigationController::SendSlice() { if (!m_BlockUpdate) { if (m_CreatedWorldGeometry.IsNotNull()) { this->InvokeEvent(GeometrySliceEvent(m_CreatedWorldGeometry, m_Slice->GetPos())); RenderingManager::GetInstance()->RequestUpdateAll(); } } } void SliceNavigationController::SendTime() { if (!m_BlockUpdate) { if (m_CreatedWorldGeometry.IsNotNull()) { this->InvokeEvent(GeometryTimeEvent(m_CreatedWorldGeometry, m_Time->GetPos())); RenderingManager::GetInstance()->RequestUpdateAll(); } } } void SliceNavigationController::SetGeometry(const itk::EventObject&) { // not implemented } void SliceNavigationController::SetGeometryTime(const itk::EventObject& geometryTimeEvent) { if (m_CreatedWorldGeometry.IsNull()) { return; } const auto* timeEvent = dynamic_cast(&geometryTimeEvent); assert(timeEvent != nullptr); TimeGeometry* timeGeometry = timeEvent->GetTimeGeometry(); assert(timeGeometry != nullptr); auto timeStep = (int)timeEvent->GetPos(); ScalarType timeInMS; timeInMS = timeGeometry->TimeStepToTimePoint(timeStep); timeStep = m_CreatedWorldGeometry->TimePointToTimeStep(timeInMS); this->GetTime()->SetPos(timeStep); } void SliceNavigationController::SetGeometrySlice(const itk::EventObject& geometrySliceEvent) { const auto* sliceEvent = dynamic_cast(&geometrySliceEvent); assert(sliceEvent != nullptr); this->GetSlice()->SetPos(sliceEvent->GetPos()); } void SliceNavigationController::SelectSliceByPoint(const Point3D& point) { if (m_CreatedWorldGeometry.IsNull()) { return; } - //@todo add time to PositionEvent and use here!! - const auto* slicedWorldGeometry = dynamic_cast( - m_CreatedWorldGeometry->GetGeometryForTimeStep(this->GetTime()->GetPos()).GetPointer()); - - if (nullptr == slicedWorldGeometry) - { - return; - } - - int bestSlice = -1; - double bestDistance = itk::NumericTraits::max(); - - if (slicedWorldGeometry->GetEvenlySpaced()) + int selectedSlice = -1; + try { - PlaneGeometry* plane = slicedWorldGeometry->GetPlaneGeometry(0); - - const Vector3D& direction = slicedWorldGeometry->GetDirectionVector(); - - Point3D projectedPoint; - plane->Project(point, projectedPoint); - - // Check whether the point is somewhere within the slice stack volume; - // otherwise, the default slice (0) will be selected - if (direction[0] * (point[0] - projectedPoint[0]) + direction[1] * (point[1] - projectedPoint[1]) + - direction[2] * (point[2] - projectedPoint[2]) >= 0) - { - bestSlice = static_cast(plane->Distance(point) / slicedWorldGeometry->GetSpacing()[2] + 0.5); - } + selectedSlice = SliceNavigationHelper::SelectSliceByPoint(m_CreatedWorldGeometry, point); } - else + catch (const mitk::Exception& e) { - int numberOfSlices = slicedWorldGeometry->GetSlices(); - Point3D projectedPoint; - for (int slice = 0; slice < numberOfSlices; ++slice) - { - slicedWorldGeometry->GetPlaneGeometry(slice)->Project(point, projectedPoint); - const Vector3D distance = projectedPoint - point; - ScalarType currentDistance = distance.GetSquaredNorm(); - - if (currentDistance < bestDistance) - { - bestDistance = currentDistance; - bestSlice = slice; - } - } + MITK_ERROR << "Unable to select a slice by the given point " << point << "\n" + << "Reason: " << e.GetDescription(); } - if (bestSlice >= 0) + if (-1 == selectedSlice) { - this->GetSlice()->SetPos(bestSlice); - } - else - { - this->GetSlice()->SetPos(0); + return; } + this->GetSlice()->SetPos(selectedSlice); + this->SendCreatedWorldGeometryUpdate(); // send crosshair event SetCrosshairEvent.Send(point); } void SliceNavigationController::ReorientSlices(const Point3D& point, const Vector3D& normal) { if (m_CreatedWorldGeometry.IsNull()) { return; } PlaneOperation op(OpORIENT, point, normal); m_CreatedWorldGeometry->ExecuteOperation(&op); this->SendCreatedWorldGeometryUpdate(); } void SliceNavigationController::ReorientSlices(const Point3D& point, const Vector3D& axisVec0, const Vector3D& axisVec1) { if (m_CreatedWorldGeometry.IsNull()) { return; } PlaneOperation op(OpORIENT, point, axisVec0, axisVec1); m_CreatedWorldGeometry->ExecuteOperation(&op); this->SendCreatedWorldGeometryUpdate(); } const BaseGeometry* SliceNavigationController::GetCurrentGeometry3D() { if (m_CreatedWorldGeometry.IsNull()) { return nullptr; } return m_CreatedWorldGeometry->GetGeometryForTimeStep(this->GetTime()->GetPos()); } const PlaneGeometry* SliceNavigationController::GetCurrentPlaneGeometry() { const auto* slicedGeometry = dynamic_cast(this->GetCurrentGeometry3D()); if (nullptr == slicedGeometry) { return nullptr; } return slicedGeometry->GetPlaneGeometry(this->GetSlice()->GetPos()); } void SliceNavigationController::AdjustSliceStepperRange() { const auto* slicedGeometry = dynamic_cast(this->GetCurrentGeometry3D()); const Vector3D& direction = slicedGeometry->GetDirectionVector(); int c = 0; int i, k = 0; for (i = 0; i < 3; ++i) { if (fabs(direction[i]) < 0.000000001) { ++c; } else { k = i; } } if (c == 2) { ScalarType min = slicedGeometry->GetOrigin()[k]; ScalarType max = min + slicedGeometry->GetExtentInMM(k); m_Slice->SetRange(min, max); } else { m_Slice->InvalidateRange(); } } void SliceNavigationController::ExecuteOperation(Operation* operation) { // switch on type // - select best slice for a given point // - rotate created world geometry according to Operation->SomeInfo() if (!operation || m_CreatedWorldGeometry.IsNull()) { return; } switch (operation->GetOperationType()) { case OpMOVE: // should be a point operation { if (!m_SliceLocked) // do not move the cross position { // select a slice auto* po = dynamic_cast(operation); if (po && po->GetIndex() == -1) { this->SelectSliceByPoint(po->GetPoint()); } else if (po && po->GetIndex() != -1) // undo case because index != -1, index holds the old position of this slice { this->GetSlice()->SetPos(po->GetIndex()); } } break; } case OpRESTOREPLANEPOSITION: { m_CreatedWorldGeometry->ExecuteOperation(operation); this->SendCreatedWorldGeometryUpdate(); break; } case OpAPPLYTRANSFORMMATRIX: { m_CreatedWorldGeometry->ExecuteOperation(operation); this->SendCreatedWorldGeometryUpdate(); break; } default: { // do nothing break; } } } TimeStepType SliceNavigationController::GetSelectedTimeStep() const { return this->GetTime()->GetPos(); } TimePointType SliceNavigationController::GetSelectedTimePoint() const { auto timeStep = this->GetSelectedTimeStep(); if (m_CreatedWorldGeometry.IsNull()) { return 0.0; } if (!m_CreatedWorldGeometry->IsValidTimeStep(timeStep)) { mitkThrow() << "SliceNavigationController is in an invalid state. It has a time step" << "selected that is not covered by its time geometry. Selected time step: " << timeStep << "; TimeGeometry steps count: " << m_CreatedWorldGeometry->CountTimeSteps(); } return m_CreatedWorldGeometry->TimeStepToTimePoint(timeStep); } void SliceNavigationController::CreateWorldGeometry(bool top, bool frontside, bool rotated) { // initialize the viewplane SlicedGeometry3D::Pointer slicedWorldGeometry; BaseGeometry::ConstPointer currentGeometry; // get the BaseGeometry (ArbitraryTimeGeometry or ProportionalTimeGeometry) of the current time step auto currentTimeStep = this->GetTime()->GetPos(); if (m_InputWorldTimeGeometry->IsValidTimeStep(currentTimeStep)) { currentGeometry = m_InputWorldTimeGeometry->GetGeometryForTimeStep(currentTimeStep); } else { currentGeometry = m_InputWorldTimeGeometry->GetGeometryForTimeStep(0); } if (Original == m_ViewDirection) { slicedWorldGeometry = dynamic_cast( m_InputWorldTimeGeometry->GetGeometryForTimeStep(currentTimeStep).GetPointer()); if (slicedWorldGeometry.IsNull()) { slicedWorldGeometry = SlicedGeometry3D::New(); slicedWorldGeometry->InitializePlanes( currentGeometry, PlaneGeometry::None, top, frontside, rotated); slicedWorldGeometry->SetSliceNavigationController(this); } } else { slicedWorldGeometry = SlicedGeometry3D::New(); slicedWorldGeometry->InitializePlanes( currentGeometry, ViewDirectionToPlaneOrientation(m_ViewDirection), top, frontside, rotated); slicedWorldGeometry->SetSliceNavigationController(this); } // reset the stepper m_Slice->SetSteps(slicedWorldGeometry->GetSlices()); m_Slice->SetPos(0); TimeStepType inputTimeSteps = m_InputWorldTimeGeometry->CountTimeSteps(); const TimeBounds& timeBounds = m_InputWorldTimeGeometry->GetTimeBounds(); m_Time->SetSteps(inputTimeSteps); m_Time->SetPos(0); m_Time->SetRange(timeBounds[0], timeBounds[1]); currentTimeStep = this->GetTime()->GetPos(); assert(m_InputWorldTimeGeometry->GetGeometryForTimeStep(currentTimeStep).IsNotNull()); // create new time geometry and initialize it according to the type of the 'm_InputWorldTimeGeometry' // the created world geometry will either have equidistant timesteps (ProportionalTimeGeometry) // or individual time bounds for each timestep (ArbitraryTimeGeometry) m_CreatedWorldGeometry = mitk::TimeGeometry::Pointer(); if (nullptr != dynamic_cast(m_InputWorldTimeGeometry.GetPointer())) { const TimePointType minimumTimePoint = m_InputWorldTimeGeometry->TimeStepToTimePoint(currentTimeStep); const TimePointType stepDuration = m_InputWorldTimeGeometry->TimeStepToTimePoint(currentTimeStep + 1) - minimumTimePoint; auto createdTimeGeometry = ProportionalTimeGeometry::New(); createdTimeGeometry->Initialize(slicedWorldGeometry, inputTimeSteps); createdTimeGeometry->SetFirstTimePoint(minimumTimePoint); createdTimeGeometry->SetStepDuration(stepDuration); m_CreatedWorldGeometry = createdTimeGeometry; } else { auto createdTimeGeometry = mitk::ArbitraryTimeGeometry::New(); createdTimeGeometry->ReserveSpaceForGeometries(inputTimeSteps); const BaseGeometry::Pointer clonedGeometry = slicedWorldGeometry->Clone(); for (TimeStepType i = 0; i < inputTimeSteps; ++i) { const auto bounds = m_InputWorldTimeGeometry->GetTimeBounds(i); createdTimeGeometry->AppendNewTimeStep(clonedGeometry, bounds[0], bounds[1]); } createdTimeGeometry->Update(); m_CreatedWorldGeometry = createdTimeGeometry; } } } // namespace mitk diff --git a/Modules/Core/src/Controllers/mitkSliceNavigationHelper.cpp b/Modules/Core/src/Controllers/mitkSliceNavigationHelper.cpp new file mode 100644 index 0000000000..9aeec5f3e6 --- /dev/null +++ b/Modules/Core/src/Controllers/mitkSliceNavigationHelper.cpp @@ -0,0 +1,141 @@ +/*============================================================================ + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center (DKFZ) +All rights reserved. + +Use of this source code is governed by a 3-clause BSD license that can be +found in the LICENSE file. + +============================================================================*/ + +#include "mitkSliceNavigationHelper.h" + +#include +#include +#include +#include +#include +#include + +unsigned int mitk::SliceNavigationHelper::SelectSliceByPoint(const TimeGeometry* timeGeometry, + const Point3D& point) +{ + int selectedSlice = -1; + if (nullptr == timeGeometry) + { + return selectedSlice; + } + + // get the BaseGeometry of the selected time point + auto selectedTimePoint = RenderingManager::GetInstance()->GetTimeNavigationController()->GetSelectedTimePoint(); + auto currentGeometry = timeGeometry->GetGeometryForTimePoint(selectedTimePoint); + if (nullptr == currentGeometry) + { + // time point not valid for the ime geometry + mitkThrow() << "Cannot extract a base geometry. A time point is selected that is not covered by" + << "the given time geometry. Selected time point: " << selectedTimePoint; + } + + const auto* slicedGeometry = dynamic_cast(currentGeometry.GetPointer()); + if (nullptr == slicedGeometry) + { + return selectedSlice; + } + + double bestDistance = itk::NumericTraits::max(); + if (slicedGeometry->GetEvenlySpaced()) + { + PlaneGeometry* plane = slicedGeometry->GetPlaneGeometry(0); + const Vector3D& direction = slicedGeometry->GetDirectionVector(); + + Point3D projectedPoint; + plane->Project(point, projectedPoint); + + // check whether the point is somewhere within the slice stack volume + if (direction[0] * (point[0] - projectedPoint[0]) + direction[1] * (point[1] - projectedPoint[1]) + + direction[2] * (point[2] - projectedPoint[2]) >= 0) + { + selectedSlice = static_cast(plane->Distance(point) / slicedGeometry->GetSpacing()[2] + 0.5); + } + } + else + { + int numberOfSlices = slicedGeometry->GetSlices(); + Point3D projectedPoint; + for (int slice = 0; slice < numberOfSlices; ++slice) + { + slicedGeometry->GetPlaneGeometry(slice)->Project(point, projectedPoint); + const Vector3D distance = projectedPoint - point; + ScalarType currentDistance = distance.GetSquaredNorm(); + + if (currentDistance < bestDistance) + { + bestDistance = currentDistance; + selectedSlice = slice; + } + } + } + + if (selectedSlice < 0) + { + selectedSlice = 0; // do not allow negative slices + } + + return selectedSlice; +} + +mitk::TimeGeometry::Pointer mitk::SliceNavigationHelper::CreateOrientedTimeGeometry(const TimeGeometry* timeGeometry, + PlaneGeometry::PlaneOrientation planeOrientation, bool top, bool frontside, bool rotated) +{ + if (nullptr == timeGeometry) + { + return nullptr; + } + + // initialize the viewplane + SlicedGeometry3D::Pointer slicedGeometry; + BaseGeometry::ConstPointer currentGeometry; + + // get the BaseGeometry of the selected time point + auto selectedTimePoint = RenderingManager::GetInstance()->GetTimeNavigationController()->GetSelectedTimePoint(); + currentGeometry = timeGeometry->GetGeometryForTimePoint(selectedTimePoint); + if(nullptr == currentGeometry) + { + // time point not valid for the time geometry + mitkThrow() << "Cannot extract a base geometry. A time point is selected that is not covered by" + << "the given time geometry. Selected time point: " << selectedTimePoint; + } + + slicedGeometry = SlicedGeometry3D::New(); + slicedGeometry->InitializePlanes(currentGeometry, planeOrientation, top, frontside, rotated); + + // use the existing time geometry but replace all time steps with the newly created + // sliced geometry (base geometry), initialized to the desired plane orientation + auto createdTimeGeometry = timeGeometry->Clone(); + createdTimeGeometry->ReplaceTimeStepGeometries(slicedGeometry); + return createdTimeGeometry; +} + +mitk::PlaneGeometry* mitk::SliceNavigationHelper::GetCurrentPlaneGeometry(const TimeGeometry* timeGeometry, + TimePointType timePoint, + unsigned int slicePosition) +{ + if (nullptr == timeGeometry) + { + return nullptr; + } + + const auto* slicedGeometry = + dynamic_cast(timeGeometry->GetGeometryForTimePoint(timePoint).GetPointer()); + + if (nullptr == slicedGeometry) + { + // time point not valid for the time geometry + mitkThrow() << "Cannot extract a sliced geometry. A time point is selected that is not covered by" + << "the given time geometry. Selected time point: " << timePoint; + } + + return slicedGeometry->GetPlaneGeometry(slicePosition); +}