diff --git a/Modules/Core/files.cmake b/Modules/Core/files.cmake index 313085b1be..e9b2cedff7 100644 --- a/Modules/Core/files.cmake +++ b/Modules/Core/files.cmake @@ -1,329 +1,329 @@ 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/mitkCrosshairData.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/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 IO/mitkIPreferences.cpp IO/mitkPreferences.cpp IO/mitkIPreferencesService.cpp IO/mitkPreferencesService.cpp IO/mitkIPreferencesStorage.cpp IO/mitkXMLPreferencesStorage.cpp Rendering/mitkAbstractAnnotationRenderer.cpp Rendering/mitkAnnotationUtils.cpp Rendering/mitkBaseRenderer.cpp Rendering/mitkBaseRendererHelper.cpp - #Rendering/mitkGLMapper.cpp Moved to deprecated LegacyGL Module + Rendering/mitkCrosshairVtkMapper2D.cpp 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/mitkCrosshairData.h b/Modules/Core/include/mitkCrosshairData.h new file mode 100644 index 0000000000..c346164791 --- /dev/null +++ b/Modules/Core/include/mitkCrosshairData.h @@ -0,0 +1,46 @@ +/*============================================================================ + +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 mitkCrosshairData_h +#define mitkCrosshairData_h + +#include +#include + +namespace mitk +{ + class MITKCORE_EXPORT CrosshairData : public BaseData + { + public: + + mitkClassMacro(CrosshairData, BaseData); + itkFactorylessNewMacro(Self); + + itkGetConstMacro(Position, Point3D); + itkSetMacro(Position, Point3D); + + void SetRequestedRegionToLargestPossibleRegion() override; + bool RequestedRegionIsOutsideOfTheBufferedRegion() override; + bool VerifyRequestedRegion() override; + void SetRequestedRegion(const itk::DataObject* data) override; + + protected: + + CrosshairData(); + ~CrosshairData() override; + + Point3D m_Position; + + }; +} + +#endif diff --git a/Modules/Core/include/mitkCrosshairManager.h b/Modules/Core/include/mitkCrosshairManager.h index 575b9b0ab8..90713fc9fb 100644 --- a/Modules/Core/include/mitkCrosshairManager.h +++ b/Modules/Core/include/mitkCrosshairManager.h @@ -1,105 +1,147 @@ /*============================================================================ 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 mitkCrosshairManager_h #define mitkCrosshairManager_h #include #include +#include #include -#include -#include - -#include -#include namespace mitk { /** - * \brief The CrosshairManager takes care of the correct settings for the plane geometries - * that form the crosshair. + * \brief The CrosshairManager takes care of the correct settings for the crosshair. * - * The CrosshairManager keeps track of a TimeGeometry, which is used to compute - * the three different plane geometries (axial, coronal, sagittal). - * These three plane geometries can be added to / removed from the data storage - * using this CrosshairManager. Adding / removing them to / from the data storage + * The CrosshairManager keeps track of a crosshair data node, + * which is used to compute the lines of a crosshair. + * These crosshair data node can be added to / removed from the data storage + * using this CrosshairManager. Adding / removing the node to / from the data storage * will change the rendering behavior for the crosshair - only data nodes * inside the data storage will be rendered. - * - * */ class MITKCORE_EXPORT CrosshairManager : public itk::Object { public: mitkClassMacroItkParent(CrosshairManager, itk::Object); - mitkNewMacro2Param(Self, DataStorage*, BaseRenderer*); - - itkSetObjectMacro(DataStorage, DataStorage); - itkSetObjectMacro(BaseRenderer, BaseRenderer); - - /** - * \brief Set the input time geometry out of which the oriented geometries will be created. - */ - void ComputeOrientedTimeGeometries(const TimeGeometry* geometry); + mitkNewMacro1Param(Self, BaseRenderer*); + /** + * \brief Set the position of the managed crosshair to the given 3D position. + * + * \pre The crosshair data of this manager needs to be not null in order to + * set a new position. + * \throw mitk::Exception, if the crosshair data of this manager is null. + * + * \param selectedPoint The 3D point that will be newly set for the crosshair data. + */ void SetCrosshairPosition(const Point3D& selectedPoint); + /** + * \brief Update the position of the managed crosshair to the current slice + * position of the given slice navigation controller, depending on the view + * direction of the slice navigation controller. + * + * \pre The crosshair data of this manager needs to be not null in order to + * update the crosshair position. + * \throw mitk::Exception, if the crosshair data of this manager is null. + * + * \param sliceNavigationController The slice navigation controller whose current position + * will be used for updating the corresponding 3D coordinate + * of the crosshair data's position. + */ + void UpdateCrosshairPosition(const SliceNavigationController* sliceNavigationController); + /** + * \brief Get the position of the managed crosshair. + * + * \pre The crosshair data of this manager needs to be not null in order to + * get the current crosshair position. + * \throw mitk::Exception, if the crosshair data of this manager is null. + * + * \return The 3D point that represents the position of the crosshair data. + */ Point3D GetCrosshairPosition() const; - - void UpdateSlice(const SliceNavigationController* sliceNavigationController); - - void SetCrosshairVisibility(bool visible); - bool GetCrosshairVisibility() const; + /** + * \brief Set the visibility of the managed crosshair to the given value. + * + * \pre The crosshair data of this manager needs to be not null in order to + * set the visibility. + * \throw mitk::Exception, if the crosshair data of this manager is null. + * + * \param visible A boolean value that will define the visibility of the crosshair. + * \param baseRenderer A base renderer for which the renderer-specific visibility property + * should be set. + */ + void SetCrosshairVisibility(bool visible, const BaseRenderer* baseRenderer); + /** + * \brief Get the visibility of the managed crosshair. + * + * \pre The crosshair data of this manager needs to be not null in order to + * get the visibility. + * \throw mitk::Exception, if the crosshair data of this manager is null. + * + * \param baseRenderer A base renderer for which the renderer-specific visibility property + * should be get. + * \return The boolean value that represents the visibility of the crosshair. + */ + bool GetCrosshairVisibility(const BaseRenderer* baseRenderer) const; + /** + * \brief Set the gap size of the managed crosshair to the given value. + * + * \pre The crosshair data of this manager needs to be not null in order to + * set the gap size. + * \throw mitk::Exception, if the crosshair data of this manager is null. + * + * \param gapSize The size of the gap in the center of the crosshair. + */ void SetCrosshairGap(unsigned int gapSize); - - void AddPlanesToDataStorage(); - void RemovePlanesFromDataStorage(); + /** + * \brief Add the managed crosshair data node to the given data storage. + * + * \pre The crosshair data node of this manager needs to be not null in order to + * add the node to the data storage. + * \throw mitk::Exception, if the crosshair data node of this manager is null. + * \pre The given data storage needs to be not null in order to add the node to + * the data storage. + * \throw mitk::Exception, if the given data storage is null. + * + * \param dataStorage The data storage to which the crosshair data node should be added. + */ + void AddCrosshairNodeToDataStorage(DataStorage* dataStorage); + /** + * \brief Remove the managed crosshair data node from the given data storage. + * + * \pre The crosshair data node of this manager needs to be not null in order to + * remove the node from the data storage. + * \throw mitk::Exception, if the crosshair data node of this manager is null. + * \pre The given data storage needs to be not null in order to remove the node + * from the data storage. + * \throw mitk::Exception, if the given data storage is null. + * + * \param dataStorage The data storage from which the crosshair data node should be removed. + */ + void RemoveCrosshairNodeFromDataStorage(DataStorage* dataStorage); protected: - CrosshairManager(DataStorage* dataStorage, BaseRenderer* baseRenderer); + CrosshairManager(BaseRenderer* baseRenderer); ~CrosshairManager(); - void InitializePlaneProperties(DataNode::Pointer planeNode, const std::string& planeName); - void InitializePlaneData(DataNode::Pointer planeNode, const TimeGeometry* timeGeometry, unsigned int& slice); - void UpdatePlaneSlice(DataNode::Pointer planeNode, const TimeGeometry* timeGeometry, unsigned int slice); - void SetCrosshairPosition(const Point3D& selectedPoint, - DataNode::Pointer planeNode, - const TimeGeometry* timeGeometry, - unsigned int& slice); - void AddPlaneToDataStorage(DataNode::Pointer planeNode, DataNode::Pointer parent); - - DataStorage* m_DataStorage; - BaseRenderer* m_BaseRenderer; - - TimeGeometry::ConstPointer m_InputTimeGeometry; - TimeGeometry::Pointer m_AxialTimeGeometry; - TimeGeometry::Pointer m_CoronalTimeGeometry; - TimeGeometry::Pointer m_SagittalTimeGeometry; - - unsigned int m_AxialSlice; - unsigned int m_CoronalSlice; - unsigned int m_SagittalSlice; - - /** - * @brief The 3 helper objects which contain the plane geometry. - */ - DataNode::Pointer m_AxialPlaneNode; - DataNode::Pointer m_CoronalPlaneNode; - DataNode::Pointer m_SagittalPlaneNode; - DataNode::Pointer m_ParentNodeForGeometryPlanes; + DataNode::Pointer m_CrosshairDataNode; + CrosshairData::Pointer m_CrosshairData; }; -} // namespace mitk +} #endif diff --git a/Modules/Core/include/mitkCrosshairVtkMapper2D.h b/Modules/Core/include/mitkCrosshairVtkMapper2D.h new file mode 100644 index 0000000000..301c4114f9 --- /dev/null +++ b/Modules/Core/include/mitkCrosshairVtkMapper2D.h @@ -0,0 +1,93 @@ +/*============================================================================ + +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 mitkCrosshairVtkMapper2D_h +#define mitkCrosshairVtkMapper2D_h + +#include + +#include +#include +#include + +#include + +class vtkActor2D; +class vtkCellArray; +class vtkPropAssembly; +class vtkPolyDataMapper2D; + +namespace mitk +{ + /** + * @brief Vtk-based 2D mapper for rendering a crosshair using vtk mapper. + * + * CrosshairData is used to retrieve the selected position, which is stored + * in the processed data node. + * The crosshair is created by drawing lines through the selected position, + * adding an additional gap in the center of the crosshair and creating vtk poly + * data from the created points and lines. + */ + class MITKCORE_EXPORT CrosshairVtkMapper2D : public VtkMapper + { + public: + + mitkClassMacro(CrosshairVtkMapper2D, VtkMapper); + itkFactorylessNewMacro(Self); + + const CrosshairData* GetInput() const; + + /** \brief Checks whether this mapper needs to update itself and generate data. */ + void Update(mitk::BaseRenderer* renderer) override; + + vtkProp* GetVtkProp(BaseRenderer* renderer) override; + + static void SetDefaultProperties(DataNode* node, BaseRenderer* renderer = nullptr, bool overwrite = false); + + protected: + + CrosshairVtkMapper2D(); + ~CrosshairVtkMapper2D() override; + + /** \brief Internal class holding the mapper, actor, etc. for each of the 2D render windows */ + class LocalStorage : public Mapper::BaseLocalStorage + { + public: + + LocalStorage(); + ~LocalStorage() override; + + // actor + vtkSmartPointer m_CrosshairActor; + vtkSmartPointer m_Mapper; + vtkSmartPointer m_CrosshairAssembly; + }; + + /** \brief The LocalStorageHandler holds all LocalStorages for the 2D render windows. */ + LocalStorageHandler m_LSH; + + /* \brief Applies the color and opacity properties and calls CreateVTKRenderObjects */ + void GenerateDataForRenderer(BaseRenderer *renderer) override; + + void CreateVtkCrosshair(BaseRenderer *renderer); + + void DrawLine(Point3D p0, Point3D p1, vtkCellArray* lines, vtkPoints* points); + + void ApplyAllProperties(BaseRenderer* renderer); + void ApplyColorAndOpacityProperties2D(BaseRenderer* renderer, vtkActor2D* actor); + + const int defaultGapSize; + }; + +} + +#endif diff --git a/Modules/Core/include/mitkPlaneGeometryDataMapper2D.h b/Modules/Core/include/mitkPlaneGeometryDataMapper2D.h index 35811f0e84..2747ffd558 100644 --- a/Modules/Core/include/mitkPlaneGeometryDataMapper2D.h +++ b/Modules/Core/include/mitkPlaneGeometryDataMapper2D.h @@ -1,144 +1,127 @@ /*============================================================================ 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 mitkPlaneGeometryDataMapper2D_h #define mitkPlaneGeometryDataMapper2D_h #include "mitkBaseRenderer.h" #include "mitkVtkMapper.h" #include #include class vtkActor2D; class vtkPropAssembly; -class vtkFloatArray; class vtkCellArray; class vtkPolyDataMapper2D; namespace mitk { /** * @brief Vtk-based 2D mapper for rendering a crosshair with the plane geometry. * * This mapper uses the mitkPlaneGeometryData from the three helper objects in * the StdMultiWidget to render a crosshair in all 2D render windows. The crosshair * is assembled as lines and rendered with a vtkPolyDataMapper. The mapper * requires multiple plane geometry to compute the correct crosshair position. * The plane bounds are computed using either ReferenceGeometry if it is present or * the plane geometry itself otherwise. * The mapper offers the following properties: * \b Crosshair.Line width: The thickness of the crosshair. * \b Crosshair.Gap Size: The gap between the lines in pixels. * \b Crosshair.Orientation Decoration: Adds a PlaneOrientationProperty, which * indicates the direction of the plane normal. See mitkPlaneOrientationProperty. * * @ingroup Mapper */ class MITKCORE_EXPORT PlaneGeometryDataMapper2D : public VtkMapper { public: mitkClassMacro(PlaneGeometryDataMapper2D, VtkMapper); itkFactorylessNewMacro(Self); itkCloneMacro(Self); virtual const mitk::PlaneGeometryData *GetInput() const; /** \brief returns the a prop assembly */ vtkProp *GetVtkProp(mitk::BaseRenderer *renderer) override; /** Applies properties specific to this mapper */ virtual void ApplyAllProperties(BaseRenderer *renderer); - void UpdateVtkTransform(mitk::BaseRenderer *renderer) override; - /** \brief set the default properties for this mapper */ static void SetDefaultProperties(mitk::DataNode *node, mitk::BaseRenderer *renderer = nullptr, bool overwrite = false); /** \brief Internal class holding the mapper, actor, etc. for each of the 3 2D render windows */ class LocalStorage : public mitk::Mapper::BaseLocalStorage { public: /* constructor */ LocalStorage(); /* destructor */ ~LocalStorage() override; // actor vtkSmartPointer m_CrosshairActor; vtkSmartPointer m_CrosshairHelperLineActor; vtkSmartPointer m_ArrowActor; vtkSmartPointer m_HelperLinesmapper; vtkSmartPointer m_Arrowmapper; vtkSmartPointer m_Mapper; vtkSmartPointer m_CrosshairAssembly; }; /** \brief The LocalStorageHandler holds all (three) LocalStorages for the three 2D render windows. */ mitk::LocalStorageHandler m_LSH; protected: /* constructor */ PlaneGeometryDataMapper2D(); /* destructor */ ~PlaneGeometryDataMapper2D() override; /* \brief Applies the color and opacity properties and calls CreateVTKRenderObjects */ void GenerateDataForRenderer(mitk::BaseRenderer *renderer) override; void CreateVtkCrosshair(BaseRenderer *renderer); static bool TestPointInPlaneGeometry(const PlaneGeometry *planeGeometry, const Point3D &point); static bool TestPointInReferenceGeometry(const BaseGeometry *referenceGeometry, const Point3D &point); static bool CutCrossLineWithPlaneGeometry(const PlaneGeometry *planeGeometry, Line3D &crossLine); static bool CutCrossLineWithReferenceGeometry(const BaseGeometry *referenceGeometry, Line3D &crossLine); - /** - * \brief Returns the thick slice mode for the given datanode. - * - * This method returns the value of the 'reslice.thickslices' property for - * the given datanode. - * '0': thick slice mode disabled - * '1': thick slice mode enabled - * - * The variable 'thickSlicesNum' contains the value of the 'reslice.thickslices.num' - * property that defines how many slices are shown at once. - */ - int DetermineThickSliceMode(DataNode *dn, int &thickSlicesNum); - void DrawLine(Point3D p0, Point3D p1, vtkCellArray *lines, vtkPoints *points); // member variables holding the current value of the properties used in this mapper typedef std::vector NodesVectorType; NodesVectorType m_OtherPlaneGeometries; typedef std::set AllInstancesContainer; static AllInstancesContainer s_AllInstances; bool m_RenderOrientationArrows; bool m_ArrowOrientationPositive; - mitk::ScalarType m_DepthValue; void ApplyColorAndOpacityProperties2D(BaseRenderer *renderer, vtkActor2D *actor); void DrawOrientationArrow(vtkSmartPointer triangles, vtkSmartPointer triPoints, double triangleSizeMM, Vector3D &orthogonalVector, Point3D &point1, Point3D &point2); }; } // namespace mitk #endif diff --git a/Modules/Core/src/Controllers/mitkCrosshairManager.cpp b/Modules/Core/src/Controllers/mitkCrosshairManager.cpp index 8bd39e7449..2a5d497f4c 100644 --- a/Modules/Core/src/Controllers/mitkCrosshairManager.cpp +++ b/Modules/Core/src/Controllers/mitkCrosshairManager.cpp @@ -1,414 +1,167 @@ /*============================================================================ 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 "mitkCrosshairManager.h" -#include -#include -#include +#include #include -#include -#include - -mitk::CrosshairManager::CrosshairManager(DataStorage* dataStorage, BaseRenderer* baseRenderer) - : m_DataStorage(dataStorage) - , m_BaseRenderer(baseRenderer) - , m_InputTimeGeometry() - , m_AxialTimeGeometry() - , m_CoronalTimeGeometry() - , m_SagittalTimeGeometry() -{ - m_AxialPlaneNode = mitk::DataNode::New(); - m_CoronalPlaneNode = mitk::DataNode::New(); - m_SagittalPlaneNode = mitk::DataNode::New(); - - std::string rendererName = std::string(m_BaseRenderer->GetName()); - this->InitializePlaneProperties(m_AxialPlaneNode, std::string(rendererName + "axial.plane")); - this->InitializePlaneProperties(m_CoronalPlaneNode, std::string(rendererName + "coronal.plane")); - this->InitializePlaneProperties(m_SagittalPlaneNode, std::string(rendererName + "sagittal.plane")); - - m_ParentNodeForGeometryPlanes = mitk::DataNode::New(); - m_ParentNodeForGeometryPlanes->SetProperty("name", mitk::StringProperty::New(rendererName)); - m_ParentNodeForGeometryPlanes->SetProperty("helper object", mitk::BoolProperty::New(true)); -} -mitk::CrosshairManager::~CrosshairManager() -{ - this->RemovePlanesFromDataStorage(); -} +#include -void mitk::CrosshairManager::ComputeOrientedTimeGeometries(const TimeGeometry* geometry) +mitk::CrosshairManager::CrosshairManager(BaseRenderer* baseRenderer) { - if (nullptr != geometry) - { - if (geometry->GetBoundingBoxInWorld()->GetDiagonalLength2() < eps) - { - itkWarningMacro("setting an empty bounding-box"); - geometry = nullptr; - } - } - - if (m_InputTimeGeometry == geometry) - { - return; - } - - m_InputTimeGeometry = geometry; + m_CrosshairDataNode = mitk::DataNode::New(); - if (m_InputTimeGeometry.IsNull()) - { - return; - } + std::string rendererName = std::string(baseRenderer->GetName()); - if (0 == m_InputTimeGeometry->CountTimeSteps()) - { - return; - } + m_CrosshairDataNode->SetProperty("reslice.thickslices", mitk::ResliceMethodProperty::New()); + m_CrosshairDataNode->SetProperty("reslice.thickslices.num", mitk::IntProperty::New(5)); + m_CrosshairDataNode->SetProperty("Crosshair.Gap Size", mitk::IntProperty::New(32)); - try - { - m_AxialTimeGeometry = SliceNavigationHelper::CreateOrientedTimeGeometry( - m_InputTimeGeometry, AnatomicalPlane::Axial, false, false, true); - m_CoronalTimeGeometry = SliceNavigationHelper::CreateOrientedTimeGeometry( - m_InputTimeGeometry, AnatomicalPlane::Coronal, false, true, false); - m_SagittalTimeGeometry = SliceNavigationHelper::CreateOrientedTimeGeometry( - m_InputTimeGeometry, AnatomicalPlane::Sagittal, true, true, false); - } - catch (const mitk::Exception& e) - { - MITK_ERROR << "Unable to create oriented time geometries\n" - << "Reason: " << e.GetDescription(); - } + // set the crosshair only visible for this specific renderer + m_CrosshairDataNode->SetVisibility(false); + m_CrosshairDataNode->SetVisibility(true, baseRenderer); + m_CrosshairDataNode->SetProperty("name", mitk::StringProperty::New(std::string(rendererName + "crosshairData"))); + m_CrosshairDataNode->SetProperty("helper object", mitk::BoolProperty::New(true)); - this->InitializePlaneData(m_AxialPlaneNode, m_AxialTimeGeometry, m_AxialSlice); - this->InitializePlaneData(m_CoronalPlaneNode, m_CoronalTimeGeometry, m_CoronalSlice); - this->InitializePlaneData(m_SagittalPlaneNode, m_SagittalTimeGeometry, m_SagittalSlice); + m_CrosshairData = CrosshairData::New(); + m_CrosshairDataNode->SetData(m_CrosshairData); + m_CrosshairDataNode->SetMapper(mitk::BaseRenderer::Standard2D, mitk::CrosshairVtkMapper2D::New()); +} - RenderingManager::GetInstance()->RequestUpdate(m_BaseRenderer->GetRenderWindow()); +mitk::CrosshairManager::~CrosshairManager() +{ } void mitk::CrosshairManager::SetCrosshairPosition(const Point3D& selectedPoint) { - if (m_AxialPlaneNode.IsNull() || m_CoronalPlaneNode.IsNull() || m_SagittalPlaneNode.IsNull()) + if (m_CrosshairData.IsNull()) { - return; + mitkThrow() << "No crosshair data available. Crosshair is in an invalid state."; } - if (m_AxialTimeGeometry.IsNull() || m_CoronalTimeGeometry.IsNull() || m_SagittalTimeGeometry.IsNull()) - { - return; - } - - this->SetCrosshairPosition(selectedPoint, m_AxialPlaneNode, m_AxialTimeGeometry, m_AxialSlice); - this->SetCrosshairPosition(selectedPoint, m_CoronalPlaneNode, m_CoronalTimeGeometry, m_CoronalSlice); - this->SetCrosshairPosition(selectedPoint, m_SagittalPlaneNode, m_SagittalTimeGeometry, m_SagittalSlice); + m_CrosshairData->SetPosition(selectedPoint); } -mitk::Point3D mitk::CrosshairManager::GetCrosshairPosition() const +void mitk::CrosshairManager::UpdateCrosshairPosition(const SliceNavigationController* sliceNavigationController) { - if (m_InputTimeGeometry.IsNull()) + if (m_CrosshairData.IsNull()) { - // return null-point since we don't show the crosshair anyway - return Point3D(0.0); + mitkThrow() << "No crosshair data available. Crosshair is in an invalid state."; } - Point3D point = m_InputTimeGeometry->GetCenterInWorld(); - - PlaneGeometry* axialPlaneGeometry = nullptr; - PlaneGeometry* coronalPlaneGeometry = nullptr; - PlaneGeometry* sagittalPlaneGeometry = nullptr; + Point3D crosshairPosition = m_CrosshairData->GetPosition(); - // get the currently selected time point - auto selectedTimePoint = RenderingManager::GetInstance()->GetTimeNavigationController()->GetSelectedTimePoint(); - try - { - axialPlaneGeometry = - SliceNavigationHelper::GetCurrentPlaneGeometry(m_AxialTimeGeometry, selectedTimePoint, m_AxialSlice); - coronalPlaneGeometry = - SliceNavigationHelper::GetCurrentPlaneGeometry(m_CoronalTimeGeometry, selectedTimePoint, m_CoronalSlice); - sagittalPlaneGeometry = - SliceNavigationHelper::GetCurrentPlaneGeometry(m_SagittalTimeGeometry, selectedTimePoint, m_SagittalSlice); - } - catch (const mitk::Exception& e) - { - MITK_ERROR << "Unable to get plane geometries. Using default center point.\n" - << "Reason: " << e.GetDescription(); - } - - Line3D line; - if (nullptr != axialPlaneGeometry && nullptr != coronalPlaneGeometry && - axialPlaneGeometry->IntersectionLine(coronalPlaneGeometry, line)) - { - if (nullptr != sagittalPlaneGeometry && sagittalPlaneGeometry->IntersectionPoint(line, point)) - { - return point; - } - } - - // return input geometry center point if no intersection point was found - return point; -} - -void mitk::CrosshairManager::UpdateSlice(const SliceNavigationController* sliceNavigationController) -{ auto viewDirection = sliceNavigationController->GetViewDirection(); unsigned int slicePosition = sliceNavigationController->GetSlice()->GetPos(); switch (viewDirection) { case AnatomicalPlane::Original: return; case AnatomicalPlane::Axial: { - m_AxialSlice = slicePosition; - this->UpdatePlaneSlice(m_AxialPlaneNode, m_AxialTimeGeometry, m_AxialSlice); + crosshairPosition[2] = slicePosition; break; } case AnatomicalPlane::Coronal: { - m_CoronalSlice = slicePosition; - this->UpdatePlaneSlice(m_CoronalPlaneNode, m_CoronalTimeGeometry, m_CoronalSlice); + crosshairPosition[1] = slicePosition; break; } case AnatomicalPlane::Sagittal: { - m_SagittalSlice = slicePosition; - this->UpdatePlaneSlice(m_SagittalPlaneNode, m_SagittalTimeGeometry, m_SagittalSlice); + crosshairPosition[0] = slicePosition; break; } } -} -void mitk::CrosshairManager::SetCrosshairVisibility(bool visible) -{ - if (m_AxialPlaneNode.IsNull() || m_CoronalPlaneNode.IsNull() || m_SagittalPlaneNode.IsNull()) - { - return; - } - - m_AxialPlaneNode->SetVisibility(visible, m_BaseRenderer); - m_CoronalPlaneNode->SetVisibility(visible, m_BaseRenderer); - m_SagittalPlaneNode->SetVisibility(visible, m_BaseRenderer); + m_CrosshairData->SetPosition(crosshairPosition); } -bool mitk::CrosshairManager::GetCrosshairVisibility() const +mitk::Point3D mitk::CrosshairManager::GetCrosshairPosition() const { - if (m_AxialPlaneNode.IsNull() || m_CoronalPlaneNode.IsNull() || m_SagittalPlaneNode.IsNull()) - { - return false; - } - - bool axialVisibility = false; - if (m_AxialPlaneNode->GetVisibility(axialVisibility, m_BaseRenderer)) + if (m_CrosshairData.IsNull()) { - bool coronalVisibility = false; - if (m_CoronalPlaneNode->GetVisibility(coronalVisibility, m_BaseRenderer)) - { - bool sagittalVisibility = false; - if (m_SagittalPlaneNode->GetVisibility(sagittalVisibility, m_BaseRenderer)) - { - if (axialVisibility == coronalVisibility && coronalVisibility == sagittalVisibility) - { - return axialVisibility; - } - } - } + mitkThrow() << "No crosshair data available. Crosshair is in an invalid state."; } - mitkThrow() << "Invalid state of plane visibility."; -} - -void mitk::CrosshairManager::SetCrosshairGap(unsigned int gapSize) -{ - m_AxialPlaneNode->SetIntProperty("Crosshair.Gap Size", gapSize); - m_CoronalPlaneNode->SetIntProperty("Crosshair.Gap Size", gapSize); - m_SagittalPlaneNode->SetIntProperty("Crosshair.Gap Size", gapSize); + return m_CrosshairData->GetPosition(); } -void mitk::CrosshairManager::AddPlanesToDataStorage() +void mitk::CrosshairManager::SetCrosshairVisibility(bool visible, const BaseRenderer* baseRenderer) { - if (nullptr == m_DataStorage) + if (m_CrosshairDataNode.IsNull()) { - return; + mitkThrow() << "No crosshair data node available. Crosshair is in an invalid state."; } - if (m_AxialPlaneNode.IsNull() || m_CoronalPlaneNode.IsNull() - || m_SagittalPlaneNode.IsNull() || m_ParentNodeForGeometryPlanes.IsNull()) - { - return; - } - - this->AddPlaneToDataStorage(m_ParentNodeForGeometryPlanes, nullptr); - this->AddPlaneToDataStorage(m_AxialPlaneNode, m_ParentNodeForGeometryPlanes); - this->AddPlaneToDataStorage(m_CoronalPlaneNode, m_ParentNodeForGeometryPlanes); - this->AddPlaneToDataStorage(m_SagittalPlaneNode, m_ParentNodeForGeometryPlanes); + m_CrosshairDataNode->SetVisibility(visible, baseRenderer); } -void mitk::CrosshairManager::RemovePlanesFromDataStorage() +bool mitk::CrosshairManager::GetCrosshairVisibility(const BaseRenderer* baseRenderer) const { - if (nullptr == m_DataStorage) + if (m_CrosshairDataNode.IsNull()) { - return; + mitkThrow() << "No crosshair data node available. Crosshair is in an invalid state."; } - if (m_AxialPlaneNode.IsNotNull() && m_CoronalPlaneNode.IsNotNull() - && m_SagittalPlaneNode.IsNotNull() && m_ParentNodeForGeometryPlanes.IsNotNull()) + bool visibility = false; + if (m_CrosshairDataNode->GetVisibility(visibility, baseRenderer)) { - m_DataStorage->Remove(m_AxialPlaneNode); - m_DataStorage->Remove(m_CoronalPlaneNode); - m_DataStorage->Remove(m_SagittalPlaneNode); - m_DataStorage->Remove(m_ParentNodeForGeometryPlanes); + return false; } -} - -void mitk::CrosshairManager::InitializePlaneProperties(DataNode::Pointer planeNode, const std::string& planeName) -{ - planeNode->SetProperty("reslice.thickslices", mitk::ResliceMethodProperty::New()); - planeNode->SetProperty("reslice.thickslices.num", mitk::IntProperty::New(5)); - planeNode->SetProperty("Crosshair.Gap Size", mitk::IntProperty::New(32)); - // set the crosshair only visible for this specific renderer - planeNode->SetVisibility(false); - planeNode->SetVisibility(true, m_BaseRenderer); - planeNode->SetProperty("name", mitk::StringProperty::New(planeName)); - planeNode->SetProperty("helper object", mitk::BoolProperty::New(true)); + return visibility; } -void mitk::CrosshairManager::InitializePlaneData(DataNode::Pointer planeNode, const TimeGeometry* timeGeometry, unsigned int& slice) +void mitk::CrosshairManager::SetCrosshairGap(unsigned int gapSize) { - if (planeNode.IsNull()) + if (m_CrosshairDataNode.IsNull()) { - return; + mitkThrow() << "No crosshair data node available. Crosshair is in an invalid state."; } - if (nullptr == timeGeometry) - { - return; - } - - // 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 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; - } - - const auto* slicedGeometry = dynamic_cast(currentGeometry.GetPointer()); - if (nullptr == slicedGeometry) - { - return; - } - - slice = slicedGeometry->GetSlices() / 2; // center slice - PlaneGeometryData::Pointer planeData = PlaneGeometryData::New(); - planeData->SetPlaneGeometry(slicedGeometry->GetPlaneGeometry(slice)); - planeNode->SetData(planeData); - planeNode->SetMapper(mitk::BaseRenderer::Standard2D, mitk::PlaneGeometryDataMapper2D::New()); + m_CrosshairDataNode->SetIntProperty("Crosshair.Gap Size", gapSize); } -void mitk::CrosshairManager::UpdatePlaneSlice(DataNode::Pointer planeNode, const TimeGeometry* timeGeometry, unsigned int slice) +void mitk::CrosshairManager::AddCrosshairNodeToDataStorage(DataStorage* dataStorage) { - mitk::PlaneGeometry* planeGeometry = nullptr; - // get the currently selected time point - auto selectedTimePoint = RenderingManager::GetInstance()->GetTimeNavigationController()->GetSelectedTimePoint(); - try + if (m_CrosshairDataNode.IsNull()) { - planeGeometry = SliceNavigationHelper::GetCurrentPlaneGeometry(timeGeometry, selectedTimePoint, slice); - } - catch (const mitk::Exception& e) - { - MITK_ERROR << "Unable to get plane geometries\n" - << "Reason: " << e.GetDescription(); + mitkThrow() << "No crosshair data node available. Crosshair is in an invalid state."; } - if (nullptr == planeGeometry) + if (nullptr == dataStorage) { - return; + mitkThrow() << "Datastorage is invalid. Cannot add crosshair node."; } - PlaneGeometryData::Pointer planeData = dynamic_cast(planeNode->GetData()); - if (nullptr == planeData) + if (!dataStorage->Exists(m_CrosshairDataNode)) { - return; + dataStorage->Add(m_CrosshairDataNode); } - - planeData->SetPlaneGeometry(planeGeometry); - planeNode->SetData(planeData); } -void mitk::CrosshairManager::SetCrosshairPosition(const Point3D& selectedPoint, - DataNode::Pointer planeNode, - const TimeGeometry* timeGeometry, - unsigned int& slice) +void mitk::CrosshairManager::RemoveCrosshairNodeFromDataStorage(DataStorage* dataStorage) { - int selectedSlice = -1; - try - { - selectedSlice = SliceNavigationHelper::SelectSliceByPoint(timeGeometry, selectedPoint); - } - catch (const mitk::Exception& e) + if (m_CrosshairDataNode.IsNull()) { - MITK_ERROR << "Unable to select a slice by the given point " << selectedPoint << "\n" - << "Reason: " << e.GetDescription(); + mitkThrow() << "No crosshair data node available. Crosshair is in an invalid state."; } - if (-1 == selectedSlice) + if (nullptr == dataStorage) { - return; + mitkThrow() << "Datastorage is invalid. Cannot remove crosshair node."; } - slice = selectedSlice; - mitk::PlaneGeometry* planeGeometry = nullptr; - // get the currently selected time point - auto selectedTimePoint = RenderingManager::GetInstance()->GetTimeNavigationController()->GetSelectedTimePoint(); - try - { - planeGeometry = SliceNavigationHelper::GetCurrentPlaneGeometry(timeGeometry, selectedTimePoint, slice); - } - catch (const mitk::Exception& e) - { - MITK_ERROR << "Unable to get plane geometries\n" - << "Reason: " << e.GetDescription(); - } - - if (nullptr == planeGeometry) - { - return; - } - - PlaneGeometryData::Pointer planeData = dynamic_cast(planeNode->GetData()); - if (nullptr == planeData) - { - return; - } - - planeData->SetPlaneGeometry(planeGeometry); - planeNode->SetData(planeData); -} - -void mitk::CrosshairManager::AddPlaneToDataStorage(DataNode::Pointer planeNode, DataNode::Pointer parent) -{ - if (!m_DataStorage->Exists(planeNode) - && (nullptr == parent || m_DataStorage->Exists(parent))) - { - try - { - m_DataStorage->Add(planeNode, parent); - } - catch (std::invalid_argument& /*e*/) - { - return; - } - } + dataStorage->Remove(m_CrosshairDataNode); } diff --git a/Modules/Core/src/DataManagement/mitkCrosshairData.cpp b/Modules/Core/src/DataManagement/mitkCrosshairData.cpp new file mode 100644 index 0000000000..59bee7d17f --- /dev/null +++ b/Modules/Core/src/DataManagement/mitkCrosshairData.cpp @@ -0,0 +1,39 @@ +/*============================================================================ + +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 + +mitk::CrosshairData::CrosshairData() +{ +} + +mitk::CrosshairData::~CrosshairData() +{ +} + +void mitk::CrosshairData::SetRequestedRegionToLargestPossibleRegion() +{ +} + +bool mitk::CrosshairData::RequestedRegionIsOutsideOfTheBufferedRegion() +{ + return false; +} + +bool mitk::CrosshairData::VerifyRequestedRegion() +{ + return true; +} + +void mitk::CrosshairData::SetRequestedRegion(const itk::DataObject*) +{ +} diff --git a/Modules/Core/src/Rendering/mitkCrosshairVtkMapper2D.cpp b/Modules/Core/src/Rendering/mitkCrosshairVtkMapper2D.cpp new file mode 100644 index 0000000000..313dcc34ce --- /dev/null +++ b/Modules/Core/src/Rendering/mitkCrosshairVtkMapper2D.cpp @@ -0,0 +1,244 @@ +/*============================================================================ + +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 "mitkCrosshairVtkMapper2D.h" + +// mitk includes +#include +#include +#include + +// vtk includes +#include +#include +#include +#include +#include +#include +#include +#include +#include + +const mitk::CrosshairData* mitk::CrosshairVtkMapper2D::GetInput() const +{ + return static_cast(this->GetDataNode()->GetData()); +} + +mitk::CrosshairVtkMapper2D::CrosshairVtkMapper2D() + : defaultGapSize(32) +{ +} + +mitk::CrosshairVtkMapper2D::~CrosshairVtkMapper2D() +{ +} + +void mitk::CrosshairVtkMapper2D::Update(mitk::BaseRenderer* renderer) +{ + const auto* node = this->GetDataNode(); + if (nullptr == node) + { + return; + } + + bool visible = true; + node->GetVisibility(visible, renderer, "visible"); + if (!visible) + { + // Do not render crosshair for base renderer, if the + // crosshair data node is not visible in that base renderer. + return; + } + + const auto* data = static_cast(node->GetData()); + if (nullptr == data) + { + // Do not render crosshair for base renderer, if the + // base data node is no crosshair data. + return; + } + + this->GenerateDataForRenderer(renderer); +} + +vtkProp* mitk::CrosshairVtkMapper2D::GetVtkProp(BaseRenderer* renderer) +{ + LocalStorage* ls = m_LSH.GetLocalStorage(renderer); + return ls->m_CrosshairAssembly; +} + +void mitk::CrosshairVtkMapper2D::SetDefaultProperties(DataNode* node, + BaseRenderer* renderer, + bool overwrite) +{ + CoreServicePointer aliases(CoreServices::GetPropertyAliases()); + node->AddProperty("Line width", FloatProperty::New(1), renderer, overwrite); + aliases->AddAlias("line width", "Crosshair.Line Width", ""); + node->AddProperty("Crosshair.Gap Size", IntProperty::New(32), renderer, overwrite); + + Superclass::SetDefaultProperties(node, renderer, overwrite); +} + +void mitk::CrosshairVtkMapper2D::GenerateDataForRenderer(BaseRenderer* renderer) +{ + LocalStorage* ls = m_LSH.GetLocalStorage(renderer); + ls->UpdateGenerateDataTime(); + + this->CreateVtkCrosshair(renderer); + + this->ApplyAllProperties(renderer); +} + +void mitk::CrosshairVtkMapper2D::CreateVtkCrosshair(BaseRenderer* renderer) +{ + LocalStorage* ls = m_LSH.GetLocalStorage(renderer); + ls->m_CrosshairActor->SetVisibility(0); + + CrosshairData::ConstPointer input = this->GetInput(); + Point3D crosshairPosition = input->GetPosition(); + + // check if node is inside the renderer geometry + bool isInside = renderer->GetWorldTimeGeometry()->IsWorldPointInside(crosshairPosition); + if (!isInside) + { + // no need to draw crosshair lines + return; + } + + int gapSize = defaultGapSize; + this->GetDataNode()->GetPropertyValue("Crosshair.Gap Size", gapSize, nullptr); + + vtkSmartPointer lines = vtkSmartPointer::New(); + vtkSmartPointer points = vtkSmartPointer::New(); + vtkSmartPointer linesPolyData = vtkSmartPointer::New(); + + // convert 3D crosshair crosshairPosition to 2D point + Point2D displayPoint; + renderer->WorldToDisplay(crosshairPosition, displayPoint); + Point2D displayPointOne = displayPoint; + Point2D displayPointTwo = displayPoint; + Point3D worldPointOne; + Point3D worldPointTwo; + + // convert geometry bounds from 3D to 2D + const auto worldBounds = renderer->GetWorldTimeGeometry()->GetBoundsInWorld(); + Point3D worldBoundsMinimum; + worldBoundsMinimum[0] = worldBounds[0]; + worldBoundsMinimum[1] = worldBounds[2]; + worldBoundsMinimum[2] = worldBounds[4]; + + Point3D worldBoundsMaximum; + worldBoundsMaximum[0] = worldBounds[1]; + worldBoundsMaximum[1] = worldBounds[3]; + worldBoundsMaximum[2] = worldBounds[5]; + + Point2D displayBoundsMinimum; + Point2D displayBoundsMaximum; + renderer->WorldToDisplay(worldBoundsMinimum, displayBoundsMinimum); + renderer->WorldToDisplay(worldBoundsMaximum, displayBoundsMaximum); + + // define points for the first / left half of the crosshair x-line + displayPointOne[0] = displayBoundsMinimum[0]; + displayPointTwo[0] = displayPoint[0] - gapSize; + renderer->DisplayToWorld(displayPointOne, worldPointOne); + renderer->DisplayToWorld(displayPointTwo, worldPointTwo); + this->DrawLine(worldPointOne, worldPointTwo, lines, points); + + // define points for the second / right half of the crosshair x-line + displayPointOne[0] = displayPoint[0] + gapSize; + displayPointTwo[0] = displayBoundsMaximum[0]; + renderer->DisplayToWorld(displayPointOne, worldPointOne); + renderer->DisplayToWorld(displayPointTwo, worldPointTwo); + this->DrawLine(worldPointOne, worldPointTwo, lines, points); + + // reset 2D points + displayPointOne = displayPoint; + displayPointTwo = displayPoint; + + // define points for the first / bottom half of the crosshair y-line + displayPointOne[1] = displayBoundsMinimum[1]; + displayPointTwo[1] = displayPoint[1] - gapSize; + renderer->DisplayToWorld(displayPointOne, worldPointOne); + renderer->DisplayToWorld(displayPointTwo, worldPointTwo); + this->DrawLine(worldPointOne, worldPointTwo, lines, points); + + // define points for the second / top half of the crosshair y-line + displayPointOne[1] = displayPoint[1] + gapSize; + displayPointTwo[1] = displayBoundsMaximum[1]; + renderer->DisplayToWorld(displayPointOne, worldPointOne); + renderer->DisplayToWorld(displayPointTwo, worldPointTwo); + this->DrawLine(worldPointOne, worldPointTwo, lines, points); + + // set vtk data with the created points and lines + linesPolyData->SetPoints(points); + linesPolyData->SetLines(lines); + ls->m_Mapper->SetInputData(linesPolyData); + ls->m_CrosshairActor->SetMapper(ls->m_Mapper); + ls->m_CrosshairActor->SetVisibility(1); +} + +void mitk::CrosshairVtkMapper2D::DrawLine(Point3D p0, Point3D p1, vtkCellArray* lines, vtkPoints* points) +{ + vtkIdType pidStart = points->InsertNextPoint(p0[0], p0[1], p0[2]); + vtkIdType pidEnd = points->InsertNextPoint(p1[0], p1[1], p1[2]); + + vtkSmartPointer lineVtk = vtkSmartPointer::New(); + lineVtk->GetPointIds()->SetId(0, pidStart); + lineVtk->GetPointIds()->SetId(1, pidEnd); + + lines->InsertNextCell(lineVtk); +} + +void mitk::CrosshairVtkMapper2D::ApplyAllProperties(BaseRenderer* renderer) +{ + LocalStorage* ls = m_LSH.GetLocalStorage(renderer); + ApplyColorAndOpacityProperties2D(renderer, ls->m_CrosshairActor); + + float thickness; + this->GetDataNode()->GetFloatProperty("Line width", thickness, renderer); + ls->m_CrosshairActor->GetProperty()->SetLineWidth(thickness); +} + +void mitk::CrosshairVtkMapper2D::ApplyColorAndOpacityProperties2D(BaseRenderer* renderer, vtkActor2D* actor) +{ + float rgba[4] = { 1.0f, 1.0f, 1.0f, 1.0f }; + DataNode* node = GetDataNode(); + + // check for color prop and use it for rendering if it exists + node->GetColor(rgba, renderer, "color"); + // check for opacity prop and use it for rendering if it exists + node->GetOpacity(rgba[3], renderer, "opacity"); + + double drgba[4] = { rgba[0], rgba[1], rgba[2], rgba[3] }; + actor->GetProperty()->SetColor(drgba); + actor->GetProperty()->SetOpacity(drgba[3]); +} + +mitk::CrosshairVtkMapper2D::LocalStorage::LocalStorage() +{ + m_CrosshairAssembly = vtkSmartPointer::New(); + m_CrosshairActor = vtkSmartPointer::New(); + m_Mapper = vtkSmartPointer::New(); + + m_CrosshairActor->SetMapper(m_Mapper); + m_CrosshairActor->SetVisibility(0); + m_CrosshairAssembly->AddPart(m_CrosshairActor); + + vtkCoordinate *tcoord = vtkCoordinate::New(); + tcoord->SetCoordinateSystemToWorld(); + m_Mapper->SetTransformCoordinate(tcoord); + tcoord->Delete(); +} + +mitk::CrosshairVtkMapper2D::LocalStorage::~LocalStorage() +{ +} diff --git a/Modules/Core/src/Rendering/mitkPlaneGeometryDataMapper2D.cpp b/Modules/Core/src/Rendering/mitkPlaneGeometryDataMapper2D.cpp index e6c3e2ec9a..bdca8d7012 100644 --- a/Modules/Core/src/Rendering/mitkPlaneGeometryDataMapper2D.cpp +++ b/Modules/Core/src/Rendering/mitkPlaneGeometryDataMapper2D.cpp @@ -1,706 +1,676 @@ /*============================================================================ 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 "mitkPlaneGeometryDataMapper2D.h" // mitk includes #include "mitkVtkPropRenderer.h" #include #include #include #include #include -#include #include #include #include // vtk includes #include #include #include -#include #include #include #include #include #include #include /// #include #include #include #include namespace { /// Some simple interval arithmetic template class SimpleInterval { public: SimpleInterval(T start = T(), T end = T()) : m_LowerBoundary(std::min(start, end)), m_UpperBoundary(std::max(start, end)) { } T GetLowerBoundary() const { return m_LowerBoundary; } T GetUpperBoundary() const { return m_UpperBoundary; } bool empty() const { return m_LowerBoundary == m_UpperBoundary; } bool operator<(const SimpleInterval &otherInterval) const { return this->m_UpperBoundary < otherInterval.GetLowerBoundary(); } private: T m_LowerBoundary; T m_UpperBoundary; }; template class IntervalSet { public: typedef SimpleInterval IntervalType; IntervalSet(IntervalType startingInterval) { m_IntervalsContainer.insert(std::move(startingInterval)); } void operator-=(const IntervalType &interval) { // equal_range will find all the intervals in the interval set which intersect with the input interval // due to the nature of operator< of SimpleInterval auto range = m_IntervalsContainer.equal_range(interval); for (auto iter = range.first; iter != range.second;) { auto subtractionResult = SubtractIntervals(*iter, interval); // Remove the old interval from the set iter = m_IntervalsContainer.erase(iter); for (auto &&interval : subtractionResult) { if (!interval.empty()) { // Add the new interval to the set // emplace_hint adds the element at the closest valid place before the hint iterator, // which is exactly where the new interval should be iter = m_IntervalsContainer.insert(iter, std::move(interval)); ++iter; } } } } IntervalSet operator-(const IntervalType &interval) { IntervalSet result = *this; result -= interval; return result; } typedef std::set IntervalsContainer; const IntervalsContainer &getIntervals() const { return m_IntervalsContainer; } private: IntervalsContainer m_IntervalsContainer; std::array SubtractIntervals(const IntervalType &firstInterval, const IntervalType &secondInterval) { assert(secondInterval.GetUpperBoundary() >= firstInterval.GetLowerBoundary() && firstInterval.GetUpperBoundary() >= secondInterval.GetLowerBoundary()); // Non-intersecting intervals should never reach here if (secondInterval.GetLowerBoundary() < firstInterval.GetLowerBoundary()) { if (firstInterval.GetUpperBoundary() < secondInterval.GetUpperBoundary()) { std::array result = {{IntervalType(), IntervalType()}}; return result; // firstInterval completely enclosed } std::array result = { {IntervalType(firstInterval.GetUpperBoundary(), secondInterval.GetUpperBoundary()), IntervalType()}}; return result; // secondInterval removes the beginning of firstInterval } if (firstInterval.GetUpperBoundary() < secondInterval.GetUpperBoundary()) { std::array result = { {IntervalType(firstInterval.GetLowerBoundary(), secondInterval.GetLowerBoundary()), IntervalType()}}; return result; // secondInterval removes the end of firstInterval } std::array result = { {IntervalType(firstInterval.GetLowerBoundary(), secondInterval.GetLowerBoundary()), IntervalType(secondInterval.GetUpperBoundary(), firstInterval.GetUpperBoundary())}}; return result; // secondInterval is completely enclosed in firstInterval and removes the middle } }; } mitk::PlaneGeometryDataMapper2D::AllInstancesContainer mitk::PlaneGeometryDataMapper2D::s_AllInstances; // input for this mapper ( = PlaneGeometryData) const mitk::PlaneGeometryData *mitk::PlaneGeometryDataMapper2D::GetInput() const { return static_cast(GetDataNode()->GetData()); } mitk::PlaneGeometryDataMapper2D::PlaneGeometryDataMapper2D() - : m_RenderOrientationArrows(false), m_ArrowOrientationPositive(true), m_DepthValue(1.0f) + : m_RenderOrientationArrows(false), m_ArrowOrientationPositive(true) { s_AllInstances.insert(this); } mitk::PlaneGeometryDataMapper2D::~PlaneGeometryDataMapper2D() { s_AllInstances.erase(this); } vtkProp *mitk::PlaneGeometryDataMapper2D::GetVtkProp(mitk::BaseRenderer *renderer) { LocalStorage *ls = m_LSH.GetLocalStorage(renderer); return ls->m_CrosshairAssembly; } void mitk::PlaneGeometryDataMapper2D::GenerateDataForRenderer(mitk::BaseRenderer *renderer) { BaseLocalStorage *ls = m_LSH.GetLocalStorage(renderer); // The PlaneGeometryDataMapper2D mapper is special in that the rendering of // OTHER PlaneGeometryDatas affects how we render THIS PlaneGeometryData // (for the gap at the point where they intersect). A change in any of the // other PlaneGeometryData nodes could mean that we render ourself // differently, so we check for that here. for (auto it = s_AllInstances.begin(); it != s_AllInstances.end(); ++it) { bool generateDataRequired = ls->IsGenerateDataRequired(renderer, this, (*it)->GetDataNode()); if (generateDataRequired) break; } ls->UpdateGenerateDataTime(); // Collect all other PlaneGeometryDatas that are being mapped by this mapper m_OtherPlaneGeometries.clear(); for (auto it = s_AllInstances.begin(); it != s_AllInstances.end(); ++it) { Self *otherInstance = *it; // Skip ourself if (otherInstance == this) continue; mitk::DataNode *otherNode = otherInstance->GetDataNode(); if (!otherNode) continue; // Skip other PlaneGeometryData nodes that are not visible on this renderer if (!otherNode->IsVisible(renderer)) continue; auto *otherData = dynamic_cast(otherNode->GetData()); if (!otherData) continue; auto *otherGeometry = dynamic_cast(otherData->GetPlaneGeometry()); if (otherGeometry && !dynamic_cast(otherData->GetPlaneGeometry())) { m_OtherPlaneGeometries.push_back(otherNode); } } CreateVtkCrosshair(renderer); ApplyAllProperties(renderer); } void mitk::PlaneGeometryDataMapper2D::CreateVtkCrosshair(mitk::BaseRenderer *renderer) { bool visible = true; LocalStorage *ls = m_LSH.GetLocalStorage(renderer); ls->m_CrosshairActor->SetVisibility(0); ls->m_ArrowActor->SetVisibility(0); ls->m_CrosshairHelperLineActor->SetVisibility(0); GetDataNode()->GetVisibility(visible, renderer, "visible"); if (!visible) { return; } PlaneGeometryData::ConstPointer input = this->GetInput(); mitk::DataNode *geometryDataNode = renderer->GetCurrentWorldPlaneGeometryNode(); const PlaneGeometryData *rendererWorldPlaneGeometryData = dynamic_cast(geometryDataNode->GetData()); // intersecting with ourself? if (input.IsNull() || input.GetPointer() == rendererWorldPlaneGeometryData) { return; // nothing to do in this case } const auto *inputPlaneGeometry = dynamic_cast(input->GetPlaneGeometry()); const auto *worldPlaneGeometry = dynamic_cast(rendererWorldPlaneGeometryData->GetPlaneGeometry()); if (worldPlaneGeometry && dynamic_cast(worldPlaneGeometry) == nullptr && inputPlaneGeometry && dynamic_cast(input->GetPlaneGeometry()) == nullptr) { const BaseGeometry *referenceGeometry = inputPlaneGeometry->GetReferenceGeometry(); // calculate intersection of the plane data with the border of the // world geometry rectangle Point3D point1, point2; Line3D crossLine; // Calculate the intersection line of the input plane with the world plane if (worldPlaneGeometry->IntersectionLine(inputPlaneGeometry, crossLine)) { bool hasIntersection = referenceGeometry ? CutCrossLineWithReferenceGeometry(referenceGeometry, crossLine) : CutCrossLineWithPlaneGeometry(inputPlaneGeometry, crossLine); if (!hasIntersection) { return; } point1 = crossLine.GetPoint1(); point2 = crossLine.GetPoint2(); vtkSmartPointer lines = vtkSmartPointer::New(); vtkSmartPointer points = vtkSmartPointer::New(); vtkSmartPointer linesPolyData = vtkSmartPointer::New(); // Now iterate through all other lines displayed in this window and // calculate the positions of intersection with the line to be // rendered; these positions will be stored in lineParams to form a // gap afterwards. auto otherPlanesIt = m_OtherPlaneGeometries.begin(); auto otherPlanesEnd = m_OtherPlaneGeometries.end(); int gapSize = 32; this->GetDataNode()->GetPropertyValue("Crosshair.Gap Size", gapSize, nullptr); auto intervals = IntervalSet(SimpleInterval(0, 1)); ScalarType lineLength = point1.EuclideanDistanceTo(point2); ScalarType gapInMM = gapSize * renderer->GetScaleFactorMMPerDisplayUnit(); float gapSizeParam = gapInMM / lineLength; if (gapSize != 0) { while (otherPlanesIt != otherPlanesEnd) { bool ignorePlane = false; (*otherPlanesIt)->GetPropertyValue("Crosshair.Ignore", ignorePlane); if (ignorePlane) { ++otherPlanesIt; continue; } auto *otherPlaneGeometry = static_cast( static_cast((*otherPlanesIt)->GetData())->GetPlaneGeometry()); if (otherPlaneGeometry != inputPlaneGeometry && otherPlaneGeometry != worldPlaneGeometry) { double intersectionParam; if (otherPlaneGeometry->IntersectionPointParam(crossLine, intersectionParam) && intersectionParam > 0 && intersectionParam < 1) { Point3D point = crossLine.GetPoint() + intersectionParam * crossLine.GetDirection(); bool intersectionPointInsideOtherPlane = otherPlaneGeometry->HasReferenceGeometry() ? TestPointInReferenceGeometry(otherPlaneGeometry->GetReferenceGeometry(), point) : TestPointInPlaneGeometry(otherPlaneGeometry, point); if (intersectionPointInsideOtherPlane) { intervals -= SimpleInterval(intersectionParam - gapSizeParam, intersectionParam + gapSizeParam); } } } ++otherPlanesIt; } } for (const auto &interval : intervals.getIntervals()) { this->DrawLine(crossLine.GetPoint(interval.GetLowerBoundary()), crossLine.GetPoint(interval.GetUpperBoundary()), lines, points); } // Add the points to the dataset linesPolyData->SetPoints(points); // Add the lines to the dataset linesPolyData->SetLines(lines); Vector3D orthogonalVector; orthogonalVector = inputPlaneGeometry->GetNormal(); worldPlaneGeometry->Project(orthogonalVector, orthogonalVector); orthogonalVector.Normalize(); // Visualize ls->m_Mapper->SetInputData(linesPolyData); ls->m_CrosshairActor->SetMapper(ls->m_Mapper); // Determine if we should draw the area covered by the thick slicing, default is false. // This will also show the area of slices that do not have thick slice mode enabled bool showAreaOfThickSlicing = false; GetDataNode()->GetBoolProperty("reslice.thickslices.showarea", showAreaOfThickSlicing); // determine the pixelSpacing in that direction double thickSliceDistance = SlicedGeometry3D::CalculateSpacing( referenceGeometry ? referenceGeometry->GetSpacing() : inputPlaneGeometry->GetSpacing(), orthogonalVector); IntProperty *intProperty = nullptr; if (GetDataNode()->GetProperty(intProperty, "reslice.thickslices.num") && intProperty) thickSliceDistance *= intProperty->GetValue() + 0.5; else showAreaOfThickSlicing = false; // not the nicest place to do it, but we have the width of the visible bloc in MM here // so we store it in this fancy property GetDataNode()->SetFloatProperty("reslice.thickslices.sizeinmm", thickSliceDistance * 2); ls->m_CrosshairActor->SetVisibility(1); vtkSmartPointer arrowPolyData = vtkSmartPointer::New(); ls->m_Arrowmapper->SetInputData(arrowPolyData); if (this->m_RenderOrientationArrows) { ScalarType triangleSizeMM = 7.0 * renderer->GetScaleFactorMMPerDisplayUnit(); vtkSmartPointer triangles = vtkSmartPointer::New(); vtkSmartPointer triPoints = vtkSmartPointer::New(); DrawOrientationArrow(triangles, triPoints, triangleSizeMM, orthogonalVector, point1, point2); DrawOrientationArrow(triangles, triPoints, triangleSizeMM, orthogonalVector, point2, point1); arrowPolyData->SetPoints(triPoints); arrowPolyData->SetPolys(triangles); ls->m_ArrowActor->SetVisibility(1); } // Visualize vtkSmartPointer helperlinesPolyData = vtkSmartPointer::New(); ls->m_HelperLinesmapper->SetInputData(helperlinesPolyData); if (showAreaOfThickSlicing) { vtkSmartPointer helperlines = vtkSmartPointer::New(); // vectorToHelperLine defines how to reach the helperLine from the mainLine // got the right direction, so we multiply the width Vector3D vecToHelperLine = orthogonalVector * thickSliceDistance; this->DrawLine(point1 - vecToHelperLine, point2 - vecToHelperLine, helperlines, points); this->DrawLine(point1 + vecToHelperLine, point2 + vecToHelperLine, helperlines, points); // Add the points to the dataset helperlinesPolyData->SetPoints(points); // Add the lines to the dataset helperlinesPolyData->SetLines(helperlines); ls->m_CrosshairActor->GetProperty()->SetLineStipplePattern(0xf0f0); ls->m_CrosshairActor->GetProperty()->SetLineStippleRepeatFactor(1); ls->m_CrosshairHelperLineActor->SetVisibility(1); } } } } bool mitk::PlaneGeometryDataMapper2D::TestPointInPlaneGeometry(const PlaneGeometry *planeGeometry, const Point3D &point) { Point2D mappedPoint; planeGeometry->Map(point, mappedPoint); planeGeometry->WorldToIndex(mappedPoint, mappedPoint); return (planeGeometry->GetBounds()[0] < mappedPoint[0] && mappedPoint[0] < planeGeometry->GetBounds()[1] && planeGeometry->GetBounds()[2] < mappedPoint[1] && mappedPoint[1] < planeGeometry->GetBounds()[3]); } bool mitk::PlaneGeometryDataMapper2D::TestPointInReferenceGeometry(const BaseGeometry *referenceGeometry, const Point3D &point) { return referenceGeometry->IsInside(point); } bool mitk::PlaneGeometryDataMapper2D::CutCrossLineWithPlaneGeometry(const PlaneGeometry *planeGeometry, Line3D &crossLine) { Point2D indexLinePoint; Vector2D indexLineDirection; planeGeometry->Map(crossLine.GetPoint(), indexLinePoint); planeGeometry->Map(crossLine.GetPoint(), crossLine.GetDirection(), indexLineDirection); planeGeometry->WorldToIndex(indexLinePoint, indexLinePoint); planeGeometry->WorldToIndex(indexLineDirection, indexLineDirection); mitk::Point2D intersectionPoints[2]; // Then, clip this line with the (transformed) bounding box of the // reference geometry. int nIntersections = Line3D::RectangleLineIntersection(planeGeometry->GetBounds()[0], planeGeometry->GetBounds()[2], planeGeometry->GetBounds()[1], planeGeometry->GetBounds()[3], indexLinePoint, indexLineDirection, intersectionPoints[0], intersectionPoints[1]); if (nIntersections < 2) { return false; } planeGeometry->IndexToWorld(intersectionPoints[0], intersectionPoints[0]); planeGeometry->IndexToWorld(intersectionPoints[1], intersectionPoints[1]); Point3D point1, point2; planeGeometry->Map(intersectionPoints[0], point1); planeGeometry->Map(intersectionPoints[1], point2); crossLine.SetPoints(point1, point2); return true; } bool mitk::PlaneGeometryDataMapper2D::CutCrossLineWithReferenceGeometry(const BaseGeometry *referenceGeometry, Line3D &crossLine) { Point3D boundingBoxMin, boundingBoxMax; boundingBoxMin = referenceGeometry->GetCornerPoint(0); boundingBoxMax = referenceGeometry->GetCornerPoint(7); Point3D indexLinePoint; Vector3D indexLineDirection; referenceGeometry->WorldToIndex(crossLine.GetPoint(), indexLinePoint); referenceGeometry->WorldToIndex(crossLine.GetDirection(), indexLineDirection); referenceGeometry->WorldToIndex(boundingBoxMin, boundingBoxMin); referenceGeometry->WorldToIndex(boundingBoxMax, boundingBoxMax); Point3D point1, point2; // Then, clip this line with the (transformed) bounding box of the // reference geometry. int nIntersections = Line3D::BoxLineIntersection(boundingBoxMin[0], boundingBoxMin[1], boundingBoxMin[2], boundingBoxMax[0], boundingBoxMax[1], boundingBoxMax[2], indexLinePoint, indexLineDirection, point1, point2); if (nIntersections < 2) { return false; } referenceGeometry->IndexToWorld(point1, point1); referenceGeometry->IndexToWorld(point2, point2); crossLine.SetPoints(point1, point2); return true; } void mitk::PlaneGeometryDataMapper2D::DrawLine(mitk::Point3D p0, mitk::Point3D p1, vtkCellArray *lines, vtkPoints *points) { vtkIdType pidStart = points->InsertNextPoint(p0[0], p0[1], p0[2]); vtkIdType pidEnd = points->InsertNextPoint(p1[0], p1[1], p1[2]); vtkSmartPointer lineVtk = vtkSmartPointer::New(); lineVtk->GetPointIds()->SetId(0, pidStart); lineVtk->GetPointIds()->SetId(1, pidEnd); lines->InsertNextCell(lineVtk); } void mitk::PlaneGeometryDataMapper2D::DrawOrientationArrow(vtkSmartPointer triangles, vtkSmartPointer triPoints, double triangleSizeMM, Vector3D &orthogonalVector, Point3D &point1, Point3D &point2) { // Draw arrows to indicate plane orientation // Vector along line Vector3D v1 = point2 - point1; v1.Normalize(); v1 *= triangleSizeMM; // Orthogonal vector Vector3D v2 = orthogonalVector; v2 *= triangleSizeMM; if (!this->m_ArrowOrientationPositive) v2 *= -1.0; // Initialize remaining triangle coordinates accordingly Point3D p1 = point1 + v1 * 2.0; Point3D p2 = point1 + v1 + v2; vtkIdType t0 = triPoints->InsertNextPoint(point1[0], point1[1], point1[2]); // start of the line vtkIdType t1 = triPoints->InsertNextPoint(p1[0], p1[1], p1[2]); // point on line vtkIdType t2 = triPoints->InsertNextPoint(p2[0], p2[1], p2[2]); // direction point vtkSmartPointer triangle = vtkSmartPointer::New(); triangle->GetPointIds()->SetId(0, t0); triangle->GetPointIds()->SetId(1, t1); triangle->GetPointIds()->SetId(2, t2); triangles->InsertNextCell(triangle); } -int mitk::PlaneGeometryDataMapper2D::DetermineThickSliceMode(DataNode *dn, int &thickSlicesNum) -{ - int thickSlicesMode = 0; - // determine the state and the extend of the thick-slice mode - mitk::ResliceMethodProperty *resliceMethodEnumProperty = nullptr; - if (dn->GetProperty(resliceMethodEnumProperty, "reslice.thickslices") && resliceMethodEnumProperty) - thickSlicesMode = resliceMethodEnumProperty->GetValueAsId(); - - IntProperty *intProperty = nullptr; - if (dn->GetProperty(intProperty, "reslice.thickslices.num") && intProperty) - { - thickSlicesNum = intProperty->GetValue(); - if (thickSlicesNum < 1) - thickSlicesNum = 0; - if (thickSlicesNum > 10) - thickSlicesNum = 10; - } - - if (thickSlicesMode == 0) - thickSlicesNum = 0; - - return thickSlicesMode; -} - void mitk::PlaneGeometryDataMapper2D::ApplyAllProperties(BaseRenderer *renderer) { LocalStorage *ls = m_LSH.GetLocalStorage(renderer); ApplyColorAndOpacityProperties2D(renderer, ls->m_CrosshairActor); ApplyColorAndOpacityProperties2D(renderer, ls->m_CrosshairHelperLineActor); ApplyColorAndOpacityProperties2D(renderer, ls->m_ArrowActor); float thickness; this->GetDataNode()->GetFloatProperty("Line width", thickness, renderer); ls->m_CrosshairActor->GetProperty()->SetLineWidth(thickness); ls->m_CrosshairHelperLineActor->GetProperty()->SetLineWidth(thickness); PlaneOrientationProperty *decorationProperty; this->GetDataNode()->GetProperty(decorationProperty, "decoration", renderer); if (decorationProperty != nullptr) { if (decorationProperty->GetPlaneDecoration() == PlaneOrientationProperty::PLANE_DECORATION_POSITIVE_ORIENTATION) { m_RenderOrientationArrows = true; m_ArrowOrientationPositive = true; } else if (decorationProperty->GetPlaneDecoration() == PlaneOrientationProperty::PLANE_DECORATION_NEGATIVE_ORIENTATION) { m_RenderOrientationArrows = true; m_ArrowOrientationPositive = false; } else { m_RenderOrientationArrows = false; } } } void mitk::PlaneGeometryDataMapper2D::ApplyColorAndOpacityProperties2D(BaseRenderer *renderer, vtkActor2D *actor) { float rgba[4] = {1.0f, 1.0f, 1.0f, 1.0f}; DataNode *node = GetDataNode(); // check for color prop and use it for rendering if it exists node->GetColor(rgba, renderer, "color"); // check for opacity prop and use it for rendering if it exists node->GetOpacity(rgba[3], renderer, "opacity"); double drgba[4] = {rgba[0], rgba[1], rgba[2], rgba[3]}; actor->GetProperty()->SetColor(drgba); actor->GetProperty()->SetOpacity(drgba[3]); } void mitk::PlaneGeometryDataMapper2D::SetDefaultProperties(mitk::DataNode *node, mitk::BaseRenderer *renderer, bool overwrite) { mitk::CoreServicePointer aliases(mitk::CoreServices::GetPropertyAliases()); node->AddProperty("Line width", mitk::FloatProperty::New(1), renderer, overwrite); aliases->AddAlias("line width", "Crosshair.Line Width", ""); node->AddProperty("Crosshair.Gap Size", mitk::IntProperty::New(32), renderer, overwrite); node->AddProperty("decoration", mitk::PlaneOrientationProperty::New(PlaneOrientationProperty::PLANE_DECORATION_NONE), renderer, overwrite); aliases->AddAlias("decoration", "Crosshair.Orientation Decoration", ""); Superclass::SetDefaultProperties(node, renderer, overwrite); } -void mitk::PlaneGeometryDataMapper2D::UpdateVtkTransform(mitk::BaseRenderer * /*renderer*/) -{ -} - mitk::PlaneGeometryDataMapper2D::LocalStorage::LocalStorage() { m_CrosshairAssembly = vtkSmartPointer::New(); m_CrosshairActor = vtkSmartPointer::New(); m_ArrowActor = vtkSmartPointer::New(); m_CrosshairHelperLineActor = vtkSmartPointer::New(); m_HelperLinesmapper = vtkSmartPointer::New(); m_Mapper = vtkSmartPointer::New(); m_Arrowmapper = vtkSmartPointer::New(); m_CrosshairActor->SetMapper(m_Mapper); m_ArrowActor->SetMapper(m_Arrowmapper); m_CrosshairHelperLineActor->SetMapper(m_HelperLinesmapper); m_CrosshairActor->SetVisibility(0); m_ArrowActor->SetVisibility(0); m_CrosshairHelperLineActor->SetVisibility(0); m_CrosshairAssembly->AddPart(m_CrosshairActor); m_CrosshairAssembly->AddPart(m_ArrowActor); m_CrosshairAssembly->AddPart(m_CrosshairHelperLineActor); vtkCoordinate *tcoord = vtkCoordinate::New(); tcoord->SetCoordinateSystemToWorld(); m_HelperLinesmapper->SetTransformCoordinate(tcoord); m_Mapper->SetTransformCoordinate(tcoord); // tcoord->SetCoordinateSystemToNormalizedDisplay(); m_Arrowmapper->SetTransformCoordinate(tcoord); tcoord->Delete(); } mitk::PlaneGeometryDataMapper2D::LocalStorage::~LocalStorage() { } diff --git a/Modules/Core/src/mitkCoreObjectFactory.cpp b/Modules/Core/src/mitkCoreObjectFactory.cpp index 084c19c0b3..7ea47dcdfa 100644 --- a/Modules/Core/src/mitkCoreObjectFactory.cpp +++ b/Modules/Core/src/mitkCoreObjectFactory.cpp @@ -1,454 +1,460 @@ /*============================================================================ 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 "mitkCoreObjectFactory.h" #include "mitkConfig.h" #include "mitkColorProperty.h" #include "mitkDataNode.h" #include "mitkEnumerationProperty.h" #include "mitkGeometry3D.h" #include "mitkGeometryData.h" #include "mitkImage.h" #include "mitkLevelWindowProperty.h" #include "mitkLookupTable.h" #include "mitkLookupTableProperty.h" #include "mitkPlaneGeometry.h" #include "mitkPlaneGeometryData.h" #include "mitkPlaneGeometryDataMapper2D.h" #include "mitkPlaneGeometryDataVtkMapper3D.h" #include "mitkPointSet.h" #include "mitkPointSetVtkMapper2D.h" #include "mitkPointSetVtkMapper3D.h" #include "mitkProperties.h" #include "mitkPropertyList.h" #include "mitkSlicedGeometry3D.h" #include "mitkSmartPointerProperty.h" #include "mitkStringProperty.h" #include "mitkSurface.h" #include "mitkSurface.h" #include "mitkSurfaceVtkMapper2D.h" #include "mitkSurfaceVtkMapper3D.h" #include "mitkTimeGeometry.h" #include "mitkTransferFunctionProperty.h" #include "mitkVtkInterpolationProperty.h" #include "mitkVtkRepresentationProperty.h" #include "mitkVtkResliceInterpolationProperty.h" #include // Legacy Support: #include #include #include +#include +#include + void mitk::CoreObjectFactory::RegisterExtraFactory(CoreObjectFactoryBase *factory) { MITK_DEBUG << "CoreObjectFactory: registering extra factory of type " << factory->GetNameOfClass(); m_ExtraFactories.insert(CoreObjectFactoryBase::Pointer(factory)); // Register Legacy Reader and Writer this->RegisterLegacyReaders(factory); this->RegisterLegacyWriters(factory); } void mitk::CoreObjectFactory::UnRegisterExtraFactory(CoreObjectFactoryBase *factory) { MITK_DEBUG << "CoreObjectFactory: un-registering extra factory of type " << factory->GetNameOfClass(); this->UnRegisterLegacyWriters(factory); this->UnRegisterLegacyReaders(factory); try { m_ExtraFactories.erase(factory); } catch ( const std::exception &e ) { MITK_ERROR << "Caught exception while unregistering: " << e.what(); } } mitk::CoreObjectFactory::Pointer mitk::CoreObjectFactory::GetInstance() { static mitk::CoreObjectFactory::Pointer instance; if (instance.IsNull()) { instance = mitk::CoreObjectFactory::New(); } return instance; } mitk::CoreObjectFactory::~CoreObjectFactory() { for (auto iter = m_LegacyReaders.begin(); iter != m_LegacyReaders.end(); ++iter) { for (auto &elem : iter->second) { delete elem; } } for (auto iter = m_LegacyWriters.begin(); iter != m_LegacyWriters.end(); ++iter) { for (auto &elem : iter->second) { delete elem; } } } void mitk::CoreObjectFactory::SetDefaultProperties(mitk::DataNode *node) { if (node == nullptr) return; mitk::DataNode::Pointer nodePointer = node; - mitk::Image::Pointer image = dynamic_cast(node->GetData()); - if (image.IsNotNull() && image->IsInitialized()) + mitk::Image* image = dynamic_cast(node->GetData()); + if (nullptr != image && image->IsInitialized()) { mitk::ImageVtkMapper2D::SetDefaultProperties(node); } - mitk::PlaneGeometryData::Pointer planeGeometry = dynamic_cast(node->GetData()); - if (planeGeometry.IsNotNull()) + if (nullptr != dynamic_cast(node->GetData())) { mitk::PlaneGeometryDataMapper2D::SetDefaultProperties(node); } - mitk::Surface::Pointer surface = dynamic_cast(node->GetData()); - if (surface.IsNotNull()) + if (nullptr != dynamic_cast(node->GetData())) { mitk::SurfaceVtkMapper2D::SetDefaultProperties(node); mitk::SurfaceVtkMapper3D::SetDefaultProperties(node); } - mitk::PointSet::Pointer pointSet = dynamic_cast(node->GetData()); - if (pointSet.IsNotNull()) + if (nullptr != dynamic_cast(node->GetData())) { mitk::PointSetVtkMapper2D::SetDefaultProperties(node); mitk::PointSetVtkMapper3D::SetDefaultProperties(node); } + + if (nullptr != dynamic_cast(node->GetData())) + { + mitk::CrosshairVtkMapper2D::SetDefaultProperties(node); + } + for (auto it = m_ExtraFactories.begin(); it != m_ExtraFactories.end(); ++it) { (*it)->SetDefaultProperties(node); } } mitk::CoreObjectFactory::CoreObjectFactory() { static bool alreadyDone = false; if (!alreadyDone) { CreateFileExtensionsMap(); // RegisterLegacyReaders(this); // RegisterLegacyWriters(this); alreadyDone = true; } } mitk::Mapper::Pointer mitk::CoreObjectFactory::CreateMapper(mitk::DataNode *node, MapperSlotId id) { mitk::Mapper::Pointer newMapper = nullptr; mitk::Mapper::Pointer tmpMapper = nullptr; // check whether extra factories provide mapper for (auto it = m_ExtraFactories.begin(); it != m_ExtraFactories.end(); ++it) { tmpMapper = (*it)->CreateMapper(node, id); if (tmpMapper.IsNotNull()) newMapper = tmpMapper; } if (newMapper.IsNull()) { mitk::BaseData *data = node->GetData(); if (id == mitk::BaseRenderer::Standard2D) { if ((dynamic_cast(data) != nullptr)) { newMapper = mitk::ImageVtkMapper2D::New(); newMapper->SetDataNode(node); } else if ((dynamic_cast(data) != nullptr)) { newMapper = mitk::PlaneGeometryDataMapper2D::New(); newMapper->SetDataNode(node); } else if ((dynamic_cast(data) != nullptr)) { newMapper = mitk::SurfaceVtkMapper2D::New(); // cast because SetDataNode is not virtual auto *castedMapper = dynamic_cast(newMapper.GetPointer()); castedMapper->SetDataNode(node); } else if ((dynamic_cast(data) != nullptr)) { newMapper = mitk::PointSetVtkMapper2D::New(); newMapper->SetDataNode(node); } } else if (id == mitk::BaseRenderer::Standard3D) { if ((dynamic_cast(data) != nullptr)) { newMapper = mitk::PlaneGeometryDataVtkMapper3D::New(); newMapper->SetDataNode(node); } else if ((dynamic_cast(data) != nullptr)) { newMapper = mitk::SurfaceVtkMapper3D::New(); newMapper->SetDataNode(node); } else if ((dynamic_cast(data) != nullptr)) { newMapper = mitk::PointSetVtkMapper3D::New(); newMapper->SetDataNode(node); } } } return newMapper; } std::string mitk::CoreObjectFactory::GetFileExtensions() { MultimapType aMap; for (auto it = m_ExtraFactories.begin(); it != m_ExtraFactories.end(); ++it) { aMap = (*it)->GetFileExtensionsMap(); this->MergeFileExtensions(m_FileExtensionsMap, aMap); } this->CreateFileExtensions(m_FileExtensionsMap, m_FileExtensions); return m_FileExtensions.c_str(); } void mitk::CoreObjectFactory::MergeFileExtensions(MultimapType &fileExtensionsMap, MultimapType inputMap) { std::pair pairOfIter; for (auto it = inputMap.begin(); it != inputMap.end(); ++it) { bool duplicateFound = false; pairOfIter = fileExtensionsMap.equal_range((*it).first); for (auto it2 = pairOfIter.first; it2 != pairOfIter.second; ++it2) { // cout << " [" << (*it).first << ", " << (*it).second << "]" << endl; std::string aString = (*it2).second; if (aString.compare((*it).second) == 0) { // cout << " DUP!! [" << (*it).first << ", " << (*it).second << "]" << endl; duplicateFound = true; break; } } if (!duplicateFound) { fileExtensionsMap.insert(std::pair((*it).first, (*it).second)); } } } mitk::CoreObjectFactoryBase::MultimapType mitk::CoreObjectFactory::GetFileExtensionsMap() { return m_FileExtensionsMap; } void mitk::CoreObjectFactory::CreateFileExtensionsMap() { /* m_FileExtensionsMap.insert(std::pair("*.dcm", "DICOM files")); m_FileExtensionsMap.insert(std::pair("*.DCM", "DICOM files")); m_FileExtensionsMap.insert(std::pair("*.dc3", "DICOM files")); m_FileExtensionsMap.insert(std::pair("*.DC3", "DICOM files")); m_FileExtensionsMap.insert(std::pair("*.gdcm", "DICOM files")); m_FileExtensionsMap.insert(std::pair("*.seq", "DKFZ Pic")); m_FileExtensionsMap.insert(std::pair("*.seq.gz", "DKFZ Pic")); m_FileExtensionsMap.insert(std::pair("*.dcm", "Sets of 2D slices")); m_FileExtensionsMap.insert(std::pair("*.gdcm", "Sets of 2D slices")); */ } std::string mitk::CoreObjectFactory::GetSaveFileExtensions() { MultimapType aMap; for (auto it = m_ExtraFactories.begin(); it != m_ExtraFactories.end(); ++it) { aMap = (*it)->GetSaveFileExtensionsMap(); this->MergeFileExtensions(m_SaveFileExtensionsMap, aMap); } this->CreateFileExtensions(m_SaveFileExtensionsMap, m_SaveFileExtensions); return m_SaveFileExtensions.c_str(); } mitk::CoreObjectFactoryBase::MultimapType mitk::CoreObjectFactory::GetSaveFileExtensionsMap() { return m_SaveFileExtensionsMap; } mitk::CoreObjectFactory::FileWriterList mitk::CoreObjectFactory::GetFileWriters() { FileWriterList allWriters = m_FileWriters; // sort to merge lists later on typedef std::set FileWriterSet; FileWriterSet fileWritersSet; fileWritersSet.insert(allWriters.begin(), allWriters.end()); // collect all extra factories for (auto it = m_ExtraFactories.begin(); it != m_ExtraFactories.end(); ++it) { FileWriterList list2 = (*it)->GetFileWriters(); // add them to the sorted set fileWritersSet.insert(list2.begin(), list2.end()); } // write back to allWriters to return a list allWriters.clear(); allWriters.insert(allWriters.end(), fileWritersSet.begin(), fileWritersSet.end()); return allWriters; } void mitk::CoreObjectFactory::MapEvent(const mitk::Event *, const int) { } std::string mitk::CoreObjectFactory::GetDescriptionForExtension(const std::string &extension) { std::multimap fileExtensionMap = GetSaveFileExtensionsMap(); for (auto it = fileExtensionMap.begin(); it != fileExtensionMap.end(); ++it) if (it->first == extension) return it->second; return ""; // If no matching extension was found, return empty string } void mitk::CoreObjectFactory::RegisterLegacyReaders(mitk::CoreObjectFactoryBase *factory) { // We are not really interested in the string, just call the method since // many readers initialize the map the first time when this method is called factory->GetFileExtensions(); std::map> extensionsByCategories; std::multimap fileExtensionMap = factory->GetFileExtensionsMap(); for (auto it = fileExtensionMap.begin(); it != fileExtensionMap.end(); ++it) { std::string extension = it->first; // remove "*." extension = extension.erase(0, 2); extensionsByCategories[it->second].push_back(extension); } for (auto &extensionsByCategorie : extensionsByCategories) { m_LegacyReaders[factory].push_back( new mitk::LegacyFileReaderService(extensionsByCategorie.second, extensionsByCategorie.first)); } } void mitk::CoreObjectFactory::UnRegisterLegacyReaders(mitk::CoreObjectFactoryBase *factory) { auto iter = m_LegacyReaders.find(factory); if (iter != m_LegacyReaders.end()) { for (auto &elem : iter->second) { delete elem; } m_LegacyReaders.erase(iter); } } void mitk::CoreObjectFactory::RegisterLegacyWriters(mitk::CoreObjectFactoryBase *factory) { // Get all external Writers mitk::CoreObjectFactory::FileWriterList writers = factory->GetFileWriters(); // We are not really interested in the string, just call the method since // many writers initialize the map the first time when this method is called factory->GetSaveFileExtensions(); MultimapType fileExtensionMap = factory->GetSaveFileExtensionsMap(); for (auto it = writers.begin(); it != writers.end(); ++it) { std::vector extensions = (*it)->GetPossibleFileExtensions(); if (extensions.empty()) continue; std::string description; for (auto ext = extensions.begin(); ext != extensions.end(); ++ext) { if (ext->empty()) continue; std::string extension = *ext; std::string extensionWithStar = extension; if (extension.find_first_of('*') == 0) { // remove "*." extension = extension.substr(0, extension.size() - 2); } else { extensionWithStar.insert(extensionWithStar.begin(), '*'); } for (auto fileExtensionIter = fileExtensionMap.begin(); fileExtensionIter != fileExtensionMap.end(); ++fileExtensionIter) { if (fileExtensionIter->first == extensionWithStar) { description = fileExtensionIter->second; break; } } if (!description.empty()) break; } if (description.empty()) { description = std::string("Legacy ") + (*it)->GetNameOfClass() + " Reader"; } mitk::FileWriter::Pointer fileWriter(it->GetPointer()); mitk::LegacyFileWriterService *lfws = new mitk::LegacyFileWriterService(fileWriter, description); m_LegacyWriters[factory].push_back(lfws); } } void mitk::CoreObjectFactory::UnRegisterLegacyWriters(mitk::CoreObjectFactoryBase *factory) { auto iter = m_LegacyWriters.find(factory); if (iter != m_LegacyWriters.end()) { for (auto &elem : iter->second) { delete elem; } m_LegacyWriters.erase(iter); } } diff --git a/Modules/QtWidgets/include/QmitkMxNMultiWidget.h b/Modules/QtWidgets/include/QmitkMxNMultiWidget.h index 8cd87a16d5..702191a81e 100644 --- a/Modules/QtWidgets/include/QmitkMxNMultiWidget.h +++ b/Modules/QtWidgets/include/QmitkMxNMultiWidget.h @@ -1,135 +1,135 @@ /*============================================================================ 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 QmitkMxNMultiWidget_h #define QmitkMxNMultiWidget_h #include "MitkQtWidgetsExports.h" // qt widgets module #include "QmitkAbstractMultiWidget.h" #include #include #include class QSplitter; /** * @brief The 'QmitkMxNMultiWidget' is a 'QmitkAbstractMultiWidget' that is used to display multiple render windows at once. * Render windows can dynamically be added and removed to change the layout of the multi widget. This * is done by using the 'SetLayout'-function to define a layout. This will automatically add or remove * the appropriate number of render window widgets. */ class MITKQTWIDGETS_EXPORT QmitkMxNMultiWidget : public QmitkAbstractMultiWidget { Q_OBJECT public: QmitkMxNMultiWidget(QWidget* parent = nullptr, Qt::WindowFlags f = 0, const QString& multiWidgetName = "mxnmulti"); ~QmitkMxNMultiWidget(); void InitializeMultiWidget() override; void Synchronize(bool synchronized) override; QmitkRenderWindow* GetRenderWindow(const QString& widgetName) const override; QmitkRenderWindow* GetRenderWindow(const mitk::AnatomicalPlane& orientation) const override; void SetActiveRenderWindowWidget(RenderWindowWidgetPointer activeRenderWindowWidget) override; /** * @brief Initialize the active render windows of the MxNMultiWidget to the given geometry. * * @param geometry The geometry to be used to initialize / update the * active render window's time and slice navigation controller. * @param resetCamera If true, the camera and crosshair will be reset to the default view (centered, no zoom). * If false, the current crosshair position and the camera zoom will be stored and reset * after the reference geometry has been updated. */ void InitializeViews(const mitk::TimeGeometry* geometry, bool resetCamera) override; /** * @brief Forward the given time geometry to all base renderers, so that they can store it as their * interaction reference geometry. * This will update the alignment status of the reference geometry for each base renderer. * For more details, see 'BaseRenderer::SetInteractionReferenceGeometry'. * Overridem from 'QmitkAbstractMultiWidget'. */ void SetInteractionReferenceGeometry(const mitk::TimeGeometry* referenceGeometry) override; /** * @brief Returns true if the render windows are coupled; false if not. * * For the MxNMultiWidget the render windows are typically decoupled. */ bool HasCoupledRenderWindows() const override; void SetSelectedPosition(const mitk::Point3D& newPosition, const QString& widgetName) override; const mitk::Point3D GetSelectedPosition(const QString& widgetName) const override; void SetCrosshairVisibility(bool visible) override; bool GetCrosshairVisibility() const override; void SetCrosshairGap(unsigned int gapSize) override; void ResetCrosshair() override; void SetWidgetPlaneMode(int userMode) override; mitk::SliceNavigationController* GetTimeNavigationController(); - void AddPlanesToDataStorage(); - void RemovePlanesFromDataStorage(); + void EnableCrosshair(); + void DisableCrosshair(); public Q_SLOTS: // mouse events void wheelEvent(QWheelEvent* e) override; void mousePressEvent(QMouseEvent* e) override; void moveEvent(QMoveEvent* e) override; void LoadLayout(const nlohmann::json* jsonData); void SaveLayout(std::ostream* outStream); Q_SIGNALS: void WheelMoved(QWheelEvent *); void Moved(); void UpdateUtilityWidgetViewPlanes(); protected: void RemoveRenderWindowWidget() override; private: void SetLayoutImpl() override; void SetInteractionSchemeImpl() override { } QmitkAbstractMultiWidget::RenderWindowWidgetPointer CreateRenderWindowWidget(); void SetInitialSelection(); void ToggleSynchronization(QmitkSynchronizedNodeSelectionWidget* synchronizedWidget); static nlohmann::json BuildJSONFromLayout(const QSplitter* splitter); QSplitter* BuildLayoutFromJSON(const nlohmann::json* jsonData, unsigned int* windowCounter, QSplitter* parentSplitter = nullptr); mitk::SliceNavigationController* m_TimeNavigationController; std::unique_ptr m_SynchronizedWidgetConnector; bool m_CrosshairVisibility; }; #endif diff --git a/Modules/QtWidgets/include/QmitkRenderWindowWidget.h b/Modules/QtWidgets/include/QmitkRenderWindowWidget.h index 6716bdf177..98f734fec0 100644 --- a/Modules/QtWidgets/include/QmitkRenderWindowWidget.h +++ b/Modules/QtWidgets/include/QmitkRenderWindowWidget.h @@ -1,123 +1,123 @@ /*============================================================================ 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 QmitkRenderWindowWidget_h #define QmitkRenderWindowWidget_h #include "MitkQtWidgetsExports.h" // qt widgets module #include // mitk core #include #include #include // qt #include #include #include class vtkCornerAnnotation; /** * @brief The 'QmitkRenderWindowWidget' is a QFrame that holds a render window * and some associates properties, e.g. decorations. * Decorations are corner annotation (text and color), frame color or background color * and can be set using this class. * The 'QmitkRenderWindowWidget' is used inside a 'QmitkAbstractMultiWidget', where a map contains * several render window widgets to create the multi widget display. * This class uses a CrosshairManager, which allows to use plane geometries as crosshair. */ class MITKQTWIDGETS_EXPORT QmitkRenderWindowWidget : public QFrame { Q_OBJECT public: QmitkRenderWindowWidget( QWidget* parent = nullptr, const QString& widgetName = "", mitk::DataStorage* dataStorage = nullptr); ~QmitkRenderWindowWidget() override; void SetDataStorage(mitk::DataStorage* dataStorage); const QString& GetWidgetName() const { return m_WidgetName; }; QmitkRenderWindow* GetRenderWindow() const { return m_RenderWindow; }; mitk::SliceNavigationController* GetSliceNavigationController() const; void RequestUpdate(); void ForceImmediateUpdate(); void AddUtilityWidget(QWidget* utilityWidget); void SetGradientBackgroundColors(const mitk::Color& upper, const mitk::Color& lower); void ShowGradientBackground(bool enable); std::pair GetGradientBackgroundColors() const { return m_GradientBackgroundColors; }; bool IsGradientBackgroundOn() const; void SetDecorationColor(const mitk::Color& color); mitk::Color GetDecorationColor() const { return m_DecorationColor; }; void ShowColoredRectangle(bool show); bool IsColoredRectangleVisible() const; void ShowCornerAnnotation(bool show); bool IsCornerAnnotationVisible() const; void SetCornerAnnotationText(const std::string& cornerAnnotation); std::string GetCornerAnnotationText() const; bool IsRenderWindowMenuActivated() const; void SetCrosshairVisibility(bool visible); bool GetCrosshairVisibility(); void SetCrosshairGap(unsigned int gapSize); - void AddPlanesToDataStorage(); - void RemovePlanesFromDataStorage(); + void EnableCrosshair(); + void DisableCrosshair(); void SetCrosshairPosition(const mitk::Point3D& newPosition); mitk::Point3D GetCrosshairPosition() const; void SetGeometry(const itk::EventObject& event); void SetGeometrySlice(const itk::EventObject& event); public Q_SLOTS: void OnResetGeometry(); private: void InitializeGUI(); void InitializeDecorations(); void ResetGeometry(const mitk::TimeGeometry* referenceGeometry); QString m_WidgetName; QVBoxLayout* m_Layout; mitk::DataStorage* m_DataStorage; QmitkRenderWindow* m_RenderWindow; mitk::CrosshairManager::Pointer m_CrosshairManager; std::pair m_GradientBackgroundColors; mitk::Color m_DecorationColor; vtkSmartPointer m_CornerAnnotation; }; #endif diff --git a/Modules/QtWidgets/src/QmitkMxNMultiWidget.cpp b/Modules/QtWidgets/src/QmitkMxNMultiWidget.cpp index 095cd35522..a7d674b631 100644 --- a/Modules/QtWidgets/src/QmitkMxNMultiWidget.cpp +++ b/Modules/QtWidgets/src/QmitkMxNMultiWidget.cpp @@ -1,630 +1,630 @@ /*============================================================================ 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 "QmitkMxNMultiWidget.h" // mitk core #include #include #include #include #include #include // mitk qt widget #include #include // qt #include #include #include #include QmitkMxNMultiWidget::QmitkMxNMultiWidget(QWidget* parent, Qt::WindowFlags f/* = 0*/, const QString& multiWidgetName/* = "mxnmulti"*/) : QmitkAbstractMultiWidget(parent, f, multiWidgetName) , m_TimeNavigationController(nullptr) , m_SynchronizedWidgetConnector(std::make_unique()) , m_CrosshairVisibility(false) { m_TimeNavigationController = mitk::RenderingManager::GetInstance()->GetTimeNavigationController(); } QmitkMxNMultiWidget::~QmitkMxNMultiWidget() { auto allRenderWindows = this->GetRenderWindows(); for (auto& renderWindow : allRenderWindows) { m_TimeNavigationController->Disconnect(renderWindow->GetSliceNavigationController()); } } void QmitkMxNMultiWidget::InitializeMultiWidget() { SetLayout(1, 1); SetDisplayActionEventHandler(std::make_unique()); auto displayActionEventHandler = GetDisplayActionEventHandler(); if (nullptr != displayActionEventHandler) { displayActionEventHandler->InitActions(); } this->SetInitialSelection(); } void QmitkMxNMultiWidget::Synchronize(bool synchronized) { if (synchronized) { SetDisplayActionEventHandler(std::make_unique()); } else { SetDisplayActionEventHandler(std::make_unique()); } auto displayActionEventHandler = GetDisplayActionEventHandler(); if (nullptr != displayActionEventHandler) { displayActionEventHandler->InitActions(); } } QmitkRenderWindow* QmitkMxNMultiWidget::GetRenderWindow(const QString& widgetName) const { if ("axial" == widgetName || "sagittal" == widgetName || "coronal" == widgetName || "3d" == widgetName) { return GetActiveRenderWindowWidget()->GetRenderWindow(); } return QmitkAbstractMultiWidget::GetRenderWindow(widgetName); } QmitkRenderWindow* QmitkMxNMultiWidget::GetRenderWindow(const mitk::AnatomicalPlane& /*orientation*/) const { // currently no mapping between plane orientation and render windows // simply return the currently active render window return GetActiveRenderWindowWidget()->GetRenderWindow(); } void QmitkMxNMultiWidget::SetActiveRenderWindowWidget(RenderWindowWidgetPointer activeRenderWindowWidget) { auto currentActiveRenderWindowWidget = GetActiveRenderWindowWidget(); if (currentActiveRenderWindowWidget == activeRenderWindowWidget) { return; } // reset the decoration color of the previously active render window widget if (nullptr != currentActiveRenderWindowWidget) { auto decorationColor = currentActiveRenderWindowWidget->GetDecorationColor(); QColor hexColor(decorationColor[0] * 255, decorationColor[1] * 255, decorationColor[2] * 255); currentActiveRenderWindowWidget->setStyleSheet("QmitkRenderWindowWidget { border: 2px solid " + hexColor.name(QColor::HexRgb) + "; }"); } // set the new decoration color of the currently active render window widget if (nullptr != activeRenderWindowWidget) { activeRenderWindowWidget->setStyleSheet("QmitkRenderWindowWidget { border: 2px solid #FF6464; }"); } QmitkAbstractMultiWidget::SetActiveRenderWindowWidget(activeRenderWindowWidget); } void QmitkMxNMultiWidget::InitializeViews(const mitk::TimeGeometry* geometry, bool resetCamera) { auto* renderingManager = mitk::RenderingManager::GetInstance(); mitk::Point3D currentPosition = mitk::Point3D(); unsigned int imageTimeStep = 0; if (!resetCamera) { // store the current position to set it again later, if the camera should not be reset currentPosition = this->GetSelectedPosition(""); // store the current time step to set it again later, if the camera should not be reset const auto currentTimePoint = renderingManager->GetTimeNavigationController()->GetSelectedTimePoint(); if (geometry->IsValidTimePoint(currentTimePoint)) { imageTimeStep = geometry->TimePointToTimeStep(currentTimePoint); } } // initialize active render window renderingManager->InitializeView( this->GetActiveRenderWindowWidget()->GetRenderWindow()->GetVtkRenderWindow(), geometry, resetCamera); if (!resetCamera) { this->SetSelectedPosition(currentPosition, ""); renderingManager->GetTimeNavigationController()->GetTime()->SetPos(imageTimeStep); } } void QmitkMxNMultiWidget::SetInteractionReferenceGeometry(const mitk::TimeGeometry* referenceGeometry) { // Set the interaction reference referenceGeometry for all render windows. auto allRenderWindows = this->GetRenderWindows(); for (auto& renderWindow : allRenderWindows) { auto* baseRenderer = mitk::BaseRenderer::GetInstance(renderWindow->GetRenderWindow()); baseRenderer->SetInteractionReferenceGeometry(referenceGeometry); } } bool QmitkMxNMultiWidget::HasCoupledRenderWindows() const { return false; } void QmitkMxNMultiWidget::SetSelectedPosition(const mitk::Point3D& newPosition, const QString& widgetName) { RenderWindowWidgetPointer renderWindowWidget; if (widgetName.isNull() || widgetName.isEmpty()) { renderWindowWidget = GetActiveRenderWindowWidget(); } else { renderWindowWidget = GetRenderWindowWidget(widgetName); } if (nullptr != renderWindowWidget) { renderWindowWidget->GetSliceNavigationController()->SelectSliceByPoint(newPosition); return; } MITK_ERROR << "Position can not be set for an unknown render window widget."; } const mitk::Point3D QmitkMxNMultiWidget::GetSelectedPosition(const QString& widgetName) const { RenderWindowWidgetPointer renderWindowWidget; if (widgetName.isNull() || widgetName.isEmpty()) { renderWindowWidget = GetActiveRenderWindowWidget(); } else { renderWindowWidget = GetRenderWindowWidget(widgetName); } if (nullptr != renderWindowWidget) { return renderWindowWidget->GetCrosshairPosition(); } MITK_ERROR << "Crosshair position can not be retrieved."; return mitk::Point3D(0.0); } void QmitkMxNMultiWidget::SetCrosshairVisibility(bool visible) { // get the specific render window that sent the signal QmitkRenderWindow* renderWindow = qobject_cast(sender()); if (nullptr == renderWindow) { return; } auto renderWindowWidget = this->GetRenderWindowWidget(renderWindow); renderWindowWidget->SetCrosshairVisibility(visible); } bool QmitkMxNMultiWidget::GetCrosshairVisibility() const { // get the specific render window that sent the signal QmitkRenderWindow* renderWindow = qobject_cast(sender()); if (nullptr == renderWindow) { return false; } auto renderWindowWidget = this->GetRenderWindowWidget(renderWindow); return renderWindowWidget->GetCrosshairVisibility(); } void QmitkMxNMultiWidget::SetCrosshairGap(unsigned int gapSize) { auto renderWindowWidgets = this->GetRenderWindowWidgets(); for (const auto& renderWindowWidget : renderWindowWidgets) { renderWindowWidget.second->SetCrosshairGap(gapSize); } } void QmitkMxNMultiWidget::ResetCrosshair() { auto dataStorage = GetDataStorage(); if (nullptr == dataStorage) { return; } // get the specific render window that sent the signal QmitkRenderWindow* renderWindow = qobject_cast(sender()); if (nullptr == renderWindow) { return; } mitk::RenderingManager::GetInstance()->InitializeViewByBoundingObjects(renderWindow->GetRenderWindow(), dataStorage); SetWidgetPlaneMode(mitk::InteractionSchemeSwitcher::MITKStandard); } void QmitkMxNMultiWidget::SetWidgetPlaneMode(int userMode) { MITK_DEBUG << "Changing crosshair mode to " << userMode; switch (userMode) { case 0: SetInteractionScheme(mitk::InteractionSchemeSwitcher::MITKStandard); break; case 1: SetInteractionScheme(mitk::InteractionSchemeSwitcher::MITKRotationUncoupled); break; case 2: SetInteractionScheme(mitk::InteractionSchemeSwitcher::MITKRotationCoupled); break; case 3: SetInteractionScheme(mitk::InteractionSchemeSwitcher::MITKSwivel); break; } } mitk::SliceNavigationController* QmitkMxNMultiWidget::GetTimeNavigationController() { return m_TimeNavigationController; } -void QmitkMxNMultiWidget::AddPlanesToDataStorage() +void QmitkMxNMultiWidget::EnableCrosshair() { auto renderWindowWidgets = this->GetRenderWindowWidgets(); for (const auto& renderWindowWidget : renderWindowWidgets) { - renderWindowWidget.second->AddPlanesToDataStorage(); + renderWindowWidget.second->EnableCrosshair(); } } -void QmitkMxNMultiWidget::RemovePlanesFromDataStorage() +void QmitkMxNMultiWidget::DisableCrosshair() { auto renderWindowWidgets = this->GetRenderWindowWidgets(); for (const auto& renderWindowWidget : renderWindowWidgets) { - renderWindowWidget.second->RemovePlanesFromDataStorage(); + renderWindowWidget.second->DisableCrosshair(); } } ////////////////////////////////////////////////////////////////////////// // PUBLIC SLOTS // MOUSE EVENTS ////////////////////////////////////////////////////////////////////////// void QmitkMxNMultiWidget::wheelEvent(QWheelEvent* e) { emit WheelMoved(e); } void QmitkMxNMultiWidget::mousePressEvent(QMouseEvent*) { // nothing here, but necessary for mouse interactions (.xml-configuration files) } void QmitkMxNMultiWidget::moveEvent(QMoveEvent* e) { QWidget::moveEvent(e); // it is necessary to readjust the position of the overlays as the MultiWidget has moved // unfortunately it's not done by QmitkRenderWindow::moveEvent -> must be done here emit Moved(); } void QmitkMxNMultiWidget::RemoveRenderWindowWidget() { auto renderWindowWidgets = this->GetRenderWindowWidgets(); auto iterator = renderWindowWidgets.find(this->GetNameFromIndex(this->GetNumberOfRenderWindowWidgets() - 1)); if (iterator == renderWindowWidgets.end()) { return; } // disconnect each signal of this render window widget RenderWindowWidgetPointer renderWindowWidgetToRemove = iterator->second; m_TimeNavigationController->Disconnect(renderWindowWidgetToRemove->GetSliceNavigationController()); QmitkAbstractMultiWidget::RemoveRenderWindowWidget(); } ////////////////////////////////////////////////////////////////////////// // PRIVATE ////////////////////////////////////////////////////////////////////////// void QmitkMxNMultiWidget::SetLayoutImpl() { int requiredRenderWindowWidgets = GetRowCount() * GetColumnCount(); int existingRenderWindowWidgets = GetRenderWindowWidgets().size(); int difference = requiredRenderWindowWidgets - existingRenderWindowWidgets; while (0 < difference) { // more render window widgets needed CreateRenderWindowWidget(); --difference; } while (0 > difference) { // less render window widgets needed RemoveRenderWindowWidget(); ++difference; } auto firstRenderWindowWidget = GetFirstRenderWindowWidget(); if (nullptr != firstRenderWindowWidget) { SetActiveRenderWindowWidget(firstRenderWindowWidget); } GetMultiWidgetLayoutManager()->SetLayoutDesign(QmitkMultiWidgetLayoutManager::LayoutDesign::DEFAULT); } QmitkAbstractMultiWidget::RenderWindowWidgetPointer QmitkMxNMultiWidget::CreateRenderWindowWidget() { // create the render window widget and connect signal / slot QString renderWindowWidgetName = GetNameFromIndex(GetNumberOfRenderWindowWidgets()); RenderWindowWidgetPointer renderWindowWidget = std::make_shared(this, renderWindowWidgetName, GetDataStorage()); renderWindowWidget->SetCornerAnnotationText(renderWindowWidgetName.toStdString()); AddRenderWindowWidget(renderWindowWidgetName, renderWindowWidget); auto renderWindow = renderWindowWidget->GetRenderWindow(); QmitkRenderWindowUtilityWidget* utilityWidget = new QmitkRenderWindowUtilityWidget(this, renderWindow, GetDataStorage()); renderWindowWidget->AddUtilityWidget(utilityWidget); connect(utilityWidget, &QmitkRenderWindowUtilityWidget::SynchronizationToggled, this, &QmitkMxNMultiWidget::ToggleSynchronization); connect(this, &QmitkMxNMultiWidget::UpdateUtilityWidgetViewPlanes, utilityWidget, &QmitkRenderWindowUtilityWidget::UpdateViewPlaneSelection); // needs to be done after 'QmitkRenderWindowUtilityWidget::ToggleSynchronization' has been connected // initially synchronize the node selection widget utilityWidget->ToggleSynchronization(true); auto layoutManager = GetMultiWidgetLayoutManager(); connect(renderWindow, &QmitkRenderWindow::LayoutDesignChanged, layoutManager, &QmitkMultiWidgetLayoutManager::SetLayoutDesign); connect(renderWindow, &QmitkRenderWindow::ResetView, this, &QmitkMxNMultiWidget::ResetCrosshair); connect(renderWindow, &QmitkRenderWindow::CrosshairVisibilityChanged, this, &QmitkMxNMultiWidget::SetCrosshairVisibility); connect(renderWindow, &QmitkRenderWindow::CrosshairRotationModeChanged, this, &QmitkMxNMultiWidget::SetWidgetPlaneMode); // connect time navigation controller to react on referenceGeometry time events with the render window's slice naviation controller m_TimeNavigationController->ConnectGeometryTimeEvent(renderWindow->GetSliceNavigationController()); // reverse connection between the render window's slice navigation controller and the time navigation controller renderWindow->GetSliceNavigationController()->ConnectGeometryTimeEvent(m_TimeNavigationController); return renderWindowWidget; } void QmitkMxNMultiWidget::LoadLayout(const nlohmann::json* jsonData) { if ((*jsonData).is_null()) { QMessageBox::warning(this, "Load layout", "Could not read window layout"); return; } unsigned int windowCounter = 0; try { auto version = jsonData->at("version").get(); if (version != "1.0") { QMessageBox::warning(this, "Load layout", "Unknown layout version, could not load"); return; } delete this->layout(); auto content = BuildLayoutFromJSON(jsonData, &windowCounter); auto hBoxLayout = new QHBoxLayout(this); this->setLayout(hBoxLayout); hBoxLayout->addWidget(content); emit UpdateUtilityWidgetViewPlanes(); } catch (nlohmann::json::out_of_range& e) { MITK_ERROR << "Error in loading window layout from JSON: " << e.what(); return; } while (GetNumberOfRenderWindowWidgets() > windowCounter) { RemoveRenderWindowWidget(); } } void QmitkMxNMultiWidget::SaveLayout(std::ostream* outStream) { if (outStream == nullptr) { return; } auto layout = this->layout(); if (layout == nullptr) return; // There should only ever be one item: a splitter auto widget = layout->itemAt(0)->widget(); auto splitter = dynamic_cast(widget); if (!splitter) { MITK_ERROR << "Tried to save unexpected layout format. Make sure the layout of this instance contains a single QSplitter."; return; } auto layoutJSON = BuildJSONFromLayout(splitter); layoutJSON["version"] = "1.0"; layoutJSON["name"] = "Custom Layout"; *outStream << std::setw(4) << layoutJSON << std::endl; } nlohmann::json QmitkMxNMultiWidget::BuildJSONFromLayout(const QSplitter* splitter) { nlohmann::json resultJSON; resultJSON["isWindow"] = false; resultJSON["vertical"] = (splitter->orientation() == Qt::Vertical) ? true : false; auto sizes = splitter->sizes(); auto content = nlohmann::json::array(); auto countSplitter = splitter->count(); for (int i = 0; i < countSplitter; ++i) { auto widget = splitter->widget(i); nlohmann::json widgetJSON; if (auto widgetSplitter = dynamic_cast(widget); widgetSplitter) { widgetJSON = BuildJSONFromLayout(widgetSplitter); } else if (auto widgetWindow = dynamic_cast(widget); widgetWindow) { widgetJSON["isWindow"] = true; widgetJSON["viewDirection"] = widgetWindow->GetSliceNavigationController()->GetViewDirectionAsString(); } widgetJSON["size"] = sizes[i]; content.push_back(widgetJSON); } resultJSON["content"] = content; return resultJSON; } QSplitter* QmitkMxNMultiWidget::BuildLayoutFromJSON(const nlohmann::json* jsonData, unsigned int* windowCounter, QSplitter* parentSplitter) { bool vertical = jsonData->at("vertical").get(); auto orientation = vertical ? Qt::Vertical : Qt::Horizontal; auto split = new QSplitter(orientation, parentSplitter); QList sizes; for (auto object : jsonData->at("content")) { bool isWindow = object["isWindow"].get(); int size = object["size"].get(); sizes.append(size); if (isWindow) { auto viewDirection = object["viewDirection"].get(); mitk::AnatomicalPlane viewPlane = mitk::AnatomicalPlane::Sagittal; if (viewDirection == "Axial") { viewPlane = mitk::AnatomicalPlane::Axial; } else if (viewDirection == "Coronal") { viewPlane = mitk::AnatomicalPlane::Coronal; } else if (viewDirection == "Original") { viewPlane = mitk::AnatomicalPlane::Original; } else if (viewDirection == "Sagittal") { viewPlane = mitk::AnatomicalPlane::Sagittal; } QmitkAbstractMultiWidget::RenderWindowWidgetPointer window = nullptr; QString renderWindowName; QmitkAbstractMultiWidget::RenderWindowWidgetMap::iterator it; // repurpose existing render windows as far as they already exist if (*windowCounter < GetRenderWindowWidgets().size()) { renderWindowName = this->GetNameFromIndex(*windowCounter); auto renderWindowWidgets = GetRenderWindowWidgets(); it = renderWindowWidgets.find(renderWindowName); if (it != renderWindowWidgets.end()) { window = it->second; } else { MITK_ERROR << "Could not find render window " << renderWindowName.toStdString() << ", although it should be there."; } } if (window == nullptr) { window = CreateRenderWindowWidget(); } window->GetSliceNavigationController()->SetDefaultViewDirection(viewPlane); window->GetSliceNavigationController()->Update(); split->addWidget(window.get()); window->show(); (*windowCounter)++; } else { auto subSplitter = BuildLayoutFromJSON(&object, windowCounter, split); split->addWidget(subSplitter); } } split->setSizes(sizes); return split; } void QmitkMxNMultiWidget::SetInitialSelection() { auto dataStorage = this->GetDataStorage(); if (nullptr == dataStorage) { return; } mitk::NodePredicateAnd::Pointer noHelperObjects = mitk::NodePredicateAnd::New(); noHelperObjects->AddPredicate(mitk::NodePredicateNot::New(mitk::NodePredicateProperty::New("helper object"))); noHelperObjects->AddPredicate(mitk::NodePredicateNot::New(mitk::NodePredicateProperty::New("hidden object"))); auto allNodes = dataStorage->GetSubset(noHelperObjects); QmitkSynchronizedNodeSelectionWidget::NodeList currentSelection; for (auto& node : *allNodes) { currentSelection.append(node); } m_SynchronizedWidgetConnector->ChangeSelection(currentSelection); } void QmitkMxNMultiWidget::ToggleSynchronization(QmitkSynchronizedNodeSelectionWidget* synchronizedWidget) { bool synchronized = synchronizedWidget->IsSynchronized(); if (synchronized) { m_SynchronizedWidgetConnector->ConnectWidget(synchronizedWidget); m_SynchronizedWidgetConnector->SynchronizeWidget(synchronizedWidget); } else { m_SynchronizedWidgetConnector->DisconnectWidget(synchronizedWidget); } } diff --git a/Modules/QtWidgets/src/QmitkRenderWindow.cpp b/Modules/QtWidgets/src/QmitkRenderWindow.cpp index 3776aeabf5..1f5676dd42 100644 --- a/Modules/QtWidgets/src/QmitkRenderWindow.cpp +++ b/Modules/QtWidgets/src/QmitkRenderWindow.cpp @@ -1,507 +1,513 @@ /*============================================================================ 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 "QmitkRenderWindow.h" #include "mitkInteractionKeyEvent.h" #include "mitkInternalEvent.h" #include "mitkMouseDoubleClickEvent.h" #include "mitkMouseMoveEvent.h" #include "mitkMousePressEvent.h" #include "mitkMouseReleaseEvent.h" #include "mitkMouseWheelEvent.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include QmitkRenderWindow::QmitkRenderWindow(QWidget *parent, const QString &name, mitk::VtkPropRenderer *) : QVTKOpenGLNativeWidget(parent) , m_ResendQtEvents(true) , m_MenuWidget(nullptr) , m_MenuWidgetActivated(false) , m_LayoutIndex(QmitkRenderWindowMenu::LayoutIndex::Axial) , m_GeometryViolationWarningOverlay(nullptr) { m_InternalRenderWindow = vtkSmartPointer::New(); m_InternalRenderWindow->SetMultiSamples(0); m_InternalRenderWindow->SetAlphaBitPlanes(0); setRenderWindow(m_InternalRenderWindow); Initialize(name.toStdString().c_str()); setFocusPolicy(Qt::StrongFocus); setMouseTracking(true); QSizePolicy sizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); setSizePolicy(sizePolicy); // setup overlay widget to show a warning message with a button m_GeometryViolationWarningOverlay = new QmitkButtonOverlayWidget(this); m_GeometryViolationWarningOverlay->setVisible(false); m_GeometryViolationWarningOverlay->SetOverlayText( QStringLiteral("

