diff --git a/Modules/Core/include/mitkCoreObjectFactory.h b/Modules/Core/include/mitkCoreObjectFactory.h index 444a70d99b..c1a28c340c 100644 --- a/Modules/Core/include/mitkCoreObjectFactory.h +++ b/Modules/Core/include/mitkCoreObjectFactory.h @@ -1,143 +1,145 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef COREOBJECTFACTORY_H_INCLUDED #define COREOBJECTFACTORY_H_INCLUDED #include #include "mitkCoreObjectFactoryBase.h" #include "mitkFileWriterWithInformation.h" #include +#include +#include namespace mitk { class Event; class LegacyFileReaderService; class LegacyFileWriterService; class LegacyImageWriterService; class MITKCORE_EXPORT CoreObjectFactory : public CoreObjectFactoryBase { public: mitkClassMacro(CoreObjectFactory, CoreObjectFactoryBase) itkFactorylessNewMacro(CoreObjectFactory) Mapper::Pointer CreateMapper(mitk::DataNode *node, MapperSlotId slotId) override; void SetDefaultProperties(mitk::DataNode *node) override; virtual void MapEvent(const mitk::Event *event, const int eventID); virtual void RegisterExtraFactory(CoreObjectFactoryBase *factory); virtual void UnRegisterExtraFactory(CoreObjectFactoryBase *factory); static Pointer GetInstance(); ~CoreObjectFactory() override; /** * @brief This method gets the supported (open) file extensions as string. * * This string can then used by the Qt QFileDialog widget. * * @return The c-string that contains the file extensions * @deprecatedSince{2014_10} See mitk::FileReaderRegistry and QmitkIOUtil */ DEPRECATED(virtual const char *GetFileExtensions() override); /** * @brief get the defined (open) file extension map * * @return the defined (open) file extension map * @deprecatedSince{2014_10} See mitk::FileReaderRegistry and QmitkIOUtil */ DEPRECATED(virtual MultimapType GetFileExtensionsMap() override); /** * @brief This method gets the supported (save) file extensions as string. * * This string can then used by the Qt QFileDialog widget. * * @return The c-string that contains the (save) file extensions * @deprecatedSince{2014_10} See mitk::FileWriterRegistry and QmitkIOUtil */ DEPRECATED(virtual const char *GetSaveFileExtensions() override); /** * @brief get the defined (save) file extension map * * @return the defined (save) file extension map * @deprecatedSince{2014_10} See mitk::FileWriterRegistry and QmitkIOUtil */ MultimapType GetSaveFileExtensionsMap() override; /** * @deprecatedSince{2014_10} See mitk::FileWriterRegistry */ DEPRECATED(virtual FileWriterList GetFileWriters()); /** * @deprecatedSince{2014_10} See mitk::FileWriterRegistry and QmitkIOUtil */ DEPRECATED(std::string GetDescriptionForExtension(const std::string &extension)); protected: CoreObjectFactory(); /** * @brief Merge the input map into the fileExtensionsMap. Duplicate entries are removed * * @param fileExtensionsMap the existing map, it contains value pairs like * ("*.dcm", "DICOM files"),("*.dc3", "DICOM files"). * This map is extented/merged with the values from the input map. * @param inputMap the input map, it contains value pairs like ("*.dcm", * "DICOM files"),("*.dc3", "DICOM files") returned by the extra factories. * @deprecatedSince{2014_10} */ void MergeFileExtensions(MultimapType &fileExtensionsMap, MultimapType inputMap); /** * @brief initialize the file extension entries for open and save * @deprecatedSince{2014_10} */ void CreateFileExtensionsMap(); /** * @deprecatedSince{2014_10} */ DEPRECATED(void CreateSaveFileExtensions()); typedef std::set ExtraFactoriesContainer; ExtraFactoriesContainer m_ExtraFactories; FileWriterList m_FileWriters; std::string m_FileExtensions; MultimapType m_FileExtensionsMap; std::string m_SaveFileExtensions; MultimapType m_SaveFileExtensionsMap; private: void RegisterLegacyReaders(mitk::CoreObjectFactoryBase *factory); void RegisterLegacyWriters(mitk::CoreObjectFactoryBase *factory); void UnRegisterLegacyReaders(mitk::CoreObjectFactoryBase *factory); void UnRegisterLegacyWriters(mitk::CoreObjectFactoryBase *factory); std::map> m_LegacyReaders; std::map> m_LegacyWriters; }; } // namespace mitk #endif diff --git a/Modules/Core/include/mitkMapper.h b/Modules/Core/include/mitkMapper.h index 5c94bb7b16..1fdfedcfa1 100644 --- a/Modules/Core/include/mitkMapper.h +++ b/Modules/Core/include/mitkMapper.h @@ -1,218 +1,219 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef MAPPER_H_HEADER_INCLUDED_C1E6EA08 #define MAPPER_H_HEADER_INCLUDED_C1E6EA08 #include "mitkBaseRenderer.h" #include "mitkCommon.h" #include "mitkLevelWindow.h" #include "mitkLocalStorageHandler.h" #include "mitkVtkPropRenderer.h" #include #include #include // Just included to get VTK version #include class vtkWindow; class vtkProp; namespace mitk { class BaseRenderer; class BaseData; class DataNode; /** \brief Base class of all mappers, Vtk as well as OpenGL mappers * * By the help of mappers, the input data is transformed to tangible primitives, * such as surfaces, points, lines, etc. * This is the base class of all mappers, Vtk as well as OpenGL mappers. * Subclasses of mitk::Mapper control the creation of rendering primitives * that interface to the graphics library (e.g., OpenGL, vtk). * * \todo Should Mapper be a subclass of ImageSource? * \ingroup Mapper */ class MITKCORE_EXPORT Mapper : public itk::Object { public: mitkClassMacroItkParent(Mapper, itk::Object); /** \brief Set the DataNode containing the data to map */ itkSetObjectMacro(DataNode, DataNode); + us::ServiceRegistration RegisterAtServiceRegistry(std::string supportedDataTypeName, mitk::BaseRenderer::MapperSlotId supportedSlot, int priority = 100); /** \brief Get the DataNode containing the data to map. * Method only returns valid DataNode Pointer if the mapper belongs to a data node. * Otherwise, the returned DataNode Pointer might be invalid. */ virtual DataNode *GetDataNode() const; /**\brief Get the data to map * * Returns the mitk::BaseData object associated with this mapper. * \return the mitk::BaseData associated with this mapper. * \deprecatedSince{2013_03} Use GetDataNode()->GetData() instead to access the data */ DEPRECATED(BaseData *GetData() const); /** \brief Convenience access method for color properties (instances of * ColorProperty) * \return \a true property was found * \deprecatedSince{2013_03} Use GetDataNode()->GetColor(...) instead to get the color */ DEPRECATED(virtual bool GetColor(float rgb[3], BaseRenderer *renderer, const char *name = "color") const); /** \brief Convenience access method for visibility properties (instances * of BoolProperty) * \return \a true property was found * \sa IsVisible * \deprecatedSince{2013_03} Use GetDataNode()->GetVisibility(...) instead to get the visibility */ DEPRECATED(virtual bool GetVisibility(bool &visible, BaseRenderer *renderer, const char *name = "visible") const); /** \brief Convenience access method for opacity properties (instances of * FloatProperty) * \return \a true property was found * \deprecatedSince{2013_03} Use GetDataNode()->GetOpacity(...) instead to get the opacity */ DEPRECATED(virtual bool GetOpacity(float &opacity, BaseRenderer *renderer, const char *name = "opacity") const); /** \brief Convenience access method for color properties (instances of * LevelWindoProperty) * \return \a true property was found * \deprecatedSince{2013_03} Use GetDataNode->GetLevelWindow(...) instead to get the levelwindow */ DEPRECATED(virtual bool GetLevelWindow(LevelWindow &levelWindow, BaseRenderer *renderer, const char *name = "levelwindow") const); /** \brief Convenience access method for visibility properties (instances * of BoolProperty). Return value is the visibility. Default is * visible==true, i.e., true is returned even if the property (\a * propertyKey) is not found. * * Thus, the return value has a different meaning than in the * GetVisibility method! * \sa GetVisibility * \deprecatedSince{2013_03} Use GetDataNode()->GetVisibility(...) instead */ DEPRECATED(virtual bool IsVisible(BaseRenderer *renderer, const char *name = "visible") const); /** \brief Returns whether this is an vtk-based mapper * \deprecatedSince{2013_03} All mappers of superclass VTKMapper are vtk based, use a dynamic_cast instead */ virtual bool IsVtkBased() const { return true; } /** \brief Calls the time step of the input data for the specified renderer and checks * whether the time step is valid and calls method GenerateDataForRenderer() */ virtual void Update(BaseRenderer *renderer); /** \brief Responsible for calling the appropriate render functions. * To be implemented in sub-classes. */ virtual void MitkRender(mitk::BaseRenderer *renderer, mitk::VtkPropRenderer::RenderType type) = 0; /** * \brief Apply specific color and opacity properties read from the PropertyList. * Reimplemented in GLmapper (does not use the actor) and the VtkMapper class. * The function is called by the individual mapper (mostly in the ApplyProperties() or ApplyAllProperties() * method). */ virtual void ApplyColorAndOpacityProperties(mitk::BaseRenderer *renderer, vtkActor *actor = nullptr) = 0; /** \brief Set default values of properties used by this mapper * to \a node * * \param node The node for which the properties are set * \param overwrite overwrite existing properties (default: \a false) * \param renderer defines which property list of node is used * (default: \a nullptr, i.e. default property list) */ static void SetDefaultProperties(DataNode *node, BaseRenderer *renderer = nullptr, bool overwrite = false); /** \brief Returns the current time step as calculated from the renderer */ int GetTimestep() const { return m_TimeStep; } /** Returns true if this Mapper currently allows for Level-of-Detail rendering. * This reflects whether this Mapper currently invokes StartEvent, EndEvent, and * ProgressEvent on BaseRenderer. */ virtual bool IsLODEnabled(BaseRenderer * /*renderer*/) const { return false; } protected: /** \brief explicit constructor which disallows implicit conversions */ explicit Mapper(); /** \brief virtual destructor in order to derive from this class */ ~Mapper() override; /** \brief Generate the data needed for rendering (independent of a specific renderer) * \deprecatedSince{2013_03} Use GenerateDataForRenderer(BaseRenderer* renderer) instead. */ DEPRECATED(virtual void GenerateData()) {} /** \brief Generate the data needed for rendering into \a renderer */ virtual void GenerateDataForRenderer(BaseRenderer * /* renderer */) {} /** \brief Updates the time step, which is sometimes needed in subclasses */ virtual void CalculateTimeStep(BaseRenderer *renderer); /** \brief Reset the mapper (i.e., make sure that nothing is displayed) if no * valid data is present. In most cases the reimplemented function * disables the according actors (toggling visibility off) * * To be implemented in sub-classes. */ virtual void ResetMapper(BaseRenderer * /*renderer*/) {} mitk::DataNode *m_DataNode; private: /** \brief The current time step of the dataset to be rendered, * for use in subclasses. * The current timestep can be accessed via the GetTimestep() method. */ int m_TimeStep; /** \brief copy constructor */ Mapper(const Mapper &); /** \brief assignment operator */ Mapper &operator=(const Mapper &); public: /** \brief Base class for mapper specific rendering ressources. */ class MITKCORE_EXPORT BaseLocalStorage { public: BaseLocalStorage() = default; virtual ~BaseLocalStorage() = default; BaseLocalStorage(const BaseLocalStorage &) = delete; BaseLocalStorage & operator=(const BaseLocalStorage &) = delete; bool IsGenerateDataRequired(mitk::BaseRenderer *renderer, mitk::Mapper *mapper, mitk::DataNode *dataNode) const; inline void UpdateGenerateDataTime() { m_LastGenerateDataTime.Modified(); } inline itk::TimeStamp &GetLastGenerateDataTime() { return m_LastGenerateDataTime; } protected: /** \brief timestamp of last update of stored data */ itk::TimeStamp m_LastGenerateDataTime; }; }; } // namespace mitk #endif /* MAPPER_H_HEADER_INCLUDED_C1E6EA08 */ diff --git a/Modules/Core/src/Rendering/mitkImageVtkMapper2D.cpp b/Modules/Core/src/Rendering/mitkImageVtkMapper2D.cpp index bf8dae62d6..92c86c85d8 100644 --- a/Modules/Core/src/Rendering/mitkImageVtkMapper2D.cpp +++ b/Modules/Core/src/Rendering/mitkImageVtkMapper2D.cpp @@ -1,1124 +1,1125 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ // MITK #include #include #include #include #include #include #include #include #include #include #include //#include #include "mitkImageStatisticsHolder.h" #include "mitkPlaneClipping.h" #include // MITK Rendering #include "mitkImageVtkMapper2D.h" #include "vtkMitkLevelWindowFilter.h" #include "vtkMitkThickSlicesFilter.h" #include "vtkNeverTranslucentTexture.h" // VTK #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // ITK #include #include mitk::ImageVtkMapper2D::ImageVtkMapper2D() { + RegisterAtServiceRegistry(typeid(mitk::Image).name(), mitk::BaseRenderer::Standard2D); } mitk::ImageVtkMapper2D::~ImageVtkMapper2D() { // The 3D RW Mapper (PlaneGeometryDataVtkMapper3D) is listening to this event, // in order to delete the images from the 3D RW. this->InvokeEvent(itk::DeleteEvent()); } // set the two points defining the textured plane according to the dimension and spacing void mitk::ImageVtkMapper2D::GeneratePlane(mitk::BaseRenderer *renderer, double planeBounds[6]) { LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); float depth = this->CalculateLayerDepth(renderer); // Set the origin to (xMin; yMin; depth) of the plane. This is necessary for obtaining the correct // plane size in crosshair rotation and swivel mode. localStorage->m_Plane->SetOrigin(planeBounds[0], planeBounds[2], depth); // These two points define the axes of the plane in combination with the origin. // Point 1 is the x-axis and point 2 the y-axis. // Each plane is transformed according to the view (axial, coronal and saggital) afterwards. localStorage->m_Plane->SetPoint1(planeBounds[1], planeBounds[2], depth); // P1: (xMax, yMin, depth) localStorage->m_Plane->SetPoint2(planeBounds[0], planeBounds[3], depth); // P2: (xMin, yMax, depth) } float mitk::ImageVtkMapper2D::CalculateLayerDepth(mitk::BaseRenderer *renderer) { // get the clipping range to check how deep into z direction we can render images double maxRange = renderer->GetVtkRenderer()->GetActiveCamera()->GetClippingRange()[1]; // Due to a VTK bug, we cannot use the whole clipping range. /100 is empirically determined float depth = -maxRange * 0.01; // divide by 100 int layer = 0; GetDataNode()->GetIntProperty("layer", layer, renderer); // add the layer property for each image to render images with a higher layer on top of the others depth += layer * 10; //*10: keep some room for each image (e.g. for ODFs in between) if (depth > 0.0f) { depth = 0.0f; MITK_WARN << "Layer value exceeds clipping range. Set to minimum instead."; } return depth; } const mitk::Image *mitk::ImageVtkMapper2D::GetInput(void) { return static_cast(GetDataNode()->GetData()); } vtkProp *mitk::ImageVtkMapper2D::GetVtkProp(mitk::BaseRenderer *renderer) { // return the actor corresponding to the renderer return m_LSH.GetLocalStorage(renderer)->m_Actors; } void mitk::ImageVtkMapper2D::GenerateDataForRenderer(mitk::BaseRenderer *renderer) { LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); auto *image = const_cast(this->GetInput()); mitk::DataNode *datanode = this->GetDataNode(); if (nullptr == image || !image->IsInitialized()) { return; } // check if there is a valid worldGeometry const PlaneGeometry *worldGeometry = renderer->GetCurrentWorldPlaneGeometry(); if (nullptr == worldGeometry || !worldGeometry->IsValid() || !worldGeometry->HasReferenceGeometry()) { return; } image->Update(); // early out if there is no intersection of the current rendering geometry // and the geometry of the image that is to be rendered. if (!RenderingGeometryIntersectsImage(worldGeometry, image->GetSlicedGeometry())) { // set image to nullptr, to clear the texture in 3D, because // the latest image is used there if the plane is out of the geometry // see bug-13275 localStorage->m_ReslicedImage = nullptr; localStorage->m_Mapper->SetInputData(localStorage->m_EmptyPolyData); return; } // set main input for ExtractSliceFilter localStorage->m_Reslicer->SetInput(image); localStorage->m_Reslicer->SetWorldGeometry(worldGeometry); localStorage->m_Reslicer->SetTimeStep(this->GetTimestep()); // set the transformation of the image to adapt reslice axis localStorage->m_Reslicer->SetResliceTransformByGeometry( image->GetTimeGeometry()->GetGeometryForTimeStep(this->GetTimestep())); // is the geometry of the slice based on the input image or the worldgeometry? bool inPlaneResampleExtentByGeometry = false; datanode->GetBoolProperty("in plane resample extent by geometry", inPlaneResampleExtentByGeometry, renderer); localStorage->m_Reslicer->SetInPlaneResampleExtentByGeometry(inPlaneResampleExtentByGeometry); // Initialize the interpolation mode for resampling; switch to nearest // neighbor if the input image is too small. if ((image->GetDimension() >= 3) && (image->GetDimension(2) > 1)) { VtkResliceInterpolationProperty *resliceInterpolationProperty; datanode->GetProperty(resliceInterpolationProperty, "reslice interpolation", renderer); int interpolationMode = VTK_RESLICE_NEAREST; if (resliceInterpolationProperty != nullptr) { interpolationMode = resliceInterpolationProperty->GetInterpolation(); } switch (interpolationMode) { case VTK_RESLICE_NEAREST: localStorage->m_Reslicer->SetInterpolationMode(ExtractSliceFilter::RESLICE_NEAREST); break; case VTK_RESLICE_LINEAR: localStorage->m_Reslicer->SetInterpolationMode(ExtractSliceFilter::RESLICE_LINEAR); break; case VTK_RESLICE_CUBIC: localStorage->m_Reslicer->SetInterpolationMode(ExtractSliceFilter::RESLICE_CUBIC); break; } } else { localStorage->m_Reslicer->SetInterpolationMode(ExtractSliceFilter::RESLICE_NEAREST); } // set the vtk output property to true, makes sure that no unneeded mitk image convertion // is done. localStorage->m_Reslicer->SetVtkOutputRequest(true); // Thickslicing int thickSlicesMode = 0; int thickSlicesNum = 1; // Thick slices parameters if (image->GetPixelType().GetNumberOfComponents() == 1) // for now only single component are allowed { DataNode *dn = renderer->GetCurrentWorldPlaneGeometryNode(); if (dn) { ResliceMethodProperty *resliceMethodEnumProperty = nullptr; if (dn->GetProperty(resliceMethodEnumProperty, "reslice.thickslices", renderer) && resliceMethodEnumProperty) thickSlicesMode = resliceMethodEnumProperty->GetValueAsId(); IntProperty *intProperty = nullptr; if (dn->GetProperty(intProperty, "reslice.thickslices.num", renderer) && intProperty) { thickSlicesNum = intProperty->GetValue(); if (thickSlicesNum < 1) thickSlicesNum = 1; } } else { MITK_WARN << "no associated widget plane data tree node found"; } } const auto *planeGeometry = dynamic_cast(worldGeometry); if (thickSlicesMode > 0) { double dataZSpacing = 1.0; Vector3D normInIndex, normal; const auto *abstractGeometry = dynamic_cast(worldGeometry); if (abstractGeometry != nullptr) normal = abstractGeometry->GetPlane()->GetNormal(); else { if (planeGeometry != nullptr) { normal = planeGeometry->GetNormal(); } else return; // no fitting geometry set } normal.Normalize(); image->GetTimeGeometry()->GetGeometryForTimeStep(this->GetTimestep())->WorldToIndex(normal, normInIndex); dataZSpacing = 1.0 / normInIndex.GetNorm(); localStorage->m_Reslicer->SetOutputDimensionality(3); localStorage->m_Reslicer->SetOutputSpacingZDirection(dataZSpacing); localStorage->m_Reslicer->SetOutputExtentZDirection(-thickSlicesNum, 0 + thickSlicesNum); // Do the reslicing. Modified() is called to make sure that the reslicer is // executed even though the input geometry information did not change; this // is necessary when the input /em data, but not the /em geometry changes. localStorage->m_TSFilter->SetThickSliceMode(thickSlicesMode - 1); localStorage->m_TSFilter->SetInputData(localStorage->m_Reslicer->GetVtkOutput()); // vtkFilter=>mitkFilter=>vtkFilter update mechanism will fail without calling manually localStorage->m_Reslicer->Modified(); localStorage->m_Reslicer->Update(); localStorage->m_TSFilter->Modified(); localStorage->m_TSFilter->Update(); localStorage->m_ReslicedImage = localStorage->m_TSFilter->GetOutput(); } else { // this is needed when thick mode was enable bevore. These variable have to be reset to default values localStorage->m_Reslicer->SetOutputDimensionality(2); localStorage->m_Reslicer->SetOutputSpacingZDirection(1.0); localStorage->m_Reslicer->SetOutputExtentZDirection(0, 0); localStorage->m_Reslicer->Modified(); // start the pipeline with updating the largest possible, needed if the geometry of the input has changed localStorage->m_Reslicer->UpdateLargestPossibleRegion(); localStorage->m_ReslicedImage = localStorage->m_Reslicer->GetVtkOutput(); } // Bounds information for reslicing (only reuqired if reference geometry // is present) // this used for generating a vtkPLaneSource with the right size double sliceBounds[6]; for (auto &sliceBound : sliceBounds) { sliceBound = 0.0; } localStorage->m_Reslicer->GetClippedPlaneBounds(sliceBounds); // get the spacing of the slice localStorage->m_mmPerPixel = localStorage->m_Reslicer->GetOutputSpacing(); // calculate minimum bounding rect of IMAGE in texture { double textureClippingBounds[6]; for (auto &textureClippingBound : textureClippingBounds) { textureClippingBound = 0.0; } // Calculate the actual bounds of the transformed plane clipped by the // dataset bounding box; this is required for drawing the texture at the // correct position during 3D mapping. mitk::PlaneClipping::CalculateClippedPlaneBounds(image->GetGeometry(), planeGeometry, textureClippingBounds); textureClippingBounds[0] = static_cast(textureClippingBounds[0] / localStorage->m_mmPerPixel[0] + 0.5); textureClippingBounds[1] = static_cast(textureClippingBounds[1] / localStorage->m_mmPerPixel[0] + 0.5); textureClippingBounds[2] = static_cast(textureClippingBounds[2] / localStorage->m_mmPerPixel[1] + 0.5); textureClippingBounds[3] = static_cast(textureClippingBounds[3] / localStorage->m_mmPerPixel[1] + 0.5); // clipping bounds for cutting the image localStorage->m_LevelWindowFilter->SetClippingBounds(textureClippingBounds); } // get the number of scalar components to distinguish between different image types int numberOfComponents = localStorage->m_ReslicedImage->GetNumberOfScalarComponents(); // get the binary property bool binary = false; bool binaryOutline = false; datanode->GetBoolProperty("binary", binary, renderer); if (binary) // binary image { datanode->GetBoolProperty("outline binary", binaryOutline, renderer); if (binaryOutline) // contour rendering { // get pixel type of vtk image itk::ImageIOBase::IOComponentType componentType = static_cast(image->GetPixelType().GetComponentType()); switch (componentType) { case itk::ImageIOBase::UCHAR: // generate contours/outlines localStorage->m_OutlinePolyData = CreateOutlinePolyData(renderer); break; case itk::ImageIOBase::USHORT: // generate contours/outlines localStorage->m_OutlinePolyData = CreateOutlinePolyData(renderer); break; default: binaryOutline = false; this->ApplyLookuptable(renderer); MITK_WARN << "Type of all binary images should be unsigned char or unsigned short. Outline does not work on other pixel types!"; } if (binaryOutline) // binary outline is still true --> add outline { float binaryOutlineWidth = 1.0; if (datanode->GetFloatProperty("outline width", binaryOutlineWidth, renderer)) { if (localStorage->m_Actors->GetNumberOfPaths() > 1) { float binaryOutlineShadowWidth = 1.5; datanode->GetFloatProperty("outline shadow width", binaryOutlineShadowWidth, renderer); dynamic_cast(localStorage->m_Actors->GetParts()->GetItemAsObject(0)) ->GetProperty() ->SetLineWidth(binaryOutlineWidth * binaryOutlineShadowWidth); } localStorage->m_Actor->GetProperty()->SetLineWidth(binaryOutlineWidth); } } } else // standard binary image { if (numberOfComponents != 1) { MITK_ERROR << "Rendering Error: Binary Images with more then 1 component are not supported!"; } } } this->ApplyOpacity(renderer); this->ApplyRenderingMode(renderer); // do not use a VTK lookup table (we do that ourselves in m_LevelWindowFilter) localStorage->m_Texture->SetColorModeToDirectScalars(); int displayedComponent = 0; if (datanode->GetIntProperty("Image.Displayed Component", displayedComponent, renderer) && numberOfComponents > 1) { localStorage->m_VectorComponentExtractor->SetComponents(displayedComponent); localStorage->m_VectorComponentExtractor->SetInputData(localStorage->m_ReslicedImage); localStorage->m_LevelWindowFilter->SetInputConnection(localStorage->m_VectorComponentExtractor->GetOutputPort(0)); } else { // connect the input with the levelwindow filter localStorage->m_LevelWindowFilter->SetInputData(localStorage->m_ReslicedImage); } // check for texture interpolation property bool textureInterpolation = false; GetDataNode()->GetBoolProperty("texture interpolation", textureInterpolation, renderer); // set the interpolation modus according to the property localStorage->m_Texture->SetInterpolate(textureInterpolation); // connect the texture with the output of the levelwindow filter localStorage->m_Texture->SetInputConnection(localStorage->m_LevelWindowFilter->GetOutputPort()); this->TransformActor(renderer); auto *contourShadowActor = dynamic_cast(localStorage->m_Actors->GetParts()->GetItemAsObject(0)); if (binary && binaryOutline) // connect the mapper with the polyData which contains the lines { // We need the contour for the binary outline property as actor localStorage->m_Mapper->SetInputData(localStorage->m_OutlinePolyData); localStorage->m_Actor->SetTexture(nullptr); // no texture for contours bool binaryOutlineShadow = false; datanode->GetBoolProperty("outline binary shadow", binaryOutlineShadow, renderer); if (binaryOutlineShadow) { contourShadowActor->SetVisibility(true); } else { contourShadowActor->SetVisibility(false); } } else { // Connect the mapper with the input texture. This is the standard case. // setup the textured plane this->GeneratePlane(renderer, sliceBounds); // set the plane as input for the mapper localStorage->m_Mapper->SetInputConnection(localStorage->m_Plane->GetOutputPort()); // set the texture for the actor localStorage->m_Actor->SetTexture(localStorage->m_Texture); contourShadowActor->SetVisibility(false); } // We have been modified => save this for next Update() localStorage->m_LastUpdateTime.Modified(); } void mitk::ImageVtkMapper2D::ApplyLevelWindow(mitk::BaseRenderer *renderer) { LocalStorage *localStorage = this->GetLocalStorage(renderer); LevelWindow levelWindow; this->GetDataNode()->GetLevelWindow(levelWindow, renderer, "levelwindow"); localStorage->m_LevelWindowFilter->GetLookupTable()->SetRange(levelWindow.GetLowerWindowBound(), levelWindow.GetUpperWindowBound()); mitk::LevelWindow opacLevelWindow; if (this->GetDataNode()->GetLevelWindow(opacLevelWindow, renderer, "opaclevelwindow")) { // pass the opaque level window to the filter localStorage->m_LevelWindowFilter->SetMinOpacity(opacLevelWindow.GetLowerWindowBound()); localStorage->m_LevelWindowFilter->SetMaxOpacity(opacLevelWindow.GetUpperWindowBound()); } else { // no opaque level window localStorage->m_LevelWindowFilter->SetMinOpacity(0.0); localStorage->m_LevelWindowFilter->SetMaxOpacity(255.0); } } void mitk::ImageVtkMapper2D::ApplyColor(mitk::BaseRenderer *renderer) { LocalStorage *localStorage = this->GetLocalStorage(renderer); float rgb[3] = {1.0f, 1.0f, 1.0f}; // check for color prop and use it for rendering if it exists // binary image hovering & binary image selection bool hover = false; bool selected = false; bool binary = false; GetDataNode()->GetBoolProperty("binaryimage.ishovering", hover, renderer); GetDataNode()->GetBoolProperty("selected", selected, renderer); GetDataNode()->GetBoolProperty("binary", binary, renderer); if (binary && hover && !selected) { mitk::ColorProperty::Pointer colorprop = dynamic_cast(GetDataNode()->GetProperty("binaryimage.hoveringcolor", renderer)); if (colorprop.IsNotNull()) { memcpy(rgb, colorprop->GetColor().GetDataPointer(), 3 * sizeof(float)); } else { GetDataNode()->GetColor(rgb, renderer, "color"); } } if (binary && selected) { mitk::ColorProperty::Pointer colorprop = dynamic_cast(GetDataNode()->GetProperty("binaryimage.selectedcolor", renderer)); if (colorprop.IsNotNull()) { memcpy(rgb, colorprop->GetColor().GetDataPointer(), 3 * sizeof(float)); } else { GetDataNode()->GetColor(rgb, renderer, "color"); } } if (!binary || (!hover && !selected)) { GetDataNode()->GetColor(rgb, renderer, "color"); } double rgbConv[3] = {(double)rgb[0], (double)rgb[1], (double)rgb[2]}; // conversion to double for VTK dynamic_cast(localStorage->m_Actors->GetParts()->GetItemAsObject(0))->GetProperty()->SetColor(rgbConv); localStorage->m_Actor->GetProperty()->SetColor(rgbConv); if (localStorage->m_Actors->GetParts()->GetNumberOfItems() > 1) { float rgb[3] = {1.0f, 1.0f, 1.0f}; mitk::ColorProperty::Pointer colorprop = dynamic_cast(GetDataNode()->GetProperty("outline binary shadow color", renderer)); if (colorprop.IsNotNull()) { memcpy(rgb, colorprop->GetColor().GetDataPointer(), 3 * sizeof(float)); } double rgbConv[3] = {(double)rgb[0], (double)rgb[1], (double)rgb[2]}; // conversion to double for VTK dynamic_cast(localStorage->m_Actors->GetParts()->GetItemAsObject(0))->GetProperty()->SetColor(rgbConv); } } void mitk::ImageVtkMapper2D::ApplyOpacity(mitk::BaseRenderer *renderer) { LocalStorage *localStorage = this->GetLocalStorage(renderer); float opacity = 1.0f; // check for opacity prop and use it for rendering if it exists GetDataNode()->GetOpacity(opacity, renderer, "opacity"); // set the opacity according to the properties localStorage->m_Actor->GetProperty()->SetOpacity(opacity); if (localStorage->m_Actors->GetParts()->GetNumberOfItems() > 1) { dynamic_cast(localStorage->m_Actors->GetParts()->GetItemAsObject(0)) ->GetProperty() ->SetOpacity(opacity); } } void mitk::ImageVtkMapper2D::ApplyRenderingMode(mitk::BaseRenderer *renderer) { LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); bool binary = false; this->GetDataNode()->GetBoolProperty("binary", binary, renderer); if (binary) // is it a binary image? { // for binary images, we always use our default LuT and map every value to (0,1) // the opacity of 0 will always be 0.0. We never a apply a LuT/TfF nor a level window. localStorage->m_LevelWindowFilter->SetLookupTable(localStorage->m_BinaryLookupTable); } else { // all other image types can make use of the rendering mode int renderingMode = mitk::RenderingModeProperty::LOOKUPTABLE_LEVELWINDOW_COLOR; mitk::RenderingModeProperty::Pointer mode = dynamic_cast(this->GetDataNode()->GetProperty("Image Rendering.Mode", renderer)); if (mode.IsNotNull()) { renderingMode = mode->GetRenderingMode(); } switch (renderingMode) { case mitk::RenderingModeProperty::LOOKUPTABLE_LEVELWINDOW_COLOR: MITK_DEBUG << "'Image Rendering.Mode' = LevelWindow_LookupTable_Color"; this->ApplyLookuptable(renderer); this->ApplyLevelWindow(renderer); break; case mitk::RenderingModeProperty::COLORTRANSFERFUNCTION_LEVELWINDOW_COLOR: MITK_DEBUG << "'Image Rendering.Mode' = LevelWindow_ColorTransferFunction_Color"; this->ApplyColorTransferFunction(renderer); this->ApplyLevelWindow(renderer); break; case mitk::RenderingModeProperty::LOOKUPTABLE_COLOR: MITK_DEBUG << "'Image Rendering.Mode' = LookupTable_Color"; this->ApplyLookuptable(renderer); break; case mitk::RenderingModeProperty::COLORTRANSFERFUNCTION_COLOR: MITK_DEBUG << "'Image Rendering.Mode' = ColorTransferFunction_Color"; this->ApplyColorTransferFunction(renderer); break; default: MITK_ERROR << "No valid 'Image Rendering.Mode' set. Using LOOKUPTABLE_LEVELWINDOW_COLOR instead."; this->ApplyLookuptable(renderer); this->ApplyLevelWindow(renderer); break; } } // we apply color for all images (including binaries). this->ApplyColor(renderer); } void mitk::ImageVtkMapper2D::ApplyLookuptable(mitk::BaseRenderer *renderer) { LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); vtkLookupTable *usedLookupTable = localStorage->m_ColorLookupTable; // If lookup table or transferfunction use is requested... mitk::LookupTableProperty::Pointer lookupTableProp = dynamic_cast(this->GetDataNode()->GetProperty("LookupTable")); if (lookupTableProp.IsNotNull()) // is a lookuptable set? { usedLookupTable = lookupTableProp->GetLookupTable()->GetVtkLookupTable(); } else { //"Image Rendering.Mode was set to use a lookup table but there is no property 'LookupTable'. // A default (rainbow) lookup table will be used. // Here have to do nothing. Warning for the user has been removed, due to unwanted console output // in every interation of the rendering. } localStorage->m_LevelWindowFilter->SetLookupTable(usedLookupTable); } void mitk::ImageVtkMapper2D::ApplyColorTransferFunction(mitk::BaseRenderer *renderer) { mitk::TransferFunctionProperty::Pointer transferFunctionProp = dynamic_cast( this->GetDataNode()->GetProperty("Image Rendering.Transfer Function", renderer)); if (transferFunctionProp.IsNull()) { MITK_ERROR << "'Image Rendering.Mode'' was set to use a color transfer function but there is no property 'Image " "Rendering.Transfer Function'. Nothing will be done."; return; } LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); // pass the transfer function to our level window filter localStorage->m_LevelWindowFilter->SetLookupTable(transferFunctionProp->GetValue()->GetColorTransferFunction()); localStorage->m_LevelWindowFilter->SetOpacityPiecewiseFunction( transferFunctionProp->GetValue()->GetScalarOpacityFunction()); } void mitk::ImageVtkMapper2D::Update(mitk::BaseRenderer *renderer) { bool visible = true; GetDataNode()->GetVisibility(visible, renderer, "visible"); if (!visible) { return; } auto *data = const_cast(this->GetInput()); if (data == nullptr) { return; } // Calculate time step of the input data for the specified renderer (integer value) this->CalculateTimeStep(renderer); // Check if time step is valid const TimeGeometry *dataTimeGeometry = data->GetTimeGeometry(); if ((dataTimeGeometry == nullptr) || (dataTimeGeometry->CountTimeSteps() == 0) || (!dataTimeGeometry->IsValidTimeStep(this->GetTimestep()))) { return; } const DataNode *node = this->GetDataNode(); data->UpdateOutputInformation(); LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); // check if something important has changed and we need to rerender if ((localStorage->m_LastUpdateTime < node->GetMTime()) || (localStorage->m_LastUpdateTime < data->GetPipelineMTime()) || (localStorage->m_LastUpdateTime < renderer->GetCurrentWorldPlaneGeometryUpdateTime()) || (localStorage->m_LastUpdateTime < renderer->GetCurrentWorldPlaneGeometry()->GetMTime()) || (localStorage->m_LastUpdateTime < node->GetPropertyList()->GetMTime()) || (localStorage->m_LastUpdateTime < node->GetPropertyList(renderer)->GetMTime()) || (localStorage->m_LastUpdateTime < data->GetPropertyList()->GetMTime())) { this->GenerateDataForRenderer(renderer); } // since we have checked that nothing important has changed, we can set // m_LastUpdateTime to the current time localStorage->m_LastUpdateTime.Modified(); } void mitk::ImageVtkMapper2D::SetDefaultProperties(mitk::DataNode *node, mitk::BaseRenderer *renderer, bool overwrite) { mitk::Image::Pointer image = dynamic_cast(node->GetData()); // Properties common for both images and segmentations node->AddProperty("depthOffset", mitk::FloatProperty::New(0.0), renderer, overwrite); node->AddProperty("outline binary", mitk::BoolProperty::New(false), renderer, overwrite); node->AddProperty("outline width", mitk::FloatProperty::New(1.0), renderer, overwrite); node->AddProperty("outline binary shadow", mitk::BoolProperty::New(false), renderer, overwrite); node->AddProperty("outline binary shadow color", ColorProperty::New(0.0, 0.0, 0.0), renderer, overwrite); node->AddProperty("outline shadow width", mitk::FloatProperty::New(1.5), renderer, overwrite); if (image->IsRotated()) node->AddProperty("reslice interpolation", mitk::VtkResliceInterpolationProperty::New(VTK_RESLICE_CUBIC)); else node->AddProperty("reslice interpolation", mitk::VtkResliceInterpolationProperty::New()); node->AddProperty("texture interpolation", mitk::BoolProperty::New(false)); node->AddProperty("in plane resample extent by geometry", mitk::BoolProperty::New(false)); node->AddProperty("bounding box", mitk::BoolProperty::New(false)); mitk::RenderingModeProperty::Pointer renderingModeProperty = mitk::RenderingModeProperty::New(); node->AddProperty("Image Rendering.Mode", renderingModeProperty); // Set default grayscale look-up table mitk::LookupTable::Pointer mitkLut = mitk::LookupTable::New(); mitkLut->SetType(mitk::LookupTable::GRAYSCALE); mitk::LookupTableProperty::Pointer mitkLutProp = mitk::LookupTableProperty::New(); mitkLutProp->SetLookupTable(mitkLut); node->SetProperty("LookupTable", mitkLutProp); std::string photometricInterpretation; // DICOM tag telling us how pixel values should be displayed if (node->GetStringProperty("dicom.pixel.PhotometricInterpretation", photometricInterpretation)) { // modality provided by DICOM or other reader if (photometricInterpretation.find("MONOCHROME1") != std::string::npos) // meaning: display MINIMUM pixels as WHITE { // Set inverse grayscale look-up table mitkLut->SetType(mitk::LookupTable::INVERSE_GRAYSCALE); mitkLutProp->SetLookupTable(mitkLut); node->SetProperty("LookupTable", mitkLutProp); renderingModeProperty->SetValue(mitk::RenderingModeProperty::LOOKUPTABLE_LEVELWINDOW_COLOR); // USE lookuptable } // Otherwise do nothing - the default grayscale look-up table has already been set } bool isBinaryImage(false); if (!node->GetBoolProperty("binary", isBinaryImage) && image->GetPixelType().GetNumberOfComponents() == 1) { // ok, property is not set, use heuristic to determine if this // is a binary image mitk::Image::Pointer centralSliceImage; ScalarType minValue = 0.0; ScalarType maxValue = 0.0; ScalarType min2ndValue = 0.0; ScalarType max2ndValue = 0.0; mitk::ImageSliceSelector::Pointer sliceSelector = mitk::ImageSliceSelector::New(); sliceSelector->SetInput(image); sliceSelector->SetSliceNr(image->GetDimension(2) / 2); sliceSelector->SetTimeNr(image->GetDimension(3) / 2); sliceSelector->SetChannelNr(image->GetDimension(4) / 2); sliceSelector->Update(); centralSliceImage = sliceSelector->GetOutput(); if (centralSliceImage.IsNotNull() && centralSliceImage->IsInitialized()) { minValue = centralSliceImage->GetStatistics()->GetScalarValueMin(); maxValue = centralSliceImage->GetStatistics()->GetScalarValueMax(); min2ndValue = centralSliceImage->GetStatistics()->GetScalarValue2ndMin(); max2ndValue = centralSliceImage->GetStatistics()->GetScalarValue2ndMax(); } if ((maxValue == min2ndValue && minValue == max2ndValue) || minValue == maxValue) { // centralSlice is strange, lets look at all data minValue = image->GetStatistics()->GetScalarValueMin(); maxValue = image->GetStatistics()->GetScalarValueMaxNoRecompute(); min2ndValue = image->GetStatistics()->GetScalarValue2ndMinNoRecompute(); max2ndValue = image->GetStatistics()->GetScalarValue2ndMaxNoRecompute(); } isBinaryImage = (maxValue == min2ndValue && minValue == max2ndValue); } std::string className = image->GetNameOfClass(); if (className != "TensorImage" && className != "OdfImage" && className != "ShImage") { PixelType pixelType = image->GetPixelType(); size_t numComponents = pixelType.GetNumberOfComponents(); if ((pixelType.GetPixelType() == itk::ImageIOBase::VECTOR && numComponents > 1) || numComponents == 2 || numComponents > 4) { node->AddProperty("Image.Displayed Component", mitk::IntProperty::New(0), renderer, overwrite); } } // some more properties specific for a binary... if (isBinaryImage) { node->AddProperty("opacity", mitk::FloatProperty::New(0.3f), renderer, overwrite); node->AddProperty("color", ColorProperty::New(1.0, 0.0, 0.0), renderer, overwrite); node->AddProperty("binaryimage.selectedcolor", ColorProperty::New(1.0, 0.0, 0.0), renderer, overwrite); node->AddProperty("binaryimage.selectedannotationcolor", ColorProperty::New(1.0, 0.0, 0.0), renderer, overwrite); node->AddProperty("binaryimage.hoveringcolor", ColorProperty::New(1.0, 0.0, 0.0), renderer, overwrite); node->AddProperty("binaryimage.hoveringannotationcolor", ColorProperty::New(1.0, 0.0, 0.0), renderer, overwrite); node->AddProperty("binary", mitk::BoolProperty::New(true), renderer, overwrite); node->AddProperty("layer", mitk::IntProperty::New(10), renderer, overwrite); } else //...or image type object { node->AddProperty("opacity", mitk::FloatProperty::New(1.0f), renderer, overwrite); node->AddProperty("color", ColorProperty::New(1.0, 1.0, 1.0), renderer, overwrite); node->AddProperty("binary", mitk::BoolProperty::New(false), renderer, overwrite); node->AddProperty("layer", mitk::IntProperty::New(0), renderer, overwrite); } if (image.IsNotNull() && image->IsInitialized()) { if ((overwrite) || (node->GetProperty("levelwindow", renderer) == nullptr)) { /* initialize level/window from DICOM tags */ std::string sLevel = ""; std::string sWindow = ""; if (GetBackwardsCompatibleDICOMProperty( 0x0028, 0x1050, "dicom.voilut.WindowCenter", image->GetPropertyList(), sLevel) && GetBackwardsCompatibleDICOMProperty( 0x0028, 0x1051, "dicom.voilut.WindowWidth", image->GetPropertyList(), sWindow)) { float level = atof(sLevel.c_str()); float window = atof(sWindow.c_str()); mitk::LevelWindow contrast; std::string sSmallestPixelValueInSeries; std::string sLargestPixelValueInSeries; if (GetBackwardsCompatibleDICOMProperty(0x0028, 0x0108, "dicom.series.SmallestPixelValueInSeries", image->GetPropertyList(), sSmallestPixelValueInSeries) && GetBackwardsCompatibleDICOMProperty(0x0028, 0x0109, "dicom.series.LargestPixelValueInSeries", image->GetPropertyList(), sLargestPixelValueInSeries)) { float smallestPixelValueInSeries = atof(sSmallestPixelValueInSeries.c_str()); float largestPixelValueInSeries = atof(sLargestPixelValueInSeries.c_str()); contrast.SetRangeMinMax(smallestPixelValueInSeries - 1, largestPixelValueInSeries + 1); // why not a little buffer? // might remedy some l/w widget challenges } else { contrast.SetAuto(static_cast(node->GetData()), false, true); // we need this as a fallback } contrast.SetLevelWindow(level, window, true); node->SetProperty("levelwindow", LevelWindowProperty::New(contrast), renderer); } } if (((overwrite) || (node->GetProperty("opaclevelwindow", renderer) == nullptr)) && (image->GetPixelType().GetPixelType() == itk::ImageIOBase::RGBA) && (image->GetPixelType().GetComponentType() == itk::ImageIOBase::UCHAR)) { mitk::LevelWindow opaclevwin; opaclevwin.SetRangeMinMax(0, 255); opaclevwin.SetWindowBounds(0, 255); mitk::LevelWindowProperty::Pointer prop = mitk::LevelWindowProperty::New(opaclevwin); node->SetProperty("opaclevelwindow", prop, renderer); } } Superclass::SetDefaultProperties(node, renderer, overwrite); } mitk::ImageVtkMapper2D::LocalStorage *mitk::ImageVtkMapper2D::GetLocalStorage(mitk::BaseRenderer *renderer) { return m_LSH.GetLocalStorage(renderer); } template vtkSmartPointer mitk::ImageVtkMapper2D::CreateOutlinePolyData(mitk::BaseRenderer *renderer) { LocalStorage *localStorage = this->GetLocalStorage(renderer); // get the min and max index values of each direction int *extent = localStorage->m_ReslicedImage->GetExtent(); int xMin = extent[0]; int xMax = extent[1]; int yMin = extent[2]; int yMax = extent[3]; int *dims = localStorage->m_ReslicedImage->GetDimensions(); // dimensions of the image int line = dims[0]; // how many pixels per line? int x = xMin; // pixel index x int y = yMin; // pixel index y // get the depth for each contour float depth = CalculateLayerDepth(renderer); vtkSmartPointer points = vtkSmartPointer::New(); // the points to draw vtkSmartPointer lines = vtkSmartPointer::New(); // the lines to connect the points // We take the pointer to the first pixel of the image auto* currentPixel = static_cast(localStorage->m_ReslicedImage->GetScalarPointer()); while (y <= yMax) { // if the current pixel value is set to something if ((currentPixel) && (*currentPixel != 0)) { // check in which direction a line is necessary // a line is added if the neighbor of the current pixel has the value 0 // and if the pixel is located at the edge of the image // if vvvvv not the first line vvvvv if (y > yMin && *(currentPixel - line) == 0) { // x direction - bottom edge of the pixel // add the 2 points vtkIdType p1 = points->InsertNextPoint(x * localStorage->m_mmPerPixel[0], y * localStorage->m_mmPerPixel[1], depth); vtkIdType p2 = points->InsertNextPoint((x + 1) * localStorage->m_mmPerPixel[0], y * localStorage->m_mmPerPixel[1], depth); // add the line between both points lines->InsertNextCell(2); lines->InsertCellPoint(p1); lines->InsertCellPoint(p2); } // if vvvvv not the last line vvvvv if (y < yMax && *(currentPixel + line) == 0) { // x direction - top edge of the pixel vtkIdType p1 = points->InsertNextPoint(x * localStorage->m_mmPerPixel[0], (y + 1) * localStorage->m_mmPerPixel[1], depth); vtkIdType p2 = points->InsertNextPoint( (x + 1) * localStorage->m_mmPerPixel[0], (y + 1) * localStorage->m_mmPerPixel[1], depth); lines->InsertNextCell(2); lines->InsertCellPoint(p1); lines->InsertCellPoint(p2); } // if vvvvv not the first pixel vvvvv if ((x > xMin || y > yMin) && *(currentPixel - 1) == 0) { // y direction - left edge of the pixel vtkIdType p1 = points->InsertNextPoint(x * localStorage->m_mmPerPixel[0], y * localStorage->m_mmPerPixel[1], depth); vtkIdType p2 = points->InsertNextPoint(x * localStorage->m_mmPerPixel[0], (y + 1) * localStorage->m_mmPerPixel[1], depth); lines->InsertNextCell(2); lines->InsertCellPoint(p1); lines->InsertCellPoint(p2); } // if vvvvv not the last pixel vvvvv if ((y < yMax || (x < xMax)) && *(currentPixel + 1) == 0) { // y direction - right edge of the pixel vtkIdType p1 = points->InsertNextPoint((x + 1) * localStorage->m_mmPerPixel[0], y * localStorage->m_mmPerPixel[1], depth); vtkIdType p2 = points->InsertNextPoint( (x + 1) * localStorage->m_mmPerPixel[0], (y + 1) * localStorage->m_mmPerPixel[1], depth); lines->InsertNextCell(2); lines->InsertCellPoint(p1); lines->InsertCellPoint(p2); } /* now consider pixels at the edge of the image */ // if vvvvv left edge of image vvvvv if (x == xMin) { // draw left edge of the pixel vtkIdType p1 = points->InsertNextPoint(x * localStorage->m_mmPerPixel[0], y * localStorage->m_mmPerPixel[1], depth); vtkIdType p2 = points->InsertNextPoint(x * localStorage->m_mmPerPixel[0], (y + 1) * localStorage->m_mmPerPixel[1], depth); lines->InsertNextCell(2); lines->InsertCellPoint(p1); lines->InsertCellPoint(p2); } // if vvvvv right edge of image vvvvv if (x == xMax) { // draw right edge of the pixel vtkIdType p1 = points->InsertNextPoint((x + 1) * localStorage->m_mmPerPixel[0], y * localStorage->m_mmPerPixel[1], depth); vtkIdType p2 = points->InsertNextPoint( (x + 1) * localStorage->m_mmPerPixel[0], (y + 1) * localStorage->m_mmPerPixel[1], depth); lines->InsertNextCell(2); lines->InsertCellPoint(p1); lines->InsertCellPoint(p2); } // if vvvvv bottom edge of image vvvvv if (y == yMin) { // draw bottom edge of the pixel vtkIdType p1 = points->InsertNextPoint(x * localStorage->m_mmPerPixel[0], y * localStorage->m_mmPerPixel[1], depth); vtkIdType p2 = points->InsertNextPoint((x + 1) * localStorage->m_mmPerPixel[0], y * localStorage->m_mmPerPixel[1], depth); lines->InsertNextCell(2); lines->InsertCellPoint(p1); lines->InsertCellPoint(p2); } // if vvvvv top edge of image vvvvv if (y == yMax) { // draw top edge of the pixel vtkIdType p1 = points->InsertNextPoint(x * localStorage->m_mmPerPixel[0], (y + 1) * localStorage->m_mmPerPixel[1], depth); vtkIdType p2 = points->InsertNextPoint( (x + 1) * localStorage->m_mmPerPixel[0], (y + 1) * localStorage->m_mmPerPixel[1], depth); lines->InsertNextCell(2); lines->InsertCellPoint(p1); lines->InsertCellPoint(p2); } } // end if currentpixel is set x++; if (x > xMax) { // reached end of line x = xMin; y++; } // Increase the pointer-position to the next pixel. // This is safe, as the while-loop and the x-reset logic above makes // sure we do not exceed the bounds of the image currentPixel++; } // end of while // Create a polydata to store everything in vtkSmartPointer polyData = vtkSmartPointer::New(); // Add the points to the dataset polyData->SetPoints(points); // Add the lines to the dataset polyData->SetLines(lines); return polyData; } void mitk::ImageVtkMapper2D::TransformActor(mitk::BaseRenderer *renderer) { LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); // get the transformation matrix of the reslicer in order to render the slice as axial, coronal or saggital vtkSmartPointer trans = vtkSmartPointer::New(); vtkSmartPointer matrix = localStorage->m_Reslicer->GetResliceAxes(); trans->SetMatrix(matrix); // transform the plane/contour (the actual actor) to the corresponding view (axial, coronal or saggital) localStorage->m_Actor->SetUserTransform(trans); // transform the origin to center based coordinates, because MITK is center based. localStorage->m_Actor->SetPosition(-0.5 * localStorage->m_mmPerPixel[0], -0.5 * localStorage->m_mmPerPixel[1], 0.0); if (localStorage->m_Actors->GetNumberOfPaths() > 1) { auto *secondaryActor = dynamic_cast(localStorage->m_Actors->GetParts()->GetItemAsObject(0)); secondaryActor->SetUserTransform(trans); secondaryActor->SetPosition(-0.5 * localStorage->m_mmPerPixel[0], -0.5 * localStorage->m_mmPerPixel[1], 0.0); } } bool mitk::ImageVtkMapper2D::RenderingGeometryIntersectsImage(const PlaneGeometry *renderingGeometry, SlicedGeometry3D *imageGeometry) { // if either one of the two geometries is nullptr we return true // for safety reasons if (renderingGeometry == nullptr || imageGeometry == nullptr) return true; // get the distance for the first cornerpoint ScalarType initialDistance = renderingGeometry->SignedDistance(imageGeometry->GetCornerPoint(0)); for (int i = 1; i < 8; i++) { mitk::Point3D cornerPoint = imageGeometry->GetCornerPoint(i); // get the distance to the other cornerpoints ScalarType distance = renderingGeometry->SignedDistance(cornerPoint); // if it has not the same signing as the distance of the first point if (initialDistance * distance < 0) { // we have an intersection and return true return true; } } // all distances have the same sign, no intersection and we return false return false; } mitk::ImageVtkMapper2D::LocalStorage::~LocalStorage() { } mitk::ImageVtkMapper2D::LocalStorage::LocalStorage() : m_VectorComponentExtractor(vtkSmartPointer::New()) { m_LevelWindowFilter = vtkSmartPointer::New(); // Do as much actions as possible in here to avoid double executions. m_Plane = vtkSmartPointer::New(); m_Texture = vtkSmartPointer::New().GetPointer(); m_DefaultLookupTable = vtkSmartPointer::New(); m_BinaryLookupTable = vtkSmartPointer::New(); m_ColorLookupTable = vtkSmartPointer::New(); m_Mapper = vtkSmartPointer::New(); m_Actor = vtkSmartPointer::New(); m_Actors = vtkSmartPointer::New(); m_Reslicer = mitk::ExtractSliceFilter::New(); m_TSFilter = vtkSmartPointer::New(); m_OutlinePolyData = vtkSmartPointer::New(); m_ReslicedImage = vtkSmartPointer::New(); m_EmptyPolyData = vtkSmartPointer::New(); // the following actions are always the same and thus can be performed // in the constructor for each image (i.e. the image-corresponding local storage) m_TSFilter->ReleaseDataFlagOn(); mitk::LookupTable::Pointer mitkLUT = mitk::LookupTable::New(); // built a default lookuptable mitkLUT->SetType(mitk::LookupTable::GRAYSCALE); m_DefaultLookupTable = mitkLUT->GetVtkLookupTable(); mitkLUT->SetType(mitk::LookupTable::LEGACY_BINARY); m_BinaryLookupTable = mitkLUT->GetVtkLookupTable(); mitkLUT->SetType(mitk::LookupTable::LEGACY_RAINBOW_COLOR); m_ColorLookupTable = mitkLUT->GetVtkLookupTable(); // do not repeat the texture (the image) m_Texture->RepeatOff(); // set the mapper for the actor m_Actor->SetMapper(m_Mapper); vtkSmartPointer outlineShadowActor = vtkSmartPointer::New(); outlineShadowActor->SetMapper(m_Mapper); m_Actors->AddPart(outlineShadowActor); m_Actors->AddPart(m_Actor); } diff --git a/Modules/Core/src/Rendering/mitkMapper.cpp b/Modules/Core/src/Rendering/mitkMapper.cpp index a917acf960..b794f02df6 100644 --- a/Modules/Core/src/Rendering/mitkMapper.cpp +++ b/Modules/Core/src/Rendering/mitkMapper.cpp @@ -1,152 +1,203 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ -#include "mitkMapper.h" #include "mitkBaseRenderer.h" #include "mitkDataNode.h" +#include "mitkMapper.h" #include "mitkProperties.h" +#include "usGetModuleContext.h" +#include "usLDAPProp.h" +#include "usPrototypeServiceFactory.h" -mitk::Mapper::Mapper() : m_DataNode(nullptr), m_TimeStep(0) -{ -} +mitk::Mapper::Mapper() : m_DataNode(nullptr), m_TimeStep(0) {} -mitk::Mapper::~Mapper() -{ -} +mitk::Mapper::~Mapper() {} mitk::BaseData *mitk::Mapper::GetData() const { return m_DataNode == nullptr ? nullptr : m_DataNode->GetData(); } +us::ServiceRegistration mitk::Mapper::RegisterAtServiceRegistry(std::string supportedDataTypeName, mitk::BaseRenderer::MapperSlotId supportedSlot, int priority) +{ + //Check if a prototype factory for this specific mapper type already exists + std::string typeidName = typeid(*this).name(); + auto context = us::GetModuleContext(); + auto refs = context->GetServiceReferences(us::LDAPProp("PROP_TYPEID_NAME") == typeidName); + const bool alreadyExists = !refs.empty(); + + struct PrototypeFactory : public us::PrototypeServiceFactory + { + Pointer const m_Prototype; + std::vector m_CreatedObjects; + + PrototypeFactory(Pointer prototype) + : m_Prototype(prototype), m_CreatedObjects(std::vector()) + { + } + us::InterfaceMap GetService(us::Module * /*module*/, const us::ServiceRegistrationBase & /*registration*/) override + { + auto another = m_Prototype->CreateAnother(); + m_CreatedObjects.push_back(another); + + Mapper *rawPointer = dynamic_cast(another.GetPointer()); + assert(rawPointer != nullptr); + + return us::MakeInterfaceMap(rawPointer); + } + + void UngetService(us::Module * /*module*/, + const us::ServiceRegistrationBase & /*registration*/, + const us::InterfaceMap &service) override + { + // delete us::ExtractInterface(service); + // us::ExtractInterface(service)->UnRegister(); + } + }; + + auto registration = us::ServiceRegistration(); + // Only create a new factory once + if (!alreadyExists) + { + //Todo check if CppMicroservices frees this memory when the factory is unregistered + auto prototypeFactory = new PrototypeFactory(this); + us::ServiceProperties props { std::make_pair("PROP_TYPEID_NAME", typeidName), + std::make_pair("PROP_SUPPORTED_DATA_TYPE_NAME", supportedDataTypeName), + std::make_pair("PROP_SUPPORTED_SLOT_ID", supportedSlot), + std::make_pair("PROP_PRIORITY", priority)}; + registration = context->RegisterService(prototypeFactory, props); + } + return registration; +} + mitk::DataNode *mitk::Mapper::GetDataNode() const { return this->m_DataNode; } bool mitk::Mapper::GetColor(float rgb[3], mitk::BaseRenderer *renderer, const char *name) const { const mitk::DataNode *node = GetDataNode(); if (node == nullptr) return false; return node->GetColor(rgb, renderer, name); } bool mitk::Mapper::GetVisibility(bool &visible, mitk::BaseRenderer *renderer, const char *name) const { const mitk::DataNode *node = GetDataNode(); if (node == nullptr) return false; return node->GetVisibility(visible, renderer, name); } bool mitk::Mapper::GetOpacity(float &opacity, mitk::BaseRenderer *renderer, const char *name) const { const mitk::DataNode *node = GetDataNode(); if (node == nullptr) return false; return node->GetOpacity(opacity, renderer, name); } bool mitk::Mapper::GetLevelWindow(mitk::LevelWindow &levelWindow, mitk::BaseRenderer *renderer, const char *name) const { const mitk::DataNode *node = GetDataNode(); if (node == nullptr) return false; return node->GetLevelWindow(levelWindow, renderer, name); } bool mitk::Mapper::IsVisible(mitk::BaseRenderer *renderer, const char *name) const { bool visible = true; GetDataNode()->GetVisibility(visible, renderer, name); return visible; } void mitk::Mapper::CalculateTimeStep(mitk::BaseRenderer *renderer) { if ((renderer != nullptr) && (m_DataNode != nullptr)) { m_TimeStep = renderer->GetTimeStep(m_DataNode->GetData()); } else { m_TimeStep = 0; } } void mitk::Mapper::Update(mitk::BaseRenderer *renderer) { const DataNode *node = GetDataNode(); assert(node != nullptr); auto *data = static_cast(node->GetData()); if (!data) return; // Calculate time step of the input data for the specified renderer (integer value) this->CalculateTimeStep(renderer); // Check if time step is valid const TimeGeometry *dataTimeGeometry = data->GetTimeGeometry(); if ((dataTimeGeometry == nullptr) || (dataTimeGeometry->CountTimeSteps() == 0) || (!dataTimeGeometry->IsValidTimeStep(m_TimeStep))) { // TimeGeometry or time step is not valid for this data: // reset mapper so that nothing is displayed this->ResetMapper(renderer); return; } this->GenerateDataForRenderer(renderer); } bool mitk::Mapper::BaseLocalStorage::IsGenerateDataRequired(mitk::BaseRenderer *renderer, mitk::Mapper *mapper, mitk::DataNode *dataNode) const { if (mapper && m_LastGenerateDataTime < mapper->GetMTime()) return true; if (dataNode) { if (m_LastGenerateDataTime < dataNode->GetDataReferenceChangedTime()) return true; mitk::BaseData *data = dataNode->GetData(); if (data && m_LastGenerateDataTime < data->GetMTime()) return true; } if (renderer && m_LastGenerateDataTime < renderer->GetTimeStepUpdateTime()) return true; return false; } void mitk::Mapper::SetDefaultProperties(mitk::DataNode *node, mitk::BaseRenderer *renderer, bool overwrite) { node->AddProperty("visible", mitk::BoolProperty::New(true), renderer, overwrite); node->AddProperty("layer", mitk::IntProperty::New(0), renderer, overwrite); node->AddProperty("name", mitk::StringProperty::New("No Name!"), renderer, overwrite); } diff --git a/Modules/Core/src/Rendering/mitkPointSetVtkMapper2D.cpp b/Modules/Core/src/Rendering/mitkPointSetVtkMapper2D.cpp index 81ad158b38..06c610885e 100644 --- a/Modules/Core/src/Rendering/mitkPointSetVtkMapper2D.cpp +++ b/Modules/Core/src/Rendering/mitkPointSetVtkMapper2D.cpp @@ -1,749 +1,750 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkPointSetVtkMapper2D.h" // mitk includes #include "mitkVtkPropRenderer.h" #include #include #include #include // vtk includes #include #include #include #include #include #include #include #include #include #include #include #include #include #include // constructor LocalStorage mitk::PointSetVtkMapper2D::LocalStorage::LocalStorage() { // points m_UnselectedPoints = vtkSmartPointer::New(); m_SelectedPoints = vtkSmartPointer::New(); m_ContourPoints = vtkSmartPointer::New(); // scales m_UnselectedScales = vtkSmartPointer::New(); m_SelectedScales = vtkSmartPointer::New(); // distances m_DistancesBetweenPoints = vtkSmartPointer::New(); // lines m_ContourLines = vtkSmartPointer::New(); // glyph source (provides the different shapes) m_UnselectedGlyphSource2D = vtkSmartPointer::New(); m_SelectedGlyphSource2D = vtkSmartPointer::New(); // glyphs m_UnselectedGlyph3D = vtkSmartPointer::New(); m_SelectedGlyph3D = vtkSmartPointer::New(); // polydata m_VtkUnselectedPointListPolyData = vtkSmartPointer::New(); m_VtkSelectedPointListPolyData = vtkSmartPointer::New(); m_VtkContourPolyData = vtkSmartPointer::New(); // actors m_UnselectedActor = vtkSmartPointer::New(); m_SelectedActor = vtkSmartPointer::New(); m_ContourActor = vtkSmartPointer::New(); // mappers m_VtkUnselectedPolyDataMapper = vtkSmartPointer::New(); m_VtkSelectedPolyDataMapper = vtkSmartPointer::New(); m_VtkContourPolyDataMapper = vtkSmartPointer::New(); // propassembly m_PropAssembly = vtkSmartPointer::New(); } // destructor LocalStorage mitk::PointSetVtkMapper2D::LocalStorage::~LocalStorage() { } // input for this mapper ( = point set) const mitk::PointSet *mitk::PointSetVtkMapper2D::GetInput() const { return static_cast(GetDataNode()->GetData()); } // constructor PointSetVtkMapper2D mitk::PointSetVtkMapper2D::PointSetVtkMapper2D() : m_ShowContour(false), m_CloseContour(false), m_ShowPoints(true), m_ShowDistances(false), m_DistancesDecimalDigits(1), m_ShowAngles(false), m_ShowDistantLines(false), m_LineWidth(1), m_PointLineWidth(1), m_Point2DSize(6), m_IDShapeProperty(mitk::PointSetShapeProperty::CROSS), m_FillShape(false), m_DistanceToPlane(4.0f) { + RegisterAtServiceRegistry(typeid(mitk::PointSet).name(), mitk::BaseRenderer::Standard2D); } // destructor mitk::PointSetVtkMapper2D::~PointSetVtkMapper2D() { } // reset mapper so that nothing is displayed e.g. toggle visiblity of the propassembly void mitk::PointSetVtkMapper2D::ResetMapper(BaseRenderer *renderer) { LocalStorage *ls = m_LSH.GetLocalStorage(renderer); ls->m_PropAssembly->VisibilityOff(); } // returns propassembly vtkProp *mitk::PointSetVtkMapper2D::GetVtkProp(mitk::BaseRenderer *renderer) { LocalStorage *ls = m_LSH.GetLocalStorage(renderer); return ls->m_PropAssembly; } static bool makePerpendicularVector2D(const mitk::Vector2D &in, mitk::Vector2D &out) { // The dot product of orthogonal vectors is zero. // In two dimensions the slopes of perpendicular lines are negative reciprocals. if ((fabs(in[0]) > 0) && ((fabs(in[0]) > fabs(in[1])) || (in[1] == 0))) { // negative reciprocal out[0] = -in[1] / in[0]; out[1] = 1; out.Normalize(); return true; } else if (fabs(in[1]) > 0) { out[0] = 1; // negative reciprocal out[1] = -in[0] / in[1]; out.Normalize(); return true; } else return false; } void mitk::PointSetVtkMapper2D::CreateVTKRenderObjects(mitk::BaseRenderer *renderer) { LocalStorage *ls = m_LSH.GetLocalStorage(renderer); unsigned i = 0; // The vtk text actors need to be removed manually from the propassembly // since the same vtk text actors are not overwriten within this function, // but new actors are added to the propassembly each time this function is executed. // Thus, the actors from the last call must be removed in the beginning. for (i = 0; i < ls->m_VtkTextLabelActors.size(); i++) { if (ls->m_PropAssembly->GetParts()->IsItemPresent(ls->m_VtkTextLabelActors.at(i))) ls->m_PropAssembly->RemovePart(ls->m_VtkTextLabelActors.at(i)); } for (i = 0; i < ls->m_VtkTextDistanceActors.size(); i++) { if (ls->m_PropAssembly->GetParts()->IsItemPresent(ls->m_VtkTextDistanceActors.at(i))) ls->m_PropAssembly->RemovePart(ls->m_VtkTextDistanceActors.at(i)); } for (i = 0; i < ls->m_VtkTextAngleActors.size(); i++) { if (ls->m_PropAssembly->GetParts()->IsItemPresent(ls->m_VtkTextAngleActors.at(i))) ls->m_PropAssembly->RemovePart(ls->m_VtkTextAngleActors.at(i)); } // initialize polydata here, otherwise we have update problems when // executing this function again ls->m_VtkUnselectedPointListPolyData = vtkSmartPointer::New(); ls->m_VtkSelectedPointListPolyData = vtkSmartPointer::New(); ls->m_VtkContourPolyData = vtkSmartPointer::New(); // get input point set and update the PointSet mitk::PointSet::Pointer input = const_cast(this->GetInput()); // only update the input data, if the property tells us to bool update = true; this->GetDataNode()->GetBoolProperty("updateDataOnRender", update); if (update == true) input->Update(); int timestep = this->GetTimestep(); mitk::PointSet::DataType::Pointer itkPointSet = input->GetPointSet(timestep); if (itkPointSet.GetPointer() == nullptr) { ls->m_PropAssembly->VisibilityOff(); return; } // iterator for point set mitk::PointSet::PointsContainer::Iterator pointsIter = itkPointSet->GetPoints()->Begin(); // PointDataContainer has additional information to each point, e.g. whether // it is selected or not mitk::PointSet::PointDataContainer::Iterator pointDataIter; pointDataIter = itkPointSet->GetPointData()->Begin(); // check if the list for the PointDataContainer is the same size as the PointsContainer. // If not, then the points were inserted manually and can not be visualized according to the PointData // (selected/unselected) bool pointDataBroken = (itkPointSet->GetPointData()->Size() != itkPointSet->GetPoints()->Size()); if (itkPointSet->GetPointData()->size() == 0 || pointDataBroken) { ls->m_PropAssembly->VisibilityOff(); return; } ls->m_PropAssembly->VisibilityOn(); // empty point sets, cellarrays, scalars ls->m_UnselectedPoints->Reset(); ls->m_SelectedPoints->Reset(); ls->m_ContourPoints->Reset(); ls->m_ContourLines->Reset(); ls->m_UnselectedScales->Reset(); ls->m_SelectedScales->Reset(); ls->m_DistancesBetweenPoints->Reset(); ls->m_VtkTextLabelActors.clear(); ls->m_VtkTextDistanceActors.clear(); ls->m_VtkTextAngleActors.clear(); ls->m_UnselectedScales->SetNumberOfComponents(3); ls->m_SelectedScales->SetNumberOfComponents(3); int NumberContourPoints = 0; bool pointsOnSameSideOfPlane = false; const int text2dDistance = 10; // initialize points with a random start value // current point in point set itk::Point point = pointsIter->Value(); mitk::Point3D p = point; // currently visited point mitk::Point3D lastP = point; // last visited point (predecessor in point set of "point") mitk::Vector3D vec; // p - lastP mitk::Vector3D lastVec; // lastP - point before lastP vec.Fill(0.0); lastVec.Fill(0.0); mitk::Point2D pt2d; pt2d[0] = point[0]; // projected_p in display coordinates pt2d[1] = point[1]; mitk::Point2D lastPt2d = pt2d; // last projected_p in display coordinates (predecessor in point set of "pt2d") mitk::Point2D preLastPt2d = pt2d; // projected_p in display coordinates before lastPt2 const mitk::PlaneGeometry *geo2D = renderer->GetCurrentWorldPlaneGeometry(); vtkLinearTransform *dataNodeTransform = input->GetGeometry()->GetVtkTransform(); int count = 0; for (pointsIter = itkPointSet->GetPoints()->Begin(); pointsIter != itkPointSet->GetPoints()->End(); pointsIter++) { lastP = p; // valid for number of points count > 0 preLastPt2d = lastPt2d; // valid only for count > 1 lastPt2d = pt2d; // valid for number of points count > 0 lastVec = vec; // valid only for counter > 1 // get current point in point set point = pointsIter->Value(); // transform point { float vtkp[3]; itk2vtk(point, vtkp); dataNodeTransform->TransformPoint(vtkp, vtkp); vtk2itk(vtkp, point); } p[0] = point[0]; p[1] = point[1]; p[2] = point[2]; renderer->WorldToDisplay(p, pt2d); vec = p - lastP; // valid only for counter > 0 // compute distance to current plane float dist = geo2D->Distance(point); // draw markers on slices a certain distance away from the points // location according to the tolerance threshold (m_DistanceToPlane) if (dist < m_DistanceToPlane) { // is point selected or not? if (pointDataIter->Value().selected) { ls->m_SelectedPoints->InsertNextPoint(point[0], point[1], point[2]); // point is scaled according to its distance to the plane ls->m_SelectedScales->InsertNextTuple3(std::max(0.0f, m_Point2DSize - (2 * dist)), 0, 0); } else { ls->m_UnselectedPoints->InsertNextPoint(point[0], point[1], point[2]); // point is scaled according to its distance to the plane ls->m_UnselectedScales->InsertNextTuple3(std::max(0.0f, m_Point2DSize - (2 * dist)), 0, 0); } //---- LABEL -----// // paint label for each point if available if (dynamic_cast(this->GetDataNode()->GetProperty("label")) != nullptr) { const char *pointLabel = dynamic_cast(this->GetDataNode()->GetProperty("label"))->GetValue(); std::string l = pointLabel; if (input->GetSize() > 1) { std::stringstream ss; ss << pointsIter->Index(); l.append(ss.str()); } ls->m_VtkTextActor = vtkSmartPointer::New(); ls->m_VtkTextActor->SetDisplayPosition(pt2d[0] + text2dDistance, pt2d[1] + text2dDistance); ls->m_VtkTextActor->SetInput(l.c_str()); ls->m_VtkTextActor->GetTextProperty()->SetOpacity(100); float unselectedColor[4] = {1.0, 1.0, 0.0, 1.0}; // check if there is a color property GetDataNode()->GetColor(unselectedColor); ls->m_VtkTextActor->GetTextProperty()->SetColor(unselectedColor[0], unselectedColor[1], unselectedColor[2]); ls->m_VtkTextLabelActors.push_back(ls->m_VtkTextActor); } } // draw contour, distance text and angle text in render window // lines between points, which intersect the current plane, are drawn if (m_ShowContour && count > 0) { ScalarType distance = renderer->GetCurrentWorldPlaneGeometry()->SignedDistance(point); ScalarType lastDistance = renderer->GetCurrentWorldPlaneGeometry()->SignedDistance(lastP); pointsOnSameSideOfPlane = (distance * lastDistance) > 0.5; // Points must be on different side of plane in order to draw a contour. // If "show distant lines" is enabled this condition is disregarded. if (!pointsOnSameSideOfPlane || m_ShowDistantLines) { vtkSmartPointer line = vtkSmartPointer::New(); ls->m_ContourPoints->InsertNextPoint(lastP[0], lastP[1], lastP[2]); line->GetPointIds()->SetId(0, NumberContourPoints); NumberContourPoints++; ls->m_ContourPoints->InsertNextPoint(point[0], point[1], point[2]); line->GetPointIds()->SetId(1, NumberContourPoints); NumberContourPoints++; ls->m_ContourLines->InsertNextCell(line); if (m_ShowDistances) // calculate and print distance between adjacent points { float distancePoints = point.EuclideanDistanceTo(lastP); std::stringstream buffer; buffer << std::fixed << std::setprecision(m_DistancesDecimalDigits) << distancePoints << " mm"; // compute desired display position of text Vector2D vec2d = pt2d - lastPt2d; makePerpendicularVector2D(vec2d, vec2d); // text is rendered within text2dDistance perpendicular to current line Vector2D pos2d = (lastPt2d.GetVectorFromOrigin() + pt2d.GetVectorFromOrigin()) * 0.5 + vec2d * text2dDistance; ls->m_VtkTextActor = vtkSmartPointer::New(); ls->m_VtkTextActor->SetDisplayPosition(pos2d[0], pos2d[1]); ls->m_VtkTextActor->SetInput(buffer.str().c_str()); ls->m_VtkTextActor->GetTextProperty()->SetColor(0.0, 1.0, 0.0); ls->m_VtkTextDistanceActors.push_back(ls->m_VtkTextActor); } if (m_ShowAngles && count > 1) // calculate and print angle between connected lines { std::stringstream buffer; buffer << angle(vec.GetVnlVector(), -lastVec.GetVnlVector()) * 180 / vnl_math::pi << "°"; // compute desired display position of text Vector2D vec2d = pt2d - lastPt2d; // first arm enclosing the angle vec2d.Normalize(); Vector2D lastVec2d = lastPt2d - preLastPt2d; // second arm enclosing the angle lastVec2d.Normalize(); vec2d = vec2d - lastVec2d; // vector connecting both arms vec2d.Normalize(); // middle between two vectors that enclose the angle Vector2D pos2d = lastPt2d.GetVectorFromOrigin() + vec2d * text2dDistance * text2dDistance; ls->m_VtkTextActor = vtkSmartPointer::New(); ls->m_VtkTextActor->SetDisplayPosition(pos2d[0], pos2d[1]); ls->m_VtkTextActor->SetInput(buffer.str().c_str()); ls->m_VtkTextActor->GetTextProperty()->SetColor(0.0, 1.0, 0.0); ls->m_VtkTextAngleActors.push_back(ls->m_VtkTextActor); } } } if (pointDataIter != itkPointSet->GetPointData()->End()) { pointDataIter++; count++; } } // add each single text actor to the assembly for (i = 0; i < ls->m_VtkTextLabelActors.size(); i++) { ls->m_PropAssembly->AddPart(ls->m_VtkTextLabelActors.at(i)); } for (i = 0; i < ls->m_VtkTextDistanceActors.size(); i++) { ls->m_PropAssembly->AddPart(ls->m_VtkTextDistanceActors.at(i)); } for (i = 0; i < ls->m_VtkTextAngleActors.size(); i++) { ls->m_PropAssembly->AddPart(ls->m_VtkTextAngleActors.at(i)); } //---- CONTOUR -----// // create lines between the points which intersect the plane if (m_ShowContour) { // draw line between first and last point which is rendered if (m_CloseContour && NumberContourPoints > 1) { vtkSmartPointer closingLine = vtkSmartPointer::New(); closingLine->GetPointIds()->SetId(0, 0); // index of first point closingLine->GetPointIds()->SetId(1, NumberContourPoints - 1); // index of last point ls->m_ContourLines->InsertNextCell(closingLine); } ls->m_VtkContourPolyData->SetPoints(ls->m_ContourPoints); ls->m_VtkContourPolyData->SetLines(ls->m_ContourLines); ls->m_VtkContourPolyDataMapper->SetInputData(ls->m_VtkContourPolyData); ls->m_ContourActor->SetMapper(ls->m_VtkContourPolyDataMapper); ls->m_ContourActor->GetProperty()->SetLineWidth(m_LineWidth); ls->m_PropAssembly->AddPart(ls->m_ContourActor); } // the point set must be transformed in order to obtain the appropriate glyph orientation // according to the current view vtkSmartPointer transform = vtkSmartPointer::New(); vtkSmartPointer a, b = vtkSmartPointer::New(); a = geo2D->GetVtkTransform()->GetMatrix(); b->DeepCopy(a); // delete transformation from matrix, only take orientation b->SetElement(3, 3, 1); b->SetElement(2, 3, 0); b->SetElement(1, 3, 0); b->SetElement(0, 3, 0); b->SetElement(3, 2, 0); b->SetElement(3, 1, 0); b->SetElement(3, 0, 0); Vector3D spacing = geo2D->GetSpacing(); // If you find a way to simplyfy the following, feel free to change! b->SetElement(0, 0, b->GetElement(0, 0) / spacing[0]); b->SetElement(1, 0, b->GetElement(1, 0) / spacing[0]); b->SetElement(2, 0, b->GetElement(2, 0) / spacing[0]); b->SetElement(1, 1, b->GetElement(1, 1) / spacing[1]); b->SetElement(2, 1, b->GetElement(2, 1) / spacing[1]); b->SetElement(0, 2, b->GetElement(0, 2) / spacing[2]); b->SetElement(1, 2, b->GetElement(1, 2) / spacing[2]); b->SetElement(2, 2, b->GetElement(2, 2) / spacing[2]); transform->SetMatrix(b); //---- UNSELECTED POINTS -----// // apply properties to glyph ls->m_UnselectedGlyphSource2D->SetGlyphType(m_IDShapeProperty); if (m_FillShape) ls->m_UnselectedGlyphSource2D->FilledOn(); else ls->m_UnselectedGlyphSource2D->FilledOff(); // apply transform vtkSmartPointer transformFilterU = vtkSmartPointer::New(); transformFilterU->SetInputConnection(ls->m_UnselectedGlyphSource2D->GetOutputPort()); transformFilterU->SetTransform(transform); ls->m_VtkUnselectedPointListPolyData->SetPoints(ls->m_UnselectedPoints); ls->m_VtkUnselectedPointListPolyData->GetPointData()->SetVectors(ls->m_UnselectedScales); // apply transform of current plane to glyphs ls->m_UnselectedGlyph3D->SetSourceConnection(transformFilterU->GetOutputPort()); ls->m_UnselectedGlyph3D->SetInputData(ls->m_VtkUnselectedPointListPolyData); ls->m_UnselectedGlyph3D->SetScaleModeToScaleByVector(); ls->m_UnselectedGlyph3D->SetVectorModeToUseVector(); ls->m_VtkUnselectedPolyDataMapper->SetInputConnection(ls->m_UnselectedGlyph3D->GetOutputPort()); ls->m_UnselectedActor->SetMapper(ls->m_VtkUnselectedPolyDataMapper); ls->m_UnselectedActor->GetProperty()->SetLineWidth(m_PointLineWidth); ls->m_PropAssembly->AddPart(ls->m_UnselectedActor); //---- SELECTED POINTS -----// ls->m_SelectedGlyphSource2D->SetGlyphTypeToDiamond(); ls->m_SelectedGlyphSource2D->CrossOn(); ls->m_SelectedGlyphSource2D->FilledOff(); // apply transform vtkSmartPointer transformFilterS = vtkSmartPointer::New(); transformFilterS->SetInputConnection(ls->m_SelectedGlyphSource2D->GetOutputPort()); transformFilterS->SetTransform(transform); ls->m_VtkSelectedPointListPolyData->SetPoints(ls->m_SelectedPoints); ls->m_VtkSelectedPointListPolyData->GetPointData()->SetVectors(ls->m_SelectedScales); // apply transform of current plane to glyphs ls->m_SelectedGlyph3D->SetSourceConnection(transformFilterS->GetOutputPort()); ls->m_SelectedGlyph3D->SetInputData(ls->m_VtkSelectedPointListPolyData); ls->m_SelectedGlyph3D->SetScaleModeToScaleByVector(); ls->m_SelectedGlyph3D->SetVectorModeToUseVector(); ls->m_VtkSelectedPolyDataMapper->SetInputConnection(ls->m_SelectedGlyph3D->GetOutputPort()); ls->m_SelectedActor->SetMapper(ls->m_VtkSelectedPolyDataMapper); ls->m_SelectedActor->GetProperty()->SetLineWidth(m_PointLineWidth); ls->m_PropAssembly->AddPart(ls->m_SelectedActor); } void mitk::PointSetVtkMapper2D::GenerateDataForRenderer(mitk::BaseRenderer *renderer) { const mitk::DataNode *node = GetDataNode(); if (node == nullptr) return; LocalStorage *ls = m_LSH.GetLocalStorage(renderer); // check whether the input data has been changed bool needGenerateData = ls->IsGenerateDataRequired(renderer, this, GetDataNode()); // toggle visibility bool visible = true; node->GetVisibility(visible, renderer, "visible"); if (!visible) { ls->m_UnselectedActor->VisibilityOff(); ls->m_SelectedActor->VisibilityOff(); ls->m_ContourActor->VisibilityOff(); ls->m_PropAssembly->VisibilityOff(); return; } else { ls->m_PropAssembly->VisibilityOn(); } node->GetBoolProperty("show contour", m_ShowContour, renderer); node->GetBoolProperty("close contour", m_CloseContour, renderer); node->GetBoolProperty("show points", m_ShowPoints, renderer); node->GetBoolProperty("show distances", m_ShowDistances, renderer); node->GetIntProperty("distance decimal digits", m_DistancesDecimalDigits, renderer); node->GetBoolProperty("show angles", m_ShowAngles, renderer); node->GetBoolProperty("show distant lines", m_ShowDistantLines, renderer); node->GetIntProperty("line width", m_LineWidth, renderer); node->GetIntProperty("point line width", m_PointLineWidth, renderer); if (!node->GetFloatProperty( "point 2D size", m_Point2DSize, renderer)) // re-defined to float 2015-08-13, keep a fallback { int oldPointSize = m_Point2DSize; if (node->GetIntProperty("point 2D size", oldPointSize, renderer)) { m_Point2DSize = oldPointSize; } } node->GetBoolProperty("Pointset.2D.fill shape", m_FillShape, renderer); node->GetFloatProperty("Pointset.2D.distance to plane", m_DistanceToPlane, renderer); mitk::PointSetShapeProperty::Pointer shape = dynamic_cast(this->GetDataNode()->GetProperty("Pointset.2D.shape", renderer)); if (shape.IsNotNull()) { m_IDShapeProperty = shape->GetPointSetShape(); } // check for color props and use it for rendering of selected/unselected points and contour // due to different params in VTK (double/float) we have to convert float opacity = 1.0; GetDataNode()->GetOpacity(opacity, renderer); // apply color and opacity if (m_ShowPoints) { float unselectedColor[4]; double selectedColor[4] = {1.0f, 0.0f, 0.0f, 1.0f}; // red ls->m_UnselectedActor->VisibilityOn(); ls->m_SelectedActor->VisibilityOn(); // check if there is a color property GetDataNode()->GetColor(unselectedColor); // get selected color property if (dynamic_cast( this->GetDataNode()->GetPropertyList(renderer)->GetProperty("selectedcolor")) != nullptr) { mitk::Color tmpColor = dynamic_cast( this->GetDataNode()->GetPropertyList(renderer)->GetProperty("selectedcolor")) ->GetValue(); selectedColor[0] = tmpColor[0]; selectedColor[1] = tmpColor[1]; selectedColor[2] = tmpColor[2]; selectedColor[3] = 1.0f; // alpha value } else if (dynamic_cast( this->GetDataNode()->GetPropertyList(nullptr)->GetProperty("selectedcolor")) != nullptr) { mitk::Color tmpColor = dynamic_cast(this->GetDataNode()->GetPropertyList(nullptr)->GetProperty("selectedcolor")) ->GetValue(); selectedColor[0] = tmpColor[0]; selectedColor[1] = tmpColor[1]; selectedColor[2] = tmpColor[2]; selectedColor[3] = 1.0f; // alpha value } ls->m_SelectedActor->GetProperty()->SetColor(selectedColor); ls->m_SelectedActor->GetProperty()->SetOpacity(opacity); ls->m_UnselectedActor->GetProperty()->SetColor(unselectedColor[0], unselectedColor[1], unselectedColor[2]); ls->m_UnselectedActor->GetProperty()->SetOpacity(opacity); } else { ls->m_UnselectedActor->VisibilityOff(); ls->m_SelectedActor->VisibilityOff(); } if (m_ShowContour) { double contourColor[4] = {1.0f, 0.0f, 0.0f, 1.0f}; // red ls->m_ContourActor->VisibilityOn(); // get contour color property if (dynamic_cast( this->GetDataNode()->GetPropertyList(renderer)->GetProperty("contourcolor")) != nullptr) { mitk::Color tmpColor = dynamic_cast(this->GetDataNode()->GetPropertyList(renderer)->GetProperty("contourcolor")) ->GetValue(); contourColor[0] = tmpColor[0]; contourColor[1] = tmpColor[1]; contourColor[2] = tmpColor[2]; contourColor[3] = 1.0f; } else if (dynamic_cast( this->GetDataNode()->GetPropertyList(nullptr)->GetProperty("contourcolor")) != nullptr) { mitk::Color tmpColor = dynamic_cast(this->GetDataNode()->GetPropertyList(nullptr)->GetProperty("contourcolor")) ->GetValue(); contourColor[0] = tmpColor[0]; contourColor[1] = tmpColor[1]; contourColor[2] = tmpColor[2]; contourColor[3] = 1.0f; } ls->m_ContourActor->GetProperty()->SetColor(contourColor); ls->m_ContourActor->GetProperty()->SetOpacity(opacity); } else { ls->m_ContourActor->VisibilityOff(); } if (needGenerateData) { // create new vtk render objects (e.g. a circle for a point) this->CreateVTKRenderObjects(renderer); } } void mitk::PointSetVtkMapper2D::SetDefaultProperties(mitk::DataNode *node, mitk::BaseRenderer *renderer, bool overwrite) { node->AddProperty("line width", mitk::IntProperty::New(2), renderer, overwrite); node->AddProperty("point line width", mitk::IntProperty::New(1), renderer, overwrite); node->AddProperty("point 2D size", mitk::FloatProperty::New(6), renderer, overwrite); node->AddProperty("show contour", mitk::BoolProperty::New(false), renderer, overwrite); node->AddProperty("close contour", mitk::BoolProperty::New(false), renderer, overwrite); node->AddProperty("show points", mitk::BoolProperty::New(true), renderer, overwrite); node->AddProperty("show distances", mitk::BoolProperty::New(false), renderer, overwrite); node->AddProperty("distance decimal digits", mitk::IntProperty::New(2), renderer, overwrite); node->AddProperty("show angles", mitk::BoolProperty::New(false), renderer, overwrite); node->AddProperty("show distant lines", mitk::BoolProperty::New(false), renderer, overwrite); node->AddProperty("layer", mitk::IntProperty::New(1), renderer, overwrite); node->AddProperty("Pointset.2D.fill shape", mitk::BoolProperty::New(false), renderer, overwrite); // fill or do not fill the glyph shape mitk::PointSetShapeProperty::Pointer pointsetShapeProperty = mitk::PointSetShapeProperty::New(); node->AddProperty("Pointset.2D.shape", pointsetShapeProperty, renderer, overwrite); node->AddProperty("Pointset.2D.distance to plane", mitk::FloatProperty::New(4.0f), renderer, overwrite); // show the point at a certain distance above/below the 2D imaging plane. Superclass::SetDefaultProperties(node, renderer, overwrite); } diff --git a/Modules/Core/src/mitkCoreActivator.cpp b/Modules/Core/src/mitkCoreActivator.cpp index 12f59e65fd..69f1713664 100644 --- a/Modules/Core/src/mitkCoreActivator.cpp +++ b/Modules/Core/src/mitkCoreActivator.cpp @@ -1,325 +1,330 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkCoreActivator.h" // File IO #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "mitkLegacyFileWriterService.h" #include #include #include // Micro Services #include #include #include #include #include #include #include #include #include #include // ITK "injects" static initialization code for IO factories // via the itkImageIOFactoryRegisterManager.h header (which // is generated in the application library build directory). // To ensure that the code is called *before* the CppMicroServices // static initialization code (which triggers the Activator::Start // method), we include the ITK header here. #include +#include "mitkImageVtkMapper2D.h" +#include "mitkPointSetVtkMapper2D.h" void HandleMicroServicesMessages(us::MsgType type, const char *msg) { switch (type) { case us::DebugMsg: MITK_DEBUG << msg; break; case us::InfoMsg: MITK_INFO << msg; break; case us::WarningMsg: MITK_WARN << msg; break; case us::ErrorMsg: MITK_ERROR << msg; break; } } void AddMitkAutoLoadPaths(const std::string &programPath) { us::ModuleSettings::AddAutoLoadPath(programPath); #ifdef __APPLE__ // Walk up three directories since that is where the .dylib files are located // for build trees. std::string additionalPath = programPath; bool addPath = true; for (int i = 0; i < 3; ++i) { std::size_t index = additionalPath.find_last_of('/'); if (index != std::string::npos) { additionalPath = additionalPath.substr(0, index); } else { addPath = false; break; } } if (addPath) { us::ModuleSettings::AddAutoLoadPath(additionalPath); } #endif } class FixedNiftiImageIO : public itk::NiftiImageIO { public: /** Standard class typedefs. */ typedef FixedNiftiImageIO Self; typedef itk::NiftiImageIO Superclass; typedef itk::SmartPointer Pointer; /** Method for creation through the object factory. */ itkNewMacro(Self) /** Run-time type information (and related methods). */ itkTypeMacro(FixedNiftiImageIO, Superclass) bool SupportsDimension(unsigned long dim) override { return dim > 1 && dim < 5; } }; void MitkCoreActivator::Load(us::ModuleContext *context) { // Handle messages from CppMicroServices us::installMsgHandler(HandleMicroServicesMessages); this->m_Context = context; // Add the current application directory to the auto-load paths. // This is useful for third-party executables. std::string programPath = mitk::IOUtil::GetProgramPath(); if (programPath.empty()) { MITK_WARN << "Could not get the program path."; } else { AddMitkAutoLoadPaths(programPath); } // m_RenderingManager = mitk::RenderingManager::New(); // context->RegisterService(renderingManager.GetPointer()); m_PlanePositionManager.reset(new mitk::PlanePositionManagerService); context->RegisterService(m_PlanePositionManager.get()); m_PropertyAliases.reset(new mitk::PropertyAliases); context->RegisterService(m_PropertyAliases.get()); m_PropertyDescriptions.reset(new mitk::PropertyDescriptions); context->RegisterService(m_PropertyDescriptions.get()); m_PropertyExtensions.reset(new mitk::PropertyExtensions); context->RegisterService(m_PropertyExtensions.get()); m_PropertyFilters.reset(new mitk::PropertyFilters); context->RegisterService(m_PropertyFilters.get()); m_PropertyPersistence.reset(new mitk::PropertyPersistence); context->RegisterService(m_PropertyPersistence.get()); m_PropertyRelations.reset(new mitk::PropertyRelations); context->RegisterService(m_PropertyRelations.get()); m_MimeTypeProvider.reset(new mitk::MimeTypeProvider); m_MimeTypeProvider->Start(); m_MimeTypeProviderReg = context->RegisterService(m_MimeTypeProvider.get()); this->RegisterDefaultMimeTypes(); this->RegisterItkReaderWriter(); this->RegisterVtkReaderWriter(); // Add custom Reader / Writer Services m_FileReaders.push_back(new mitk::PointSetReaderService()); m_FileWriters.push_back(new mitk::PointSetWriterService()); m_FileReaders.push_back(new mitk::GeometryDataReaderService()); m_FileWriters.push_back(new mitk::GeometryDataWriterService()); m_FileReaders.push_back(new mitk::RawImageFileReaderService()); /* There IS an option to exchange ALL vtkTexture instances against vtkNeverTranslucentTextureFactory. This code is left here as a reminder, just in case we might need to do that some time. vtkNeverTranslucentTextureFactory* textureFactory = vtkNeverTranslucentTextureFactory::New(); vtkObjectFactory::RegisterFactory( textureFactory ); textureFactory->Delete(); */ this->RegisterLegacyWriter(); + + m_Mappers.push_back(mitk::PointSetVtkMapper2D::New().GetPointer()); + m_Mappers.push_back(mitk::ImageVtkMapper2D::New().GetPointer()); } void MitkCoreActivator::Unload(us::ModuleContext *) { for (auto &elem : m_FileReaders) { delete elem; } for (auto &elem : m_FileWriters) { delete elem; } for (auto &elem : m_FileIOs) { delete elem; } for (auto &elem : m_LegacyWriters) { delete elem; } // The mitk::ModuleContext* argument of the Unload() method // will always be 0 for the Mitk library. It makes no sense // to use it at this stage anyway, since all libraries which // know about the module system have already been unloaded. // we need to close the internal service tracker of the // MimeTypeProvider class here. Otherwise it // would hold on to the ModuleContext longer than it is // actually valid. m_MimeTypeProviderReg.Unregister(); m_MimeTypeProvider->Stop(); for (std::vector::const_iterator mimeTypeIter = m_DefaultMimeTypes.begin(), iterEnd = m_DefaultMimeTypes.end(); mimeTypeIter != iterEnd; ++mimeTypeIter) { delete *mimeTypeIter; } } void MitkCoreActivator::RegisterDefaultMimeTypes() { // Register some default mime-types std::vector mimeTypes = mitk::IOMimeTypes::Get(); for (std::vector::const_iterator mimeTypeIter = mimeTypes.begin(), iterEnd = mimeTypes.end(); mimeTypeIter != iterEnd; ++mimeTypeIter) { m_DefaultMimeTypes.push_back(*mimeTypeIter); m_Context->RegisterService(m_DefaultMimeTypes.back()); } } void MitkCoreActivator::RegisterItkReaderWriter() { std::list allobjects = itk::ObjectFactoryBase::CreateAllInstance("itkImageIOBase"); for (auto &allobject : allobjects) { auto *io = dynamic_cast(allobject.GetPointer()); // NiftiImageIO does not provide a correct "SupportsDimension()" methods // and the supported read/write extensions are not ordered correctly if (dynamic_cast(io)) continue; // Use a custom mime-type for GDCMImageIO below if (dynamic_cast(allobject.GetPointer())) { // MITK provides its own DICOM reader (which internally uses GDCMImageIO). continue; } if (io) { m_FileIOs.push_back(new mitk::ItkImageIO(io)); } else { MITK_WARN << "Error ImageIO factory did not return an ImageIOBase: " << (allobject)->GetNameOfClass(); } } FixedNiftiImageIO::Pointer itkNiftiIO = FixedNiftiImageIO::New(); mitk::ItkImageIO *niftiIO = new mitk::ItkImageIO(mitk::IOMimeTypes::NIFTI_MIMETYPE(), itkNiftiIO.GetPointer(), 0); m_FileIOs.push_back(niftiIO); } void MitkCoreActivator::RegisterVtkReaderWriter() { m_FileIOs.push_back(new mitk::SurfaceVtkXmlIO()); m_FileIOs.push_back(new mitk::SurfaceStlIO()); m_FileIOs.push_back(new mitk::SurfaceVtkLegacyIO()); m_FileIOs.push_back(new mitk::ImageVtkXmlIO()); m_FileIOs.push_back(new mitk::ImageVtkLegacyIO()); } void MitkCoreActivator::RegisterLegacyWriter() { std::list allobjects = itk::ObjectFactoryBase::CreateAllInstance("IOWriter"); for (auto i = allobjects.begin(); i != allobjects.end(); ++i) { mitk::FileWriter::Pointer io = dynamic_cast(i->GetPointer()); if (io) { std::string description = std::string("Legacy ") + io->GetNameOfClass() + " Writer"; mitk::IFileWriter *writer = new mitk::LegacyFileWriterService(io, description); m_LegacyWriters.push_back(writer); } else { MITK_ERROR << "Error IOWriter override is not of type mitk::FileWriter: " << (*i)->GetNameOfClass() << std::endl; } } } US_EXPORT_MODULE_ACTIVATOR(MitkCoreActivator) // Call CppMicroservices initialization code at the end of the file. // This especially ensures that VTK object factories have already // been registered (VTK initialization code is injected by implicitly // include VTK header files at the top of this file). US_INITIALIZE_MODULE diff --git a/Modules/Core/src/mitkCoreActivator.h b/Modules/Core/src/mitkCoreActivator.h index e0a010576d..e5923c54c4 100644 --- a/Modules/Core/src/mitkCoreActivator.h +++ b/Modules/Core/src/mitkCoreActivator.h @@ -1,84 +1,87 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef MITKCOREACTIVATOR_H_ #define MITKCOREACTIVATOR_H_ // File IO #include #include #include #include #include #include #include #include #include #include #include // Micro Services #include #include #include #include #include +#include "mitkVtkMapper.h" /* * This is the module activator for the "Mitk" module. It registers core services * like ... */ class MitkCoreActivator : public us::ModuleActivator { public: void Load(us::ModuleContext *context) override; void Unload(us::ModuleContext *) override; private: void HandleModuleEvent(const us::ModuleEvent moduleEvent); void RegisterDefaultMimeTypes(); void RegisterItkReaderWriter(); void RegisterVtkReaderWriter(); void RegisterLegacyWriter(); // mitk::RenderingManager::Pointer m_RenderingManager; std::unique_ptr m_PlanePositionManager; std::unique_ptr m_PropertyAliases; std::unique_ptr m_PropertyDescriptions; std::unique_ptr m_PropertyExtensions; std::unique_ptr m_PropertyFilters; std::unique_ptr m_PropertyPersistence; std::unique_ptr m_PropertyRelations; std::unique_ptr m_MimeTypeProvider; // File IO std::vector m_FileReaders; std::vector m_FileWriters; std::vector m_FileIOs; std::vector m_LegacyWriters; std::vector m_DefaultMimeTypes; + std::vector> m_Mappers; + us::ServiceRegistration m_MimeTypeProviderReg; us::ModuleContext *m_Context; }; #endif // MITKCOREACTIVATOR_H_ diff --git a/Modules/Core/src/mitkCoreObjectFactory.cpp b/Modules/Core/src/mitkCoreObjectFactory.cpp index 5ee3a790aa..3a0a11a7d5 100644 --- a/Modules/Core/src/mitkCoreObjectFactory.cpp +++ b/Modules/Core/src/mitkCoreObjectFactory.cpp @@ -1,458 +1,487 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ -#include "mitkCoreObjectFactory.h" #include "mitkConfig.h" +#include "mitkCoreObjectFactory.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 "usLDAPProp.h" #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 (std::exception const &e) { MITK_ERROR << "Caugt 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 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 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::ImageVtkMapper2D::SetDefaultProperties(node); } mitk::PlaneGeometryData::Pointer planeGeometry = dynamic_cast(node->GetData()); if (planeGeometry.IsNotNull()) { mitk::PlaneGeometryDataMapper2D::SetDefaultProperties(node); } mitk::Surface::Pointer surface = dynamic_cast(node->GetData()); if (surface.IsNotNull()) { mitk::SurfaceVtkMapper2D::SetDefaultProperties(node); mitk::SurfaceVtkMapper3D::SetDefaultProperties(node); } mitk::PointSet::Pointer pointSet = dynamic_cast(node->GetData()); if (pointSet.IsNotNull()) { mitk::PointSetVtkMapper2D::SetDefaultProperties(node); mitk::PointSetVtkMapper3D::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; } + // Try getting a service + if (newMapper.IsNull() && node->GetData() != nullptr) + { + // service query + std::string filter = us::LDAPProp("PROP_SUPPORTED_DATA_TYPE_NAME") == typeid(*node->GetData()).name() && + us::LDAPProp("PROP_SUPPORTED_SLOT_ID") == id; + auto refs = us::GetModuleContext()->GetServiceReferences(filter); + + // service ranking + us::ServiceReference highestRankedRef; + int highestPriority = 0; + for (auto ref : refs) + { + if (ref.GetProperty("PROP_PRIORITY").Type() == typeid(int)) + { + int refPriority = us::any_cast(ref.GetProperty("PROP_PRIORITY")); + if (refPriority > highestPriority) + { + highestPriority = refPriority; + highestRankedRef = ref; + } + } + } + // service aquisition + try + { + // promised by the library to return false when invalid (default constructed) + if (highestRankedRef) + { + newMapper = us::GetModuleContext()->GetServiceObjects(highestRankedRef).GetService(); + newMapper->SetDataNode(node); + } + } + catch (std::exception &e) + { + // Todo check for possible memory leak + MITK_ERROR << "Tried getting a mapper service got: " << e.what(); + newMapper = nullptr; + } + } + 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; } const char *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")); */ } const char *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) -{ -} +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) + for (auto it = fileExtensionMap.begin(); it != fileExtensionMap.end(); ++it) if (it->first == extension) return it->second; return ""; // If no matching extension was found, return emtpy 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) + 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); + 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(); + 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); + auto iter = m_LegacyWriters.find(factory); if (iter != m_LegacyWriters.end()) { for (auto &elem : iter->second) { delete elem; } m_LegacyWriters.erase(iter); } }