Interaction is not possible because the " "render window geometry
does not match the interaction reference geometry.

")); m_GeometryViolationWarningOverlay->SetButtonText("Reset geometry"); m_GeometryViolationWarningOverlay->SetButtonIcon(QmitkStyleManager::ThemeIcon(QLatin1String(":/Qmitk/reset.svg"))); connect(m_GeometryViolationWarningOverlay, &QmitkButtonOverlayWidget::Clicked, this, &QmitkRenderWindow::ResetGeometry); } QmitkRenderWindow::~QmitkRenderWindow() { Destroy(); // Destroy mitkRenderWindowBase } void QmitkRenderWindow::SetResendQtEvents(bool resend) { m_ResendQtEvents = resend; } void QmitkRenderWindow::SetLayoutIndex(QmitkRenderWindowMenu::LayoutIndex layoutIndex) { m_LayoutIndex = layoutIndex; if (nullptr != m_MenuWidget) { m_MenuWidget->SetLayoutIndex(layoutIndex); } } QmitkRenderWindowMenu::LayoutIndex QmitkRenderWindow::GetLayoutIndex() { if (nullptr != m_MenuWidget) { return m_MenuWidget->GetLayoutIndex(); } else { return QmitkRenderWindowMenu::LayoutIndex::Axial; } } void QmitkRenderWindow::UpdateLayoutDesignList(QmitkRenderWindowMenu::LayoutDesign layoutDesign) { if (nullptr != m_MenuWidget) { m_MenuWidget->UpdateLayoutDesignList(layoutDesign); } } void QmitkRenderWindow::UpdateCrosshairVisibility(bool visible) { m_MenuWidget->UpdateCrosshairVisibility(visible); } void QmitkRenderWindow::UpdateCrosshairRotationMode(int mode) { m_MenuWidget->UpdateCrosshairRotationMode(mode); } void QmitkRenderWindow::ActivateMenuWidget(bool state) { if (nullptr == m_MenuWidget) { m_MenuWidget = new QmitkRenderWindowMenu(this, nullptr, m_Renderer); m_MenuWidget->SetLayoutIndex(m_LayoutIndex); } + if (m_MenuWidgetActivated == state) + { + // no new state; nothing to do + return; + } + m_MenuWidgetActivated = state; if (m_MenuWidgetActivated) { connect(m_MenuWidget, &QmitkRenderWindowMenu::LayoutDesignChanged, this, &QmitkRenderWindow::LayoutDesignChanged); connect(m_MenuWidget, &QmitkRenderWindowMenu::ResetView, this, &QmitkRenderWindow::ResetView); connect(m_MenuWidget, &QmitkRenderWindowMenu::CrosshairVisibilityChanged, this, &QmitkRenderWindow::CrosshairVisibilityChanged); connect(m_MenuWidget, &QmitkRenderWindowMenu::CrosshairRotationModeChanged, this, &QmitkRenderWindow::CrosshairRotationModeChanged); } else { disconnect(m_MenuWidget, &QmitkRenderWindowMenu::LayoutDesignChanged, this, &QmitkRenderWindow::LayoutDesignChanged); disconnect(m_MenuWidget, &QmitkRenderWindowMenu::ResetView, this, &QmitkRenderWindow::ResetView); disconnect(m_MenuWidget, &QmitkRenderWindowMenu::CrosshairVisibilityChanged, this, &QmitkRenderWindow::CrosshairVisibilityChanged); disconnect(m_MenuWidget, &QmitkRenderWindowMenu::CrosshairRotationModeChanged, this, &QmitkRenderWindow::CrosshairRotationModeChanged); m_MenuWidget->hide(); } } void QmitkRenderWindow::ShowOverlayMessage(bool show) { m_GeometryViolationWarningOverlay->setVisible(show); } void QmitkRenderWindow::moveEvent(QMoveEvent *event) { QVTKOpenGLNativeWidget::moveEvent(event); // after a move the overlays need to be positioned emit moved(); } void QmitkRenderWindow::showEvent(QShowEvent *event) { QVTKOpenGLNativeWidget::showEvent(event); // this singleshot is necessary to have the overlays positioned correctly after initial show // simple call of moved() is no use here!! QTimer::singleShot(0, this, SIGNAL(moved())); } bool QmitkRenderWindow::event(QEvent* e) { mitk::InteractionEvent::Pointer mitkEvent = nullptr; mitk::Point2D mousePosition; bool updateStatusBar = false; switch (e->type()) { case QEvent::MouseMove: { auto me = static_cast(e); mousePosition = this->GetMousePosition(me); mitkEvent = mitk::MouseMoveEvent::New(m_Renderer, mousePosition, GetButtonState(me), GetModifiers(me)); updateStatusBar = true; break; } case QEvent::MouseButtonPress: { auto me = static_cast(e); mitkEvent = mitk::MousePressEvent::New( m_Renderer, GetMousePosition(me), GetButtonState(me), GetModifiers(me), GetEventButton(me)); break; } case QEvent::MouseButtonRelease: { auto me = static_cast(e); mitkEvent = mitk::MouseReleaseEvent::New( m_Renderer, GetMousePosition(me), GetButtonState(me), GetModifiers(me), GetEventButton(me)); break; } case QEvent::MouseButtonDblClick: { auto me = static_cast(e); mitkEvent = mitk::MouseDoubleClickEvent::New( m_Renderer, GetMousePosition(me), GetButtonState(me), GetModifiers(me), GetEventButton(me)); break; } case QEvent::Wheel: { auto we = static_cast(e); mousePosition = this->GetMousePosition(we); mitkEvent = mitk::MouseWheelEvent::New(m_Renderer, mousePosition, GetButtonState(we), GetModifiers(we), GetDelta(we)); updateStatusBar = true; break; } case QEvent::KeyPress: { auto ke = static_cast(e); mitkEvent = mitk::InteractionKeyEvent::New(m_Renderer, GetKeyLetter(ke), GetModifiers(ke)); break; } case QEvent::Resize: { if (nullptr != m_MenuWidget) m_MenuWidget->MoveWidgetToCorrectPos(); } default: { break; } } if (mitkEvent != nullptr) { if (this->HandleEvent(mitkEvent.GetPointer())) { return m_ResendQtEvents ? false : true; } } if (updateStatusBar) { this->UpdateStatusBar(mousePosition); } return QVTKOpenGLNativeWidget::event(e); } void QmitkRenderWindow::enterEvent(QEvent *e) { auto* baseRenderer = mitk::BaseRenderer::GetInstance(this->GetRenderWindow()); this->ShowOverlayMessage(!baseRenderer->GetReferenceGeometryAligned()); if (nullptr != m_MenuWidget) m_MenuWidget->ShowMenu(); QVTKOpenGLNativeWidget::enterEvent(e); } void QmitkRenderWindow::leaveEvent(QEvent *e) { auto statusBar = mitk::StatusBar::GetInstance(); statusBar->DisplayGreyValueText(""); this->ShowOverlayMessage(false); if (nullptr != m_MenuWidget) m_MenuWidget->HideMenu(); QVTKOpenGLNativeWidget::leaveEvent(e); } void QmitkRenderWindow::resizeGL(int w, int h) { QVTKOpenGLNativeWidget::resizeGL(w, h); mitk::RenderingManager::GetInstance()->ForceImmediateUpdate(renderWindow()); } void QmitkRenderWindow::dragEnterEvent(QDragEnterEvent *event) { if (event->mimeData()->hasFormat("application/x-mitk-datanodes")) { event->accept(); } } void QmitkRenderWindow::dropEvent(QDropEvent *event) { QList dataNodeList = QmitkMimeTypes::ToDataNodePtrList(event->mimeData()); if (!dataNodeList.empty()) { emit NodesDropped(this, dataNodeList.toVector().toStdVector()); } } void QmitkRenderWindow::DeferredHideMenu() { MITK_DEBUG << "QmitkRenderWindow::DeferredHideMenu"; if (nullptr != m_MenuWidget) { m_MenuWidget->HideMenu(); } } mitk::Point2D QmitkRenderWindow::GetMousePosition(QMouseEvent *me) const { mitk::Point2D point; point[0] = me->x(); // We need to convert the y component, as the display and vtk have other definitions for the y direction point[1] = m_Renderer->GetSizeY() - me->y(); return point; } mitk::Point2D QmitkRenderWindow::GetMousePosition(QWheelEvent *we) const { mitk::Point2D point; point[0] = we->x(); // We need to convert the y component, as the display and vtk have other definitions for the y direction point[1] = m_Renderer->GetSizeY() - we->y(); return point; } mitk::InteractionEvent::MouseButtons QmitkRenderWindow::GetEventButton(QMouseEvent *me) const { mitk::InteractionEvent::MouseButtons eventButton; switch (me->button()) { case Qt::LeftButton: eventButton = mitk::InteractionEvent::LeftMouseButton; break; case Qt::RightButton: eventButton = mitk::InteractionEvent::RightMouseButton; break; case Qt::MidButton: eventButton = mitk::InteractionEvent::MiddleMouseButton; break; default: eventButton = mitk::InteractionEvent::NoButton; break; } return eventButton; } mitk::InteractionEvent::MouseButtons QmitkRenderWindow::GetButtonState(QMouseEvent *me) const { mitk::InteractionEvent::MouseButtons buttonState = mitk::InteractionEvent::NoButton; if (me->buttons() & Qt::LeftButton) { buttonState = buttonState | mitk::InteractionEvent::LeftMouseButton; } if (me->buttons() & Qt::RightButton) { buttonState = buttonState | mitk::InteractionEvent::RightMouseButton; } if (me->buttons() & Qt::MidButton) { buttonState = buttonState | mitk::InteractionEvent::MiddleMouseButton; } return buttonState; } mitk::InteractionEvent::ModifierKeys QmitkRenderWindow::GetModifiers(QInputEvent *me) const { mitk::InteractionEvent::ModifierKeys modifiers = mitk::InteractionEvent::NoKey; if (me->modifiers() & Qt::ALT) { modifiers = modifiers | mitk::InteractionEvent::AltKey; } if (me->modifiers() & Qt::CTRL) { modifiers = modifiers | mitk::InteractionEvent::ControlKey; } if (me->modifiers() & Qt::SHIFT) { modifiers = modifiers | mitk::InteractionEvent::ShiftKey; } return modifiers; } mitk::InteractionEvent::MouseButtons QmitkRenderWindow::GetButtonState(QWheelEvent *we) const { mitk::InteractionEvent::MouseButtons buttonState = mitk::InteractionEvent::NoButton; if (we->buttons() & Qt::LeftButton) { buttonState = buttonState | mitk::InteractionEvent::LeftMouseButton; } if (we->buttons() & Qt::RightButton) { buttonState = buttonState | mitk::InteractionEvent::RightMouseButton; } if (we->buttons() & Qt::MidButton) { buttonState = buttonState | mitk::InteractionEvent::MiddleMouseButton; } return buttonState; } std::string QmitkRenderWindow::GetKeyLetter(QKeyEvent *ke) const { // Converting Qt Key Event to string element. std::string key = ""; int tkey = ke->key(); if (tkey < 128) { // standard ascii letter key = (char)toupper(tkey); } else { // special keys switch (tkey) { case Qt::Key_Return: key = mitk::InteractionEvent::KeyReturn; break; case Qt::Key_Enter: key = mitk::InteractionEvent::KeyEnter; break; case Qt::Key_Escape: key = mitk::InteractionEvent::KeyEsc; break; case Qt::Key_Delete: key = mitk::InteractionEvent::KeyDelete; break; case Qt::Key_Up: key = mitk::InteractionEvent::KeyArrowUp; break; case Qt::Key_Down: key = mitk::InteractionEvent::KeyArrowDown; break; case Qt::Key_Left: key = mitk::InteractionEvent::KeyArrowLeft; break; case Qt::Key_Right: key = mitk::InteractionEvent::KeyArrowRight; break; case Qt::Key_F1: key = mitk::InteractionEvent::KeyF1; break; case Qt::Key_F2: key = mitk::InteractionEvent::KeyF2; break; case Qt::Key_F3: key = mitk::InteractionEvent::KeyF3; break; case Qt::Key_F4: key = mitk::InteractionEvent::KeyF4; break; case Qt::Key_F5: key = mitk::InteractionEvent::KeyF5; break; case Qt::Key_F6: key = mitk::InteractionEvent::KeyF6; break; case Qt::Key_F7: key = mitk::InteractionEvent::KeyF7; break; case Qt::Key_F8: key = mitk::InteractionEvent::KeyF8; break; case Qt::Key_F9: key = mitk::InteractionEvent::KeyF9; break; case Qt::Key_F10: key = mitk::InteractionEvent::KeyF10; break; case Qt::Key_F11: key = mitk::InteractionEvent::KeyF11; break; case Qt::Key_F12: key = mitk::InteractionEvent::KeyF12; break; case Qt::Key_End: key = mitk::InteractionEvent::KeyEnd; break; case Qt::Key_Home: key = mitk::InteractionEvent::KeyPos1; break; case Qt::Key_Insert: key = mitk::InteractionEvent::KeyInsert; break; case Qt::Key_PageDown: key = mitk::InteractionEvent::KeyPageDown; break; case Qt::Key_PageUp: key = mitk::InteractionEvent::KeyPageUp; break; case Qt::Key_Space: key = mitk::InteractionEvent::KeySpace; break; } } return key; } int QmitkRenderWindow::GetDelta(QWheelEvent *we) const { return we->delta(); } void QmitkRenderWindow::UpdateStatusBar(mitk::Point2D pointerPositionOnScreen) { mitk::Point3D worldPosition; m_Renderer->ForceImmediateUpdate(); m_Renderer->DisplayToWorld(pointerPositionOnScreen, worldPosition); auto statusBar = mitk::StatusBar::GetInstance(); statusBar->DisplayRendererInfo(worldPosition, m_Renderer->GetTime()); } diff --git a/Modules/QtWidgets/src/QmitkRenderWindowWidget.cpp b/Modules/QtWidgets/src/QmitkRenderWindowWidget.cpp index 20df5b5f9d..2704321fac 100644 --- a/Modules/QtWidgets/src/QmitkRenderWindowWidget.cpp +++ b/Modules/QtWidgets/src/QmitkRenderWindowWidget.cpp @@ -1,319 +1,316 @@ /*============================================================================ 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 "QmitkRenderWindowWidget.h" // vtk #include #include QmitkRenderWindowWidget::QmitkRenderWindowWidget(QWidget* parent/* = nullptr*/, const QString& widgetName/* = ""*/, mitk::DataStorage* dataStorage/* = nullptr*/) : QFrame(parent) , m_WidgetName(widgetName) , m_DataStorage(dataStorage) , m_RenderWindow(nullptr) , m_CrosshairManager(nullptr) { this->InitializeGUI(); } QmitkRenderWindowWidget::~QmitkRenderWindowWidget() { auto sliceNavigationController = this->GetSliceNavigationController(); if (nullptr != sliceNavigationController) { sliceNavigationController->SetCrosshairEvent.RemoveListener( mitk::MessageDelegate1( this, &QmitkRenderWindowWidget::SetCrosshairPosition)); } } void QmitkRenderWindowWidget::SetDataStorage(mitk::DataStorage* dataStorage) { if (dataStorage == m_DataStorage) { return; } m_DataStorage = dataStorage; if (nullptr != m_RenderWindow) { mitk::BaseRenderer::GetInstance(m_RenderWindow->renderWindow())->SetDataStorage(dataStorage); } - - m_CrosshairManager->SetDataStorage(m_DataStorage); } mitk::SliceNavigationController* QmitkRenderWindowWidget::GetSliceNavigationController() const { return m_RenderWindow->GetSliceNavigationController(); } void QmitkRenderWindowWidget::RequestUpdate() { mitk::RenderingManager::GetInstance()->RequestUpdate(m_RenderWindow->renderWindow()); } void QmitkRenderWindowWidget::ForceImmediateUpdate() { mitk::RenderingManager::GetInstance()->ForceImmediateUpdate(m_RenderWindow->renderWindow()); } void QmitkRenderWindowWidget::AddUtilityWidget(QWidget* utilityWidget) { m_Layout->insertWidget(0, utilityWidget); } void QmitkRenderWindowWidget::SetGradientBackgroundColors(const mitk::Color& upper, const mitk::Color& lower) { vtkRenderer* vtkRenderer = m_RenderWindow->GetRenderer()->GetVtkRenderer(); if (nullptr == vtkRenderer) { return; } m_GradientBackgroundColors.first = upper; m_GradientBackgroundColors.second = lower; vtkRenderer->SetBackground(lower[0], lower[1], lower[2]); vtkRenderer->SetBackground2(upper[0], upper[1], upper[2]); ShowGradientBackground(true); } void QmitkRenderWindowWidget::ShowGradientBackground(bool show) { m_RenderWindow->GetRenderer()->GetVtkRenderer()->SetGradientBackground(show); } bool QmitkRenderWindowWidget::IsGradientBackgroundOn() const { return m_RenderWindow->GetRenderer()->GetVtkRenderer()->GetGradientBackground(); } void QmitkRenderWindowWidget::SetDecorationColor(const mitk::Color& color) { m_DecorationColor = color; m_CornerAnnotation->GetTextProperty()->SetColor(m_DecorationColor[0], m_DecorationColor[1], m_DecorationColor[2]); QColor hexColor(m_DecorationColor[0] * 255, m_DecorationColor[1] * 255, m_DecorationColor[2] * 255); setStyleSheet("QmitkRenderWindowWidget { border: 2px solid " + hexColor.name(QColor::HexRgb) + "; }"); } void QmitkRenderWindowWidget::ShowColoredRectangle(bool show) { if (show) { setFrameStyle(QFrame::Box | QFrame::Plain); } else { setFrameStyle(NoFrame); } } bool QmitkRenderWindowWidget::IsColoredRectangleVisible() const { return frameStyle() > 0; } void QmitkRenderWindowWidget::ShowCornerAnnotation(bool show) { m_CornerAnnotation->SetVisibility(show); } bool QmitkRenderWindowWidget::IsCornerAnnotationVisible() const { return m_CornerAnnotation->GetVisibility() > 0; } void QmitkRenderWindowWidget::SetCornerAnnotationText(const std::string& cornerAnnotation) { m_CornerAnnotation->SetText(0, cornerAnnotation.c_str()); } std::string QmitkRenderWindowWidget::GetCornerAnnotationText() const { return std::string(m_CornerAnnotation->GetText(0)); } bool QmitkRenderWindowWidget::IsRenderWindowMenuActivated() const { return m_RenderWindow->GetActivateMenuWidgetFlag(); } void QmitkRenderWindowWidget::SetCrosshairVisibility(bool visible) { - m_CrosshairManager->SetCrosshairVisibility(visible); + m_CrosshairManager->SetCrosshairVisibility(visible, m_RenderWindow->GetRenderer()); this->RequestUpdate(); } bool QmitkRenderWindowWidget::GetCrosshairVisibility() { - return m_CrosshairManager->GetCrosshairVisibility(); + return m_CrosshairManager->GetCrosshairVisibility(m_RenderWindow->GetRenderer()); } void QmitkRenderWindowWidget::SetCrosshairGap(unsigned int gapSize) { m_CrosshairManager->SetCrosshairGap(gapSize); } -void QmitkRenderWindowWidget::AddPlanesToDataStorage() +void QmitkRenderWindowWidget::EnableCrosshair() { - m_CrosshairManager->AddPlanesToDataStorage(); + m_CrosshairManager->AddCrosshairNodeToDataStorage(m_DataStorage); } -void QmitkRenderWindowWidget::RemovePlanesFromDataStorage() +void QmitkRenderWindowWidget::DisableCrosshair() { - m_CrosshairManager->RemovePlanesFromDataStorage(); + m_CrosshairManager->RemoveCrosshairNodeFromDataStorage(m_DataStorage); } void QmitkRenderWindowWidget::InitializeGUI() { m_Layout = new QVBoxLayout(this); m_Layout->setMargin(0); setLayout(m_Layout); setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); setContentsMargins(0, 0, 0, 0); if (nullptr == m_DataStorage) { return; } mitk::RenderingManager::GetInstance()->SetDataStorage(m_DataStorage); // create render window for this render window widget m_RenderWindow = new QmitkRenderWindow(this, m_WidgetName, nullptr); m_RenderWindow->SetLayoutIndex(mitk::AnatomicalPlane::Sagittal); connect(m_RenderWindow, &QmitkRenderWindow::ResetGeometry, this, &QmitkRenderWindowWidget::OnResetGeometry); - auto sliceNavigationController = this->GetSliceNavigationController(); + auto* sliceNavigationController = this->GetSliceNavigationController(); sliceNavigationController->SetDefaultViewDirection(mitk::AnatomicalPlane::Sagittal); m_Layout->addWidget(m_RenderWindow); // set colors and corner annotation InitializeDecorations(); // use crosshair manager - m_CrosshairManager = mitk::CrosshairManager::New(m_DataStorage, m_RenderWindow->GetRenderer()); + m_CrosshairManager = mitk::CrosshairManager::New(m_RenderWindow->GetRenderer()); sliceNavigationController->SetCrosshairEvent.AddListener( mitk::MessageDelegate1( this, &QmitkRenderWindowWidget::SetCrosshairPosition)); // finally add observer, after all relevant objects have been created / initialized sliceNavigationController->ConnectGeometrySendEvent(this); sliceNavigationController->ConnectGeometrySliceEvent(this); mitk::TimeGeometry::ConstPointer timeGeometry = m_DataStorage->ComputeBoundingGeometry3D(m_DataStorage->GetAll()); mitk::RenderingManager::GetInstance()->InitializeView(m_RenderWindow->GetVtkRenderWindow(), timeGeometry); } void QmitkRenderWindowWidget::InitializeDecorations() { vtkRenderer* vtkRenderer = m_RenderWindow->GetRenderer()->GetVtkRenderer(); if (nullptr == vtkRenderer) { return; } // initialize background color gradients float black[3] = { 0.0f, 0.0f, 0.0f }; SetGradientBackgroundColors(black, black); // initialize annotation text and decoration color setFrameStyle(QFrame::Box | QFrame::Plain); m_CornerAnnotation = vtkSmartPointer::New(); m_CornerAnnotation->SetText(0, "Sagittal"); m_CornerAnnotation->SetMaximumFontSize(12); if (0 == vtkRenderer->HasViewProp(m_CornerAnnotation)) { vtkRenderer->AddViewProp(m_CornerAnnotation); } float white[3] = { 1.0f, 1.0f, 1.0f }; SetDecorationColor(mitk::Color(white)); } void QmitkRenderWindowWidget::SetCrosshairPosition(const mitk::Point3D& newPosition) { m_CrosshairManager->SetCrosshairPosition(newPosition); this->RequestUpdate(); } mitk::Point3D QmitkRenderWindowWidget::GetCrosshairPosition() const { return m_CrosshairManager->GetCrosshairPosition(); } void QmitkRenderWindowWidget::SetGeometry(const itk::EventObject& event) { if (!mitk::SliceNavigationController::GeometrySendEvent(nullptr, 0).CheckEvent(&event)) { return; } - auto sliceNavigationController = this->GetSliceNavigationController(); - const auto* inputTimeGeometry = sliceNavigationController->GetInputWorldTimeGeometry(); - m_CrosshairManager->ComputeOrientedTimeGeometries(inputTimeGeometry); + const auto* sliceNavigationController = this->GetSliceNavigationController(); + m_CrosshairManager->UpdateCrosshairPosition(sliceNavigationController); } void QmitkRenderWindowWidget::SetGeometrySlice(const itk::EventObject& event) { if (!mitk::SliceNavigationController::GeometrySliceEvent(nullptr, 0).CheckEvent(&event)) { return; } - auto sliceNavigationController = this->GetSliceNavigationController(); - m_CrosshairManager->UpdateSlice(sliceNavigationController); + const auto* sliceNavigationController = this->GetSliceNavigationController(); + m_CrosshairManager->UpdateCrosshairPosition(sliceNavigationController); } void QmitkRenderWindowWidget::OnResetGeometry() { - auto* baseRenderer = mitk::BaseRenderer::GetInstance(m_RenderWindow->GetRenderWindow()); + const auto* baseRenderer = mitk::BaseRenderer::GetInstance(m_RenderWindow->GetRenderWindow()); const auto* interactionReferenceGeometry = baseRenderer->GetInteractionReferenceGeometry(); this->ResetGeometry(interactionReferenceGeometry); m_RenderWindow->ShowOverlayMessage(false); } void QmitkRenderWindowWidget::ResetGeometry(const mitk::TimeGeometry* referenceGeometry) { if (nullptr == referenceGeometry) { return; } mitk::TimeStepType imageTimeStep = 0; // store the current position to set it again later, if the camera should not be reset mitk::Point3D currentPosition = this->GetCrosshairPosition(); // store the current time step to set it again later, if the camera should not be reset auto* renderingManager = mitk::RenderingManager::GetInstance(); const auto currentTimePoint = renderingManager->GetTimeNavigationController()->GetSelectedTimePoint(); if (referenceGeometry->IsValidTimePoint(currentTimePoint)) { imageTimeStep = referenceGeometry->TimePointToTimeStep(currentTimePoint); } - auto* baseRenderer = mitk::BaseRenderer::GetInstance(m_RenderWindow->renderWindow()); + const auto* baseRenderer = mitk::BaseRenderer::GetInstance(m_RenderWindow->renderWindow()); renderingManager->InitializeView(baseRenderer->GetRenderWindow(), referenceGeometry, false); // reset position and time step this->GetSliceNavigationController()->SelectSliceByPoint(currentPosition); renderingManager->GetTimeNavigationController()->GetTime()->SetPos(imageTimeStep); } diff --git a/Plugins/org.mitk.gui.qt.mxnmultiwidgeteditor/src/QmitkMxNMultiWidgetEditor.cpp b/Plugins/org.mitk.gui.qt.mxnmultiwidgeteditor/src/QmitkMxNMultiWidgetEditor.cpp index ff93413c38..bc536b0f6a 100644 --- a/Plugins/org.mitk.gui.qt.mxnmultiwidgeteditor/src/QmitkMxNMultiWidgetEditor.cpp +++ b/Plugins/org.mitk.gui.qt.mxnmultiwidgeteditor/src/QmitkMxNMultiWidgetEditor.cpp @@ -1,238 +1,238 @@ /*============================================================================ 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 "QmitkMxNMultiWidgetEditor.h" #include #include #include #include #include #include // mxn multi widget editor plugin #include "QmitkMultiWidgetDecorationManager.h" // mitk qt widgets module #include #include #include // qt #include const QString QmitkMxNMultiWidgetEditor::EDITOR_ID = "org.mitk.editors.mxnmultiwidget"; struct QmitkMxNMultiWidgetEditor::Impl final { Impl(); ~Impl() = default; QmitkInteractionSchemeToolBar* m_InteractionSchemeToolBar; QmitkMultiWidgetConfigurationToolBar* m_ConfigurationToolBar; }; QmitkMxNMultiWidgetEditor::Impl::Impl() : m_InteractionSchemeToolBar(nullptr) , m_ConfigurationToolBar(nullptr) { // nothing here } ////////////////////////////////////////////////////////////////////////// // QmitkMxNMultiWidgetEditor ////////////////////////////////////////////////////////////////////////// QmitkMxNMultiWidgetEditor::QmitkMxNMultiWidgetEditor() : QmitkAbstractMultiWidgetEditor() , m_Impl(std::make_unique()) { // nothing here } QmitkMxNMultiWidgetEditor::~QmitkMxNMultiWidgetEditor() { GetSite()->GetPage()->RemovePartListener(this); } berry::IPartListener::Events::Types QmitkMxNMultiWidgetEditor::GetPartEventTypes() const { return Events::CLOSED | Events::OPENED | Events::HIDDEN | Events::VISIBLE; } void QmitkMxNMultiWidgetEditor::PartClosed(const berry::IWorkbenchPartReference::Pointer& partRef) { if (partRef->GetId() == QmitkMxNMultiWidgetEditor::EDITOR_ID) { const auto& multiWidget = dynamic_cast(GetMultiWidget()); if (nullptr != multiWidget) { - multiWidget->RemovePlanesFromDataStorage(); + multiWidget->DisableCrosshair(); multiWidget->ActivateMenuWidget(false); } } } void QmitkMxNMultiWidgetEditor::PartOpened(const berry::IWorkbenchPartReference::Pointer& partRef) { if (partRef->GetId() == QmitkMxNMultiWidgetEditor::EDITOR_ID) { const auto& multiWidget = dynamic_cast(GetMultiWidget()); if (nullptr != multiWidget) { - multiWidget->AddPlanesToDataStorage(); + multiWidget->EnableCrosshair(); multiWidget->ActivateMenuWidget(true); } } } void QmitkMxNMultiWidgetEditor::PartHidden(const berry::IWorkbenchPartReference::Pointer& partRef) { if (partRef->GetId() == QmitkMxNMultiWidgetEditor::EDITOR_ID) { const auto& multiWidget = dynamic_cast(GetMultiWidget()); if (nullptr != multiWidget) { multiWidget->ActivateMenuWidget(false); } } } void QmitkMxNMultiWidgetEditor::PartVisible(const berry::IWorkbenchPartReference::Pointer& partRef) { if (partRef->GetId() == QmitkMxNMultiWidgetEditor::EDITOR_ID) { const auto& multiWidget = dynamic_cast(GetMultiWidget()); if (nullptr != multiWidget) { multiWidget->ActivateMenuWidget(true); } } } void QmitkMxNMultiWidgetEditor::OnLayoutSet(int row, int column) { const auto &multiWidget = dynamic_cast(GetMultiWidget()); if (nullptr != multiWidget) { QmitkAbstractMultiWidgetEditor::OnLayoutSet(row, column); - multiWidget->AddPlanesToDataStorage(); + multiWidget->EnableCrosshair(); } } void QmitkMxNMultiWidgetEditor::OnInteractionSchemeChanged(mitk::InteractionSchemeSwitcher::InteractionScheme scheme) { const auto &multiWidget = GetMultiWidget(); if (nullptr == multiWidget) { return; } if (mitk::InteractionSchemeSwitcher::PACSStandard == scheme) { m_Impl->m_InteractionSchemeToolBar->setVisible(true); } else { m_Impl->m_InteractionSchemeToolBar->setVisible(false); } QmitkAbstractMultiWidgetEditor::OnInteractionSchemeChanged(scheme); } ////////////////////////////////////////////////////////////////////////// // PRIVATE ////////////////////////////////////////////////////////////////////////// void QmitkMxNMultiWidgetEditor::SetFocus() { const auto& multiWidget = GetMultiWidget(); if (nullptr != multiWidget) { multiWidget->setFocus(); } } void QmitkMxNMultiWidgetEditor::CreateQtPartControl(QWidget* parent) { QHBoxLayout *layout = new QHBoxLayout(parent); layout->setContentsMargins(0, 0, 0, 0); auto* preferences = this->GetPreferences(); auto multiWidget = GetMultiWidget(); if (nullptr == multiWidget) { multiWidget = new QmitkMxNMultiWidget(parent, 0, nullptr); // create left toolbar: interaction scheme toolbar to switch how the render window navigation behaves in PACS mode if (nullptr == m_Impl->m_InteractionSchemeToolBar) { m_Impl->m_InteractionSchemeToolBar = new QmitkInteractionSchemeToolBar(parent); layout->addWidget(m_Impl->m_InteractionSchemeToolBar); } m_Impl->m_InteractionSchemeToolBar->SetInteractionEventHandler(multiWidget->GetInteractionEventHandler()); multiWidget->SetDataStorage(GetDataStorage()); multiWidget->InitializeMultiWidget(); SetMultiWidget(multiWidget); } layout->addWidget(multiWidget); // create right toolbar: configuration toolbar to change the render window widget layout if (nullptr == m_Impl->m_ConfigurationToolBar) { m_Impl->m_ConfigurationToolBar = new QmitkMultiWidgetConfigurationToolBar(multiWidget); layout->addWidget(m_Impl->m_ConfigurationToolBar); } connect(m_Impl->m_ConfigurationToolBar, &QmitkMultiWidgetConfigurationToolBar::LayoutSet, this, &QmitkMxNMultiWidgetEditor::OnLayoutSet); connect(m_Impl->m_ConfigurationToolBar, &QmitkMultiWidgetConfigurationToolBar::Synchronized, this, &QmitkMxNMultiWidgetEditor::OnSynchronize); connect(m_Impl->m_ConfigurationToolBar, &QmitkMultiWidgetConfigurationToolBar::InteractionSchemeChanged, this, &QmitkMxNMultiWidgetEditor::OnInteractionSchemeChanged); connect(m_Impl->m_ConfigurationToolBar, &QmitkMultiWidgetConfigurationToolBar::SaveLayout, static_cast(GetMultiWidget()), &QmitkMxNMultiWidget::SaveLayout, Qt::DirectConnection); connect(m_Impl->m_ConfigurationToolBar, &QmitkMultiWidgetConfigurationToolBar::LoadLayout, static_cast(GetMultiWidget()), &QmitkMxNMultiWidget::LoadLayout); GetSite()->GetPage()->AddPartListener(this); OnPreferencesChanged(preferences); } void QmitkMxNMultiWidgetEditor::OnPreferencesChanged(const mitk::IPreferences* preferences) { const auto& multiWidget = GetMultiWidget(); if (nullptr == multiWidget) { return; } // update decoration preferences //m_Impl->m_MultiWidgetDecorationManager->DecorationPreferencesChanged(preferences); int crosshairGapSize = preferences->GetInt("crosshair gap size", 32); multiWidget->SetCrosshairGap(crosshairGapSize); // zooming and panning preferences bool constrainedZooming = preferences->GetBool("Use constrained zooming and panning", true); mitk::RenderingManager::GetInstance()->SetConstrainedPanningZooming(constrainedZooming); bool PACSInteractionScheme = preferences->GetBool("PACS like mouse interaction", false); OnInteractionSchemeChanged(PACSInteractionScheme ? mitk::InteractionSchemeSwitcher::PACSStandard : mitk::InteractionSchemeSwitcher::MITKStandard); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); }