diff --git a/CMakeExternals/VTK.cmake b/CMakeExternals/VTK.cmake index e1c02d0d21..6e186c4776 100644 --- a/CMakeExternals/VTK.cmake +++ b/CMakeExternals/VTK.cmake @@ -1,113 +1,101 @@ #----------------------------------------------------------------------------- # VTK #----------------------------------------------------------------------------- if(WIN32) option(VTK_USE_SYSTEM_FREETYPE OFF) else(WIN32) option(VTK_USE_SYSTEM_FREETYPE ON) endif(WIN32) # Sanity checks if(DEFINED VTK_DIR AND NOT EXISTS ${VTK_DIR}) message(FATAL_ERROR "VTK_DIR variable is defined but corresponds to non-existing directory") endif() set(proj VTK) set(proj_DEPENDENCIES ) set(VTK_DEPENDS ${proj}) if(MITK_USE_HDF5) list(APPEND proj_DEPENDENCIES HDF5) endif() if(NOT DEFINED VTK_DIR) set(additional_cmake_args ) - if(MINGW) - set(additional_cmake_args - -DCMAKE_USE_WIN32_THREADS:BOOL=ON - -DCMAKE_USE_PTHREADS:BOOL=OFF - -DVTK_USE_VIDEO4WINDOWS:BOOL=OFF # no header files provided by MinGW - ) - endif() # Optionally enable memory leak checks for any objects derived from vtkObject. This # will force unit tests to fail if they have any of these memory leaks. option(MITK_VTK_DEBUG_LEAKS OFF) mark_as_advanced(MITK_VTK_DEBUG_LEAKS) list(APPEND additional_cmake_args -DVTK_DEBUG_LEAKS:BOOL=${MITK_VTK_DEBUG_LEAKS} ) if(MITK_USE_Python) if(NOT MITK_USE_SYSTEM_PYTHON) list(APPEND proj_DEPENDENCIES Python) set(_vtk_install_python_dir -DVTK_INSTALL_PYTHON_MODULE_DIR:FILEPATH=${MITK_PYTHON_SITE_DIR}) else() set(_vtk_install_python_dir -DVTK_INSTALL_PYTHON_MODULE_DIR:PATH=${ep_prefix}/lib/python${PYTHON_VERSION_MAJOR}.${PYTHON_VERSION_MINOR}/site-packages) endif() list(APPEND additional_cmake_args -DVTK_WRAP_PYTHON:BOOL=ON -DVTK_USE_TK:BOOL=OFF -DVTK_WINDOWS_PYTHON_DEBUGGABLE:BOOL=OFF -DPYTHON_EXECUTABLE:FILEPATH=${PYTHON_EXECUTABLE} -DPYTHON_INCLUDE_DIR:PATH=${PYTHON_INCLUDE_DIR} -DPYTHON_INCLUDE_DIR2:PATH=${PYTHON_INCLUDE_DIR2} -DPYTHON_LIBRARY:FILEPATH=${PYTHON_LIBRARY} ${_vtk_install_python_dir} ) else() list(APPEND additional_cmake_args -DVTK_WRAP_PYTHON:BOOL=OFF -DVTK_WINDOWS_PYTHON_DEBUGGABLE:BOOL=OFF ) endif() if(MITK_USE_Qt5) list(APPEND additional_cmake_args - -DVTK_QT_VERSION:STRING=5 -DVTK_Group_Qt:BOOL=ON - -DVTK_INSTALL_NO_QT_PLUGIN:BOOL=ON ) endif() if(CTEST_USE_LAUNCHERS) list(APPEND additional_cmake_args "-DCMAKE_PROJECT_${proj}_INCLUDE:FILEPATH=${CMAKE_ROOT}/Modules/CTestUseLaunchers.cmake" ) endif() ExternalProject_Add(${proj} LIST_SEPARATOR ${sep} - URL ${MITK_THIRDPARTY_DOWNLOAD_PREFIX_URL}/VTK-8.0.0.tar.gz - URL_MD5 8de89b8c7a729016ab7128da5e881cf4 + URL ${MITK_THIRDPARTY_DOWNLOAD_PREFIX_URL}/VTK-8.1.0.tar.gz + URL_MD5 4fa5eadbc8723ba0b8d203f05376d932 CMAKE_GENERATOR ${gen} CMAKE_ARGS ${ep_common_args} -DVTK_WRAP_TCL:BOOL=OFF -DVTK_WRAP_PYTHON:BOOL=OFF -DVTK_WRAP_JAVA:BOOL=OFF -DVTK_USE_SYSTEM_FREETYPE:BOOL=${VTK_USE_SYSTEM_FREETYPE} -DVTK_LEGACY_REMOVE:BOOL=ON -DModule_vtkTestingRendering:BOOL=ON - -DVTK_MAKE_INSTANTIATORS:BOOL=ON - -DVTK_USE_CXX11_FEATURES:BOOL=ON - -DVTK_RENDERING_BACKEND:STRING=OpenGL2 ${additional_cmake_args} CMAKE_CACHE_ARGS ${ep_common_cache_args} CMAKE_CACHE_DEFAULT_ARGS ${ep_common_cache_default_args} DEPENDS ${proj_DEPENDENCIES} ) set(VTK_DIR ${ep_prefix}) mitkFunctionInstallExternalCMakeProject(${proj}) else() mitkMacroEmptyExternalProject(${proj} "${proj_DEPENDENCIES}") endif() diff --git a/Modules/Core/include/mitkVtkMapper.h b/Modules/Core/include/mitkVtkMapper.h index 151c62eef3..82544243ab 100644 --- a/Modules/Core/include/mitkVtkMapper.h +++ b/Modules/Core/include/mitkVtkMapper.h @@ -1,161 +1,155 @@ /*=================================================================== 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. ===================================================================*/ // change number #ifndef VTKMAPPER_H_HEADER_INCLUDED_C1C5453B #define VTKMAPPER_H_HEADER_INCLUDED_C1C5453B #include "mitkBaseRenderer.h" #include "mitkDataNode.h" #include "mitkLocalStorageHandler.h" #include "mitkMapper.h" #include "mitkVtkPropRenderer.h" #include #include #include #include #include #include #include #include #include #include class vtkProp; class vtkProp3D; class vtkActor; namespace mitk { /** \brief Base class of all Vtk Mappers in order to display primitives * by exploiting Vtk functionality. * * Rendering of opaque, translucent or volumetric geometry and overlays * is done in consecutive render passes. * * \ingroup Mapper */ class MITKCORE_EXPORT VtkMapper : public Mapper { public: mitkClassMacro(VtkMapper, Mapper); virtual vtkProp *GetVtkProp(mitk::BaseRenderer *renderer) = 0; - /** \brief Re-issues all drawing commands required to describe - * the entire scene each time a new frame is required, - * regardless of actual changes. - */ - static void SetVtkMapperImmediateModeRendering(vtkMapper *mapper); - /** * \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 */ DEPRECATED(virtual bool IsVtkBased() const override); /** \brief Determines which geometry should be rendered * (opaque, translucent, volumetric, overlay) * and calls the appropriate function. * * Called by mitk::VtkPropRenderer::Render */ void MitkRender(mitk::BaseRenderer *renderer, mitk::VtkPropRenderer::RenderType type) override; /** \brief Checks visibility and renders the overlay */ virtual void MitkRenderOverlay(BaseRenderer *renderer); /** \brief Checks visibility and renders untransparent geometry */ virtual void MitkRenderOpaqueGeometry(BaseRenderer *renderer); /** \brief Checks visiblity and renders transparent geometry */ virtual void MitkRenderTranslucentGeometry(BaseRenderer *renderer); /** \brief Checks visibility and renders volumes */ virtual void MitkRenderVolumetricGeometry(BaseRenderer *renderer); /** \brief Returns true if this mapper owns the specified vtkProp for * the given BaseRenderer. * * Note: returns false by default; should be implemented for VTK-based * Mapper subclasses. */ virtual bool HasVtkProp(const vtkProp *prop, BaseRenderer *renderer); /** \brief Set the vtkTransform of the m_Prop3D for * the current time step of \a renderer * * Called by mitk::VtkPropRenderer::Update before rendering. This * method will transform all actors (e.g. of an vtkAssembly) according * the geometry. * * \warning This method transforms only props which derive * from vtkProp3D. Make sure to use vtkAssembly, if you have * multiple props. vtkPropAssembly does not work, since it derives * from vtkProp. */ virtual void UpdateVtkTransform(mitk::BaseRenderer *renderer); /** * \brief Apply color and opacity properties read from the PropertyList * \deprecatedSince{2013_03} Use ApplyColorAndOpacityProperties(mitk::BaseRenderer* renderer, vtkActor * actor) * instead */ DEPRECATED(inline virtual void ApplyProperties(vtkActor *actor, mitk::BaseRenderer *renderer)) { ApplyColorAndOpacityProperties(renderer, actor); } /** * \deprecatedSince{2017_08} */ DEPRECATED(void ApplyShaderProperties(mitk::BaseRenderer *)){} /** * \brief Apply color and opacity properties read from the PropertyList. * Called by mapper subclasses. */ void ApplyColorAndOpacityProperties(mitk::BaseRenderer *renderer, vtkActor *actor) override; /** * \brief Release vtk-based graphics resources that are being consumed by this mapper. * * Method called by mitk::VtkPropRenderer. The parameter renderer could be used to * determine which graphic resources to release. The local storage is accessible * by the parameter renderer. Should be overwritten in subclasses. */ virtual void ReleaseGraphicsResources(mitk::BaseRenderer * /*renderer*/) {} class LocalStorage : public mitk::Mapper::BaseLocalStorage { }; protected: /** constructor */ VtkMapper(); /** virtual destructor in order to derive from this class */ ~VtkMapper() override; private: /** copy constructor */ VtkMapper(const VtkMapper &); /** assignment operator */ VtkMapper &operator=(const VtkMapper &); }; } // namespace mitk #endif /* VTKMAPPER_H_HEADER_INCLUDED_C1C5453B */ diff --git a/Modules/Core/src/Rendering/mitkImageVtkMapper2D.cpp b/Modules/Core/src/Rendering/mitkImageVtkMapper2D.cpp index c2d0a577b1..bf8dae62d6 100644 --- a/Modules/Core/src/Rendering/mitkImageVtkMapper2D.cpp +++ b/Modules/Core/src/Rendering/mitkImageVtkMapper2D.cpp @@ -1,1126 +1,1124 @@ /*=================================================================== 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() { } 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); - SetVtkMapperImmediateModeRendering(localStorage->m_Mapper); - 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->MapColorScalarsThroughLookupTableOff(); + 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/mitkPlaneGeometryDataVtkMapper3D.cpp b/Modules/Core/src/Rendering/mitkPlaneGeometryDataVtkMapper3D.cpp index 3b86f21e6a..b6be815b6c 100644 --- a/Modules/Core/src/Rendering/mitkPlaneGeometryDataVtkMapper3D.cpp +++ b/Modules/Core/src/Rendering/mitkPlaneGeometryDataVtkMapper3D.cpp @@ -1,589 +1,584 @@ /*=================================================================== 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 "mitkPlaneGeometryDataVtkMapper3D.h" #include "mitkImageVtkMapper2D.h" #include "mitkNodePredicateDataType.h" #include "mitkNodePredicateOr.h" #include "mitkSmartPointerProperty.h" #include "mitkSurface.h" #include "mitkVtkRepresentationProperty.h" #include "mitkWeakPointerProperty.h" #include "vtkMitkLevelWindowFilter.h" #include "vtkNeverTranslucentTexture.h" #include #include #include #include #include #include #include #include #include #include #include #include namespace mitk { PlaneGeometryDataVtkMapper3D::PlaneGeometryDataVtkMapper3D() : m_NormalsActorAdded(false), m_DataStorage(nullptr) { m_EdgeTuber = vtkTubeFilter::New(); m_EdgeMapper = vtkPolyDataMapper::New(); m_SurfaceCreator = PlaneGeometryDataToSurfaceFilter::New(); m_SurfaceCreatorBoundingBox = BoundingBox::New(); m_SurfaceCreatorPointsContainer = BoundingBox::PointsContainer::New(); m_Edges = vtkFeatureEdges::New(); m_Edges->BoundaryEdgesOn(); m_Edges->FeatureEdgesOff(); m_Edges->NonManifoldEdgesOff(); m_Edges->ManifoldEdgesOff(); m_EdgeTransformer = vtkTransformPolyDataFilter::New(); m_NormalsTransformer = vtkTransformPolyDataFilter::New(); m_EdgeActor = vtkActor::New(); m_BackgroundMapper = vtkPolyDataMapper::New(); m_BackgroundActor = vtkActor::New(); m_Prop3DAssembly = vtkAssembly::New(); m_ImageAssembly = vtkAssembly::New(); m_SurfaceCreatorBoundingBox->SetPoints(m_SurfaceCreatorPointsContainer); m_Cleaner = vtkCleanPolyData::New(); m_Cleaner->PieceInvariantOn(); m_Cleaner->ConvertLinesToPointsOn(); m_Cleaner->ConvertPolysToLinesOn(); m_Cleaner->ConvertStripsToPolysOn(); m_Cleaner->PointMergingOn(); // Make sure that the FeatureEdge algorithm is initialized with a "valid" // (though empty) input vtkPolyData *emptyPolyData = vtkPolyData::New(); m_Cleaner->SetInputData(emptyPolyData); emptyPolyData->Delete(); m_Edges->SetInputConnection(m_Cleaner->GetOutputPort()); m_EdgeTransformer->SetInputConnection(m_Edges->GetOutputPort()); m_EdgeTuber->SetInputConnection(m_EdgeTransformer->GetOutputPort()); m_EdgeTuber->SetVaryRadiusToVaryRadiusOff(); m_EdgeTuber->SetNumberOfSides(12); m_EdgeTuber->CappingOn(); m_EdgeMapper->SetInputConnection(m_EdgeTuber->GetOutputPort()); m_EdgeMapper->ScalarVisibilityOff(); m_BackgroundMapper->SetInputData(emptyPolyData); m_BackgroundMapper->Update(); m_EdgeActor->SetMapper(m_EdgeMapper); m_BackgroundActor->GetProperty()->SetAmbient(0.5); m_BackgroundActor->GetProperty()->SetColor(0.0, 0.0, 0.0); m_BackgroundActor->GetProperty()->SetOpacity(0.0); m_BackgroundActor->SetMapper(m_BackgroundMapper); vtkProperty *backfaceProperty = m_BackgroundActor->MakeProperty(); backfaceProperty->SetColor(0.0, 0.0, 0.0); m_BackgroundActor->SetBackfaceProperty(backfaceProperty); backfaceProperty->Delete(); m_FrontHedgeHog = vtkHedgeHog::New(); m_BackHedgeHog = vtkHedgeHog::New(); m_FrontNormalsMapper = vtkPolyDataMapper::New(); m_FrontNormalsMapper->SetInputConnection(m_FrontHedgeHog->GetOutputPort()); m_BackNormalsMapper = vtkPolyDataMapper::New(); m_Prop3DAssembly->AddPart(m_EdgeActor); m_Prop3DAssembly->AddPart(m_ImageAssembly); m_FrontNormalsActor = vtkActor::New(); m_FrontNormalsActor->SetMapper(m_FrontNormalsMapper); m_BackNormalsActor = vtkActor::New(); m_BackNormalsActor->SetMapper(m_BackNormalsMapper); m_ImageMapperDeletedCommand = MemberCommandType::New(); m_ImageMapperDeletedCommand->SetCallbackFunction(this, &PlaneGeometryDataVtkMapper3D::ImageMapperDeletedCallback); } PlaneGeometryDataVtkMapper3D::~PlaneGeometryDataVtkMapper3D() { m_ImageAssembly->Delete(); m_Prop3DAssembly->Delete(); m_EdgeTuber->Delete(); m_EdgeMapper->Delete(); m_EdgeTransformer->Delete(); m_Cleaner->Delete(); m_Edges->Delete(); m_NormalsTransformer->Delete(); m_EdgeActor->Delete(); m_BackgroundMapper->Delete(); m_BackgroundActor->Delete(); m_FrontNormalsMapper->Delete(); m_FrontNormalsActor->Delete(); m_FrontHedgeHog->Delete(); m_BackNormalsMapper->Delete(); m_BackNormalsActor->Delete(); m_BackHedgeHog->Delete(); for (auto it = m_ImageActors.begin(); it != m_ImageActors.end(); ++it) it->second.m_Actor->ReleaseGraphicsResources(nullptr); // Delete entries in m_ImageActors list one by one m_ImageActors.clear(); m_DataStorage = nullptr; } vtkProp *PlaneGeometryDataVtkMapper3D::GetVtkProp(mitk::BaseRenderer * /*renderer*/) { if ((this->GetDataNode() != nullptr) && (m_ImageAssembly != nullptr)) { // Do not transform the entire Prop3D assembly, but only the image part // here. The colored frame is transformed elsewhere (via m_EdgeTransformer), // since only vertices should be transformed there, not the poly data // itself, to avoid distortion for anisotropic datasets. m_ImageAssembly->SetUserTransform(this->GetDataNode()->GetVtkTransform()); } return m_Prop3DAssembly; } void PlaneGeometryDataVtkMapper3D::UpdateVtkTransform(mitk::BaseRenderer * /*renderer*/) { m_ImageAssembly->SetUserTransform(this->GetDataNode()->GetVtkTransform(this->GetTimestep())); } const PlaneGeometryData *PlaneGeometryDataVtkMapper3D::GetInput() { return static_cast(GetDataNode()->GetData()); } void PlaneGeometryDataVtkMapper3D::SetDataStorageForTexture(mitk::DataStorage *storage) { if (storage != nullptr && m_DataStorage != storage) { m_DataStorage = storage; this->Modified(); } } void PlaneGeometryDataVtkMapper3D::ImageMapperDeletedCallback(itk::Object *caller, const itk::EventObject & /*event*/) { auto *imageMapper = dynamic_cast(caller); if ((imageMapper != nullptr)) { if (m_ImageActors.count(imageMapper) > 0) { m_ImageActors[imageMapper].m_Sender = nullptr; // sender is already destroying itself m_ImageActors.erase(imageMapper); } } } void PlaneGeometryDataVtkMapper3D::GenerateDataForRenderer(BaseRenderer *renderer) { - SetVtkMapperImmediateModeRendering(m_EdgeMapper); - SetVtkMapperImmediateModeRendering(m_BackgroundMapper); - // Remove all actors from the assembly, and re-initialize it with the // edge actor m_ImageAssembly->GetParts()->RemoveAllItems(); bool visible = true; GetDataNode()->GetVisibility(visible, renderer, "visible"); if (!visible) { // visibility has explicitly to be set in the single actors // due to problems when using cell picking: // even if the assembly is invisible, the renderer contains // references to the assemblies parts. During picking the // visibility of each part is checked, and not only for the // whole assembly. m_ImageAssembly->VisibilityOff(); m_EdgeActor->VisibilityOff(); return; } // visibility has explicitly to be set in the single actors // due to problems when using cell picking: // even if the assembly is invisible, the renderer contains // references to the assemblies parts. During picking the // visibility of each part is checked, and not only for the // whole assembly. m_ImageAssembly->VisibilityOn(); bool drawEdges = true; this->GetDataNode()->GetBoolProperty("draw edges", drawEdges, renderer); m_EdgeActor->SetVisibility(drawEdges); PlaneGeometryData::ConstPointer input = this->GetInput(); if (input.IsNotNull() && (input->GetPlaneGeometry() != nullptr)) { SmartPointerProperty::Pointer surfacecreatorprop; surfacecreatorprop = dynamic_cast(GetDataNode()->GetProperty("surfacegeometry", renderer)); if ((surfacecreatorprop.IsNull()) || (surfacecreatorprop->GetSmartPointer().IsNull()) || ((m_SurfaceCreator = dynamic_cast(surfacecreatorprop->GetSmartPointer().GetPointer())) .IsNull())) { m_SurfaceCreator->PlaceByGeometryOn(); surfacecreatorprop = SmartPointerProperty::New(m_SurfaceCreator); GetDataNode()->SetProperty("surfacegeometry", surfacecreatorprop); } m_SurfaceCreator->SetInput(input); int res; if (GetDataNode()->GetIntProperty("xresolution", res, renderer)) { m_SurfaceCreator->SetXResolution(res); } if (GetDataNode()->GetIntProperty("yresolution", res, renderer)) { m_SurfaceCreator->SetYResolution(res); } double tubeRadius = 1.0; // Radius of tubular edge surrounding plane // Clip the PlaneGeometry with the reference geometry bounds (if available) if (input->GetPlaneGeometry()->HasReferenceGeometry()) { const BaseGeometry *referenceGeometry = input->GetPlaneGeometry()->GetReferenceGeometry(); BoundingBox::PointType boundingBoxMin, boundingBoxMax; boundingBoxMin = referenceGeometry->GetBoundingBox()->GetMinimum(); boundingBoxMax = referenceGeometry->GetBoundingBox()->GetMaximum(); if (referenceGeometry->GetImageGeometry()) { for (unsigned int i = 0; i < 3; ++i) { boundingBoxMin[i] -= 0.5; boundingBoxMax[i] -= 0.5; } } m_SurfaceCreatorPointsContainer->CreateElementAt(0) = boundingBoxMin; m_SurfaceCreatorPointsContainer->CreateElementAt(1) = boundingBoxMax; m_SurfaceCreatorBoundingBox->ComputeBoundingBox(); m_SurfaceCreator->SetBoundingBox(m_SurfaceCreatorBoundingBox); tubeRadius = referenceGeometry->GetDiagonalLength() / 450.0; } // If no reference geometry is available, clip with the current global // bounds else if (!m_DataStorage.IsExpired()) { m_SurfaceCreator->SetBoundingBox(m_DataStorage.Lock()->ComputeVisibleBoundingBox(nullptr, "includeInBoundingBox")); tubeRadius = sqrt(m_SurfaceCreator->GetBoundingBox()->GetDiagonalLength2()) / 450.0; } // Calculate the surface of the PlaneGeometry m_SurfaceCreator->Update(); Surface *surface = m_SurfaceCreator->GetOutput(); // Check if there's something to display, otherwise return if ((surface->GetVtkPolyData() == nullptr) || (surface->GetVtkPolyData()->GetNumberOfCells() == 0)) { m_ImageAssembly->VisibilityOff(); return; } // add a graphical representation of the surface normals if requested DataNode *node = this->GetDataNode(); bool displayNormals = false; bool colorTwoSides = false; bool invertNormals = false; node->GetBoolProperty("draw normals 3D", displayNormals, renderer); node->GetBoolProperty("color two sides", colorTwoSides, renderer); node->GetBoolProperty("invert normals", invertNormals, renderer); // if we want to draw the display normals or render two sides we have to get the colors if (displayNormals || colorTwoSides) { // get colors float frontColor[3] = {0.0, 0.0, 1.0}; node->GetColor(frontColor, renderer, "front color"); float backColor[3] = {1.0, 0.0, 0.0}; node->GetColor(backColor, renderer, "back color"); if (displayNormals) { m_NormalsTransformer->SetInputData(surface->GetVtkPolyData()); m_NormalsTransformer->SetTransform(node->GetVtkTransform(this->GetTimestep())); m_FrontHedgeHog->SetInputConnection(m_NormalsTransformer->GetOutputPort()); m_FrontHedgeHog->SetVectorModeToUseNormal(); m_FrontHedgeHog->SetScaleFactor(invertNormals ? 1.0 : -1.0); m_FrontHedgeHog->Update(); m_FrontNormalsActor->GetProperty()->SetColor(frontColor[0], frontColor[1], frontColor[2]); m_BackHedgeHog->SetInputConnection(m_NormalsTransformer->GetOutputPort()); m_BackHedgeHog->SetVectorModeToUseNormal(); m_BackHedgeHog->SetScaleFactor(invertNormals ? -1.0 : 1.0); m_BackHedgeHog->Update(); m_BackNormalsActor->GetProperty()->SetColor(backColor[0], backColor[1], backColor[2]); // if there is no actor added yet, add one if (!m_NormalsActorAdded) { m_Prop3DAssembly->AddPart(m_FrontNormalsActor); m_Prop3DAssembly->AddPart(m_BackNormalsActor); m_NormalsActorAdded = true; } } // if we don't want to display normals AND there is an actor added remove the actor else if (m_NormalsActorAdded) { m_Prop3DAssembly->RemovePart(m_FrontNormalsActor); m_Prop3DAssembly->RemovePart(m_BackNormalsActor); m_NormalsActorAdded = false; } if (colorTwoSides) { if (!invertNormals) { m_BackgroundActor->GetProperty()->SetColor(backColor[0], backColor[1], backColor[2]); m_BackgroundActor->GetBackfaceProperty()->SetColor(frontColor[0], frontColor[1], frontColor[2]); } else { m_BackgroundActor->GetProperty()->SetColor(frontColor[0], frontColor[1], frontColor[2]); m_BackgroundActor->GetBackfaceProperty()->SetColor(backColor[0], backColor[1], backColor[2]); } } } // Add black background for all images (which may be transparent) m_BackgroundMapper->SetInputData(surface->GetVtkPolyData()); // m_ImageAssembly->AddPart(m_BackgroundActor); LayerSortedActorList layerSortedActors; // Traverse the data tree to find nodes resliced by ImageMapperGL2D // use a predicate to get all data nodes which are "images" or inherit from mitk::Image mitk::TNodePredicateDataType::Pointer predicateAllImages = mitk::TNodePredicateDataType::New(); mitk::DataStorage::SetOfObjects::ConstPointer all = m_DataStorage.Lock()->GetSubset(predicateAllImages); // process all found images for (mitk::DataStorage::SetOfObjects::ConstIterator it = all->Begin(); it != all->End(); ++it) { DataNode *node = it->Value(); if (node != nullptr) this->ProcessNode(node, renderer, surface, layerSortedActors); } // Add all image actors to the assembly, sorted according to // layer property LayerSortedActorList::iterator actorIt; for (actorIt = layerSortedActors.begin(); actorIt != layerSortedActors.end(); ++actorIt) { m_ImageAssembly->AddPart(actorIt->second); } // Configurate the tube-shaped frame: size according to the surface // bounds, color as specified in the plane's properties vtkPolyData *surfacePolyData = surface->GetVtkPolyData(); m_Cleaner->SetInputData(surfacePolyData); m_EdgeTransformer->SetTransform(this->GetDataNode()->GetVtkTransform(this->GetTimestep())); // Adjust the radius according to extent m_EdgeTuber->SetRadius(tubeRadius); // Get the plane's color and set the tube properties accordingly ColorProperty::Pointer colorProperty; colorProperty = dynamic_cast(this->GetDataNode()->GetProperty("color")); if (colorProperty.IsNotNull()) { const Color &color = colorProperty->GetColor(); m_EdgeActor->GetProperty()->SetColor(color.GetRed(), color.GetGreen(), color.GetBlue()); } else { m_EdgeActor->GetProperty()->SetColor(1.0, 1.0, 1.0); } m_ImageAssembly->SetUserTransform(this->GetDataNode()->GetVtkTransform(this->GetTimestep())); } VtkRepresentationProperty *representationProperty; this->GetDataNode()->GetProperty(representationProperty, "material.representation", renderer); if (representationProperty != nullptr) m_BackgroundActor->GetProperty()->SetRepresentation(representationProperty->GetVtkRepresentation()); } void PlaneGeometryDataVtkMapper3D::ProcessNode(DataNode *node, BaseRenderer *renderer, Surface *surface, LayerSortedActorList &layerSortedActors) { if (node != nullptr) { // we need to get the information from the 2D mapper to render the texture on the 3D plane auto *imageMapper = dynamic_cast(node->GetMapper(1)); // GetMapper(1) provides the 2D mapper for the data node // if there is a 2D mapper, which is not the standard image mapper... if (!imageMapper && node->GetMapper(1)) { //... check if it is the composite mapper std::string cname(node->GetMapper(1)->GetNameOfClass()); if (!cname.compare("CompositeMapper")) // string.compare returns 0 if the two strings are equal. { // get the standard image mapper. // This is a special case in MITK and does only work for the CompositeMapper. imageMapper = dynamic_cast(node->GetMapper(3)); } } if ((node->IsVisible(renderer)) && imageMapper) { WeakPointerProperty::Pointer rendererProp = dynamic_cast(GetDataNode()->GetPropertyList()->GetProperty("renderer")); if (rendererProp.IsNotNull()) { BaseRenderer::Pointer planeRenderer = dynamic_cast(rendererProp->GetWeakPointer().GetPointer()); // Retrieve and update image to be mapped const ImageVtkMapper2D::LocalStorage *localStorage = imageMapper->GetLocalStorage(planeRenderer); if (planeRenderer.IsNotNull()) { // perform update of imagemapper if needed (maybe the respective 2D renderwindow is not rendered/update // before) imageMapper->Update(planeRenderer); // If it has not been initialized already in a previous pass, // generate an actor and a texture object to // render the image associated with the ImageVtkMapper2D. vtkActor *imageActor; vtkDataSetMapper *dataSetMapper = nullptr; vtkTexture *texture; if (m_ImageActors.count(imageMapper) == 0) { dataSetMapper = vtkDataSetMapper::New(); - // Enable rendering without copying the image. - dataSetMapper->ImmediateModeRenderingOn(); texture = vtkNeverTranslucentTexture::New(); texture->RepeatOff(); imageActor = vtkActor::New(); imageActor->SetMapper(dataSetMapper); imageActor->SetTexture(texture); imageActor->GetProperty()->SetOpacity( 0.999); // HACK! otherwise VTK wouldn't recognize this as translucent // surface (if LUT values map to alpha < 255 // improvement: apply "opacity" property onle HERE and also in 2D image mapper. DO NOT change LUT to // achieve // translucent images (see method ChangeOpacity in image mapper 2D) // Make imageActor the sole owner of the mapper and texture // objects dataSetMapper->UnRegister(nullptr); texture->UnRegister(nullptr); // Store the actor so that it may be accessed in following // passes. m_ImageActors[imageMapper].Initialize(imageActor, imageMapper, m_ImageMapperDeletedCommand); } else { // Else, retrieve the actor and associated objects from the // previous pass. imageActor = m_ImageActors[imageMapper].m_Actor; dataSetMapper = (vtkDataSetMapper *)imageActor->GetMapper(); texture = imageActor->GetTexture(); } // Set poly data new each time its object changes (e.g. when // switching between planar and curved geometries) if ((dataSetMapper != nullptr) && (dataSetMapper->GetInput() != surface->GetVtkPolyData())) { dataSetMapper->SetInputData(surface->GetVtkPolyData()); } dataSetMapper->Update(); // Check if the m_ReslicedImage is nullptr. // This is the case when no image geometry is met by // the reslicer. In that case, the texture has to be // empty (black) and we don't have to do anything. // See fixed bug #13275 if (localStorage->m_ReslicedImage != nullptr) { texture->SetInputConnection(localStorage->m_LevelWindowFilter->GetOutputPort()); // do not use a VTK lookup table (we do that ourselves in m_LevelWindowFilter) - texture->MapColorScalarsThroughLookupTableOff(); + texture->SetColorModeToDirectScalars(); // re-use properties from the 2D image mapper imageActor->SetProperty(localStorage->m_Actor->GetProperty()); imageActor->GetProperty()->SetAmbient(0.5); // Set texture interpolation on/off bool textureInterpolation = node->IsOn("texture interpolation", renderer); texture->SetInterpolate(textureInterpolation); // Store this actor to be added to the actor assembly, sort // by layer int layer = 1; node->GetIntProperty("layer", layer); layerSortedActors.insert(std::pair(layer, imageActor)); } } } } } } void PlaneGeometryDataVtkMapper3D::ActorInfo::Initialize(vtkActor *actor, itk::Object *sender, itk::Command *command) { m_Actor = actor; m_Sender = sender; // Get informed when ImageMapper object is deleted, so that // the data structures built here can be deleted as well m_ObserverID = sender->AddObserver(itk::DeleteEvent(), command); } PlaneGeometryDataVtkMapper3D::ActorInfo::ActorInfo() : m_Actor(nullptr), m_Sender(nullptr), m_ObserverID(0) {} PlaneGeometryDataVtkMapper3D::ActorInfo::~ActorInfo() { if (m_Sender != nullptr) { m_Sender->RemoveObserver(m_ObserverID); } if (m_Actor != nullptr) { m_Actor->ReleaseGraphicsResources(nullptr); m_Actor->Delete(); } } } // namespace mitk diff --git a/Modules/Core/src/Rendering/mitkPointSetVtkMapper3D.cpp b/Modules/Core/src/Rendering/mitkPointSetVtkMapper3D.cpp index 21834c5db9..baf9b720ce 100644 --- a/Modules/Core/src/Rendering/mitkPointSetVtkMapper3D.cpp +++ b/Modules/Core/src/Rendering/mitkPointSetVtkMapper3D.cpp @@ -1,705 +1,701 @@ /*=================================================================== 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 "mitkPointSetVtkMapper3D.h" #include "mitkColorProperty.h" #include "mitkDataNode.h" #include "mitkPointSet.h" #include "mitkProperties.h" #include "mitkVtkPropRenderer.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include const mitk::PointSet *mitk::PointSetVtkMapper3D::GetInput() { return static_cast(GetDataNode()->GetData()); } mitk::PointSetVtkMapper3D::PointSetVtkMapper3D() : m_vtkSelectedPointList(nullptr), m_vtkUnselectedPointList(nullptr), m_VtkSelectedPolyDataMapper(nullptr), m_VtkUnselectedPolyDataMapper(nullptr), m_vtkTextList(nullptr), m_NumberOfSelectedAdded(0), m_NumberOfUnselectedAdded(0), m_PointSize(1.0), m_ContourRadius(0.5), m_VertexRendering(false) { // propassembly m_PointsAssembly = vtkSmartPointer::New(); // creating actors to be able to set transform m_SelectedActor = vtkSmartPointer::New(); m_UnselectedActor = vtkSmartPointer::New(); m_ContourActor = vtkSmartPointer::New(); } mitk::PointSetVtkMapper3D::~PointSetVtkMapper3D() { } void mitk::PointSetVtkMapper3D::ReleaseGraphicsResources(vtkWindow *renWin) { m_PointsAssembly->ReleaseGraphicsResources(renWin); m_SelectedActor->ReleaseGraphicsResources(renWin); m_UnselectedActor->ReleaseGraphicsResources(renWin); m_ContourActor->ReleaseGraphicsResources(renWin); } void mitk::PointSetVtkMapper3D::ReleaseGraphicsResources(mitk::BaseRenderer *renderer) { m_PointsAssembly->ReleaseGraphicsResources(renderer->GetRenderWindow()); m_SelectedActor->ReleaseGraphicsResources(renderer->GetRenderWindow()); m_UnselectedActor->ReleaseGraphicsResources(renderer->GetRenderWindow()); m_ContourActor->ReleaseGraphicsResources(renderer->GetRenderWindow()); } void mitk::PointSetVtkMapper3D::CreateVTKRenderObjects() { m_vtkSelectedPointList = vtkSmartPointer::New(); m_vtkUnselectedPointList = vtkSmartPointer::New(); m_PointsAssembly->VisibilityOn(); if (m_PointsAssembly->GetParts()->IsItemPresent(m_SelectedActor)) m_PointsAssembly->RemovePart(m_SelectedActor); if (m_PointsAssembly->GetParts()->IsItemPresent(m_UnselectedActor)) m_PointsAssembly->RemovePart(m_UnselectedActor); if (m_PointsAssembly->GetParts()->IsItemPresent(m_ContourActor)) m_PointsAssembly->RemovePart(m_ContourActor); // exceptional displaying for PositionTracker -> MouseOrientationTool int mapperID; bool isInputDevice = false; if (this->GetDataNode()->GetBoolProperty("inputdevice", isInputDevice) && isInputDevice) { if (this->GetDataNode()->GetIntProperty("BaseRendererMapperID", mapperID) && mapperID == BaseRenderer::Standard3D) return; // The event for the PositionTracker came from the 3d widget and not needs to be displayed } // get 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) { m_PointsAssembly->VisibilityOff(); return; } // now fill selected and unselected pointList // get size of Points in Property m_PointSize = 2; mitk::FloatProperty::Pointer pointSizeProp = dynamic_cast(this->GetDataNode()->GetProperty("pointsize")); if (pointSizeProp.IsNotNull()) m_PointSize = pointSizeProp->GetValue(); // get the property for creating a label onto every point only once bool showLabel = true; this->GetDataNode()->GetBoolProperty("show label", showLabel); const char *pointLabel = nullptr; if (showLabel) { if (dynamic_cast(this->GetDataNode()->GetPropertyList()->GetProperty("label")) != nullptr) pointLabel = dynamic_cast(this->GetDataNode()->GetPropertyList()->GetProperty("label"))->GetValue(); else showLabel = false; } // whether or not to creat a "contour" - connecting lines between all the points int nbPoints = itkPointSet->GetPointData()->Size(); bool makeContour = false; this->GetDataNode()->GetBoolProperty("show contour", makeContour); bool closeContour = false; this->GetDataNode()->GetBoolProperty("close contour", closeContour); int contourPointLimit = 0; // NO contour if (makeContour) { if (closeContour) contourPointLimit = nbPoints; else contourPointLimit = nbPoints - 1; } // build list of all positions for later transform in one go mitk::PointSet::PointsContainer::Iterator pointsIter; int ptIdx; m_NumberOfSelectedAdded = 0; m_NumberOfUnselectedAdded = 0; vtkSmartPointer localPoints = vtkSmartPointer::New(); m_WorldPositions = vtkSmartPointer::New(); m_PointConnections = vtkSmartPointer::New(); // m_PointConnections between points for (ptIdx = 0, pointsIter = itkPointSet->GetPoints()->Begin(); pointsIter != itkPointSet->GetPoints()->End(); pointsIter++, ptIdx++) { itk::Point currentPoint = pointsIter->Value(); localPoints->InsertPoint(ptIdx, currentPoint[0], currentPoint[1], currentPoint[2]); if (makeContour && ptIdx < contourPointLimit) { vtkIdType cell[2] = {(ptIdx + 1) % nbPoints, ptIdx}; m_PointConnections->InsertNextCell(2, cell); } } vtkSmartPointer vtktransform = this->GetDataNode()->GetVtkTransform(this->GetTimestep()); vtktransform->TransformPoints(localPoints, m_WorldPositions); // create contour if (makeContour) { this->CreateContour(m_WorldPositions, m_PointConnections); } // check if the list for the PointDataContainer is the same size as the PointsContainer. Is 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()); // now add an object for each point in data mitk::PointSet::PointDataContainer::Iterator pointDataIter = itkPointSet->GetPointData()->Begin(); for (ptIdx = 0; ptIdx < nbPoints; ++ptIdx) // pointDataIter moved at end of loop { double currentPoint[3]; m_WorldPositions->GetPoint(ptIdx, currentPoint); vtkSmartPointer source; // check for the pointtype in data and decide which geom-object to take and then add to the selected or unselected // list int pointType; if (itkPointSet->GetPointData()->size() == 0 || pointDataBroken) pointType = mitk::PTUNDEFINED; else pointType = pointDataIter.Value().pointSpec; switch (pointType) { case mitk::PTUNDEFINED: { vtkSmartPointer sphere = vtkSmartPointer::New(); sphere->SetRadius(m_PointSize / 2.0f); sphere->SetCenter(currentPoint); // sphere->SetCenter(pointsIter.Value()[0],pointsIter.Value()[1],pointsIter.Value()[2]); // MouseOrientation Tool (PositionTracker) if (isInputDevice) { sphere->SetThetaResolution(10); sphere->SetPhiResolution(10); } else { sphere->SetThetaResolution(20); sphere->SetPhiResolution(20); } source = sphere; } break; case mitk::PTSTART: { vtkSmartPointer cube = vtkSmartPointer::New(); cube->SetXLength(m_PointSize / 2); cube->SetYLength(m_PointSize / 2); cube->SetZLength(m_PointSize / 2); cube->SetCenter(currentPoint); source = cube; } break; case mitk::PTCORNER: { vtkSmartPointer cone = vtkSmartPointer::New(); cone->SetRadius(m_PointSize / 2.0f); cone->SetCenter(currentPoint); cone->SetResolution(20); source = cone; } break; case mitk::PTEDGE: { vtkSmartPointer cylinder = vtkSmartPointer::New(); cylinder->SetRadius(m_PointSize / 2.0f); cylinder->SetCenter(currentPoint); cylinder->SetResolution(20); source = cylinder; } break; case mitk::PTEND: { vtkSmartPointer sphere = vtkSmartPointer::New(); sphere->SetRadius(m_PointSize / 2.0f); // no SetCenter?? this functionality should be explained! // otherwise: join with default block! sphere->SetThetaResolution(20); sphere->SetPhiResolution(20); source = sphere; } break; default: { vtkSmartPointer sphere = vtkSmartPointer::New(); sphere->SetRadius(m_PointSize / 2.0f); sphere->SetCenter(currentPoint); sphere->SetThetaResolution(20); sphere->SetPhiResolution(20); source = sphere; } break; } if (pointDataIter.Value().selected && !pointDataBroken) { m_vtkSelectedPointList->AddInputConnection(source->GetOutputPort()); ++m_NumberOfSelectedAdded; } else { m_vtkUnselectedPointList->AddInputConnection(source->GetOutputPort()); ++m_NumberOfUnselectedAdded; } if (showLabel) { char buffer[20]; std::string l = pointLabel; if (input->GetSize() > 1) { sprintf(buffer, "%d", ptIdx + 1); l.append(buffer); } // Define the text for the label vtkSmartPointer label = vtkSmartPointer::New(); label->SetText(l.c_str()); //# Set up a transform to move the label to a new position. vtkSmartPointer aLabelTransform = vtkSmartPointer::New(); aLabelTransform->Identity(); aLabelTransform->Translate(currentPoint[0] + 2, currentPoint[1] + 2, currentPoint[2]); aLabelTransform->Scale(5.7, 5.7, 5.7); //# Move the label to a new position. vtkSmartPointer labelTransform = vtkSmartPointer::New(); labelTransform->SetTransform(aLabelTransform); labelTransform->SetInputConnection(label->GetOutputPort()); // add it to the wright PointList if (pointType) { m_vtkSelectedPointList->AddInputConnection(labelTransform->GetOutputPort()); ++m_NumberOfSelectedAdded; } else { m_vtkUnselectedPointList->AddInputConnection(labelTransform->GetOutputPort()); ++m_NumberOfUnselectedAdded; } } if (pointDataIter != itkPointSet->GetPointData()->End()) pointDataIter++; } // end FOR // now according to number of elements added to selected or unselected, build up the rendering pipeline if (m_NumberOfSelectedAdded > 0) { m_VtkSelectedPolyDataMapper = vtkSmartPointer::New(); m_VtkSelectedPolyDataMapper->SetInputConnection(m_vtkSelectedPointList->GetOutputPort()); // create a new instance of the actor m_SelectedActor = vtkSmartPointer::New(); m_SelectedActor->SetMapper(m_VtkSelectedPolyDataMapper); m_PointsAssembly->AddPart(m_SelectedActor); } if (m_NumberOfUnselectedAdded > 0) { m_VtkUnselectedPolyDataMapper = vtkSmartPointer::New(); m_VtkUnselectedPolyDataMapper->SetInputConnection(m_vtkUnselectedPointList->GetOutputPort()); // create a new instance of the actor m_UnselectedActor = vtkSmartPointer::New(); m_UnselectedActor->SetMapper(m_VtkUnselectedPolyDataMapper); m_PointsAssembly->AddPart(m_UnselectedActor); } } void mitk::PointSetVtkMapper3D::VertexRendering() { // get 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); // turn off standard actors m_UnselectedActor->VisibilityOff(); m_SelectedActor->VisibilityOff(); // point size m_PointSize = 2.0; mitk::FloatProperty::Pointer pointSizeProp = dynamic_cast(this->GetDataNode()->GetProperty("pointsize")); if (pointSizeProp.IsNotNull()) m_PointSize = pointSizeProp->GetValue(); double *color = m_UnselectedActor->GetProperty()->GetColor(); double opacity = m_UnselectedActor->GetProperty()->GetOpacity(); glClearColor(0.0, 0.0, 0.0, 0.0); glDisable(GL_COLOR_MATERIAL); glDisable(GL_LIGHTING); glEnable(GL_POINT_SMOOTH); glPointSize(m_PointSize); glBegin(GL_POINTS); glColor4d(color[0], color[1], color[2], opacity); for (auto pointsIter = itkPointSet->GetPoints()->Begin(); pointsIter != itkPointSet->GetPoints()->End(); pointsIter++) { const itk::Point &point = pointsIter->Value(); glVertex3d(point[0], point[1], point[2]); } glEnd(); // reset context glPointSize(1.0); glDisable(GL_POINT_SMOOTH); glEnable(GL_COLOR_MATERIAL); glEnable(GL_LIGHTING); } void mitk::PointSetVtkMapper3D::GenerateDataForRenderer(mitk::BaseRenderer *renderer) { bool visible = true; GetDataNode()->GetVisibility(visible, renderer, "visible"); if (!visible) { m_UnselectedActor->VisibilityOff(); m_SelectedActor->VisibilityOff(); m_ContourActor->VisibilityOff(); return; } // create new vtk render objects (e.g. sphere for a point) - - SetVtkMapperImmediateModeRendering(m_VtkSelectedPolyDataMapper); - SetVtkMapperImmediateModeRendering(m_VtkUnselectedPolyDataMapper); - BaseLocalStorage *ls = m_LSH.GetLocalStorage(renderer); bool needGenerateData = ls->IsGenerateDataRequired(renderer, this, GetDataNode()); if (!needGenerateData) { auto *pointSizeProp = dynamic_cast(this->GetDataNode()->GetProperty("pointsize")); auto *contourSizeProp = dynamic_cast(this->GetDataNode()->GetProperty("contoursize")); bool useVertexRendering = false; this->GetDataNode()->GetBoolProperty("Vertex Rendering", useVertexRendering); // only create new vtk render objects if property values were changed if (pointSizeProp && m_PointSize != pointSizeProp->GetValue()) needGenerateData = true; if (contourSizeProp && m_ContourRadius != contourSizeProp->GetValue()) needGenerateData = true; // when vertex rendering is enabled the pointset is always // drawn with opengl, thus we leave needGenerateData always false if (useVertexRendering && m_VertexRendering != useVertexRendering) { needGenerateData = false; m_VertexRendering = true; } else if (!useVertexRendering && m_VertexRendering) { m_VertexRendering = false; needGenerateData = true; } } if (needGenerateData) { this->CreateVTKRenderObjects(); ls->UpdateGenerateDataTime(); } this->ApplyAllProperties(renderer, m_ContourActor); bool showPoints = true; this->GetDataNode()->GetBoolProperty("show points", showPoints); m_UnselectedActor->SetVisibility(showPoints && !m_VertexRendering); m_SelectedActor->SetVisibility(showPoints && !m_VertexRendering); if (false && dynamic_cast(this->GetDataNode()->GetProperty("opacity")) != nullptr) { mitk::FloatProperty::Pointer pointOpacity = dynamic_cast(this->GetDataNode()->GetProperty("opacity")); float opacity = pointOpacity->GetValue(); m_ContourActor->GetProperty()->SetOpacity(opacity); m_UnselectedActor->GetProperty()->SetOpacity(opacity); m_SelectedActor->GetProperty()->SetOpacity(opacity); } bool showContour = false; this->GetDataNode()->GetBoolProperty("show contour", showContour); m_ContourActor->SetVisibility(showContour); // use vertex rendering if (m_VertexRendering) { VertexRendering(); ls->UpdateGenerateDataTime(); } } void mitk::PointSetVtkMapper3D::ResetMapper(BaseRenderer * /*renderer*/) { m_PointsAssembly->VisibilityOff(); } vtkProp *mitk::PointSetVtkMapper3D::GetVtkProp(mitk::BaseRenderer * /*renderer*/) { return m_PointsAssembly; } void mitk::PointSetVtkMapper3D::UpdateVtkTransform(mitk::BaseRenderer * /*renderer*/) { } void mitk::PointSetVtkMapper3D::ApplyAllProperties(mitk::BaseRenderer *renderer, vtkActor *actor) { Superclass::ApplyColorAndOpacityProperties(renderer, actor); // 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! // vars to convert to double unselectedColor[4] = {1.0f, 1.0f, 0.0f, 1.0f}; // yellow double selectedColor[4] = {1.0f, 0.0f, 0.0f, 1.0f}; // red double contourColor[4] = {1.0f, 0.0f, 0.0f, 1.0f}; // red // different types for color!!! mitk::Color tmpColor; double opacity = 1.0; // check if there is an unselected property if (dynamic_cast( this->GetDataNode()->GetPropertyList(renderer)->GetProperty("unselectedcolor")) != nullptr) { tmpColor = dynamic_cast( this->GetDataNode()->GetPropertyList(renderer)->GetProperty("unselectedcolor")) ->GetValue(); unselectedColor[0] = tmpColor[0]; unselectedColor[1] = tmpColor[1]; unselectedColor[2] = tmpColor[2]; unselectedColor[3] = 1.0f; //!!define a new ColorProp to be able to pass alpha value } else if (dynamic_cast( this->GetDataNode()->GetPropertyList(nullptr)->GetProperty("unselectedcolor")) != nullptr) { tmpColor = dynamic_cast(this->GetDataNode()->GetPropertyList(nullptr)->GetProperty("unselectedcolor")) ->GetValue(); unselectedColor[0] = tmpColor[0]; unselectedColor[1] = tmpColor[1]; unselectedColor[2] = tmpColor[2]; unselectedColor[3] = 1.0f; //!!define a new ColorProp to be able to pass alpha value } else { // check if the node has a color float unselectedColorTMP[4] = {1.0f, 1.0f, 0.0f, 1.0f}; // yellow m_DataNode->GetColor(unselectedColorTMP, nullptr); unselectedColor[0] = unselectedColorTMP[0]; unselectedColor[1] = unselectedColorTMP[1]; unselectedColor[2] = unselectedColorTMP[2]; // unselectedColor[3] stays 1.0f } // get selected property if (dynamic_cast( this->GetDataNode()->GetPropertyList(renderer)->GetProperty("selectedcolor")) != nullptr) { 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; } else if (dynamic_cast( this->GetDataNode()->GetPropertyList(nullptr)->GetProperty("selectedcolor")) != nullptr) { 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; } // get contour property if (dynamic_cast( this->GetDataNode()->GetPropertyList(renderer)->GetProperty("contourcolor")) != nullptr) { 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) { 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; } if (dynamic_cast(this->GetDataNode()->GetPropertyList(renderer)->GetProperty("opacity")) != nullptr) { mitk::FloatProperty::Pointer pointOpacity = dynamic_cast(this->GetDataNode()->GetPropertyList(renderer)->GetProperty("opacity")); opacity = pointOpacity->GetValue(); } else if (dynamic_cast(this->GetDataNode()->GetPropertyList(nullptr)->GetProperty("opacity")) != nullptr) { mitk::FloatProperty::Pointer pointOpacity = dynamic_cast(this->GetDataNode()->GetPropertyList(nullptr)->GetProperty("opacity")); opacity = pointOpacity->GetValue(); } // finished color / opacity fishing! // check if a contour shall be drawn bool showContour = false; this->GetDataNode()->GetBoolProperty("show contour", showContour, renderer); if (showContour && (m_ContourActor != nullptr)) { this->CreateContour(m_WorldPositions, m_PointConnections); m_ContourActor->GetProperty()->SetColor(contourColor); m_ContourActor->GetProperty()->SetOpacity(opacity); } m_SelectedActor->GetProperty()->SetColor(selectedColor); m_SelectedActor->GetProperty()->SetOpacity(opacity); m_UnselectedActor->GetProperty()->SetColor(unselectedColor); m_UnselectedActor->GetProperty()->SetOpacity(opacity); } void mitk::PointSetVtkMapper3D::CreateContour(vtkPoints *points, vtkCellArray *m_PointConnections) { vtkSmartPointer vtkContourPolyData = vtkSmartPointer::New(); vtkSmartPointer vtkContourPolyDataMapper = vtkSmartPointer::New(); vtkSmartPointer contour = vtkSmartPointer::New(); contour->SetPoints(points); contour->SetLines(m_PointConnections); vtkSmartPointer tubeFilter = vtkSmartPointer::New(); tubeFilter->SetNumberOfSides(12); tubeFilter->SetInputData(contour); // check for property contoursize. m_ContourRadius = 0.5; mitk::FloatProperty::Pointer contourSizeProp = dynamic_cast(this->GetDataNode()->GetProperty("contoursize")); if (contourSizeProp.IsNotNull()) m_ContourRadius = contourSizeProp->GetValue(); tubeFilter->SetRadius(m_ContourRadius); tubeFilter->Update(); // add to pipeline vtkContourPolyData->AddInputConnection(tubeFilter->GetOutputPort()); vtkContourPolyDataMapper->SetInputConnection(vtkContourPolyData->GetOutputPort()); m_ContourActor->SetMapper(vtkContourPolyDataMapper); m_PointsAssembly->AddPart(m_ContourActor); } void mitk::PointSetVtkMapper3D::SetDefaultProperties(mitk::DataNode *node, mitk::BaseRenderer *renderer, bool overwrite) { node->AddProperty("line width", mitk::IntProperty::New(2), renderer, overwrite); node->AddProperty("pointsize", mitk::FloatProperty::New(1.0), renderer, overwrite); node->AddProperty("selectedcolor", mitk::ColorProperty::New(1.0f, 0.0f, 0.0f), renderer, overwrite); // red node->AddProperty("color", mitk::ColorProperty::New(1.0f, 1.0f, 0.0f), renderer, overwrite); // yellow node->AddProperty("opacity", mitk::FloatProperty::New(1.0f), renderer, overwrite); node->AddProperty("show contour", mitk::BoolProperty::New(false), renderer, overwrite); node->AddProperty("close contour", mitk::BoolProperty::New(false), renderer, overwrite); node->AddProperty("contourcolor", mitk::ColorProperty::New(1.0f, 0.0f, 0.0f), renderer, overwrite); node->AddProperty("contoursize", mitk::FloatProperty::New(0.5), renderer, overwrite); node->AddProperty("show points", mitk::BoolProperty::New(true), renderer, overwrite); node->AddProperty("updateDataOnRender", mitk::BoolProperty::New(true), renderer, overwrite); node->AddProperty("Vertex Rendering", mitk::BoolProperty::New(false), renderer, overwrite); Superclass::SetDefaultProperties(node, renderer, overwrite); } diff --git a/Modules/Core/src/Rendering/mitkVtkMapper.cpp b/Modules/Core/src/Rendering/mitkVtkMapper.cpp index 6f5eac9772..6f54da4509 100644 --- a/Modules/Core/src/Rendering/mitkVtkMapper.cpp +++ b/Modules/Core/src/Rendering/mitkVtkMapper.cpp @@ -1,140 +1,134 @@ /*=================================================================== 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 "mitkVtkMapper.h" mitk::VtkMapper::VtkMapper() { } mitk::VtkMapper::~VtkMapper() { } void mitk::VtkMapper::MitkRender(mitk::BaseRenderer *renderer, mitk::VtkPropRenderer::RenderType type) { switch (type) { case mitk::VtkPropRenderer::Opaque: this->MitkRenderOpaqueGeometry(renderer); break; case mitk::VtkPropRenderer::Translucent: this->MitkRenderTranslucentGeometry(renderer); break; case mitk::VtkPropRenderer::Overlay: this->MitkRenderOverlay(renderer); break; case mitk::VtkPropRenderer::Volumetric: this->MitkRenderVolumetricGeometry(renderer); break; } } bool mitk::VtkMapper::IsVtkBased() const { return true; } void mitk::VtkMapper::MitkRenderOverlay(BaseRenderer *renderer) { bool visible = true; GetDataNode()->GetVisibility(visible, renderer, "visible"); if (!visible) return; if (this->GetVtkProp(renderer)->GetVisibility()) { GetVtkProp(renderer)->RenderOverlay(renderer->GetVtkRenderer()); } } void mitk::VtkMapper::MitkRenderOpaqueGeometry(BaseRenderer *renderer) { bool visible = true; GetDataNode()->GetVisibility(visible, renderer, "visible"); if (!visible) return; if (this->GetVtkProp(renderer)->GetVisibility()) { GetVtkProp(renderer)->RenderOpaqueGeometry(renderer->GetVtkRenderer()); } } void mitk::VtkMapper::MitkRenderTranslucentGeometry(BaseRenderer *renderer) { bool visible = true; GetDataNode()->GetVisibility(visible, renderer, "visible"); if (!visible) return; if (this->GetVtkProp(renderer)->GetVisibility()) { GetVtkProp(renderer)->RenderTranslucentPolygonalGeometry(renderer->GetVtkRenderer()); } } void mitk::VtkMapper::MitkRenderVolumetricGeometry(BaseRenderer *renderer) { bool visible = true; GetDataNode()->GetVisibility(visible, renderer, "visible"); if (!visible) return; if (GetVtkProp(renderer)->GetVisibility()) { GetVtkProp(renderer)->RenderVolumetricGeometry(renderer->GetVtkRenderer()); } } bool mitk::VtkMapper::HasVtkProp(const vtkProp *prop, BaseRenderer *renderer) { vtkProp *myProp = this->GetVtkProp(renderer); // TODO: check if myProp is a vtkAssembly and if so, check if prop is contained in its leafs return (prop == myProp); } -void mitk::VtkMapper::SetVtkMapperImmediateModeRendering(vtkMapper *mapper) -{ - if (mapper) - mapper->SetImmediateModeRendering(mitk::VtkPropRenderer::useImmediateModeRendering()); -} - void mitk::VtkMapper::UpdateVtkTransform(mitk::BaseRenderer *renderer) { vtkLinearTransform *vtktransform = GetDataNode()->GetVtkTransform(this->GetTimestep()); auto *prop = dynamic_cast(GetVtkProp(renderer)); if (prop) prop->SetUserTransform(vtktransform); } void mitk::VtkMapper::ApplyColorAndOpacityProperties(BaseRenderer *renderer, vtkActor *actor) { float rgba[4] = {1.0f, 1.0f, 1.0f, 1.0f}; DataNode *node = GetDataNode(); // check for color prop and use it for rendering if it exists node->GetColor(rgba, renderer, "color"); // check for opacity prop and use it for rendering if it exists node->GetOpacity(rgba[3], renderer, "opacity"); double drgba[4] = {rgba[0], rgba[1], rgba[2], rgba[3]}; actor->GetProperty()->SetColor(drgba); actor->GetProperty()->SetOpacity(drgba[3]); } diff --git a/Modules/DicomRT/src/mitkDoseImageVtkMapper2D.cpp b/Modules/DicomRT/src/mitkDoseImageVtkMapper2D.cpp index 6b90fcce30..f6e435fa77 100644 --- a/Modules/DicomRT/src/mitkDoseImageVtkMapper2D.cpp +++ b/Modules/DicomRT/src/mitkDoseImageVtkMapper2D.cpp @@ -1,1163 +1,1163 @@ /*=================================================================== 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 "mitkImageStatisticsHolder.h" #include "mitkPlaneClipping.h" #include "mitkPropertyNameHelper.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // MITK Rendering #include "mitkDoseImageVtkMapper2D.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 #include // ITK #include mitk::DoseImageVtkMapper2D::DoseImageVtkMapper2D() { } mitk::DoseImageVtkMapper2D::~DoseImageVtkMapper2D() { // 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::DoseImageVtkMapper2D::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::DoseImageVtkMapper2D::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::DoseImageVtkMapper2D::GetInput(void) { return static_cast(GetDataNode()->GetData()); } vtkProp *mitk::DoseImageVtkMapper2D::GetVtkProp(mitk::BaseRenderer *renderer) { // return the actor corresponding to the renderer return m_LSH.GetLocalStorage(renderer)->m_Actors; } void mitk::DoseImageVtkMapper2D::GenerateDataForRenderer(mitk::BaseRenderer *renderer) { LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); mitk::Image *input = const_cast(this->GetInput()); mitk::DataNode *datanode = this->GetDataNode(); if (input == nullptr || input->IsInitialized() == false) { return; } // check if there is a valid worldGeometry const PlaneGeometry *worldGeometry = renderer->GetCurrentWorldPlaneGeometry(); if ((worldGeometry == nullptr) || (!worldGeometry->IsValid()) || (!worldGeometry->HasReferenceGeometry())) { return; } input->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, input->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(input); 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( input->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 ((input->GetDimension() >= 3) && (input->GetDimension(2) > 1)) { VtkResliceInterpolationProperty *resliceInterpolationProperty; datanode->GetProperty(resliceInterpolationProperty, "reslice interpolation"); 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 (input->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") && resliceMethodEnumProperty) thickSlicesMode = resliceMethodEnumProperty->GetValueAsId(); IntProperty *intProperty = nullptr; if (dn->GetProperty(intProperty, "reslice.thickslices.num") && intProperty) { thickSlicesNum = intProperty->GetValue(); if (thickSlicesNum < 1) thickSlicesNum = 1; if (thickSlicesNum > 10) thickSlicesNum = 10; } } else { MITK_WARN << "no associated widget plane data tree node found"; } } const PlaneGeometry *planeGeometry = dynamic_cast(worldGeometry); if (thickSlicesMode > 0) { double dataZSpacing = 1.0; Vector3D normInIndex, normal; if (planeGeometry != nullptr) { normal = planeGeometry->GetNormal(); } else { const mitk::AbstractTransformGeometry *abstractGeometry = dynamic_cast(worldGeometry); if (abstractGeometry != nullptr) normal = abstractGeometry->GetPlane()->GetNormal(); else return; // no fitting geometry set } normal.Normalize(); input->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 (int i = 0; i < 6; ++i) { sliceBounds[i] = 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 (int i = 0; i < 6; ++i) { textureClippingBounds[i] = 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(input->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 showIsoLines property bool showIsoLines = false; datanode->GetBoolProperty("dose.showIsoLines", showIsoLines, renderer); if (showIsoLines) // contour rendering { // generate contours/outlines localStorage->m_OutlinePolyData = CreateOutlinePolyData(renderer); 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 { localStorage->m_ReslicedImage = nullptr; localStorage->m_Mapper->SetInputData(localStorage->m_EmptyPolyData); return; } this->ApplyOpacity(renderer); this->ApplyRenderingMode(renderer); // do not use a VTK lookup table (we do that ourselves in m_LevelWindowFilter) - localStorage->m_Texture->MapColorScalarsThroughLookupTableOff(); + 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); vtkActor *contourShadowActor = dynamic_cast(localStorage->m_Actors->GetParts()->GetItemAsObject(0)); if (showIsoLines) // 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::DoseImageVtkMapper2D::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::DoseImageVtkMapper2D::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; GetDataNode()->GetBoolProperty("binaryimage.ishovering", hover, renderer); GetDataNode()->GetBoolProperty("selected", selected, renderer); if (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 (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 (!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::DoseImageVtkMapper2D::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::DoseImageVtkMapper2D::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::DoseImageVtkMapper2D::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::DoseImageVtkMapper2D::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()); } void mitk::DoseImageVtkMapper2D::Update(mitk::BaseRenderer *renderer) { bool visible = true; GetDataNode()->GetVisibility(visible, renderer, "visible"); if (!visible) { return; } mitk::Image *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()) // was the node modified? || (localStorage->m_LastUpdateTime < data->GetPipelineMTime()) // Was the data modified? || (localStorage->m_LastUpdateTime < renderer->GetCurrentWorldPlaneGeometryUpdateTime()) // was the geometry modified? || (localStorage->m_LastUpdateTime < renderer->GetCurrentWorldPlaneGeometry()->GetMTime()) || (localStorage->m_LastUpdateTime < node->GetPropertyList()->GetMTime()) // was a property modified? || (localStorage->m_LastUpdateTime < node->GetPropertyList(renderer)->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::DoseImageVtkMapper2D::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)); // default value 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); } // Otherwise do nothing - the default grayscale look-up table has already been set } bool isBinaryImage(false); if (!node->GetBoolProperty("binary", isBinaryImage)) { // 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); } // 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); std::string className = image->GetNameOfClass(); if (className != "TensorImage" && className != "OdfImage" && className != "ShImage") { PixelType pixelType = image->GetPixelType(); size_t numComponents = pixelType.GetNumberOfComponents(); if ((pixelType.GetPixelTypeAsString() == "vector" && numComponents > 1) || numComponents == 2 || numComponents > 4) node->AddProperty("Image.Displayed Component", 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::DoseImageVtkMapper2D::LocalStorage *mitk::DoseImageVtkMapper2D::GetLocalStorage(mitk::BaseRenderer *renderer) { return m_LSH.GetLocalStorage(renderer); } vtkSmartPointer mitk::DoseImageVtkMapper2D::CreateOutlinePolyData(mitk::BaseRenderer *renderer) { vtkSmartPointer points = vtkSmartPointer::New(); // the points to draw vtkSmartPointer lines = vtkSmartPointer::New(); // the lines to connect the points vtkSmartPointer colors = vtkSmartPointer::New(); colors->SetNumberOfComponents(3); colors->SetName("Colors"); float pref; this->GetDataNode()->GetFloatProperty(mitk::RTConstants::REFERENCE_DOSE_PROPERTY_NAME.c_str(), pref); mitk::IsoDoseLevelSetProperty::Pointer propIsoSet = dynamic_cast( GetDataNode()->GetProperty(mitk::RTConstants::DOSE_ISO_LEVELS_PROPERTY_NAME.c_str())); mitk::IsoDoseLevelSet::Pointer isoDoseLevelSet = propIsoSet->GetValue(); for (mitk::IsoDoseLevelSet::ConstIterator doseIT = isoDoseLevelSet->Begin(); doseIT != isoDoseLevelSet->End(); ++doseIT) { if (doseIT->GetVisibleIsoLine()) { this->CreateLevelOutline(renderer, &(doseIT.Value()), pref, points, lines, colors); } // end of if visible dose value } // end of loop over all does values mitk::IsoDoseLevelVectorProperty::Pointer propfreeIsoVec = dynamic_cast( GetDataNode()->GetProperty(mitk::RTConstants::DOSE_FREE_ISO_VALUES_PROPERTY_NAME.c_str())); mitk::IsoDoseLevelVector::Pointer frereIsoDoseLevelVec = propfreeIsoVec->GetValue(); for (mitk::IsoDoseLevelVector::ConstIterator freeDoseIT = frereIsoDoseLevelVec->Begin(); freeDoseIT != frereIsoDoseLevelVec->End(); ++freeDoseIT) { if (freeDoseIT->Value()->GetVisibleIsoLine()) { this->CreateLevelOutline(renderer, freeDoseIT->Value(), pref, points, lines, colors); } // end of if visible dose value } // end of loop over all does values // 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); polyData->GetCellData()->SetScalars(colors); return polyData; } void mitk::DoseImageVtkMapper2D::CreateLevelOutline(mitk::BaseRenderer *renderer, const mitk::IsoDoseLevel *level, float pref, vtkSmartPointer points, vtkSmartPointer lines, vtkSmartPointer colors) { 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? // get the depth for each contour float depth = CalculateLayerDepth(renderer); double doseValue = level->GetDoseValue() * pref; mitk::IsoDoseLevel::ColorType isoColor = level->GetColor(); unsigned char colorLine[3] = {static_cast(isoColor.GetRed() * 255), static_cast(isoColor.GetGreen() * 255), static_cast(isoColor.GetBlue() * 255)}; int x = xMin; // pixel index x int y = yMin; // pixel index y float *currentPixel; // We take the pointer to the first pixel of the image currentPixel = static_cast(localStorage->m_ReslicedImage->GetScalarPointer()); while (y <= yMax) { // if the current pixel value is set to something if ((currentPixel) && (*currentPixel >= doseValue)) { // 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) < doseValue) { // 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); colors->InsertNextTypedTuple(colorLine); } // if vvvvv not the last line vvvvv if (y < yMax && *(currentPixel + line) < doseValue) { // 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); colors->InsertNextTypedTuple(colorLine); } // if vvvvv not the first pixel vvvvv if ((x > xMin || y > yMin) && *(currentPixel - 1) < doseValue) { // 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); colors->InsertNextTypedTuple(colorLine); } // if vvvvv not the last pixel vvvvv if ((y < yMax || (x < xMax)) && *(currentPixel + 1) < doseValue) { // 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); colors->InsertNextTypedTuple(colorLine); } /* 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); colors->InsertNextTypedTuple(colorLine); } // 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); colors->InsertNextTypedTuple(colorLine); } // 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); colors->InsertNextTypedTuple(colorLine); } // 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); colors->InsertNextTypedTuple(colorLine); } } // 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 } void mitk::DoseImageVtkMapper2D::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) { vtkActor *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::DoseImageVtkMapper2D::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::DoseImageVtkMapper2D::LocalStorage::~LocalStorage() { } mitk::DoseImageVtkMapper2D::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/DiffusionImaging/Connectomics/Rendering/mitkConnectomicsNetworkMapper3D.cpp b/Modules/DiffusionImaging/Connectomics/Rendering/mitkConnectomicsNetworkMapper3D.cpp index 9d2c0bf086..aaa528bf49 100644 --- a/Modules/DiffusionImaging/Connectomics/Rendering/mitkConnectomicsNetworkMapper3D.cpp +++ b/Modules/DiffusionImaging/Connectomics/Rendering/mitkConnectomicsNetworkMapper3D.cpp @@ -1,835 +1,830 @@ /*=================================================================== 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 "mitkConnectomicsNetworkMapper3D.h" #include "vtkGlyph3D.h" #include "vtkGlyphSource2D.h" #include "vtkGraphLayout.h" #include "vtkGraphToPolyData.h" #include #include #include #include "mitkConnectomicsRenderingEdgeColorParameterProperty.h" #include "mitkConnectomicsRenderingEdgeFilteringProperty.h" #include "mitkConnectomicsRenderingEdgeRadiusParameterProperty.h" #include "mitkConnectomicsRenderingEdgeThresholdParameterProperty.h" #include "mitkConnectomicsRenderingNodeColorParameterProperty.h" #include "mitkConnectomicsRenderingNodeFilteringProperty.h" #include "mitkConnectomicsRenderingNodeRadiusParameterProperty.h" #include "mitkConnectomicsRenderingNodeThresholdParameterProperty.h" #include "mitkConnectomicsRenderingProperties.h" #include "mitkConnectomicsRenderingSchemeProperty.h" #include #include #include #include #include mitk::ConnectomicsNetworkMapper3D::ConnectomicsNetworkMapper3D() { m_NetworkAssembly = vtkPropAssembly::New(); m_Translator = mitk::FreeSurferParcellationTranslator::New(); } mitk::ConnectomicsNetworkMapper3D::~ConnectomicsNetworkMapper3D() { m_NetworkAssembly->Delete(); } void mitk::ConnectomicsNetworkMapper3D::GenerateDataForRenderer(mitk::BaseRenderer *renderer) { if (this->GetInput() == nullptr) { m_TextOverlay3D->UnRegisterMicroservice(); return; } bool propertiesHaveChanged = this->PropertiesChanged(); if (this->GetInput()->GetIsModified() || propertiesHaveChanged) { m_NetworkAssembly->Delete(); m_NetworkAssembly = vtkPropAssembly::New(); // Here is the part where a graph is given and converted to points and connections between points... std::vector vectorOfNodes = this->GetInput()->GetVectorOfAllNodes(); std::vector, mitk::ConnectomicsNetwork::NetworkEdge>> vectorOfEdges = this->GetInput()->GetVectorOfAllEdges(); // Decide on the style of rendering due to property if (m_ChosenRenderingScheme == connectomicsRenderingMITKScheme) { mitk::Point3D tempWorldPoint, tempCNFGeometryPoint; ////// Prepare BalloonWidgets/Overlays: //////////////////// if ((m_ChosenNodeLabel == "" || m_ChosenNodeLabel == "-1") && m_TextOverlay3D) { m_TextOverlay3D->UnRegisterMicroservice(); GetDataNode()->SetProperty( connectomicsRenderingBalloonTextName.c_str(), mitk::StringProperty::New(""), nullptr); GetDataNode()->SetProperty( connectomicsRenderingBalloonNodeStatsName.c_str(), mitk::StringProperty::New(""), nullptr); } //////////////////////Prepare coloring and radius//////////// std::vector vectorOfNodeRadiusParameterValues; vectorOfNodeRadiusParameterValues.resize(vectorOfNodes.size()); double maxNodeRadiusParameterValue( FillNodeParameterVector(&vectorOfNodeRadiusParameterValues, m_NodeRadiusParameter)); std::vector vectorOfNodeColorParameterValues; vectorOfNodeColorParameterValues.resize(vectorOfNodes.size()); double maxNodeColorParameterValue( FillNodeParameterVector(&vectorOfNodeColorParameterValues, m_NodeColorParameter)); std::vector vectorOfEdgeRadiusParameterValues; vectorOfEdgeRadiusParameterValues.resize(vectorOfEdges.size()); double maxEdgeRadiusParameterValue( FillEdgeParameterVector(&vectorOfEdgeRadiusParameterValues, m_EdgeRadiusParameter)); std::vector vectorOfEdgeColorParameterValues; vectorOfEdgeColorParameterValues.resize(vectorOfEdges.size()); double maxEdgeColorParameterValue( FillEdgeParameterVector(&vectorOfEdgeColorParameterValues, m_EdgeColorParameter)); //////////////////////Prepare Filtering////////////////////// // true will be rendered std::vector vectorOfNodeFilterBools(vectorOfNodes.size(), true); if (m_ChosenNodeFilter == connectomicsRenderingNodeThresholdingFilter) { FillNodeFilterBoolVector(&vectorOfNodeFilterBools, m_NodeThresholdParameter); } std::vector vectorOfEdgeFilterBools(vectorOfEdges.size(), true); if (m_ChosenEdgeFilter == connectomicsRenderingEdgeThresholdFilter) { FillEdgeFilterBoolVector(&vectorOfEdgeFilterBools, m_EdgeThresholdParameter); } //////////////////////Create Spheres///////////////////////// std::stringstream nodeLabelStream; // local stream variable to hold csv list of node label names and node label numbers. for (unsigned int i = 0; i < vectorOfNodes.size(); i++) { vtkSmartPointer sphereSource = vtkSmartPointer::New(); for (unsigned int dimension = 0; dimension < 3; dimension++) { tempCNFGeometryPoint.SetElement(dimension, vectorOfNodes[i].coordinates[dimension]); } GetDataNode()->GetData()->GetGeometry()->IndexToWorld(tempCNFGeometryPoint, tempWorldPoint); sphereSource->SetCenter(tempWorldPoint[0], tempWorldPoint[1], tempWorldPoint[2]); // determine radius double radiusFactor = vectorOfNodeRadiusParameterValues[i] / maxNodeRadiusParameterValue; double radius = m_NodeRadiusStart + (m_NodeRadiusEnd - m_NodeRadiusStart) * radiusFactor; sphereSource->SetRadius(radius); vtkSmartPointer mapper = vtkSmartPointer::New(); mapper->SetInputConnection(sphereSource->GetOutputPort()); vtkSmartPointer actor = vtkSmartPointer::New(); actor->SetMapper(mapper); // determine color double colorFactor = vectorOfNodeColorParameterValues[i] / maxNodeColorParameterValue; double redStart = m_NodeColorStart.GetElement(0); double greenStart = m_NodeColorStart.GetElement(1); double blueStart = m_NodeColorStart.GetElement(2); double redEnd = m_NodeColorEnd.GetElement(0); double greenEnd = m_NodeColorEnd.GetElement(1); double blueEnd = m_NodeColorEnd.GetElement(2); double red = redStart + (redEnd - redStart) * colorFactor; double green = greenStart + (greenEnd - greenStart) * colorFactor; double blue = blueStart + (blueEnd - blueStart) * colorFactor; actor->GetProperty()->SetColor(red, green, blue); // append to csv list of nodelabels. nodeLabelStream << m_Translator->GetName(std::stoi(vectorOfNodes[i].label)) << ": " << vectorOfNodes[i].label << ","; if (vectorOfNodeFilterBools[i]) { if (vectorOfNodes[i].label == m_ChosenNodeLabel) { // if chosen and enabled, show information in Balloon or TextOverlay: // What to show: std::stringstream balloonStringstream; balloonStringstream << "Node id: " << vectorOfNodes[i].id << "\nlabel: " << vectorOfNodes[i].label << "\nname: " << m_Translator->GetName(std::stoi(vectorOfNodes[i].label)) << std::endl; m_BalloonText = balloonStringstream.str(); GetDataNode()->SetProperty( connectomicsRenderingBalloonTextName.c_str(), mitk::StringProperty::New(m_BalloonText.c_str()), nullptr); std::stringstream balloonNodeStatsStream; balloonNodeStatsStream << "Coordinates: (" << vectorOfNodes[i].coordinates[0] << " ; " << vectorOfNodes[i].coordinates[1] << " ; " << vectorOfNodes[i].coordinates[2] << " )" << "\nDegree: " << (this->GetInput()->GetDegreeOfNodes()).at(vectorOfNodes[i].id) << "\nBetweenness centrality: " << (this->GetInput()->GetNodeBetweennessVector()).at(vectorOfNodes[i].id) << "\nClustering coefficient: " << (this->GetInput()->GetLocalClusteringCoefficients()).at(vectorOfNodes[i].id) << std::endl; m_BalloonNodeStats = balloonNodeStatsStream.str(); GetDataNode()->SetProperty(connectomicsRenderingBalloonNodeStatsName.c_str(), mitk::StringProperty::New(m_BalloonNodeStats.c_str()), nullptr); // Where to show: float r[3]; r[0] = vectorOfNodes[i].coordinates[0]; r[1] = vectorOfNodes[i].coordinates[1]; r[2] = vectorOfNodes[i].coordinates[2]; mitk::Point3D BalloonAnchor(r); mitk::Point3D BalloonAnchorWorldCoord(r); GetDataNode()->GetData()->GetGeometry()->IndexToWorld(BalloonAnchor, BalloonAnchorWorldCoord); // How to show: if (m_ChosenNodeLabel != "-1") { if (m_TextOverlay3D != nullptr) { m_TextOverlay3D->UnRegisterMicroservice(); } m_TextOverlay3D = mitk::TextAnnotation3D::New(); mitk::ManualPlacementAnnotationRenderer::AddAnnotation(m_TextOverlay3D.GetPointer(), renderer); m_TextOverlay3D->SetFontSize(2); m_TextOverlay3D->SetColor(0.96, 0.69, 0.01); m_TextOverlay3D->SetOpacity(0.81); m_TextOverlay3D->SetPosition3D(BalloonAnchorWorldCoord); m_TextOverlay3D->SetText("...." + m_BalloonText); m_TextOverlay3D->SetForceInForeground(true); // TODO: does not work anymore. m_TextOverlay3D->SetVisibility(GetDataNode()->IsVisible(renderer)); // Colorize chosen node: actor->GetProperty()->SetColor(1.0, 0.69, 0.01); } } m_NetworkAssembly->AddPart(actor); } } m_AllNodeLabels = nodeLabelStream.str(); // Store all Node Names and Node Labels in 1 Property. m_AllNodeLabels.erase(m_AllNodeLabels.rfind(","), 1); // remove trailing ,. GetDataNode()->SetProperty(connectomicsRenderingBalloonAllNodeLabelsName.c_str(), mitk::StringProperty::New(m_AllNodeLabels.c_str()), nullptr); //////////////////////Create Tubes///////////////////////// for (unsigned int i = 0; i < vectorOfEdges.size(); i++) { vtkSmartPointer lineSource = vtkSmartPointer::New(); for (unsigned int dimension = 0; dimension < 3; dimension++) { tempCNFGeometryPoint[dimension] = vectorOfEdges[i].first.first.coordinates[dimension]; } GetDataNode()->GetData()->GetGeometry()->IndexToWorld(tempCNFGeometryPoint, tempWorldPoint); lineSource->SetPoint1(tempWorldPoint[0], tempWorldPoint[1], tempWorldPoint[2]); for (unsigned int dimension = 0; dimension < 3; dimension++) { tempCNFGeometryPoint[dimension] = vectorOfEdges[i].first.second.coordinates[dimension]; } GetDataNode()->GetData()->GetGeometry()->IndexToWorld(tempCNFGeometryPoint, tempWorldPoint); lineSource->SetPoint2(tempWorldPoint[0], tempWorldPoint[1], tempWorldPoint[2]); vtkSmartPointer tubes = vtkSmartPointer::New(); tubes->SetInputConnection(lineSource->GetOutputPort()); tubes->SetNumberOfSides(12); // determine radius double radiusFactor = vectorOfEdgeRadiusParameterValues[i] / maxEdgeRadiusParameterValue; double radius = m_EdgeRadiusStart + (m_EdgeRadiusEnd - m_EdgeRadiusStart) * radiusFactor; tubes->SetRadius(radius); // originally we used a logarithmic scaling, // double radiusFactor = 1.0 + ((double) vectorOfEdges[i].second.weight) / 10.0 ; // tubes->SetRadius( std::log10( radiusFactor ) ); vtkSmartPointer mapper2 = vtkSmartPointer::New(); mapper2->SetInputConnection(tubes->GetOutputPort()); vtkSmartPointer actor = vtkSmartPointer::New(); actor->SetMapper(mapper2); // determine color double colorFactor = vectorOfEdgeColorParameterValues[i] / maxEdgeColorParameterValue; double redStart = m_EdgeColorStart.GetElement(0); double greenStart = m_EdgeColorStart.GetElement(1); double blueStart = m_EdgeColorStart.GetElement(2); double redEnd = m_EdgeColorEnd.GetElement(0); double greenEnd = m_EdgeColorEnd.GetElement(1); double blueEnd = m_EdgeColorEnd.GetElement(2); double red = redStart + (redEnd - redStart) * colorFactor; double green = greenStart + (greenEnd - greenStart) * colorFactor; double blue = blueStart + (blueEnd - blueStart) * colorFactor; actor->GetProperty()->SetColor(red, green, blue); if (vectorOfEdgeFilterBools[i]) { m_NetworkAssembly->AddPart(actor); } } } else if (m_ChosenRenderingScheme == connectomicsRenderingVTKScheme) { vtkSmartPointer graph = vtkSmartPointer::New(); std::vector networkToVTKvector; networkToVTKvector.resize(vectorOfNodes.size()); for (unsigned int i = 0; i < vectorOfNodes.size(); i++) { networkToVTKvector[vectorOfNodes[i].id] = graph->AddVertex(); } for (unsigned int i = 0; i < vectorOfEdges.size(); i++) { graph->AddEdge(networkToVTKvector[vectorOfEdges[i].first.first.id], networkToVTKvector[vectorOfEdges[i].first.second.id]); } vtkSmartPointer points = vtkSmartPointer::New(); for (unsigned int i = 0; i < vectorOfNodes.size(); i++) { double x = vectorOfNodes[i].coordinates[0]; double y = vectorOfNodes[i].coordinates[1]; double z = vectorOfNodes[i].coordinates[2]; points->InsertNextPoint(x, y, z); } graph->SetPoints(points); vtkGraphLayout *layout = vtkGraphLayout::New(); layout->SetInputData(graph); vtkPassThroughLayoutStrategy *ptls = vtkPassThroughLayoutStrategy::New(); layout->SetLayoutStrategy(ptls); vtkGraphToPolyData *graphToPoly = vtkGraphToPolyData::New(); graphToPoly->SetInputConnection(layout->GetOutputPort()); // Create the standard VTK polydata mapper and actor // for the connections (edges) in the tree. vtkPolyDataMapper *edgeMapper = vtkPolyDataMapper::New(); edgeMapper->SetInputConnection(graphToPoly->GetOutputPort()); vtkActor *edgeActor = vtkActor::New(); edgeActor->SetMapper(edgeMapper); edgeActor->GetProperty()->SetColor(0.0, 0.5, 1.0); // Glyph the points of the tree polydata to create // VTK_VERTEX cells at each vertex in the tree. vtkGlyph3D *vertGlyph = vtkGlyph3D::New(); vertGlyph->SetInputConnection(0, graphToPoly->GetOutputPort()); vtkGlyphSource2D *glyphSource = vtkGlyphSource2D::New(); glyphSource->SetGlyphTypeToVertex(); vertGlyph->SetInputConnection(1, glyphSource->GetOutputPort()); // Create a mapper for the vertices, and tell the mapper // to use the specified color array. vtkPolyDataMapper *vertMapper = vtkPolyDataMapper::New(); vertMapper->SetInputConnection(vertGlyph->GetOutputPort()); /*if (colorArray) { vertMapper->SetScalarModeToUsePointFieldData(); vertMapper->SelectColorArray(colorArray); vertMapper->SetScalarRange(colorRange); }*/ // Create an actor for the vertices. Move the actor forward // in the z direction so it is drawn on top of the edge actor. vtkActor *vertActor = vtkActor::New(); vertActor->SetMapper(vertMapper); vertActor->GetProperty()->SetPointSize(5); vertActor->SetPosition(0, 0, 0.001); // vtkProp3D.h: virtual void SetPosition(double,double,double): // Set/Get/Add the position of the Prop3D in world coordinates. m_NetworkAssembly->AddPart(edgeActor); m_NetworkAssembly->AddPart(vertActor); } (static_cast(GetDataNode()->GetData()))->SetIsModified(false); } } const mitk::ConnectomicsNetwork *mitk::ConnectomicsNetworkMapper3D::GetInput() { return static_cast(GetDataNode()->GetData()); } void mitk::ConnectomicsNetworkMapper3D::SetDefaultProperties(DataNode *node, BaseRenderer *renderer, bool overwrite) { // Initialize enumeration properties mitk::ConnectomicsRenderingSchemeProperty::Pointer connectomicsRenderingScheme = mitk::ConnectomicsRenderingSchemeProperty::New(); mitk::ConnectomicsRenderingEdgeFilteringProperty::Pointer connectomicsRenderingEdgeFiltering = mitk::ConnectomicsRenderingEdgeFilteringProperty::New(); mitk::ConnectomicsRenderingNodeFilteringProperty::Pointer connectomicsRenderingNodeFiltering = mitk::ConnectomicsRenderingNodeFilteringProperty::New(); mitk::ConnectomicsRenderingNodeColorParameterProperty::Pointer connectomicsRenderingNodeGradientColorParameter = mitk::ConnectomicsRenderingNodeColorParameterProperty::New(); mitk::ConnectomicsRenderingNodeRadiusParameterProperty::Pointer connectomicsRenderingNodeRadiusParameter = mitk::ConnectomicsRenderingNodeRadiusParameterProperty::New(); mitk::ConnectomicsRenderingEdgeColorParameterProperty::Pointer connectomicsRenderingEdgeGradientColorParameter = mitk::ConnectomicsRenderingEdgeColorParameterProperty::New(); mitk::ConnectomicsRenderingEdgeRadiusParameterProperty::Pointer connectomicsRenderingEdgeRadiusParameter = mitk::ConnectomicsRenderingEdgeRadiusParameterProperty::New(); mitk::ConnectomicsRenderingNodeThresholdParameterProperty::Pointer connectomicsRenderingNodeThresholdParameter = mitk::ConnectomicsRenderingNodeThresholdParameterProperty::New(); mitk::ConnectomicsRenderingEdgeThresholdParameterProperty::Pointer connectomicsRenderingEdgeThresholdParameter = mitk::ConnectomicsRenderingEdgeThresholdParameterProperty::New(); mitk::StringProperty::Pointer balloonText = mitk::StringProperty::New(); // set the properties node->AddProperty(connectomicsRenderingSchemePropertyName.c_str(), connectomicsRenderingScheme, renderer, overwrite); node->AddProperty( connectomicsRenderingEdgeFilteringPropertyName.c_str(), connectomicsRenderingEdgeFiltering, renderer, overwrite); node->AddProperty(connectomicsRenderingEdgeThresholdFilterParameterName.c_str(), connectomicsRenderingEdgeThresholdParameter, renderer, overwrite); node->AddProperty(connectomicsRenderingEdgeThresholdFilterThresholdName.c_str(), connectomicsRenderingEdgeThresholdFilterThresholdDefault, renderer, overwrite); node->AddProperty( connectomicsRenderingNodeFilteringPropertyName.c_str(), connectomicsRenderingNodeFiltering, renderer, overwrite); node->AddProperty(connectomicsRenderingNodeThresholdFilterParameterName.c_str(), connectomicsRenderingNodeThresholdParameter, renderer, overwrite); node->AddProperty(connectomicsRenderingNodeThresholdFilterThresholdName.c_str(), connectomicsRenderingNodeThresholdFilterThresholdDefault, renderer, overwrite); node->AddProperty(connectomicsRenderingNodeGradientStartColorName.c_str(), connectomicsRenderingNodeGradientStartColorDefault, renderer, overwrite); node->AddProperty(connectomicsRenderingNodeGradientEndColorName.c_str(), connectomicsRenderingNodeGradientEndColorDefault, renderer, overwrite); node->AddProperty(connectomicsRenderingNodeGradientColorParameterName.c_str(), connectomicsRenderingNodeGradientColorParameter, renderer, overwrite); node->AddProperty( connectomicsRenderingNodeRadiusStartName.c_str(), connectomicsRenderingNodeRadiusStartDefault, renderer, overwrite); node->AddProperty( connectomicsRenderingNodeRadiusEndName.c_str(), connectomicsRenderingNodeRadiusEndDefault, renderer, overwrite); node->AddProperty(connectomicsRenderingNodeRadiusParameterName.c_str(), connectomicsRenderingNodeRadiusParameter, renderer, overwrite); node->AddProperty( connectomicsRenderingNodeChosenNodeName.c_str(), connectomicsRenderingNodeChosenNodeDefault, renderer, overwrite); node->AddProperty(connectomicsRenderingEdgeGradientStartColorName.c_str(), connectomicsRenderingEdgeGradientStartColorDefault, renderer, overwrite); node->AddProperty(connectomicsRenderingEdgeGradientEndColorName.c_str(), connectomicsRenderingEdgeGradientEndColorDefault, renderer, overwrite); node->AddProperty(connectomicsRenderingEdgeGradientColorParameterName.c_str(), connectomicsRenderingEdgeGradientColorParameter, renderer, overwrite); node->AddProperty( connectomicsRenderingEdgeRadiusStartName.c_str(), connectomicsRenderingEdgeRadiusStartDefault, renderer, overwrite); node->AddProperty( connectomicsRenderingEdgeRadiusEndName.c_str(), connectomicsRenderingEdgeRadiusEndDefault, renderer, overwrite); node->AddProperty(connectomicsRenderingEdgeRadiusParameterName.c_str(), connectomicsRenderingEdgeRadiusParameter, renderer, overwrite); node->AddProperty(connectomicsRenderingBalloonTextName.c_str(), balloonText, nullptr, overwrite); // renderer=nullptr: Property is renderer independent. Superclass::SetDefaultProperties(node, renderer, overwrite); } -void mitk::ConnectomicsNetworkMapper3D::SetVtkMapperImmediateModeRendering(vtkMapper *mapper) -{ - mapper->ImmediateModeRenderingOn(); -} - void mitk::ConnectomicsNetworkMapper3D::UpdateVtkObjects() { // TODO: implement } vtkProp *mitk::ConnectomicsNetworkMapper3D::GetVtkProp(mitk::BaseRenderer * /*renderer*/) { return m_NetworkAssembly; } bool mitk::ConnectomicsNetworkMapper3D::PropertiesChanged() { mitk::ConnectomicsRenderingSchemeProperty *renderingScheme = static_cast( this->GetDataNode()->GetProperty(connectomicsRenderingSchemePropertyName.c_str())); mitk::ConnectomicsRenderingEdgeFilteringProperty *edgeFilter = static_cast( this->GetDataNode()->GetProperty(connectomicsRenderingEdgeFilteringPropertyName.c_str())); mitk::FloatProperty *edgeThreshold = static_cast( this->GetDataNode()->GetProperty(connectomicsRenderingEdgeThresholdFilterThresholdName.c_str())); mitk::ConnectomicsRenderingNodeFilteringProperty *nodeFilter = static_cast( this->GetDataNode()->GetProperty(connectomicsRenderingNodeFilteringPropertyName.c_str())); mitk::ConnectomicsRenderingNodeThresholdParameterProperty *nodeThresholdParameter = static_cast( this->GetDataNode()->GetProperty(connectomicsRenderingNodeThresholdFilterParameterName.c_str())); mitk::ConnectomicsRenderingEdgeThresholdParameterProperty *edgeThresholdParameter = static_cast( this->GetDataNode()->GetProperty(connectomicsRenderingEdgeThresholdFilterParameterName.c_str())); mitk::FloatProperty *nodeThreshold = static_cast( this->GetDataNode()->GetProperty(connectomicsRenderingNodeThresholdFilterThresholdName.c_str())); mitk::ColorProperty *nodeColorStart = static_cast( this->GetDataNode()->GetProperty(connectomicsRenderingNodeGradientStartColorName.c_str())); mitk::ColorProperty *nodeColorEnd = static_cast( this->GetDataNode()->GetProperty(connectomicsRenderingNodeGradientEndColorName.c_str())); mitk::FloatProperty *nodeRadiusStart = static_cast( this->GetDataNode()->GetProperty(connectomicsRenderingNodeRadiusStartName.c_str())); mitk::FloatProperty *nodeRadiusEnd = static_cast( this->GetDataNode()->GetProperty(connectomicsRenderingNodeRadiusEndName.c_str())); mitk::StringProperty *chosenNode = static_cast( this->GetDataNode()->GetProperty(connectomicsRenderingNodeChosenNodeName.c_str())); mitk::ColorProperty *edgeColorStart = static_cast( this->GetDataNode()->GetProperty(connectomicsRenderingEdgeGradientStartColorName.c_str())); mitk::ColorProperty *edgeColorEnd = static_cast( this->GetDataNode()->GetProperty(connectomicsRenderingEdgeGradientEndColorName.c_str())); mitk::FloatProperty *edgeRadiusStart = static_cast( this->GetDataNode()->GetProperty(connectomicsRenderingEdgeRadiusStartName.c_str())); mitk::FloatProperty *edgeRadiusEnd = static_cast( this->GetDataNode()->GetProperty(connectomicsRenderingEdgeRadiusEndName.c_str())); mitk::ConnectomicsRenderingNodeColorParameterProperty *nodeColorParameter = static_cast( this->GetDataNode()->GetProperty(connectomicsRenderingNodeGradientColorParameterName.c_str())); mitk::ConnectomicsRenderingNodeRadiusParameterProperty *nodeRadiusParameter = static_cast( this->GetDataNode()->GetProperty(connectomicsRenderingNodeRadiusParameterName.c_str())); mitk::ConnectomicsRenderingEdgeColorParameterProperty *edgeColorParameter = static_cast( this->GetDataNode()->GetProperty(connectomicsRenderingEdgeGradientColorParameterName.c_str())); mitk::ConnectomicsRenderingEdgeRadiusParameterProperty *edgeRadiusParameter = static_cast( this->GetDataNode()->GetProperty(connectomicsRenderingEdgeRadiusParameterName.c_str())); if (m_ChosenRenderingScheme != renderingScheme->GetValueAsString() || m_ChosenEdgeFilter != edgeFilter->GetValueAsString() || m_EdgeThreshold != edgeThreshold->GetValue() || m_EdgeThresholdParameter != edgeThresholdParameter->GetValueAsString() || m_ChosenNodeFilter != nodeFilter->GetValueAsString() || m_NodeThreshold != nodeThreshold->GetValue() || m_NodeThresholdParameter != nodeThresholdParameter->GetValueAsString() || m_NodeColorStart != nodeColorStart->GetValue() || m_NodeColorEnd != nodeColorEnd->GetValue() || m_NodeRadiusStart != nodeRadiusStart->GetValue() || m_NodeRadiusEnd != nodeRadiusEnd->GetValue() || m_ChosenNodeLabel != chosenNode->GetValueAsString() || m_EdgeColorStart != edgeColorStart->GetValue() || m_EdgeColorEnd != edgeColorEnd->GetValue() || m_EdgeRadiusStart != edgeRadiusStart->GetValue() || m_EdgeRadiusEnd != edgeRadiusEnd->GetValue() || m_NodeColorParameter != nodeColorParameter->GetValueAsString() || m_NodeRadiusParameter != nodeRadiusParameter->GetValueAsString() || m_EdgeColorParameter != edgeColorParameter->GetValueAsString() || m_EdgeRadiusParameter != edgeRadiusParameter->GetValueAsString()) { m_ChosenRenderingScheme = renderingScheme->GetValueAsString(); m_ChosenEdgeFilter = edgeFilter->GetValueAsString(); m_EdgeThreshold = edgeThreshold->GetValue(); m_EdgeThresholdParameter = edgeThresholdParameter->GetValueAsString(); m_ChosenNodeFilter = nodeFilter->GetValueAsString(); m_NodeThreshold = nodeThreshold->GetValue(); m_NodeThresholdParameter = nodeThresholdParameter->GetValueAsString(); m_NodeColorStart = nodeColorStart->GetValue(); m_NodeColorEnd = nodeColorEnd->GetValue(); m_NodeRadiusStart = nodeRadiusStart->GetValue(); m_NodeRadiusEnd = nodeRadiusEnd->GetValue(); m_ChosenNodeLabel = chosenNode->GetValueAsString(); m_EdgeColorStart = edgeColorStart->GetValue(); m_EdgeColorEnd = edgeColorEnd->GetValue(); m_EdgeRadiusStart = edgeRadiusStart->GetValue(); m_EdgeRadiusEnd = edgeRadiusEnd->GetValue(); m_NodeColorParameter = nodeColorParameter->GetValueAsString(); m_NodeRadiusParameter = nodeRadiusParameter->GetValueAsString(); m_EdgeColorParameter = edgeColorParameter->GetValueAsString(); m_EdgeRadiusParameter = edgeRadiusParameter->GetValueAsString(); return true; } return false; } double mitk::ConnectomicsNetworkMapper3D::FillNodeParameterVector(std::vector *parameterVector, std::string parameterName) { int end(parameterVector->size()); // constant parameter - uniform style if (parameterName == connectomicsRenderingNodeParameterConstant) { for (int index(0); index < end; index++) { parameterVector->at(index) = 1.0; } return 1.0; } double maximum(0.0); // using the degree as parameter if (parameterName == connectomicsRenderingNodeParameterDegree) { std::vector vectorOfDegree = this->GetInput()->GetDegreeOfNodes(); for (int index(0); index < end; index++) { parameterVector->at(index) = vectorOfDegree[index]; } maximum = *std::max_element(parameterVector->begin(), parameterVector->end()); } // using betweenness centrality as parameter if (parameterName == connectomicsRenderingNodeParameterBetweenness) { std::vector vectorOfBetweenness = this->GetInput()->GetNodeBetweennessVector(); for (int index(0); index < end; index++) { parameterVector->at(index) = vectorOfBetweenness[index]; } maximum = *std::max_element(parameterVector->begin(), parameterVector->end()); } // using clustering coefficient as parameter if (parameterName == connectomicsRenderingNodeParameterClustering) { const std::vector vectorOfClustering = this->GetInput()->GetLocalClusteringCoefficients(); for (int index(0); index < end; index++) { parameterVector->at(index) = vectorOfClustering[index]; } maximum = *std::max_element(parameterVector->begin(), parameterVector->end()); } // using distance to a specific node as parameter if (parameterName == connectomicsRenderingNodeParameterColoringShortestPath) { bool labelFound(this->GetInput()->CheckForLabel(m_ChosenNodeLabel)); // check whether the chosen node is valid if (!labelFound) { MITK_WARN << "Node chosen for rendering is not valid."; for (int index(0); index < end; index++) { parameterVector->at(index) = 1.0; } return 1.0; } else { const std::vector distanceVector = this->GetInput()->GetShortestDistanceVectorFromLabel(m_ChosenNodeLabel); for (int index(0); index < end; index++) { parameterVector->at(index) = distanceVector[index]; } maximum = *std::max_element(parameterVector->begin(), parameterVector->end()); } } // if the maximum is nearly zero if (std::abs(maximum) < mitk::eps) { maximum = 1.0; } return maximum; } double mitk::ConnectomicsNetworkMapper3D::FillEdgeParameterVector(std::vector *parameterVector, std::string parameterName) { int end(parameterVector->size()); // constant parameter - uniform style if (parameterName == connectomicsRenderingEdgeParameterConstant) { for (int index(0); index < end; index++) { parameterVector->at(index) = 1.0; } return 1.0; } double maximum(0.0); // using the weight as parameter if (parameterName == connectomicsRenderingEdgeParameterWeight) { std::vector, mitk::ConnectomicsNetwork::NetworkEdge>> vectorOfEdges = this->GetInput()->GetVectorOfAllEdges(); for (int index(0); index < end; index++) { parameterVector->at(index) = vectorOfEdges[index].second.fiber_count; } maximum = *std::max_element(parameterVector->begin(), parameterVector->end()); } // using the edge centrality as parameter if (parameterName == connectomicsRenderingEdgeParameterCentrality) { const std::vector vectorOfCentrality = this->GetInput()->GetEdgeBetweennessVector(); for (int index(0); index < end; index++) { parameterVector->at(index) = vectorOfCentrality[index]; } maximum = *std::max_element(parameterVector->begin(), parameterVector->end()); } // if the maximum is nearly zero if (std::abs(maximum) < mitk::eps) { maximum = 1.0; } return maximum; } void mitk::ConnectomicsNetworkMapper3D::FillNodeFilterBoolVector(std::vector *boolVector, std::string parameterName) { std::vector parameterVector; parameterVector.resize(boolVector->size()); int end(parameterVector.size()); // using the degree as parameter if (parameterName == connectomicsRenderingNodeParameterDegree) { std::vector vectorOfDegree = this->GetInput()->GetDegreeOfNodes(); for (int index(0); index < end; index++) { parameterVector.at(index) = vectorOfDegree[index]; } } // using betweenness centrality as parameter if (parameterName == connectomicsRenderingNodeParameterBetweenness) { std::vector vectorOfBetweenness = this->GetInput()->GetNodeBetweennessVector(); for (int index(0); index < end; index++) { parameterVector.at(index) = vectorOfBetweenness[index]; } } // using clustering coefficient as parameter if (parameterName == connectomicsRenderingNodeParameterClustering) { const std::vector vectorOfClustering = this->GetInput()->GetLocalClusteringCoefficients(); for (int index(0); index < end; index++) { parameterVector.at(index) = vectorOfClustering[index]; } } for (int index(0), end(boolVector->size()); index < end; index++) { if (parameterVector.at(index) >= m_NodeThreshold) { boolVector->at(index) = true; } else { boolVector->at(index) = false; } } return; } void mitk::ConnectomicsNetworkMapper3D::FillEdgeFilterBoolVector(std::vector *boolVector, std::string parameterName) { std::vector parameterVector; parameterVector.resize(boolVector->size()); int end(parameterVector.size()); // using the weight as parameter if (parameterName == connectomicsRenderingEdgeParameterWeight) { std::vector, mitk::ConnectomicsNetwork::NetworkEdge>> vectorOfEdges = this->GetInput()->GetVectorOfAllEdges(); for (int index(0); index < end; index++) { parameterVector.at(index) = vectorOfEdges[index].second.fiber_count; } } // using the edge centrality as parameter if (parameterName == connectomicsRenderingEdgeParameterCentrality) { const std::vector vectorOfCentrality = this->GetInput()->GetEdgeBetweennessVector(); for (int index(0); index < end; index++) { parameterVector.at(index) = vectorOfCentrality[index]; } } for (int index(0), end(boolVector->size()); index < end; index++) { if (parameterVector.at(index) >= m_EdgeThreshold) { boolVector->at(index) = true; } else { boolVector->at(index) = false; } } return; } diff --git a/Modules/DiffusionImaging/Connectomics/Rendering/mitkConnectomicsNetworkMapper3D.h b/Modules/DiffusionImaging/Connectomics/Rendering/mitkConnectomicsNetworkMapper3D.h index d4cc1567f6..f796dc604e 100644 --- a/Modules/DiffusionImaging/Connectomics/Rendering/mitkConnectomicsNetworkMapper3D.h +++ b/Modules/DiffusionImaging/Connectomics/Rendering/mitkConnectomicsNetworkMapper3D.h @@ -1,147 +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 ConnectomicsNetworkMapper3D_H_HEADER_INCLUDED #define ConnectomicsNetworkMapper3D_H_HEADER_INCLUDED // VTK includes #include "vtkPropAssembly.h" #include // MITK includes // base class #include "mitkVtkMapper.h" // data type #include "mitkConnectomicsNetwork.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "mitkFreeSurferParcellationTranslator.h" #include "mitkTextAnnotation2D.h" #include "mitkTextAnnotation3D.h" #include "mitkVtkInteractorStyle.h" #include #include #include #include #include #include #include #include #include namespace mitk { /** * \brief Mapper for Networks * \ingroup Mapper */ class MITKCONNECTOMICS_EXPORT ConnectomicsNetworkMapper3D : public VtkMapper { public: mitkClassMacro(ConnectomicsNetworkMapper3D, VtkMapper); itkFactorylessNewMacro(Self) itkCloneMacro(Self) vtkProp *GetVtkProp( mitk::BaseRenderer *renderer) override; // looks like deprecated.. should be replaced bz GetViewProp() static void SetDefaultProperties(DataNode *node, BaseRenderer *renderer = nullptr, bool overwrite = false); - static void SetVtkMapperImmediateModeRendering(vtkMapper *mapper); - void GenerateDataForRenderer(mitk::BaseRenderer *renderer) override; virtual const mitk::ConnectomicsNetwork *GetInput(); protected: ConnectomicsNetworkMapper3D(); ~ConnectomicsNetworkMapper3D() override; void UpdateVtkObjects(); vtkPropAssembly *m_NetworkAssembly; /** * \brief Returns true if the properties have changed since the last data generation */ bool PropertiesChanged(); // Create vectors for customizing color and radius and return maximum double FillNodeParameterVector(std::vector *parameterVector, std::string parameterName); double FillEdgeParameterVector(std::vector *parameterVector, std::string parameterName); void FillNodeFilterBoolVector(std::vector *boolVector, std::string parameterName); void FillEdgeFilterBoolVector(std::vector *boolVector, std::string parameterName); // Property storing members std::string m_ChosenRenderingScheme; std::string m_ChosenEdgeFilter; std::string m_EdgeThresholdParameter; double m_EdgeThreshold; std::string m_ChosenNodeFilter; std::string m_NodeThresholdParameter; double m_NodeThreshold; mitk::Color m_NodeColorStart; mitk::Color m_NodeColorEnd; double m_NodeRadiusStart; double m_NodeRadiusEnd; std::string m_ChosenNodeLabel; mitk::Color m_EdgeColorStart; mitk::Color m_EdgeColorEnd; double m_EdgeRadiusStart; double m_EdgeRadiusEnd; std::string m_NodeRadiusParameter; std::string m_NodeColorParameter; std::string m_EdgeRadiusParameter; std::string m_EdgeColorParameter; // Balloons std::string m_BalloonText; std::string m_BalloonNodeStats; mitk::FreeSurferParcellationTranslator::Pointer m_Translator; std::string m_AllNodeLabels; mitk::TextAnnotation3D::Pointer m_TextOverlay3D; }; } // namespace mitk #endif /* ConnectomicsNetworkMapper3D_H_HEADER_INCLUDED */ diff --git a/Modules/DiffusionImaging/DiffusionIO/mitkFiberBundleMapper3D.cpp b/Modules/DiffusionImaging/DiffusionIO/mitkFiberBundleMapper3D.cpp index 277dd04701..ec9dc1b570 100644 --- a/Modules/DiffusionImaging/DiffusionIO/mitkFiberBundleMapper3D.cpp +++ b/Modules/DiffusionImaging/DiffusionIO/mitkFiberBundleMapper3D.cpp @@ -1,412 +1,407 @@ /*=================================================================== 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 "mitkFiberBundleMapper3D.h" #include #include #include #include #include #include #include #include #include #include #include #include #include class vtkShaderCallback3D : public vtkCommand { public: static vtkShaderCallback3D *New() { return new vtkShaderCallback3D; } mitk::BaseRenderer *renderer; mitk::DataNode *node; void Execute(vtkObject *, unsigned long, void*cbo) override { vtkOpenGLHelper *cellBO = reinterpret_cast(cbo); float fiberOpacity; node->GetOpacity(fiberOpacity, nullptr); cellBO->Program->SetUniformf("fiberOpacity", fiberOpacity); if (this->renderer) { mitk::Vector3D plane_vec; node->GetPropertyValue("Fiber3DClippingPlane",plane_vec); float distance = plane_vec.GetNorm(); plane_vec.Normalize(); bool flip; node->GetBoolProperty("Fiber3DClippingPlaneFlip",flip); if (flip) { plane_vec *= -1; distance *= -1; } node->GetBoolProperty("Fiber3DClippingPlaneSecondFlip",flip); if (flip) { plane_vec *= -1; distance *= -1; } float* a = new float[4]; for (int i = 0; i < 3; ++i) a[i] = plane_vec[i]; a[3] = distance; cellBO->Program->SetUniform4f("slicingPlane", a); float v = 1; node->GetFloatProperty("light.ambient", v); cellBO->Program->SetUniformf("ambient", v); node->GetFloatProperty("light.diffuse", v); cellBO->Program->SetUniformf("diffuse", v); node->GetFloatProperty("light.specular", v); cellBO->Program->SetUniformf("intensity", v); node->GetFloatProperty("light.intensity", v); cellBO->Program->SetUniformf("intensity", v); bool enable_light = false; node->GetBoolProperty("light.enable_light", enable_light); cellBO->Program->SetUniformi("enable_light", enable_light); } } vtkShaderCallback3D() { this->renderer = nullptr; } }; mitk::FiberBundleMapper3D::FiberBundleMapper3D() : m_TubeRadius(0.0) , m_TubeSides(15) , m_LineWidth(1) { m_lut = vtkLookupTable::New(); m_lut->Build(); } mitk::FiberBundleMapper3D::~FiberBundleMapper3D() { } const mitk::FiberBundle* mitk::FiberBundleMapper3D::GetInput() { return static_cast ( GetDataNode()->GetData() ); } /* This method is called once the mapper gets new input, for UI rotation or changes in colorcoding this method is NOT called */ void mitk::FiberBundleMapper3D::InternalGenerateData(mitk::BaseRenderer *renderer) { m_FiberPolyData->GetPointData()->AddArray(m_FiberBundle->GetFiberColors()); float tmpopa; this->GetDataNode()->GetOpacity(tmpopa, nullptr); FBXLocalStorage3D *localStorage = m_LocalStorageHandler.GetLocalStorage(renderer); if (m_TubeRadius>0.0) { vtkSmartPointer tubeFilter = vtkSmartPointer::New(); tubeFilter->SetInputData(m_FiberPolyData); tubeFilter->SetNumberOfSides(m_TubeSides); tubeFilter->SetRadius(m_TubeRadius); tubeFilter->Update(); m_FiberPolyData = tubeFilter->GetOutput(); } else if (m_RibbonWidth>0.0) { vtkSmartPointer tubeFilter = vtkSmartPointer::New(); tubeFilter->SetInputData(m_FiberPolyData); tubeFilter->SetWidth(m_RibbonWidth); tubeFilter->Update(); m_FiberPolyData = tubeFilter->GetOutput(); } if (tmpopa<1) { vtkSmartPointer depthSort = vtkSmartPointer::New(); depthSort->SetInputData( m_FiberPolyData ); depthSort->SetCamera( renderer->GetVtkRenderer()->GetActiveCamera() ); depthSort->SetDirectionToBackToFront(); depthSort->Update(); localStorage->m_FiberMapper->SetInputConnection(depthSort->GetOutputPort()); } else { localStorage->m_FiberMapper->SetInputData(m_FiberPolyData); } if (m_Lighting) { float floatProp = 1.0; GetDataNode()->GetFloatProperty("light.ambient", floatProp); localStorage->m_FiberActor->GetProperty()->SetAmbient(floatProp); GetDataNode()->GetFloatProperty("light.diffuse", floatProp); localStorage->m_FiberActor->GetProperty()->SetDiffuse(floatProp); GetDataNode()->GetFloatProperty("light.specular", floatProp); localStorage->m_FiberActor->GetProperty()->SetSpecular(floatProp); GetDataNode()->GetFloatProperty("light.specularpower", floatProp); localStorage->m_FiberActor->GetProperty()->SetSpecularPower( floatProp ); mitk::ColorProperty* ambientC = dynamic_cast(GetDataNode()->GetProperty("light.ambientcolor")); mitk::ColorProperty* diffuseC = dynamic_cast(GetDataNode()->GetProperty("light.diffusecolor")); mitk::ColorProperty* specularC = dynamic_cast(GetDataNode()->GetProperty("light.specularcolor")); localStorage->m_FiberActor->GetProperty()->SetAmbientColor( ambientC->GetColor()[0], ambientC->GetColor()[1], ambientC->GetColor()[2] ); localStorage->m_FiberActor->GetProperty()->SetDiffuseColor( diffuseC->GetColor()[0], diffuseC->GetColor()[1], diffuseC->GetColor()[2] ); localStorage->m_FiberActor->GetProperty()->SetSpecularColor( specularC->GetColor()[0], specularC->GetColor()[1], specularC->GetColor()[2] ); localStorage->m_FiberActor->GetProperty()->SetLighting(true); } else { localStorage->m_FiberActor->GetProperty()->SetLighting(false); } localStorage->m_FiberMapper->SelectColorArray("FIBER_COLORS"); localStorage->m_FiberMapper->ScalarVisibilityOn(); localStorage->m_FiberMapper->SetScalarModeToUsePointFieldData(); localStorage->m_FiberActor->SetMapper(localStorage->m_FiberMapper); localStorage->m_FiberMapper->SetLookupTable(m_lut); // set Opacity localStorage->m_FiberActor->GetProperty()->SetOpacity((double) tmpopa); localStorage->m_FiberActor->GetProperty()->SetLineWidth(m_LineWidth); localStorage->m_FiberAssembly->AddPart(localStorage->m_FiberActor); localStorage->m_FiberMapper->SetVertexShaderCode( "//VTK::System::Dec\n" "attribute vec4 vertexMC;\n" "//VTK::Normal::Dec\n" "uniform mat4 MCDCMatrix;\n" "uniform mat4 MCVCMatrix;\n" "//VTK::Color::Dec\n" "attribute vec3 normalMC;\n" "uniform mat3 normalMatrix;\n" "varying vec4 positionWorld;\n" "varying vec4 colorVertex;\n" "varying vec3 N;\n" "varying vec4 v;\n" "void main(void)\n" "{\n" " colorVertex = scalarColor;\n" " positionWorld = vertexMC;\n" " v = MCVCMatrix * vertexMC;\n" " mat4 glNormalMatrix = transpose(inverse(MCVCMatrix));\n" " N = normalize(normalMatrix * normalMC);\n" " gl_Position = MCDCMatrix * vertexMC;\n" "}\n" ); localStorage->m_FiberMapper->SetFragmentShaderCode( "//VTK::System::Dec\n" // always start with this line "//VTK::Output::Dec\n" // always have this line in your FS "uniform vec4 slicingPlane;\n" "uniform float fiberOpacity;\n" "uniform float ambient;\n" "uniform float diffuse;\n" "uniform float specular;\n" "uniform float intensity;\n" "uniform int enable_light;\n" "varying vec4 positionWorld;\n" "varying vec4 colorVertex;\n" "varying vec3 N;\n" "varying vec4 v;\n" "out vec4 out_Color;\n" "void main(void)\n" "{\n" " float r1 = dot(positionWorld.xyz, slicingPlane.xyz) - slicingPlane.w;\n" " if ( r1 > 0 )\n" " discard;\n" " if (enable_light!=0)\n" " {\n" " vec3 L = normalize(-v.xyz);\n" // "normalize(gl_LightSource[0].position.xyz - v.xyz);\n" " vec3 E = normalize(-v.xyz); // we are in Eye Coordinates, so EyePos is (0,0,0)\n" " vec3 R = normalize(-reflect(L,N));\n" //calculate Diffuse Term: " float Idiff = diffuse * max(dot(N,L), 0.0);\n" " Idiff = clamp(Idiff, 0.0, 1.0);\n" // calculate Specular Term: " float Ispec = specular * pow(max(dot(R,E),0.0),0.3);\n" " Ispec = clamp(Ispec, 0.0, 1.0);\n" " out_Color = vec4(colorVertex.xyz, fiberOpacity)*(1-intensity) + vec4(colorVertex.xyz * (ambient + Idiff + Ispec) * intensity, fiberOpacity);\n" " }\n" " else\n" " {\n" " out_Color = vec4(colorVertex.xyz, fiberOpacity);\n" " }\n" "}\n" ); vtkSmartPointer myCallback = vtkSmartPointer::New(); myCallback->renderer = renderer; myCallback->node = this->GetDataNode(); localStorage->m_FiberMapper->AddObserver(vtkCommand::UpdateShaderEvent,myCallback); localStorage->m_LastUpdateTime.Modified(); } void mitk::FiberBundleMapper3D::GenerateDataForRenderer( mitk::BaseRenderer *renderer ) { bool visible = true; GetDataNode()->GetVisibility(visible, renderer, "visible"); if ( !visible ) return; const DataNode* node = this->GetDataNode(); FBXLocalStorage3D* localStorage = m_LocalStorageHandler.GetLocalStorage(renderer); m_FiberBundle = dynamic_cast(node->GetData()); m_FiberPolyData = m_FiberBundle->GetFiberPolyData(); // this->ApplyShaderProperties(renderer, "shader_3d"); // did any rendering properties change? float tubeRadius = 0; node->GetFloatProperty("shape.tuberadius", tubeRadius); if (m_TubeRadius!=tubeRadius) { m_TubeRadius = tubeRadius; m_FiberBundle->RequestUpdate3D(); } int tubeSides = 0; node->GetIntProperty("shape.tubesides", tubeSides); if (m_TubeSides!=tubeSides) { m_TubeSides = tubeSides; m_FiberBundle->RequestUpdate3D(); } int lineWidth = 0; node->GetIntProperty("shape.linewidth", lineWidth); if (m_LineWidth!=lineWidth) { m_LineWidth = lineWidth; m_FiberBundle->RequestUpdate3D(); } float ribbonWidth = 0; node->GetFloatProperty("shape.ribbonwidth", ribbonWidth); if (m_RibbonWidth!=ribbonWidth) { m_RibbonWidth = ribbonWidth; m_FiberBundle->RequestUpdate3D(); } bool lighting = false; node->GetBoolProperty("light.enable", lighting); if (m_Lighting!=lighting) { m_Lighting = lighting; m_FiberBundle->RequestUpdate3D(); } if (localStorage->m_LastUpdateTime>=m_FiberBundle->GetUpdateTime3D()) return; // Calculate time step of the input data for the specified renderer (integer value) // this method is implemented in mitkMapper this->CalculateTimeStep( renderer ); this->InternalGenerateData(renderer); } void mitk::FiberBundleMapper3D::UpdateShaderParameter(mitk::BaseRenderer * ) { // see new vtkShaderCallback3D } void mitk::FiberBundleMapper3D::SetDefaultProperties(mitk::DataNode* node, mitk::BaseRenderer* renderer, bool overwrite) { Superclass::SetDefaultProperties(node, renderer, overwrite); mitk::Vector3D plane_vec; plane_vec.Fill(0.0); node->AddProperty( "Fiber3DClippingPlane", mitk::Vector3DProperty::New( plane_vec ), renderer, overwrite ); node->AddProperty( "Fiber3DClippingPlaneId", mitk::IntProperty::New( 0 ), renderer, overwrite ); node->AddProperty( "Fiber3DClippingPlaneFlip", mitk::BoolProperty::New( false ), renderer, overwrite ); node->AddProperty( "Fiber3DClippingPlaneSecondFlip", mitk::BoolProperty::New( false ), renderer, overwrite ); node->AddProperty( "opacity", mitk::FloatProperty::New( 1.0 ), renderer, overwrite); node->AddProperty( "color", mitk::ColorProperty::New(1.0,1.0,1.0), renderer, overwrite); node->AddProperty( "pickable", mitk::BoolProperty::New( true ), renderer, overwrite); node->AddProperty( "shape.linewidth", mitk::IntProperty::New( true ), renderer, overwrite ); node->AddProperty( "shape.tuberadius",mitk::FloatProperty::New( 0.0 ), renderer, overwrite); node->AddProperty( "shape.tubesides",mitk::IntProperty::New( 15 ), renderer, overwrite); node->AddProperty( "shape.ribbonwidth", mitk::FloatProperty::New( 0.0 ), renderer, overwrite); node->AddProperty( "light.intensity", mitk::FloatProperty::New( 0.6 ), renderer, overwrite); node->AddProperty( "light.enable_light", mitk::BoolProperty::New( false ), renderer, overwrite); node->AddProperty( "light.ambient", mitk::FloatProperty::New( 0.05 ), renderer, overwrite); node->AddProperty( "light.diffuse", mitk::FloatProperty::New( 1.0 ), renderer, overwrite); node->AddProperty( "light.specular", mitk::FloatProperty::New( 0.0 ), renderer, overwrite); node->AddProperty( "light.specularpower", mitk::FloatProperty::New( 1.0 ), renderer, overwrite); node->AddProperty( "light.ambientcolor", mitk::ColorProperty::New(1,1,1), renderer, overwrite); node->AddProperty( "light.diffusecolor", mitk::ColorProperty::New(1,1,1), renderer, overwrite); node->AddProperty( "light.specularcolor", mitk::ColorProperty::New(1,1,1), renderer, overwrite); } vtkProp* mitk::FiberBundleMapper3D::GetVtkProp(mitk::BaseRenderer *renderer) { return m_LocalStorageHandler.GetLocalStorage(renderer)->m_FiberAssembly; } -void mitk::FiberBundleMapper3D::SetVtkMapperImmediateModeRendering(vtkMapper *) -{ - -} - mitk::FiberBundleMapper3D::FBXLocalStorage3D::FBXLocalStorage3D() { m_FiberActor = vtkSmartPointer::New(); m_FiberMapper = vtkSmartPointer::New(); m_FiberAssembly = vtkSmartPointer::New(); } diff --git a/Modules/DiffusionImaging/DiffusionIO/mitkFiberBundleMapper3D.h b/Modules/DiffusionImaging/DiffusionIO/mitkFiberBundleMapper3D.h index 22e1e770d0..7c1e584a0b 100644 --- a/Modules/DiffusionImaging/DiffusionIO/mitkFiberBundleMapper3D.h +++ b/Modules/DiffusionImaging/DiffusionIO/mitkFiberBundleMapper3D.h @@ -1,106 +1,105 @@ /*=================================================================== 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 FiberBundleMapper3D_H_HEADER_INCLUDED #define FiberBundleMapper3D_H_HEADER_INCLUDED #include #include #include #include //#define MITKFIBERBUNDLEMAPPER3D_POLYDATAMAPPER vtkOpenGLPolyDataMapper //class MITKFIBERBUNDLEMAPPER3D_POLYDATAMAPPER; class vtkPropAssembly; class vtkPolyDataMapper; class vtkLookupTable; class vtkOpenGLActor; namespace mitk { //##Documentation //## @brief Mapper for FiberBundle //## @ingroup Mapper class FiberBundleMapper3D : public VtkMapper { public: mitkClassMacro(FiberBundleMapper3D, VtkMapper) itkFactorylessNewMacro(Self) itkCloneMacro(Self) //========== essential implementation for 3D mapper ======== const FiberBundle* GetInput(); vtkProp *GetVtkProp(mitk::BaseRenderer *renderer) override; //looks like depricated.. should be replaced bz GetViewProp() static void SetDefaultProperties(DataNode* node, BaseRenderer* renderer = nullptr, bool overwrite = false ); - static void SetVtkMapperImmediateModeRendering(vtkMapper *mapper); void GenerateDataForRenderer(mitk::BaseRenderer* renderer) override; //========================================================= class FBXLocalStorage3D : public mitk::Mapper::BaseLocalStorage { public: /** \brief Point Actor of a 3D render window. */ vtkSmartPointer m_FiberActor; /** \brief Point Mapper of a 3D render window. */ vtkSmartPointer m_FiberMapper; vtkSmartPointer m_FiberAssembly; /** \brief Timestamp of last update of stored data. */ itk::TimeStamp m_LastUpdateTime; /** \brief Constructor of the local storage. Do as much actions as possible in here to avoid double executions. */ FBXLocalStorage3D(); //if u copy&paste from this 2Dmapper, be aware that the implementation of this constructor is in the cpp file ~FBXLocalStorage3D() override { } }; /** \brief This member holds all three LocalStorages for the 3D render window(s). */ mitk::LocalStorageHandler m_LocalStorageHandler; protected: FiberBundleMapper3D(); ~FiberBundleMapper3D() override; void InternalGenerateData(mitk::BaseRenderer *renderer); void UpdateShaderParameter(mitk::BaseRenderer*); private: vtkSmartPointer m_lut; float m_TubeRadius; int m_TubeSides; int m_LineWidth; float m_RibbonWidth; bool m_Lighting; vtkSmartPointer m_FiberPolyData; mitk::FiberBundle* m_FiberBundle; }; } // end namespace mitk #endif /* FiberBundleMapper3D_H_HEADER_INCLUDED */ diff --git a/Modules/DiffusionImaging/FiberTracking/Rendering/mitkFiberBundleThreadMonitorMapper3D.cpp b/Modules/DiffusionImaging/FiberTracking/Rendering/mitkFiberBundleThreadMonitorMapper3D.cpp index 4402c2d72a..9dc5a40e1a 100644 --- a/Modules/DiffusionImaging/FiberTracking/Rendering/mitkFiberBundleThreadMonitorMapper3D.cpp +++ b/Modules/DiffusionImaging/FiberTracking/Rendering/mitkFiberBundleThreadMonitorMapper3D.cpp @@ -1,206 +1,196 @@ /*=================================================================== 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 "mitkFiberBundleThreadMonitorMapper3D.h" #include #include //#include #include mitk::FiberBundleThreadMonitorMapper3D::FiberBundleThreadMonitorMapper3D() : m_FiberMonitorMapper(vtkSmartPointer::New()) , m_TextActorClose(vtkSmartPointer::New()) , m_TextActorOpen(vtkSmartPointer::New()) , m_TextActorHeading(vtkSmartPointer::New()) , m_TextActorMask(vtkSmartPointer::New()) , m_TextActorStatus(vtkSmartPointer::New()) , m_TextActorStarted(vtkSmartPointer::New()) , m_TextActorFinished(vtkSmartPointer::New()) , m_TextActorTerminated(vtkSmartPointer::New()) , m_FiberAssembly(vtkPropAssembly::New()) , m_lastModifiedMonitorNodeTime(-1) { m_FiberAssembly->AddPart(m_TextActorClose); m_FiberAssembly->AddPart(m_TextActorOpen); m_FiberAssembly->AddPart(m_TextActorHeading); m_FiberAssembly->AddPart(m_TextActorMask); m_FiberAssembly->AddPart(m_TextActorStatus); m_FiberAssembly->AddPart(m_TextActorStarted); m_FiberAssembly->AddPart(m_TextActorFinished); m_FiberAssembly->AddPart(m_TextActorTerminated); } mitk::FiberBundleThreadMonitorMapper3D::~FiberBundleThreadMonitorMapper3D() { m_FiberAssembly->Delete(); } const mitk::FiberBundleThreadMonitor* mitk::FiberBundleThreadMonitorMapper3D::GetInput() { return static_cast ( GetDataNode()->GetData() ); } /* This method is called once the mapper gets new input, for UI rotation or changes in colorcoding this method is NOT called */ void mitk::FiberBundleThreadMonitorMapper3D::GenerateDataForRenderer( mitk::BaseRenderer *renderer ) { bool visible = true; GetDataNode()->GetVisibility(visible, renderer, "visible"); if ( !visible ) return; const DataNode *node = this->GetDataNode(); if (m_lastModifiedMonitorNodeTime >= node->GetMTime()) return; m_lastModifiedMonitorNodeTime = node->GetMTime(); // MITK_INFO << m_LastUpdateTime; FiberBundleThreadMonitor* monitor = dynamic_cast ( GetDataNode()->GetData() ); // m_TextActor->SetInput( monitor->getTextL1().toStdString().c_str() ); m_TextActorClose->SetInput( monitor->getBracketClose().toStdString().c_str() ); vtkTextProperty* tpropClose = m_TextActorClose->GetTextProperty(); //tprop->SetFontFamilyToArial (); //tprop->SetLineSpacing(1.0); tpropClose->SetFontSize(16); tpropClose->SetColor(0.85,0.8,0.8); m_TextActorClose->SetDisplayPosition( monitor->getBracketClosePosition()[0], monitor->getBracketClosePosition()[1] ); //m_TextActorClose->Modified(); m_TextActorOpen->SetInput( monitor->getBracketOpen().toStdString().c_str() ); vtkTextProperty* tpropOpen = m_TextActorOpen->GetTextProperty(); //tprop->SetFontFamilyToArial (); //tprop->SetLineSpacing(1.0); tpropOpen->SetFontSize(16); tpropOpen->SetColor(0.85,0.8,0.8); m_TextActorOpen->SetDisplayPosition( monitor->getBracketOpenPosition()[0], monitor->getBracketOpenPosition()[1] ); //m_TextActorOpen->Modified(); m_TextActorHeading->SetInput( monitor->getHeading().toStdString().c_str() ); vtkTextProperty* tpropHeading = m_TextActorHeading->GetTextProperty(); tpropHeading->SetFontSize(12); tpropHeading->SetOpacity( monitor->getHeadingOpacity() * 0.1 ); tpropHeading->SetColor(0.85,0.8,0.8); m_TextActorHeading->SetDisplayPosition( monitor->getHeadingPosition()[0], monitor->getHeadingPosition()[1] ); //m_TextActorHeading->Modified(); m_TextActorMask->SetInput( monitor->getMask().toStdString().c_str() ); vtkTextProperty* tpropMask = m_TextActorMask->GetTextProperty(); tpropMask->SetFontSize(12); tpropMask->SetOpacity( monitor->getMaskOpacity() * 0.1 ); tpropMask->SetColor(1.0,1.0,1.0); m_TextActorMask->SetDisplayPosition( monitor->getMaskPosition()[0], monitor->getMaskPosition()[1] ); //m_TextActorHeading->Modified(); m_TextActorStatus->SetInput(monitor->getStatus().toStdString().c_str()); vtkTextProperty* tpropStatus = m_TextActorStatus->GetTextProperty(); tpropStatus->SetFontSize(10); tpropStatus->SetOpacity( monitor->getStatusOpacity() * 0.1 ); tpropStatus->SetColor(0.85,0.8,0.8); m_TextActorStatus->SetDisplayPosition( monitor->getStatusPosition()[0], monitor->getStatusPosition()[1] ); //m_TextActorStatus->Modified(); m_TextActorStarted->SetInput(QString::number(monitor->getStarted()).toStdString().c_str()); vtkTextProperty* tpropStarted = m_TextActorStarted->GetTextProperty(); tpropStarted->SetFontSize(12); tpropStarted->SetOpacity( monitor->getStartedOpacity() * 0.1 ); tpropStarted->SetColor(0.0,1.0,0.0); m_TextActorStarted->SetDisplayPosition( monitor->getStartedPosition()[0], monitor->getStartedPosition()[1] ); //m_TextActorStarted->Modified(); m_TextActorFinished->SetInput(QString::number(monitor->getFinished()).toStdString().c_str()); vtkTextProperty* tpropFinished = m_TextActorFinished->GetTextProperty(); tpropFinished->SetFontSize(12); tpropFinished->SetOpacity( monitor->getFinishedOpacity() * 0.1 ); tpropFinished->SetColor(1.0,1.0,1.0); m_TextActorFinished->SetDisplayPosition( monitor->getFinishedPosition()[0], monitor->getFinishedPosition()[1] ); //m_TextActorFinished->Modified(); m_TextActorTerminated->SetInput(QString::number(monitor->getTerminated()).toStdString().c_str()); vtkTextProperty* tpropTerminated = m_TextActorTerminated->GetTextProperty(); tpropTerminated->SetFontSize(12); tpropTerminated->SetOpacity( monitor->getTerminatedOpacity() * 0.1 ); tpropTerminated->SetColor(1.0,1.0,1.0); m_TextActorTerminated->SetDisplayPosition( monitor->getTerminatedPosition()[0], monitor->getTerminatedPosition()[1] ); //m_TextActorTerminated->Modified(); // Calculate time step of the input data for the specified renderer (integer value) // this method is implemented in mitkMapper // this->CalculateTimeStep( renderer ); } void mitk::FiberBundleThreadMonitorMapper3D::SetDefaultProperties(mitk::DataNode* node, mitk::BaseRenderer* renderer, bool overwrite) { // MITK_INFO << "FiberBundlexXXMapper3D()SetDefaultProperties"; Superclass::SetDefaultProperties(node, renderer, overwrite); } vtkProp* mitk::FiberBundleThreadMonitorMapper3D::GetVtkProp(mitk::BaseRenderer *renderer) { //MITK_INFO << "FiberBundlexXXMapper3D()GetVTKProp"; //this->GenerateData(); return m_FiberAssembly; } void mitk::FiberBundleThreadMonitorMapper3D::ApplyProperties(mitk::BaseRenderer* renderer) { // MITK_INFO << "FiberBundleXXXMapper3D ApplyProperties(renderer)"; } void mitk::FiberBundleThreadMonitorMapper3D::UpdateVtkObjects() { // MITK_INFO << "FiberBundlexxXMapper3D UpdateVtkObjects()"; } - -void mitk::FiberBundleThreadMonitorMapper3D::SetVtkMapperImmediateModeRendering(vtkMapper *) -{ - - - -} - - - diff --git a/Modules/DiffusionImaging/FiberTracking/Rendering/mitkFiberBundleThreadMonitorMapper3D.h b/Modules/DiffusionImaging/FiberTracking/Rendering/mitkFiberBundleThreadMonitorMapper3D.h index c94db72e7d..9e4ed51f3e 100644 --- a/Modules/DiffusionImaging/FiberTracking/Rendering/mitkFiberBundleThreadMonitorMapper3D.h +++ b/Modules/DiffusionImaging/FiberTracking/Rendering/mitkFiberBundleThreadMonitorMapper3D.h @@ -1,90 +1,89 @@ /*=================================================================== 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 FiberBundleThreadMonitorMapper3D_H_HEADER_INCLUDED #define FiberBundleThreadMonitorMapper3D_H_HEADER_INCLUDED //#include //?? necessary #include #include #include #include #include #include class vtkPropAssembly; namespace mitk { //##Documentation //## @brief Mapper for FiberBundle //## @ingroup Mapper class MITKFIBERTRACKING_EXPORT FiberBundleThreadMonitorMapper3D : public VtkMapper { public: mitkClassMacro(FiberBundleThreadMonitorMapper3D, VtkMapper); itkFactorylessNewMacro(Self) itkCloneMacro(Self) //========== essential implementation for 3D mapper ======== const FiberBundleThreadMonitor* GetInput(); virtual vtkProp *GetVtkProp(mitk::BaseRenderer *renderer); //looks like depricated.. should be replaced bz GetViewProp() static void SetDefaultProperties(DataNode* node, BaseRenderer* renderer = nullptr, bool overwrite = false ); virtual void ApplyProperties(mitk::BaseRenderer* renderer); - static void SetVtkMapperImmediateModeRendering(vtkMapper *mapper); virtual void GenerateDataForRenderer(mitk::BaseRenderer* renderer); //========================================================= protected: FiberBundleThreadMonitorMapper3D(); virtual ~FiberBundleThreadMonitorMapper3D(); void UpdateVtkObjects(); //?? vtkSmartPointer m_FiberMonitorMapper; vtkSmartPointer m_TextActorClose; vtkSmartPointer m_TextActorOpen; vtkSmartPointer m_TextActorHeading; vtkSmartPointer m_TextActorMask; vtkSmartPointer m_TextActorStatus; vtkSmartPointer m_TextActorStarted; vtkSmartPointer m_TextActorFinished; vtkSmartPointer m_TextActorTerminated; vtkPropAssembly* m_FiberAssembly; private: double m_lastModifiedMonitorNodeTime; }; } // end namespace mitk #endif /* FiberBundleMapper3D_H_HEADER_INCLUDED */ diff --git a/Modules/MapperExt/src/mitkEnhancedPointSetVtkMapper3D.cpp b/Modules/MapperExt/src/mitkEnhancedPointSetVtkMapper3D.cpp index d3e7727a55..a418b0033c 100644 --- a/Modules/MapperExt/src/mitkEnhancedPointSetVtkMapper3D.cpp +++ b/Modules/MapperExt/src/mitkEnhancedPointSetVtkMapper3D.cpp @@ -1,435 +1,433 @@ /*=================================================================== 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 "mitkEnhancedPointSetVtkMapper3D.h" //#include #include #include "mitkDataNode.h" #include "mitkLookupTables.h" #include "mitkProperties.h" #include "mitkColorProperty.h" //#include "mitkVtkPropRenderer.h" #include #include #include #include #include #include #include #include #include #include #include #include #include const mitk::PointSet *mitk::EnhancedPointSetVtkMapper3D::GetInput() { return static_cast(GetDataNode()->GetData()); } mitk::EnhancedPointSetVtkMapper3D::EnhancedPointSetVtkMapper3D() { m_Contour = vtkActor::New(); m_ContourSource = vtkTubeFilter::New(); m_PropAssembly = vtkAssembly::New(); } vtkProp *mitk::EnhancedPointSetVtkMapper3D::GetVtkProp(mitk::BaseRenderer * /*renderer*/) { return m_PropAssembly; } mitk::EnhancedPointSetVtkMapper3D::~EnhancedPointSetVtkMapper3D() { m_Contour->Delete(); m_ContourSource->Delete(); m_PropAssembly->Delete(); // TODO: do cleanup correctly // Clean up all remaining actors and poly-data sources // std::for_each(m_PointActors.begin(), m_PointActors.end(), &mitk::EnhancedPointSetVtkMapper3D::DeleteVtkObject); // std::for_each(m_SphereSources.begin(), m_SphereSources.end(), // &mitk::EnhancedPointSetVtgkMapper3D::DeleteVtkObject); // std::for_each(m_CubeSources.begin(), m_CubeSources.end(), &mitk::EnhancedPointSetVtkMapper3D::DeleteVtkObject); // std::for_each(m_ConeSources.begin(), m_ConeSources.end(), &mitk::EnhancedPointSetVtkMapper3D::DeleteVtkObject); // std::for_each(m_CylinderSources.begin(), m_CylinderSources.end(), // &mitk::EnhancedPointSetVtkMapper3D::DeleteVtkObject); // } void mitk::EnhancedPointSetVtkMapper3D::UpdateVtkObjects() { // get and update the PointSet const mitk::PointSet *pointset = this->GetInput(); // pointset->Update(); int timestep = this->GetTimestep(); mitk::PointSet::DataType *itkPointSet = pointset->GetPointSet(timestep); mitk::PointSet::PointsContainer *points = itkPointSet->GetPoints(); mitk::PointSet::PointDataContainer *pointData = itkPointSet->GetPointData(); assert(points->Size() == pointData->Size()); mitk::PointSet::PointsIterator pIt; mitk::PointSet::PointDataIterator pdIt; /* search removed points and delete the corresponding source/actor/mapper objects */ for (auto it = m_PointActors.begin(); it != m_PointActors.end();) { PointIdentifier id = it->first; if (!points->IndexExists(id)) { this->RemoveEntryFromSourceMaps(id); m_PropAssembly->GetParts()->RemoveItem(it->second.first); // remove from prop assembly if (it->second.first != nullptr) it->second.first->Delete(); // Delete actor, which deletes mapper too (reference count) auto er = it; // save iterator for deleting ++it; // advance iterator to next object m_PointActors.erase( er); // erase element from map. This invalidates er, therefore we had to advance it before deletion. } else ++it; } /* iterate over each point in the pointset and create corresponding vtk objects */ for (pIt = points->Begin(), pdIt = pointData->Begin(); pIt != itkPointSet->GetPoints()->End(); ++pIt, ++pdIt) { PointIdentifier pointID = pIt->Index(); assert(pointID == pdIt->Index()); mitk::PointSet::PointDataType data = pdIt->Value(); auto aIt = m_PointActors.find(pointID); // Does an actor exist for the point? /* Create/Update sources for the point */ vtkActor *a = nullptr; bool newPoint = (aIt == m_PointActors.end()); // current point is new bool specChanged = (!newPoint && data.pointSpec != aIt->second.second); // point spec of current point has changed if (newPoint) // point did not exist before, we have to create vtk objects for it { // create actor and mapper for the new point a = vtkActor::New(); vtkPolyDataMapper *m = vtkPolyDataMapper::New(); a->SetMapper(m); m->UnRegister(nullptr); aIt = m_PointActors.insert(std::make_pair(pointID, std::make_pair(a, data.pointSpec))) .first; // insert element and update actormap iterator to point to new element m_PropAssembly->AddPart(a); } else { a = aIt->second.first; if (specChanged) // point exists, but point spec has changed { this->RemoveEntryFromSourceMaps(pointID); } } if (newPoint || specChanged) // new point OR existing point but point spec changed { vtkPolyDataAlgorithm *source = nullptr; // works only in VTK 5+ switch (data.pointSpec) // add to new map { // TODO: look up representation in a representationlookuptable case PTSTART: // cube m_CubeSources[pointID] = vtkCubeSource::New(); source = m_CubeSources[pointID]; break; case PTCORNER: // cone m_ConeSources[pointID] = vtkConeSource::New(); source = m_ConeSources[pointID]; break; case PTEDGE: // cylinder m_CylinderSources[pointID] = vtkCylinderSource::New(); source = m_CylinderSources[pointID]; break; case PTUNDEFINED: // sphere case PTEND: default: m_SphereSources[pointID] = vtkSphereSource::New(); source = m_SphereSources[pointID]; break; } auto *m = dynamic_cast(a->GetMapper()); assert(m != nullptr); m->SetInputConnection(source->GetOutputPort()); aIt->second.second = data.pointSpec; // update point spec in actormap } } // for each point } void mitk::EnhancedPointSetVtkMapper3D::ApplyColorAndOpacityProperties(mitk::BaseRenderer *renderer, vtkActor * /*actor*/) { this->UpdateVtkObjects(); /* iterate over all points in pointset and apply properties to corresponding vtk objects */ // get and update the PointSet const mitk::PointSet *pointset = this->GetInput(); int timestep = this->GetTimestep(); mitk::PointSet::DataType *itkPointSet = pointset->GetPointSet(timestep); mitk::PointSet::PointsContainer *points = itkPointSet->GetPoints(); mitk::PointSet::PointDataContainer *pointData = itkPointSet->GetPointData(); assert(points->Size() == pointData->Size()); mitk::PointSet::PointsIterator pIt; mitk::PointSet::PointDataIterator pdIt; mitk::DataNode *n = this->GetDataNode(); assert(n != nullptr); for (pIt = points->Begin(), pdIt = pointData->Begin(); pIt != itkPointSet->GetPoints()->End(); ++pIt, ++pdIt) // for each point in the pointset { PointIdentifier pointID = pIt->Index(); assert(pointID == pdIt->Index()); mitk::PointSet::PointDataType data = pdIt->Value(); auto aIt = m_PointActors.find(pointID); // Does an actor exist for the point? assert(aIt != m_PointActors.end()); // UpdateVtkObjects() must ensure that actor exists vtkActor *a = aIt->second.first; assert(a != nullptr); - SetVtkMapperImmediateModeRendering(a->GetMapper()); - /* update properties */ // visibility bool pointVisibility = true; bool visValueFound = false; mitk::BaseProperty *visProp = n->GetProperty("visibility", renderer); auto *visLTProp = dynamic_cast(visProp); if (visLTProp != nullptr) { mitk::BoolLookupTable visLookupTable = visLTProp->GetValue(); // if (visLookupTable != nullptr) //{ try { pointVisibility = visLookupTable.GetTableValue(pointID); visValueFound = true; } catch (...) { } //} } if (visValueFound == false) { pointVisibility = n->IsVisible(renderer, "show points"); // use BoolProperty instead } a->SetVisibility(pointVisibility); // opacity float opacity = 1.0; bool opValueFound = false; mitk::BaseProperty *opProp = n->GetProperty("opacity", renderer); auto *opLTProp = dynamic_cast(opProp); if (opLTProp != nullptr) { mitk::FloatLookupTable opLookupTable = opLTProp->GetValue(); // if (opLookupTable != nullptr) //{ try { opacity = opLookupTable.GetTableValue(pointID); opValueFound = true; } catch (...) { } //} } if (opValueFound == false) { n->GetOpacity(opacity, renderer); } a->GetProperty()->SetOpacity(opacity); ////////////////////// continue here /////////////////// // pointsize & point position float pointSize = 1.0; n->GetFloatProperty("pointsize", pointSize, renderer); switch (data.pointSpec) { // TODO: look up representation in a representationlookuptable case PTSTART: // cube m_CubeSources[pointID]->SetXLength(pointSize); m_CubeSources[pointID]->SetYLength(pointSize); m_CubeSources[pointID]->SetZLength(pointSize); // m_CubeSources[pointID]->SetCenter(pos[0], pos[1], pos[2]); break; case PTCORNER: // cone m_ConeSources[pointID]->SetRadius(pointSize / 2); m_ConeSources[pointID]->SetHeight(pointSize); m_ConeSources[pointID]->SetResolution(2); // two crossed triangles. Maybe introduce an extra property for // m_ConeSources[pointID]->SetCenter(pos[0], pos[1], pos[2]); break; case PTEDGE: // cylinder m_CylinderSources[pointID]->SetRadius(pointSize / 2); m_CylinderSources[pointID]->SetHeight(pointSize); m_CylinderSources[pointID]->CappingOn(); m_CylinderSources[pointID]->SetResolution(6); // m_CylinderSources[pointID]->SetCenter(pos[0], pos[1], pos[2]); break; case PTUNDEFINED: // sphere case PTEND: default: m_SphereSources[pointID]->SetRadius(pointSize / 2); m_SphereSources[pointID]->SetThetaResolution(10); m_SphereSources[pointID]->SetPhiResolution(10); // m_SphereSources[pointID]->SetCenter(pos[0], pos[1], pos[2]); break; } // set position mitk::Point3D pos = pIt->Value(); aIt->second.first->SetPosition(pos[0], pos[1], pos[2]); // selectedcolor & color float color[3]; if (data.selected) { if (!n->GetColor(color, renderer, "selectedcolor")) n->GetColor(color, renderer); } else { mitk::BaseProperty *a = n->GetProperty("colorLookupTable", renderer); auto *b = dynamic_cast(a); if (b != nullptr) { mitk::LookupTable::Pointer c = b->GetLookupTable(); vtkLookupTable *d = c->GetVtkLookupTable(); double *e = d->GetTableValue(pointID); color[0] = e[0]; color[1] = e[1]; color[2] = e[2]; } else { if (!n->GetColor(color, renderer, "unselectedcolor")) n->GetColor(color, renderer); } } // TODO: What about "color" property? 2D Mapper only uses unselected and selected color properties a->GetProperty()->SetColor(color[0], color[1], color[2]); // TODO: label property } // TODO test different pointSpec // TODO "line width" "show contour" "contourcolor" "contoursize" "close contour" "show label", "label" // TODO "show points" vs "visibility" - is visibility evaluated at all? in a superclass maybe? // TODO create lookup tables for all properties that should be evaluated per point. also create editor widgets for // these lookup tables! // TODO check if property changes and pointset changes are reflected in the render window immediately. // TODO check behavior with large PointSets // TODO check for memory leaks on adding/deleting points } void mitk::EnhancedPointSetVtkMapper3D::GenerateDataForRenderer(mitk::BaseRenderer *renderer) { BaseLocalStorage *ls = m_LSH.GetLocalStorage(renderer); bool needGenerateData = ls->IsGenerateDataRequired(renderer, this, GetDataNode()); if (needGenerateData) { ls->UpdateGenerateDataTime(); this->UpdateVtkObjects(); } ApplyColorAndOpacityProperties(renderer, nullptr); } void mitk::EnhancedPointSetVtkMapper3D::UpdateVtkTransform(mitk::BaseRenderer * /*renderer*/) { // TODO: apply new transform if time step changed // vtkLinearTransform * vtktransform = // this->GetDataNode()->GetVtkTransform(this->GetTimestep()); // m_SelectedActor->SetUserTransform(vtktransform); // m_UnselectedActor->SetUserTransform(vtktransform); // m_ContourActor->SetUserTransform(vtktransform); } void mitk::EnhancedPointSetVtkMapper3D::SetDefaultProperties(mitk::DataNode *node, mitk::BaseRenderer *renderer, bool overwrite) { node->AddProperty("line width", mitk::IntProperty::New(2), renderer, overwrite); node->AddProperty("pointsize", mitk::FloatProperty::New(1.0), renderer, overwrite); node->AddProperty( "selectedcolor", mitk::ColorProperty::New(1.0f, 1.0f, 0.0f), renderer, overwrite); // yellow for selected node->AddProperty( "unselectedcolor", mitk::ColorProperty::New(0.5f, 1.0f, 0.5f), renderer, overwrite); // middle green for unselected node->AddProperty("color", mitk::ColorProperty::New(1.0f, 0.0f, 0.0f), renderer, overwrite); // red as standard node->AddProperty("show contour", mitk::BoolProperty::New(false), renderer, overwrite); node->AddProperty("contourcolor", mitk::ColorProperty::New(1.0f, 0.0f, 0.0f), renderer, overwrite); node->AddProperty("contoursize", mitk::FloatProperty::New(0.5), renderer, overwrite); node->AddProperty("show points", mitk::BoolProperty::New(true), renderer, overwrite); node->AddProperty("show label", mitk::BoolProperty::New(false), renderer, overwrite); node->AddProperty("label", mitk::StringProperty::New("P"), renderer, overwrite); node->AddProperty("opacity", mitk::FloatProperty::New(1.0), renderer, overwrite); Superclass::SetDefaultProperties(node, renderer, overwrite); } void mitk::EnhancedPointSetVtkMapper3D::DeleteVtkObject(vtkObject *o) { if (o != nullptr) o->Delete(); } void mitk::EnhancedPointSetVtkMapper3D::RemoveEntryFromSourceMaps(mitk::PointSet::PointIdentifier pointID) { auto aIt = m_PointActors.find(pointID); if (aIt == m_PointActors.end()) return; switch (aIt->second.second) // erase in old map { // TODO: look up representation in a representationlookuptable case PTSTART: // cube m_CubeSources[pointID]->Delete(); m_CubeSources.erase(pointID); break; case PTCORNER: // cone m_ConeSources[pointID]->Delete(); m_ConeSources.erase(pointID); break; case PTEDGE: // cylinder m_CylinderSources[pointID]->Delete(); m_CylinderSources.erase(pointID); break; case PTUNDEFINED: // sphere case PTEND: default: m_SphereSources[pointID]->Delete(); m_SphereSources.erase(pointID); break; } } diff --git a/Modules/MapperExt/src/mitkGPUVolumeMapper3D.cpp b/Modules/MapperExt/src/mitkGPUVolumeMapper3D.cpp index 30d42b37c1..e83db65613 100644 --- a/Modules/MapperExt/src/mitkGPUVolumeMapper3D.cpp +++ b/Modules/MapperExt/src/mitkGPUVolumeMapper3D.cpp @@ -1,592 +1,590 @@ /*=================================================================== 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. ===================================================================*/ #define GPU_INFO MITK_INFO("mapper.vr") #define GPU_WARN MITK_WARN("mapper.vr") #define GPU_ERROR MITK_ERROR("mapper.vr") #include "mitkGPUVolumeMapper3D.h" #include "mitkDataNode.h" #include "mitkColorProperty.h" #include "mitkColorProperty.h" #include "mitkLevelWindow.h" #include "mitkLevelWindowProperty.h" #include "mitkLookupTableProperty.h" #include "mitkProperties.h" #include "mitkRenderingManager.h" #include "mitkTransferFunctionInitializer.h" #include "mitkTransferFunctionProperty.h" #include "mitkVtkPropRenderer.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include -#include #include #include #include "vtkMitkGPUVolumeRayCastMapper.h" #include "vtkOpenGLGPUVolumeRayCastMapper.h" const mitk::Image *mitk::GPUVolumeMapper3D::GetInput() { return static_cast(GetDataNode()->GetData()); } void mitk::GPUVolumeMapper3D::MitkRenderVolumetricGeometry(mitk::BaseRenderer *renderer) { VtkMapper::MitkRenderVolumetricGeometry(renderer); // if(ls->m_gpuInitialized) // ls->m_MapperGPU->UpdateMTime(); } bool mitk::GPUVolumeMapper3D::InitGPU(mitk::BaseRenderer * /*renderer*/) { return false; } void mitk::GPUVolumeMapper3D::InitCPU(mitk::BaseRenderer *renderer) { LocalStorage *ls = m_LSH.GetLocalStorage(renderer); if (ls->m_cpuInitialized) return; ls->m_VtkRenderWindow = renderer->GetVtkRenderer()->GetRenderWindow(); ls->m_MapperCPU = vtkSmartPointer::New(); int numThreads = ls->m_MapperCPU->GetNumberOfThreads(); GPU_INFO << "initializing cpu-raycast-vr (vtkFixedPointVolumeRayCastMapper) (" << numThreads << " threads)"; ls->m_MapperCPU->SetSampleDistance(1.0); // ls->m_MapperCPU->LockSampleDistanceToInputSpacingOn(); ls->m_MapperCPU->SetImageSampleDistance(1.0); ls->m_MapperCPU->IntermixIntersectingGeometryOn(); ls->m_MapperCPU->SetAutoAdjustSampleDistances(0); ls->m_VolumePropertyCPU = vtkSmartPointer::New(); ls->m_VolumePropertyCPU->ShadeOn(); ls->m_VolumePropertyCPU->SetAmbient(0.10f); // 0.05f ls->m_VolumePropertyCPU->SetDiffuse(0.50f); // 0.45f ls->m_VolumePropertyCPU->SetSpecular(0.40f); // 0.50f ls->m_VolumePropertyCPU->SetSpecularPower(16.0f); ls->m_VolumePropertyCPU->SetInterpolationTypeToLinear(); ls->m_VolumeCPU = vtkSmartPointer::New(); ls->m_VolumeCPU->SetMapper(ls->m_MapperCPU); ls->m_VolumeCPU->SetProperty(ls->m_VolumePropertyCPU); ls->m_VolumeCPU->VisibilityOn(); ls->m_MapperCPU->SetInputConnection(m_UnitSpacingImageFilter->GetOutputPort()); // m_Resampler->GetOutput()); ls->m_cpuInitialized = true; } void mitk::GPUVolumeMapper3D::DeinitCPU(mitk::BaseRenderer *renderer) { LocalStorage *ls = m_LSH.GetLocalStorage(renderer); if (!ls->m_cpuInitialized) return; GPU_INFO << "deinitializing cpu-raycast-vr"; ls->m_VolumePropertyCPU = nullptr; ls->m_MapperCPU = nullptr; ls->m_VolumeCPU = nullptr; ls->m_cpuInitialized = false; } mitk::GPUVolumeMapper3D::GPUVolumeMapper3D() { m_Volumenullptr = nullptr; m_commonInitialized = false; } mitk::GPUVolumeMapper3D::~GPUVolumeMapper3D() { DeinitCommon(); } void mitk::GPUVolumeMapper3D::InitCommon() { if (m_commonInitialized) return; m_UnitSpacingImageFilter = vtkSmartPointer::New(); m_UnitSpacingImageFilter->SetOutputSpacing(1.0, 1.0, 1.0); CreateDefaultTransferFunctions(); m_commonInitialized = true; } void mitk::GPUVolumeMapper3D::DeinitCommon() { if (!m_commonInitialized) return; m_commonInitialized = false; } bool mitk::GPUVolumeMapper3D::IsRenderable(mitk::BaseRenderer *renderer) { if (!GetDataNode()) return false; DataNode *node = GetDataNode(); bool visible = true; node->GetVisibility(visible, renderer, "visible"); if (!visible) return false; bool value = false; if (!node->GetBoolProperty("volumerendering", value, renderer)) return false; if (!value) return false; auto *input = const_cast(this->GetInput()); if (!input || !input->IsInitialized()) return false; vtkImageData *inputData = input->GetVtkImageData(this->GetTimestep()); if (inputData == nullptr) return false; return true; } void mitk::GPUVolumeMapper3D::InitVtkMapper(mitk::BaseRenderer *renderer) { if (IsRAYEnabled(renderer)) { DeinitCPU(renderer); if (!InitRAY(renderer)) { GPU_WARN << "hardware renderer can't initialize ... falling back to software renderer"; goto fallback; } } else if (IsGPUEnabled(renderer)) { DeinitCPU(renderer); DeinitRAY(renderer); if (!InitGPU(renderer)) { GPU_WARN << "hardware renderer can't initialize ... falling back to software renderer"; goto fallback; } } else { fallback: DeinitRAY(renderer); InitCPU(renderer); } } vtkProp *mitk::GPUVolumeMapper3D::GetVtkProp(mitk::BaseRenderer *renderer) { if (!IsRenderable(renderer)) { if (!m_Volumenullptr) { m_Volumenullptr = vtkSmartPointer::New(); m_Volumenullptr->VisibilityOff(); } return m_Volumenullptr; } InitCommon(); InitVtkMapper(renderer); LocalStorage *ls = m_LSH.GetLocalStorage(renderer); if (ls->m_rayInitialized) return ls->m_VolumeRAY; if (ls->m_gpuInitialized) return ls->m_VolumeGPU; return ls->m_VolumeCPU; } void mitk::GPUVolumeMapper3D::GenerateDataForRenderer(mitk::BaseRenderer *renderer) { if (!IsRenderable(renderer)) return; InitCommon(); InitVtkMapper(renderer); auto *input = const_cast(this->GetInput()); vtkImageData *inputData = input->GetVtkImageData(this->GetTimestep()); m_UnitSpacingImageFilter->SetInputData(inputData); LocalStorage *ls = m_LSH.GetLocalStorage(renderer); if (ls->m_rayInitialized) { GenerateDataRAY(renderer); } else if (ls->m_gpuInitialized) { GenerateDataGPU(renderer); } else { GenerateDataCPU(renderer); } // UpdateTransferFunctions UpdateTransferFunctions(renderer); } void mitk::GPUVolumeMapper3D::GenerateDataGPU(mitk::BaseRenderer * /*renderer*/) { } void mitk::GPUVolumeMapper3D::GenerateDataCPU(mitk::BaseRenderer *renderer) { LocalStorage *ls = m_LSH.GetLocalStorage(renderer); int nextLod = mitk::RenderingManager::GetInstance()->GetNextLOD(renderer); if (IsLODEnabled(renderer) && nextLod == 0) { ls->m_MapperCPU->SetImageSampleDistance(3.5); ls->m_MapperCPU->SetSampleDistance(1.25); ls->m_VolumePropertyCPU->SetInterpolationTypeToNearest(); } else { ls->m_MapperCPU->SetImageSampleDistance(1.0); ls->m_MapperCPU->SetSampleDistance(1.0); ls->m_VolumePropertyCPU->SetInterpolationTypeToLinear(); } // Check raycasting mode if (IsMIPEnabled(renderer)) ls->m_MapperCPU->SetBlendModeToMaximumIntensity(); else ls->m_MapperCPU->SetBlendModeToComposite(); // Updating shadings { float value = 0; if (GetDataNode()->GetFloatProperty("volumerendering.cpu.ambient", value, renderer)) ls->m_VolumePropertyCPU->SetAmbient(value); if (GetDataNode()->GetFloatProperty("volumerendering.cpu.diffuse", value, renderer)) ls->m_VolumePropertyCPU->SetDiffuse(value); if (GetDataNode()->GetFloatProperty("volumerendering.cpu.specular", value, renderer)) ls->m_VolumePropertyCPU->SetSpecular(value); if (GetDataNode()->GetFloatProperty("volumerendering.cpu.specular.power", value, renderer)) ls->m_VolumePropertyCPU->SetSpecularPower(value); } } void mitk::GPUVolumeMapper3D::CreateDefaultTransferFunctions() { m_DefaultOpacityTransferFunction = vtkSmartPointer::New(); m_DefaultOpacityTransferFunction->AddPoint(0.0, 0.0); m_DefaultOpacityTransferFunction->AddPoint(255.0, 0.8); m_DefaultOpacityTransferFunction->ClampingOn(); m_DefaultGradientTransferFunction = vtkSmartPointer::New(); m_DefaultGradientTransferFunction->AddPoint(0.0, 0.0); m_DefaultGradientTransferFunction->AddPoint(255.0, 0.8); m_DefaultGradientTransferFunction->ClampingOn(); m_DefaultColorTransferFunction = vtkSmartPointer::New(); m_DefaultColorTransferFunction->AddRGBPoint(0.0, 0.0, 0.0, 0.0); m_DefaultColorTransferFunction->AddRGBPoint(127.5, 1, 1, 0.0); m_DefaultColorTransferFunction->AddRGBPoint(255.0, 0.8, 0.2, 0); m_DefaultColorTransferFunction->ClampingOn(); m_BinaryOpacityTransferFunction = vtkSmartPointer::New(); m_BinaryOpacityTransferFunction->AddPoint(0, 0.0); m_BinaryOpacityTransferFunction->AddPoint(1, 1.0); m_BinaryGradientTransferFunction = vtkSmartPointer::New(); m_BinaryGradientTransferFunction->AddPoint(0.0, 1.0); m_BinaryColorTransferFunction = vtkSmartPointer::New(); } void mitk::GPUVolumeMapper3D::UpdateTransferFunctions(mitk::BaseRenderer *renderer) { LocalStorage *ls = m_LSH.GetLocalStorage(renderer); vtkPiecewiseFunction *opacityTransferFunction = m_DefaultOpacityTransferFunction; vtkPiecewiseFunction *gradientTransferFunction = m_DefaultGradientTransferFunction; vtkColorTransferFunction *colorTransferFunction = m_DefaultColorTransferFunction; bool isBinary = false; GetDataNode()->GetBoolProperty("binary", isBinary, renderer); if (isBinary) { opacityTransferFunction = m_BinaryOpacityTransferFunction; gradientTransferFunction = m_BinaryGradientTransferFunction; colorTransferFunction = m_BinaryColorTransferFunction; colorTransferFunction->RemoveAllPoints(); float rgb[3]; if (!GetDataNode()->GetColor(rgb, renderer)) rgb[0] = rgb[1] = rgb[2] = 1; colorTransferFunction->AddRGBPoint(0, rgb[0], rgb[1], rgb[2]); colorTransferFunction->Modified(); } else { auto *transferFunctionProp = dynamic_cast(this->GetDataNode()->GetProperty("TransferFunction", renderer)); if (transferFunctionProp) { opacityTransferFunction = transferFunctionProp->GetValue()->GetScalarOpacityFunction(); gradientTransferFunction = transferFunctionProp->GetValue()->GetGradientOpacityFunction(); colorTransferFunction = transferFunctionProp->GetValue()->GetColorTransferFunction(); } } if (ls->m_gpuInitialized) { ls->m_VolumePropertyGPU->SetColor(colorTransferFunction); ls->m_VolumePropertyGPU->SetScalarOpacity(opacityTransferFunction); ls->m_VolumePropertyGPU->SetGradientOpacity(gradientTransferFunction); } if (ls->m_rayInitialized) { ls->m_VolumePropertyRAY->SetColor(colorTransferFunction); ls->m_VolumePropertyRAY->SetScalarOpacity(opacityTransferFunction); ls->m_VolumePropertyRAY->SetGradientOpacity(gradientTransferFunction); } if (ls->m_cpuInitialized) { ls->m_VolumePropertyCPU->SetColor(colorTransferFunction); ls->m_VolumePropertyCPU->SetScalarOpacity(opacityTransferFunction); ls->m_VolumePropertyCPU->SetGradientOpacity(gradientTransferFunction); } } void mitk::GPUVolumeMapper3D::ApplyProperties(vtkActor * /*actor*/, mitk::BaseRenderer * /*renderer*/) { // GPU_INFO << "ApplyProperties"; } void mitk::GPUVolumeMapper3D::SetDefaultProperties(mitk::DataNode *node, mitk::BaseRenderer *renderer, bool overwrite) { // GPU_INFO << "SetDefaultProperties"; node->AddProperty("volumerendering", mitk::BoolProperty::New(false), renderer, overwrite); node->AddProperty("volumerendering.usemip", mitk::BoolProperty::New(false), renderer, overwrite); node->AddProperty("volumerendering.uselod", mitk::BoolProperty::New(false), renderer, overwrite); node->AddProperty("volumerendering.cpu.ambient", mitk::FloatProperty::New(0.10f), renderer, overwrite); node->AddProperty("volumerendering.cpu.diffuse", mitk::FloatProperty::New(0.50f), renderer, overwrite); node->AddProperty("volumerendering.cpu.specular", mitk::FloatProperty::New(0.40f), renderer, overwrite); node->AddProperty("volumerendering.cpu.specular.power", mitk::FloatProperty::New(16.0f), renderer, overwrite); bool usegpu = true; #ifdef __APPLE__ usegpu = false; node->AddProperty("volumerendering.uselod", mitk::BoolProperty::New(true), renderer, overwrite); #endif node->AddProperty("volumerendering.usegpu", mitk::BoolProperty::New(usegpu), renderer, overwrite); node->AddProperty("volumerendering.useray", mitk::BoolProperty::New(false), renderer, overwrite); node->AddProperty("volumerendering.ray.ambient", mitk::FloatProperty::New(0.25f), renderer, overwrite); node->AddProperty("volumerendering.ray.diffuse", mitk::FloatProperty::New(0.50f), renderer, overwrite); node->AddProperty("volumerendering.ray.specular", mitk::FloatProperty::New(0.40f), renderer, overwrite); node->AddProperty("volumerendering.ray.specular.power", mitk::FloatProperty::New(16.0f), renderer, overwrite); node->AddProperty("volumerendering.gpu.ambient", mitk::FloatProperty::New(0.25f), renderer, overwrite); node->AddProperty("volumerendering.gpu.diffuse", mitk::FloatProperty::New(0.50f), renderer, overwrite); node->AddProperty("volumerendering.gpu.specular", mitk::FloatProperty::New(0.40f), renderer, overwrite); node->AddProperty("volumerendering.gpu.specular.power", mitk::FloatProperty::New(16.0f), renderer, overwrite); node->AddProperty("volumerendering.gpu.usetexturecompression", mitk::BoolProperty::New(false), renderer, overwrite); node->AddProperty("volumerendering.gpu.reducesliceartifacts", mitk::BoolProperty::New(false), renderer, overwrite); node->AddProperty("binary", mitk::BoolProperty::New(false), renderer, overwrite); mitk::Image::Pointer image = dynamic_cast(node->GetData()); if (image.IsNotNull() && image->IsInitialized()) { if ((overwrite) || (node->GetProperty("levelwindow", renderer) == nullptr)) { mitk::LevelWindowProperty::Pointer levWinProp = mitk::LevelWindowProperty::New(); mitk::LevelWindow levelwindow; levelwindow.SetAuto(image); levWinProp->SetLevelWindow(levelwindow); node->SetProperty("levelwindow", levWinProp, renderer); } if ((overwrite) || (node->GetProperty("TransferFunction", renderer) == nullptr)) { // add a default transfer function mitk::TransferFunction::Pointer tf = mitk::TransferFunction::New(); mitk::TransferFunctionInitializer::Pointer tfInit = mitk::TransferFunctionInitializer::New(tf); tfInit->SetTransferFunctionMode(0); node->SetProperty("TransferFunction", mitk::TransferFunctionProperty::New(tf.GetPointer())); } } Superclass::SetDefaultProperties(node, renderer, overwrite); } bool mitk::GPUVolumeMapper3D::IsLODEnabled(mitk::BaseRenderer *renderer) const { bool value = false; return GetDataNode()->GetBoolProperty("volumerendering.uselod", value, renderer) && value; } bool mitk::GPUVolumeMapper3D::IsMIPEnabled(mitk::BaseRenderer *renderer) { bool value = false; return GetDataNode()->GetBoolProperty("volumerendering.usemip", value, renderer) && value; } bool mitk::GPUVolumeMapper3D::IsGPUEnabled(mitk::BaseRenderer *renderer) { LocalStorage *ls = m_LSH.GetLocalStorage(renderer); bool value = false; return ls->m_gpuSupported && GetDataNode()->GetBoolProperty("volumerendering.usegpu", value, renderer) && value; } bool mitk::GPUVolumeMapper3D::InitRAY(mitk::BaseRenderer *renderer) { LocalStorage *ls = m_LSH.GetLocalStorage(renderer); if (ls->m_rayInitialized) return ls->m_raySupported; ls->m_VtkRenderWindow = renderer->GetVtkRenderer()->GetRenderWindow(); GPU_INFO << "initializing gpu-raycast-vr (vtkOpenGLGPUVolumeRayCastMapper)"; ls->m_MapperRAY = vtkSmartPointer::New(); ls->m_MapperRAY->SetAutoAdjustSampleDistances(0); ls->m_MapperRAY->SetSampleDistance(1.0); ls->m_VolumePropertyRAY = vtkSmartPointer::New(); ls->m_VolumePropertyRAY->ShadeOn(); ls->m_VolumePropertyRAY->SetAmbient(0.25f); // 0.05f ls->m_VolumePropertyRAY->SetDiffuse(0.50f); // 0.45f ls->m_VolumePropertyRAY->SetSpecular(0.40f); // 0.50f ls->m_VolumePropertyRAY->SetSpecularPower(16.0f); ls->m_VolumePropertyRAY->SetInterpolationTypeToLinear(); ls->m_VolumeRAY = vtkSmartPointer::New(); ls->m_VolumeRAY->SetMapper(ls->m_MapperRAY); ls->m_VolumeRAY->SetProperty(ls->m_VolumePropertyRAY); ls->m_VolumeRAY->VisibilityOn(); ls->m_MapperRAY->SetInputConnection(this->m_UnitSpacingImageFilter->GetOutputPort()); ls->m_raySupported = ls->m_MapperRAY->IsRenderSupported(renderer->GetRenderWindow(), ls->m_VolumePropertyRAY); ls->m_rayInitialized = true; return ls->m_raySupported; } void mitk::GPUVolumeMapper3D::DeinitRAY(mitk::BaseRenderer *renderer) { LocalStorage *ls = m_LSH.GetLocalStorage(renderer); if (ls->m_rayInitialized) { GPU_INFO << "deinitializing gpu-raycast-vr"; ls->m_MapperRAY = nullptr; ls->m_VolumePropertyRAY = nullptr; // Here ReleaseGraphicsResources has to be called to avoid VTK error messages. // This seems like a VTK bug, because ReleaseGraphicsResources() is ment for internal use, // but you cannot just delete the object (last smartpointer reference) without getting the // VTK error. ls->m_VolumeRAY->ReleaseGraphicsResources(renderer->GetVtkRenderer()->GetRenderWindow()); ls->m_VolumeRAY = nullptr; ls->m_rayInitialized = false; } } void mitk::GPUVolumeMapper3D::GenerateDataRAY(mitk::BaseRenderer *renderer) { LocalStorage *ls = m_LSH.GetLocalStorage(renderer); if (IsLODEnabled(renderer) && mitk::RenderingManager::GetInstance()->GetNextLOD(renderer) == 0) ls->m_MapperRAY->SetImageSampleDistance(4.0); else ls->m_MapperRAY->SetImageSampleDistance(1.0); // Check raycasting mode if (IsMIPEnabled(renderer)) ls->m_MapperRAY->SetBlendModeToMaximumIntensity(); else ls->m_MapperRAY->SetBlendModeToComposite(); // Updating shadings { float value = 0; if (GetDataNode()->GetFloatProperty("volumerendering.ray.ambient", value, renderer)) ls->m_VolumePropertyRAY->SetAmbient(value); if (GetDataNode()->GetFloatProperty("volumerendering.ray.diffuse", value, renderer)) ls->m_VolumePropertyRAY->SetDiffuse(value); if (GetDataNode()->GetFloatProperty("volumerendering.ray.specular", value, renderer)) ls->m_VolumePropertyRAY->SetSpecular(value); if (GetDataNode()->GetFloatProperty("volumerendering.ray.specular.power", value, renderer)) ls->m_VolumePropertyRAY->SetSpecularPower(value); } } bool mitk::GPUVolumeMapper3D::IsRAYEnabled(mitk::BaseRenderer *renderer) { LocalStorage *ls = m_LSH.GetLocalStorage(renderer); bool value = false; return ls->m_raySupported && GetDataNode()->GetBoolProperty("volumerendering.useray", value, renderer) && value; } diff --git a/Modules/MapperExt/src/mitkMeshVtkMapper3D.cpp b/Modules/MapperExt/src/mitkMeshVtkMapper3D.cpp index 962df7cc1d..fa9b4d15df 100644 --- a/Modules/MapperExt/src/mitkMeshVtkMapper3D.cpp +++ b/Modules/MapperExt/src/mitkMeshVtkMapper3D.cpp @@ -1,233 +1,230 @@ /*=================================================================== 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 "mitkMeshVtkMapper3D.h" #include "mitkDataNode.h" #include "mitkProperties.h" #include "mitkVtkPropRenderer.h" #ifndef VCL_VC60 #include "mitkMeshUtil.h" #endif #include #include #include #include #include #include #include #include #include const mitk::Mesh *mitk::MeshVtkMapper3D::GetInput() { return static_cast(GetDataNode()->GetData()); } vtkProp *mitk::MeshVtkMapper3D::GetVtkProp(mitk::BaseRenderer * /*renderer*/) { return m_PropAssembly; } void mitk::MeshVtkMapper3D::UpdateVtkTransform(mitk::BaseRenderer * /*renderer*/) { vtkLinearTransform *vtktransform = this->GetDataNode()->GetVtkTransform(this->GetTimestep()); m_SpheresActor->SetUserTransform(vtktransform); m_ContourActor->SetUserTransform(vtktransform); } mitk::MeshVtkMapper3D::MeshVtkMapper3D() : m_PropAssembly(nullptr) { m_Spheres = vtkAppendPolyData::New(); m_Contour = vtkPolyData::New(); m_SpheresActor = vtkActor::New(); m_SpheresMapper = vtkPolyDataMapper::New(); m_SpheresActor->SetMapper(m_SpheresMapper); m_ContourActor = vtkActor::New(); m_ContourMapper = vtkPolyDataMapper::New(); m_ContourActor->SetMapper(m_ContourMapper); m_ContourActor->GetProperty()->SetAmbient(1.0); m_PropAssembly = vtkPropAssembly::New(); // a vtkPropAssembly is not a sub-class of vtkProp3D, so // we cannot use m_Prop3D. } mitk::MeshVtkMapper3D::~MeshVtkMapper3D() { m_ContourActor->Delete(); m_SpheresActor->Delete(); m_ContourMapper->Delete(); m_SpheresMapper->Delete(); m_PropAssembly->Delete(); m_Spheres->Delete(); m_Contour->Delete(); } void mitk::MeshVtkMapper3D::GenerateDataForRenderer(mitk::BaseRenderer *renderer) { BaseLocalStorage *ls = m_LSH.GetLocalStorage(renderer); bool needGenerateData = ls->IsGenerateDataRequired(renderer, this, GetDataNode()); if (needGenerateData) { ls->UpdateGenerateDataTime(); m_PropAssembly->VisibilityOn(); if (m_PropAssembly->GetParts()->IsItemPresent(m_SpheresActor)) m_PropAssembly->RemovePart(m_SpheresActor); if (m_PropAssembly->GetParts()->IsItemPresent(m_ContourActor)) m_PropAssembly->RemovePart(m_ContourActor); m_Spheres->RemoveAllInputs(); m_Contour->Initialize(); mitk::Mesh::Pointer input = const_cast(this->GetInput()); input->Update(); mitk::Mesh::DataType::Pointer itkMesh = input->GetMesh(this->GetTimestep()); if (itkMesh.GetPointer() == nullptr) { m_PropAssembly->VisibilityOff(); return; } mitk::Mesh::PointsContainer::Iterator i; int j; float floatRgba[4] = {1.0f, 1.0f, 1.0f, 1.0f}; double doubleRgba[4] = {1.0f, 1.0f, 1.0f, 1.0f}; mitk::Color tmpColor; // check for color prop and use it for rendering if it exists m_DataNode->GetColor(floatRgba, nullptr); if (dynamic_cast(this->GetDataNode()->GetProperty("unselectedcolor")) != nullptr) { tmpColor = dynamic_cast(this->GetDataNode()->GetProperty("unselectedcolor"))->GetValue(); floatRgba[0] = tmpColor[0]; floatRgba[1] = tmpColor[1]; floatRgba[2] = tmpColor[2]; floatRgba[3] = 1.0f; //!!define a new ColorProp to be able to pass alpha value doubleRgba[0] = floatRgba[0]; doubleRgba[1] = floatRgba[1]; doubleRgba[2] = floatRgba[2]; doubleRgba[3] = floatRgba[3]; } if (itkMesh->GetNumberOfPoints() > 0) { // build m_Spheres->GetOutput() vtkPolyData float pointSize = 2.0; mitk::FloatProperty::Pointer pointSizeProp = dynamic_cast(this->GetDataNode()->GetProperty("pointsize")); if (pointSizeProp.IsNotNull()) pointSize = pointSizeProp->GetValue(); for (j = 0, i = itkMesh->GetPoints()->Begin(); i != itkMesh->GetPoints()->End(); i++, j++) { vtkSphereSource *sphere = vtkSphereSource::New(); sphere->SetRadius(pointSize); sphere->SetCenter(i.Value()[0], i.Value()[1], i.Value()[2]); m_Spheres->AddInputConnection(sphere->GetOutputPort()); sphere->Delete(); } // setup mapper, actor and add to assembly m_SpheresMapper->SetInputConnection(m_Spheres->GetOutputPort()); m_SpheresActor->GetProperty()->SetColor(doubleRgba); m_PropAssembly->AddPart(m_SpheresActor); } if (itkMesh->GetNumberOfCells() > 0) { // build m_Contour vtkPolyData #ifdef VCL_VC60 itkExceptionMacro(<< "MeshVtkMapper3D currently not working for MS Visual C++ 6.0, because MeshUtils are " "currently not supported."); #else m_Contour = MeshUtil::MeshToPolyData(itkMesh.GetPointer(), false, false, 0, nullptr, m_Contour); #endif if (m_Contour->GetNumberOfCells() > 0) { // setup mapper, actor and add to assembly m_ContourMapper->SetInputData(m_Contour); bool wireframe = true; GetDataNode()->GetVisibility(wireframe, nullptr, "wireframe"); if (wireframe) m_ContourActor->GetProperty()->SetRepresentationToWireframe(); else m_ContourActor->GetProperty()->SetRepresentationToSurface(); m_ContourActor->GetProperty()->SetColor(doubleRgba); m_PropAssembly->AddPart(m_ContourActor); } } } - SetVtkMapperImmediateModeRendering(m_ContourMapper); - SetVtkMapperImmediateModeRendering(m_SpheresMapper); - bool visible = true; GetDataNode()->GetVisibility(visible, renderer, "visible"); if (!visible) { m_SpheresActor->VisibilityOff(); m_ContourActor->VisibilityOff(); return; } bool makeContour = false; this->GetDataNode()->GetBoolProperty("show contour", makeContour); if (makeContour) { m_ContourActor->VisibilityOn(); } else { m_ContourActor->VisibilityOff(); } bool showPoints = true; this->GetDataNode()->GetBoolProperty("show points", showPoints); if (showPoints) { m_SpheresActor->VisibilityOn(); } else { m_SpheresActor->VisibilityOff(); } } void mitk::MeshVtkMapper3D::ResetMapper(BaseRenderer * /*renderer*/) { m_PropAssembly->VisibilityOff(); } diff --git a/Modules/MapperExt/src/vtkMitkGPUVolumeRayCastMapper.cpp b/Modules/MapperExt/src/vtkMitkGPUVolumeRayCastMapper.cpp index d15ed58243..c1351b96e4 100644 --- a/Modules/MapperExt/src/vtkMitkGPUVolumeRayCastMapper.cpp +++ b/Modules/MapperExt/src/vtkMitkGPUVolumeRayCastMapper.cpp @@ -1,624 +1,623 @@ /*=================================================================== 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. ===================================================================*/ /*========================================================================= Program: Visualization Toolkit Module: $RCSfile: vtkMitkGPUVolumeRayCastMapper.cxx,v $ Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen All rights reserved. See Copyright.txt or http://www.kitware.com/Copyright.htm for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notice for more information. =========================================================================*/ #include "vtkMitkGPUVolumeRayCastMapper.h" #include "vtkCamera.h" #include "vtkCellData.h" #include "vtkCommand.h" // for VolumeMapperRender{Start|End|Progress}Event #include "vtkDataArray.h" #include "vtkGPUInfo.h" #include "vtkGPUInfoList.h" #include "vtkImageData.h" #include "vtkImageResample.h" #include "vtkMultiThreader.h" #include "vtkPointData.h" #include "vtkRenderWindow.h" #include "vtkRenderer.h" #include "vtkRendererCollection.h" #include "vtkTimerLog.h" #include "vtkVolume.h" #include "vtkVolumeProperty.h" #include -vtkInstantiatorNewMacro(vtkMitkGPUVolumeRayCastMapper); vtkCxxSetObjectMacro(vtkMitkGPUVolumeRayCastMapper, MaskInput, vtkImageData); vtkCxxSetObjectMacro(vtkMitkGPUVolumeRayCastMapper, TransformedInput, vtkImageData); vtkMitkGPUVolumeRayCastMapper::vtkMitkGPUVolumeRayCastMapper() { this->AutoAdjustSampleDistances = 1; this->ImageSampleDistance = 1.0; this->MinimumImageSampleDistance = 1.0; this->MaximumImageSampleDistance = 10.0; this->SampleDistance = 1.0; this->SmallVolumeRender = 0; this->BigTimeToDraw = 0.0; this->SmallTimeToDraw = 0.0; this->FinalColorWindow = 1.0; this->FinalColorLevel = 0.5; this->GeneratingCanonicalView = 0; this->CanonicalViewImageData = nullptr; this->MaskInput = nullptr; this->MaskBlendFactor = 1.0f; this->AMRMode = 0; this->ClippedCroppingRegionPlanes[0] = VTK_DOUBLE_MAX; this->ClippedCroppingRegionPlanes[1] = VTK_DOUBLE_MIN; this->ClippedCroppingRegionPlanes[2] = VTK_DOUBLE_MAX; this->ClippedCroppingRegionPlanes[3] = VTK_DOUBLE_MIN; this->ClippedCroppingRegionPlanes[4] = VTK_DOUBLE_MAX; this->ClippedCroppingRegionPlanes[5] = VTK_DOUBLE_MIN; this->MaxMemoryInBytes = 0; vtkGPUInfoList *l = vtkGPUInfoList::New(); l->Probe(); if (l->GetNumberOfGPUs() > 0) { vtkGPUInfo *info = l->GetGPUInfo(0); this->MaxMemoryInBytes = info->GetDedicatedVideoMemory(); if (this->MaxMemoryInBytes == 0) { this->MaxMemoryInBytes = info->GetDedicatedSystemMemory(); } // we ignore info->GetSharedSystemMemory(); as this is very slow. } l->Delete(); if (this->MaxMemoryInBytes == 0) // use some default value: 128MB. { this->MaxMemoryInBytes = 128 * 1024 * 1024; } this->MaxMemoryFraction = 0.75; this->ReportProgress = true; this->TransformedInput = nullptr; this->LastInput = nullptr; } // ---------------------------------------------------------------------------- vtkMitkGPUVolumeRayCastMapper::~vtkMitkGPUVolumeRayCastMapper() { this->SetMaskInput(nullptr); this->SetTransformedInput(nullptr); this->LastInput = nullptr; } // ---------------------------------------------------------------------------- // The render method that is called from the volume. If this is a canonical // view render, a specialized version of this method will be called instead. // Otherwise we will // - Invoke a start event // - Start timing // - Check that everything is OK for rendering // - Render // - Stop the timer and record results // - Invoke an end event // ---------------------------------------------------------------------------- void vtkMitkGPUVolumeRayCastMapper::Render(vtkRenderer *ren, vtkVolume *vol) { // Catch renders that are happening due to a canonical view render and // handle them separately. if (this->GeneratingCanonicalView) { this->CanonicalViewRender(ren, vol); return; } // Invoke a VolumeMapperRenderStartEvent this->InvokeEvent(vtkCommand::VolumeMapperRenderStartEvent, nullptr); // Start the timer to time the length of this render vtkTimerLog *timer = vtkTimerLog::New(); timer->StartTimer(); // Make sure everything about this render is OK. // This is where the input is updated. if (this->ValidateRender(ren, vol)) { // Everything is OK - so go ahead and really do the render this->GPURender(ren, vol); } // Stop the timer timer->StopTimer(); double t = timer->GetElapsedTime(); // cout << "Render Timer " << t << " seconds, " << 1.0/t << " frames per second" << endl; this->TimeToDraw = t; timer->Delete(); if (vol->GetAllocatedRenderTime() < 1.0) { this->SmallTimeToDraw = t; } else { this->BigTimeToDraw = t; } // Invoke a VolumeMapperRenderEndEvent this->InvokeEvent(vtkCommand::VolumeMapperRenderEndEvent, nullptr); } // ---------------------------------------------------------------------------- // Special version for rendering a canonical view - we don't do things like // invoke start or end events, and we don't capture the render time. // ---------------------------------------------------------------------------- void vtkMitkGPUVolumeRayCastMapper::CanonicalViewRender(vtkRenderer *ren, vtkVolume *vol) { // Make sure everything about this render is OK if (this->ValidateRender(ren, vol)) { // Everything is OK - so go ahead and really do the render this->GPURender(ren, vol); } } // ---------------------------------------------------------------------------- // This method us used by the render method to validate everything before // attempting to render. This method returns 0 if something is not right - // such as missing input, a null renderer or a null volume, no scalars, etc. // In some cases it will produce a vtkErrorMacro message, and in others // (for example, in the case of cropping planes that define a region with // a volume or 0 or less) it will fail silently. If everything is OK, it will // return with a value of 1. // ---------------------------------------------------------------------------- int vtkMitkGPUVolumeRayCastMapper::ValidateRender(vtkRenderer *ren, vtkVolume *vol) { // Check that we have everything we need to render. int goodSoFar = 1; // Check for a renderer - we MUST have one if (!ren) { goodSoFar = 0; vtkErrorMacro("Renderer cannot be null."); } // Check for the volume - we MUST have one if (goodSoFar && !vol) { goodSoFar = 0; vtkErrorMacro("Volume cannot be null."); } // Don't need to check if we have a volume property // since the volume will create one if we don't. Also // don't need to check for the scalar opacity function // or the RGB transfer function since the property will // create them if they do not yet exist. // However we must currently check that the number of // color channels is 3 // TODO: lift this restriction - should work with // gray functions as well. Right now turning off test // because otherwise 4 component rendering isn't working. // Will revisit. if (goodSoFar && vol->GetProperty()->GetColorChannels() != 3) { // goodSoFar = 0; // vtkErrorMacro("Must have a color transfer function."); } // Check the cropping planes. If they are invalid, just silently // fail. This will happen when an interactive widget is dragged // such that it defines 0 or negative volume - this can happen // and should just not render the volume. // Check the cropping planes if (goodSoFar && this->Cropping && (this->CroppingRegionPlanes[0] >= this->CroppingRegionPlanes[1] || this->CroppingRegionPlanes[2] >= this->CroppingRegionPlanes[3] || this->CroppingRegionPlanes[4] >= this->CroppingRegionPlanes[5])) { // No error message here - we want to be silent goodSoFar = 0; } // Check that we have input data vtkImageData *input = this->GetInput(); // If we have a timestamp change or data change then create a new clone. if (input != this->LastInput || input->GetMTime() > this->TransformedInput->GetMTime()) { this->LastInput = input; vtkImageData *clone; if (!this->TransformedInput) { clone = vtkImageData::New(); this->SetTransformedInput(clone); clone->Delete(); } else { clone = this->TransformedInput; } clone->ShallowCopy(input); // @TODO: This is the workaround to deal with GPUVolumeRayCastMapper // not able to handle extents starting from non zero values. // There is not a easy fix in the GPU volume ray cast mapper hence // this fix has been introduced. // Get the current extents. int extents[6], real_extents[6]; clone->GetExtent(extents); clone->GetExtent(real_extents); // Get the current origin and spacing. double origin[3], spacing[3]; clone->GetOrigin(origin); clone->GetSpacing(spacing); for (int cc = 0; cc < 3; cc++) { // Transform the origin and the extents. origin[cc] = origin[cc] + extents[2 * cc] * spacing[cc]; extents[2 * cc + 1] -= extents[2 * cc]; extents[2 * cc] -= extents[2 * cc]; } clone->SetOrigin(origin); clone->SetExtent(extents); } if (goodSoFar && !this->TransformedInput) { vtkErrorMacro("Input is nullptr but is required"); goodSoFar = 0; } // Update the date then make sure we have scalars. Note // that we must have point or cell scalars because field // scalars are not supported. vtkDataArray *scalars = nullptr; if (goodSoFar) { // Here is where we update the input // this->TransformedInput->UpdateInformation(); //VTK6_TODO // this->TransformedInput->SetUpdateExtentToWholeExtent(); // this->TransformedInput->Update(); // Now make sure we can find scalars scalars = this->GetScalars( this->TransformedInput, this->ScalarMode, this->ArrayAccessMode, this->ArrayId, this->ArrayName, this->CellFlag); // We couldn't find scalars if (!scalars) { vtkErrorMacro("No scalars found on input."); goodSoFar = 0; } // Even if we found scalars, if they are field data scalars that isn't good else if (this->CellFlag == 2) { vtkErrorMacro("Only point or cell scalar support - found field scalars instead."); goodSoFar = 0; } } // Make sure the scalar type is actually supported. This mappers supports // almost all standard scalar types. if (goodSoFar) { switch (scalars->GetDataType()) { case VTK_CHAR: vtkErrorMacro(<< "scalar of type VTK_CHAR is not supported " << "because this type is platform dependent. " << "Use VTK_SIGNED_CHAR or VTK_UNSIGNED_CHAR instead."); goodSoFar = 0; break; case VTK_BIT: vtkErrorMacro("scalar of type VTK_BIT is not supported by this mapper."); goodSoFar = 0; break; case VTK_ID_TYPE: vtkErrorMacro("scalar of type VTK_ID_TYPE is not supported by this mapper."); goodSoFar = 0; break; case VTK_STRING: vtkErrorMacro("scalar of type VTK_STRING is not supported by this mapper."); goodSoFar = 0; break; default: // Don't need to do anything here break; } } // Check on the blending type - we support composite and min / max intensity if (goodSoFar) { if (this->BlendMode != vtkVolumeMapper::COMPOSITE_BLEND && this->BlendMode != vtkVolumeMapper::MAXIMUM_INTENSITY_BLEND && this->BlendMode != vtkVolumeMapper::MINIMUM_INTENSITY_BLEND) { goodSoFar = 0; vtkErrorMacro(<< "Selected blend mode not supported. " << "Only Composite and MIP and MinIP modes " << "are supported by the current implementation."); } } // This mapper supports 1 component data, or 4 component if it is not independent // component (i.e. the four components define RGBA) int numberOfComponents = 0; if (goodSoFar) { numberOfComponents = scalars->GetNumberOfComponents(); if (!(numberOfComponents == 1 || (numberOfComponents == 4 && vol->GetProperty()->GetIndependentComponents() == 0))) { goodSoFar = 0; vtkErrorMacro(<< "Only one component scalars, or four " << "component with non-independent components, " << "are supported by this mapper."); } } // If this is four component data, then it better be unsigned char (RGBA). if (goodSoFar && numberOfComponents == 4 && scalars->GetDataType() != VTK_UNSIGNED_CHAR) { goodSoFar = 0; vtkErrorMacro("Only unsigned char is supported for 4-component scalars!"); } // return our status return goodSoFar; } // ---------------------------------------------------------------------------- // Description: // Called by the AMR Volume Mapper. // Set the flag that tells if the scalars are on point data (0) or // cell data (1). void vtkMitkGPUVolumeRayCastMapper::SetCellFlag(int cellFlag) { this->CellFlag = cellFlag; } // ---------------------------------------------------------------------------- void vtkMitkGPUVolumeRayCastMapper::CreateCanonicalView(vtkRenderer *ren, vtkVolume *volume, vtkImageData *image, int vtkNotUsed(blend_mode), double viewDirection[3], double viewUp[3]) { this->GeneratingCanonicalView = 1; int oldSwap = ren->GetRenderWindow()->GetSwapBuffers(); ren->GetRenderWindow()->SwapBuffersOff(); int dim[3]; image->GetDimensions(dim); int *size = ren->GetRenderWindow()->GetSize(); vtkImageData *bigImage = vtkImageData::New(); bigImage->SetDimensions(size[0], size[1], 1); bigImage->AllocateScalars(VTK_UNSIGNED_CHAR, 3); this->CanonicalViewImageData = bigImage; double scale[2]; scale[0] = dim[0] / static_cast(size[0]); scale[1] = dim[1] / static_cast(size[1]); // Save the visibility flags of the renderers and set all to false except // for the ren. vtkRendererCollection *renderers = ren->GetRenderWindow()->GetRenderers(); int numberOfRenderers = renderers->GetNumberOfItems(); auto rendererVisibilities = new bool[numberOfRenderers]; renderers->InitTraversal(); int i = 0; while (i < numberOfRenderers) { vtkRenderer *r = renderers->GetNextItem(); rendererVisibilities[i] = r->GetDraw() == 1; if (r != ren) { r->SetDraw(false); } ++i; } // Save the visibility flags of the props and set all to false except // for the volume. vtkPropCollection *props = ren->GetViewProps(); int numberOfProps = props->GetNumberOfItems(); auto propVisibilities = new bool[numberOfProps]; props->InitTraversal(); i = 0; while (i < numberOfProps) { vtkProp *p = props->GetNextProp(); propVisibilities[i] = p->GetVisibility() == 1; if (p != volume) { p->SetVisibility(false); } ++i; } vtkCamera *savedCamera = ren->GetActiveCamera(); savedCamera->Modified(); vtkCamera *canonicalViewCamera = vtkCamera::New(); // Code from vtkFixedPointVolumeRayCastMapper: double *center = volume->GetCenter(); double bounds[6]; volume->GetBounds(bounds); double d = sqrt((bounds[1] - bounds[0]) * (bounds[1] - bounds[0]) + (bounds[3] - bounds[2]) * (bounds[3] - bounds[2]) + (bounds[5] - bounds[4]) * (bounds[5] - bounds[4])); // For now use x distance - need to change this d = bounds[1] - bounds[0]; // Set up the camera in parallel canonicalViewCamera->SetFocalPoint(center); canonicalViewCamera->ParallelProjectionOn(); canonicalViewCamera->SetPosition( center[0] - d * viewDirection[0], center[1] - d * viewDirection[1], center[2] - d * viewDirection[2]); canonicalViewCamera->SetViewUp(viewUp); canonicalViewCamera->SetParallelScale(d / 2); ren->SetActiveCamera(canonicalViewCamera); ren->GetRenderWindow()->Render(); ren->SetActiveCamera(savedCamera); canonicalViewCamera->Delete(); // Shrink to image to the desired size vtkImageResample *resample = vtkImageResample::New(); resample->SetInputData(bigImage); resample->SetAxisMagnificationFactor(0, scale[0]); resample->SetAxisMagnificationFactor(1, scale[1]); resample->SetAxisMagnificationFactor(2, 1); resample->UpdateWholeExtent(); // Copy the pixels over image->DeepCopy(resample->GetOutput()); bigImage->Delete(); resample->Delete(); // Restore the visibility flags of the props props->InitTraversal(); i = 0; while (i < numberOfProps) { vtkProp *p = props->GetNextProp(); p->SetVisibility(propVisibilities[i]); ++i; } delete[] propVisibilities; // Restore the visibility flags of the renderers renderers->InitTraversal(); i = 0; while (i < numberOfRenderers) { vtkRenderer *r = renderers->GetNextItem(); r->SetDraw(rendererVisibilities[i]); ++i; } delete[] rendererVisibilities; ren->GetRenderWindow()->SetSwapBuffers(oldSwap); this->CanonicalViewImageData = nullptr; this->GeneratingCanonicalView = 0; } // ---------------------------------------------------------------------------- // Print method for vtkMitkGPUVolumeRayCastMapper void vtkMitkGPUVolumeRayCastMapper::PrintSelf(ostream &os, vtkIndent indent) { this->Superclass::PrintSelf(os, indent); os << indent << "AutoAdjustSampleDistances: " << this->AutoAdjustSampleDistances << endl; os << indent << "MinimumImageSampleDistance: " << this->MinimumImageSampleDistance << endl; os << indent << "MaximumImageSampleDistance: " << this->MaximumImageSampleDistance << endl; os << indent << "ImageSampleDistance: " << this->ImageSampleDistance << endl; os << indent << "SampleDistance: " << this->SampleDistance << endl; os << indent << "FinalColorWindow: " << this->FinalColorWindow << endl; os << indent << "FinalColorLevel: " << this->FinalColorLevel << endl; os << indent << "MaskInput: " << this->MaskInput << endl; os << indent << "MaskBlendFactor: " << this->MaskBlendFactor << endl; os << indent << "MaxMemoryInBytes: " << this->MaxMemoryInBytes << endl; os << indent << "MaxMemoryFraction: " << this->MaxMemoryFraction << endl; os << indent << "ReportProgress: " << this->ReportProgress << endl; } // ---------------------------------------------------------------------------- // Description: // Compute the cropping planes clipped by the bounds of the volume. // The result is put into this->ClippedCroppingRegionPlanes. // NOTE: IT WILL BE MOVED UP TO vtkVolumeMapper after bullet proof usage // in this mapper. Other subclasses will use the ClippedCroppingRegionsPlanes // members instead of CroppingRegionPlanes. // \pre volume_exists: this->GetInput()!=0 // \pre valid_cropping: this->Cropping && // this->CroppingRegionPlanes[0]CroppingRegionPlanes[1] && // this->CroppingRegionPlanes[2]CroppingRegionPlanes[3] && // this->CroppingRegionPlanes[4]CroppingRegionPlanes[5]) void vtkMitkGPUVolumeRayCastMapper::ClipCroppingRegionPlanes() { assert("pre: volume_exists" && this->GetInput() != nullptr); assert("pre: valid_cropping" && this->Cropping && this->CroppingRegionPlanes[0] < this->CroppingRegionPlanes[1] && this->CroppingRegionPlanes[2] < this->CroppingRegionPlanes[3] && this->CroppingRegionPlanes[4] < this->CroppingRegionPlanes[5]); // vtkVolumeMapper::Render() will have something like: // if(this->Cropping && (this->CroppingRegionPlanes[0]>=this->CroppingRegionPlanes[1] || // this->CroppingRegionPlanes[2]>=this->CroppingRegionPlanes[3] || // this->CroppingRegionPlanes[4]>=this->CroppingRegionPlanes[5])) // { // // silentely stop because the cropping is not valid. // return; // } double volBounds[6]; this->GetInput()->GetBounds(volBounds); int i = 0; while (i < 6) { // max of the mins if (this->CroppingRegionPlanes[i] < volBounds[i]) { this->ClippedCroppingRegionPlanes[i] = volBounds[i]; } else { this->ClippedCroppingRegionPlanes[i] = this->CroppingRegionPlanes[i]; } ++i; // min of the maxs if (this->CroppingRegionPlanes[i] > volBounds[i]) { this->ClippedCroppingRegionPlanes[i] = volBounds[i]; } else { this->ClippedCroppingRegionPlanes[i] = this->CroppingRegionPlanes[i]; } ++i; } } diff --git a/Modules/MapperExt/src/vtkUnstructuredGridMapper.cpp b/Modules/MapperExt/src/vtkUnstructuredGridMapper.cpp index 8ecc1f8357..4ad4eea95f 100644 --- a/Modules/MapperExt/src/vtkUnstructuredGridMapper.cpp +++ b/Modules/MapperExt/src/vtkUnstructuredGridMapper.cpp @@ -1,223 +1,222 @@ /*=================================================================== 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 "vtkUnstructuredGridMapper.h" #include "vtkExecutive.h" #include "vtkGarbageCollector.h" #include "vtkGeometryFilter.h" #include "vtkInformation.h" #include "vtkObjectFactory.h" #include "vtkPolyData.h" #include "vtkPolyDataMapper.h" #include "vtkScalarsToColors.h" #include "vtkUnstructuredGrid.h" vtkStandardNewMacro(vtkUnstructuredGridMapper); //---------------------------------------------------------------------------- vtkUnstructuredGridMapper::vtkUnstructuredGridMapper() { this->GeometryExtractor = nullptr; this->PolyDataMapper = nullptr; } //---------------------------------------------------------------------------- vtkUnstructuredGridMapper::~vtkUnstructuredGridMapper() { // delete internally created objects. if (this->GeometryExtractor) { this->GeometryExtractor->Delete(); } if (this->PolyDataMapper) { this->PolyDataMapper->Delete(); } } void vtkUnstructuredGridMapper::SetBoundingObject(mitk::BoundingObject *bo) { m_BoundingObject = bo; } //---------------------------------------------------------------------------- void vtkUnstructuredGridMapper::SetInput(vtkUnstructuredGrid *input) { this->SetInputDataObject(input); } //---------------------------------------------------------------------------- vtkUnstructuredGrid *vtkUnstructuredGridMapper::GetInput() { // return this->Superclass::GetInputAsDataSet(); return vtkUnstructuredGrid::SafeDownCast(this->GetExecutive()->GetInputData(0, 0)); } //---------------------------------------------------------------------------- void vtkUnstructuredGridMapper::ReleaseGraphicsResources(vtkWindow *renWin) { if (this->PolyDataMapper) { this->PolyDataMapper->ReleaseGraphicsResources(renWin); } } //---------------------------------------------------------------------------- void vtkUnstructuredGridMapper::ReleaseGraphicsResources(mitk::BaseRenderer *renderer) { if (this->PolyDataMapper) { this->PolyDataMapper->ReleaseGraphicsResources(renderer->GetVtkRenderer()->GetRenderWindow()); } } //---------------------------------------------------------------------------- // Receives from Actor -> maps data to primitives // void vtkUnstructuredGridMapper::Render(vtkRenderer *ren, vtkActor *act) { // make sure that we've been properly initialized // if (!this->GetInput()) { vtkErrorMacro(<< "No input!\n"); return; } // Need a lookup table // if (this->LookupTable == nullptr) { this->CreateDefaultLookupTable(); } this->LookupTable->Build(); // Now can create appropriate mapper // if (this->PolyDataMapper == nullptr) { vtkGeometryFilter *gf = vtkGeometryFilter::New(); vtkPolyDataMapper *pm = vtkPolyDataMapper::New(); pm->SetInputConnection(gf->GetOutputPort()); this->GeometryExtractor = gf; this->PolyDataMapper = pm; } // share clipping planes with the PolyDataMapper // if (this->ClippingPlanes != this->PolyDataMapper->GetClippingPlanes()) { this->PolyDataMapper->SetClippingPlanes(this->ClippingPlanes); } if (this->m_BoundingObject) { mitk::BoundingBox::BoundsArrayType bounds = this->m_BoundingObject->GetGeometry()->CalculateBoundingBoxRelativeToTransform(nullptr)->GetBounds(); this->GeometryExtractor->SetExtent(bounds[0], bounds[1], bounds[2], bounds[3], bounds[4], bounds[5]); this->GeometryExtractor->ExtentClippingOn(); } else { this->GeometryExtractor->ExtentClippingOff(); } this->GeometryExtractor->SetInputData(this->GetInput()); this->PolyDataMapper->SetInputConnection(this->GeometryExtractor->GetOutputPort()); // update ourselves in case something has changed this->PolyDataMapper->SetLookupTable(this->GetLookupTable()); this->PolyDataMapper->SetScalarVisibility(this->GetScalarVisibility()); this->PolyDataMapper->SetUseLookupTableScalarRange(this->GetUseLookupTableScalarRange()); this->PolyDataMapper->SetScalarRange(this->GetScalarRange()); - this->PolyDataMapper->SetImmediateModeRendering(this->GetImmediateModeRendering()); this->PolyDataMapper->SetColorMode(this->GetColorMode()); this->PolyDataMapper->SetInterpolateScalarsBeforeMapping(this->GetInterpolateScalarsBeforeMapping()); this->PolyDataMapper->SetScalarMode(this->GetScalarMode()); if (this->ScalarMode == VTK_SCALAR_MODE_USE_POINT_FIELD_DATA || this->ScalarMode == VTK_SCALAR_MODE_USE_CELL_FIELD_DATA) { if (this->ArrayAccessMode == VTK_GET_ARRAY_BY_ID) { this->PolyDataMapper->ColorByArrayComponent(this->ArrayId, ArrayComponent); } else { this->PolyDataMapper->ColorByArrayComponent(this->ArrayName, ArrayComponent); } } this->PolyDataMapper->Render(ren, act); this->TimeToDraw = this->PolyDataMapper->GetTimeToDraw(); } //---------------------------------------------------------------------------- void vtkUnstructuredGridMapper::PrintSelf(ostream &os, vtkIndent indent) { this->Superclass::PrintSelf(os, indent); if (this->PolyDataMapper) { os << indent << "Poly Mapper: (" << this->PolyDataMapper << ")\n"; } else { os << indent << "Poly Mapper: (none)\n"; } if (this->GeometryExtractor) { os << indent << "Geometry Extractor: (" << this->GeometryExtractor << ")\n"; } else { os << indent << "Geometry Extractor: (none)\n"; } } //---------------------------------------------------------------------------- vtkMTimeType vtkUnstructuredGridMapper::GetMTime() { vtkMTimeType mTime = this->vtkMapper::GetMTime(); vtkMTimeType time; if (this->LookupTable != nullptr) { time = this->LookupTable->GetMTime(); mTime = (time > mTime ? time : mTime); } return mTime; } //---------------------------------------------------------------------------- int vtkUnstructuredGridMapper::FillInputPortInformation(int vtkNotUsed(port), vtkInformation *info) { info->Set(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkUnstructuredGrid"); return 1; } //---------------------------------------------------------------------------- void vtkUnstructuredGridMapper::ReportReferences(vtkGarbageCollector *collector) { this->Superclass::ReportReferences(collector); // These filters share our input and are therefore involved in a // reference loop. vtkGarbageCollectorReport(collector, this->GeometryExtractor, "GeometryExtractor"); vtkGarbageCollectorReport(collector, this->PolyDataMapper, "PolyDataMapper"); } diff --git a/Modules/MatchPointRegistration/Rendering/mitkRegEvaluationMapper2D.cpp b/Modules/MatchPointRegistration/Rendering/mitkRegEvaluationMapper2D.cpp index a39d6379d7..ea6be768d4 100644 --- a/Modules/MatchPointRegistration/Rendering/mitkRegEvaluationMapper2D.cpp +++ b/Modules/MatchPointRegistration/Rendering/mitkRegEvaluationMapper2D.cpp @@ -1,840 +1,840 @@ /*=================================================================== 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 "mitkImageStatisticsHolder.h" #include "mitkPlaneClipping.h" #include "mitkRegVisPropertyTags.h" #include "mitkRegVisHelper.h" #include "mitkRegEvalStyleProperty.h" #include "mitkRegEvalWipeStyleProperty.h" //MITK Rendering #include "mitkRegEvaluationMapper2D.h" #include "vtkMitkThickSlicesFilter.h" #include "vtkMitkLevelWindowFilter.h" #include "vtkNeverTranslucentTexture.h" //VTK #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include //ITK #include #include //MatchPoint #include #include mitk::RegEvaluationMapper2D::RegEvaluationMapper2D() { } mitk::RegEvaluationMapper2D::~RegEvaluationMapper2D() { } //set the two points defining the textured plane according to the dimension and spacing void mitk::RegEvaluationMapper2D::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::RegEvaluationMapper2D::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::RegEvaluationMapper2D::GetTargetImage( void ) { const mitk::RegEvaluationObject* evalObj = dynamic_cast< const mitk::RegEvaluationObject* >( GetDataNode()->GetData() ); if (evalObj) { return evalObj->GetTargetImage(); } return nullptr; } const mitk::Image* mitk::RegEvaluationMapper2D::GetMovingImage( void ) { const mitk::RegEvaluationObject* evalObj = dynamic_cast< const mitk::RegEvaluationObject* >( GetDataNode()->GetData() ); if (evalObj) { return evalObj->GetMovingImage(); } return nullptr; } const mitk::DataNode* mitk::RegEvaluationMapper2D::GetTargetNode(void) { const mitk::RegEvaluationObject* evalObj = dynamic_cast< const mitk::RegEvaluationObject* >(GetDataNode()->GetData()); if (evalObj) { return evalObj->GetTargetNode(); } return nullptr; } const mitk::DataNode* mitk::RegEvaluationMapper2D::GetMovingNode(void) { const mitk::RegEvaluationObject* evalObj = dynamic_cast< const mitk::RegEvaluationObject* >(GetDataNode()->GetData()); if (evalObj) { return evalObj->GetMovingNode(); } return nullptr; } const mitk::MAPRegistrationWrapper* mitk::RegEvaluationMapper2D::GetRegistration( void ) { const mitk::RegEvaluationObject* evalObj = dynamic_cast< const mitk::RegEvaluationObject* >( GetDataNode()->GetData() ); if (evalObj) { return evalObj->GetRegistration(); } return nullptr; } vtkProp* mitk::RegEvaluationMapper2D::GetVtkProp(mitk::BaseRenderer* renderer) { //return the actor corresponding to the renderer return m_LSH.GetLocalStorage(renderer)->m_Actors; } void mitk::RegEvaluationMapper2D::GenerateDataForRenderer( mitk::BaseRenderer *renderer ) { bool updated = false; LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); mitk::Image::Pointer targetInput = const_cast< mitk::Image * >( this->GetTargetImage() ); mitk::DataNode* datanode = this->GetDataNode(); if ( targetInput.IsNull() || targetInput->IsInitialized() == false ) { return; } mitk::Image::ConstPointer movingInput = this->GetMovingImage(); if ( movingInput.IsNull() || movingInput->IsInitialized() == false ) { return; } mitk::MAPRegistrationWrapper::ConstPointer reg = this->GetRegistration(); //check if there is a valid worldGeometry const PlaneGeometry *worldGeometry = renderer->GetCurrentWorldPlaneGeometry(); if( ( worldGeometry == nullptr ) || ( !worldGeometry->IsValid() ) || ( !worldGeometry->HasReferenceGeometry() )) { return; } if(targetInput->GetMTime()>localStorage->m_LastUpdateTime || (localStorage->m_LastUpdateTime < renderer->GetCurrentWorldPlaneGeometryUpdateTime()) //was the geometry modified? || (localStorage->m_LastUpdateTime < renderer->GetCurrentWorldPlaneGeometry()->GetMTime())) { //target input has been modified -> reslice target input targetInput->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, targetInput->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_EvaluationImage = nullptr; localStorage->m_Mapper->SetInputData( localStorage->m_EmptyPolyData ); return; } //set main input for ExtractSliceFilter localStorage->m_Reslicer->SetInput(targetInput); 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( targetInput->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 ( (targetInput->GetDimension() >= 3) && (targetInput->GetDimension(2) > 1) ) { VtkResliceInterpolationProperty *resliceInterpolationProperty; datanode->GetProperty( resliceInterpolationProperty, "reslice interpolation" ); 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); } //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_slicedTargetImage = localStorage->m_Reslicer->GetOutput(); updated = true; } if(updated || movingInput->GetMTime() > localStorage->m_LastUpdateTime || reg->GetMTime() > localStorage->m_LastUpdateTime) { //Map moving image localStorage->m_slicedMappedImage = mitk::ImageMappingHelper::map(movingInput,reg,false,0,localStorage->m_slicedTargetImage->GetGeometry(),false,0); updated = true; } // Bounds information for reslicing (only required if reference geometry // is present) //this used for generating a vtkPLaneSource with the right size double sliceBounds[6] = { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 }; if (updated || (localStorage->m_LastUpdateTime < datanode->GetPropertyList()->GetMTime()) //was a property modified? || (localStorage->m_LastUpdateTime < datanode->GetPropertyList(renderer)->GetMTime()) || (localStorage->m_LastUpdateTime < this->GetTargetNode()->GetMTime()) || (localStorage->m_LastUpdateTime < this->GetMovingNode()->GetMTime())) { 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] = { 0.0, 0.0, 0.0, 0.0, 0.0, 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. const PlaneGeometry *planeGeometry = dynamic_cast(worldGeometry); mitk::PlaneClipping::CalculateClippedPlaneBounds(targetInput->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_TargetLevelWindowFilter->SetClippingBounds(textureClippingBounds); localStorage->m_MappedLevelWindowFilter->SetClippingBounds(textureClippingBounds); } this->ApplyLookuptable(renderer, this->GetTargetNode(), localStorage->m_TargetLevelWindowFilter); this->ApplyLookuptable(renderer, this->GetMovingNode(), localStorage->m_MappedLevelWindowFilter); this->ApplyLevelWindow(renderer, this->GetTargetNode(), localStorage->m_TargetLevelWindowFilter); this->ApplyLevelWindow(renderer, this->GetMovingNode(), localStorage->m_MappedLevelWindowFilter); //connect the input with the levelwindow filter localStorage->m_TargetLevelWindowFilter->SetInputData(localStorage->m_slicedTargetImage->GetVtkImageData()); localStorage->m_MappedLevelWindowFilter->SetInputData(localStorage->m_slicedMappedImage->GetVtkImageData()); localStorage->m_TargetExtractFilter->SetInputConnection(localStorage->m_TargetLevelWindowFilter->GetOutputPort()); localStorage->m_MappedExtractFilter->SetInputConnection(localStorage->m_MappedLevelWindowFilter->GetOutputPort()); localStorage->m_TargetExtractFilter->SetComponents(0); localStorage->m_MappedExtractFilter->SetComponents(0); updated = true; } //Generate evaluation image bool isStyleOutdated = mitk::PropertyIsOutdated(datanode,mitk::nodeProp_RegEvalStyle,localStorage->m_LastUpdateTime); bool isBlendOutdated = mitk::PropertyIsOutdated(datanode,mitk::nodeProp_RegEvalBlendFactor,localStorage->m_LastUpdateTime); bool isCheckerOutdated = mitk::PropertyIsOutdated(datanode,mitk::nodeProp_RegEvalCheckerCount,localStorage->m_LastUpdateTime); bool isWipeStyleOutdated = mitk::PropertyIsOutdated(datanode,mitk::nodeProp_RegEvalWipeStyle,localStorage->m_LastUpdateTime); bool isContourOutdated = mitk::PropertyIsOutdated(datanode,mitk::nodeProp_RegEvalTargetContour,localStorage->m_LastUpdateTime); bool isPositionOutdated = mitk::PropertyIsOutdated(datanode, mitk::nodeProp_RegEvalCurrentPosition, localStorage->m_LastUpdateTime); if (updated || isStyleOutdated || isBlendOutdated || isCheckerOutdated || isWipeStyleOutdated || isContourOutdated || isPositionOutdated) { mitk::RegEvalStyleProperty::Pointer evalStyleProp = mitk::RegEvalStyleProperty::New(); datanode->GetProperty(evalStyleProp, mitk::nodeProp_RegEvalStyle); switch (evalStyleProp->GetValueAsId()) { case 0 : { PrepareBlend(datanode, localStorage); break; } case 1 : { PrepareColorBlend(localStorage); break; } case 2 : { PrepareCheckerBoard(datanode, localStorage); break; } case 3 : { const PlaneGeometry *worldGeometry = renderer->GetCurrentWorldPlaneGeometry(); Point3D currentPos3D; datanode->GetPropertyValue(mitk::nodeProp_RegEvalCurrentPosition, currentPos3D); Point2D currentPos2D; worldGeometry->Map(currentPos3D, currentPos2D); Point2D currentIndex2D; worldGeometry->WorldToIndex(currentPos2D, currentIndex2D); PrepareWipe(datanode, localStorage, currentIndex2D); break; } case 4 : { PrepareDifference(localStorage); break; } case 5 : { PrepareContour(datanode, localStorage); break; } } updated = true; } if(updated || (localStorage->m_LastUpdateTime < datanode->GetPropertyList()->GetMTime()) //was a property modified? || (localStorage->m_LastUpdateTime < datanode->GetPropertyList(renderer)->GetMTime()) ) { this->ApplyOpacity( renderer ); // do not use a VTK lookup table (we do that ourselves in m_LevelWindowFilter) - localStorage->m_Texture->MapColorScalarsThroughLookupTableOff(); + localStorage->m_Texture->SetColorModeToDirectScalars(); // 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->SetInputData(localStorage->m_EvaluationImage); this->TransformActor( renderer ); vtkActor* contourShadowActor = dynamic_cast (localStorage->m_Actors->GetParts()->GetItemAsObject(0)); //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::RegEvaluationMapper2D::PrepareContour( mitk::DataNode* datanode, LocalStorage * localStorage ) { bool targetContour = true; datanode->GetBoolProperty(mitk::nodeProp_RegEvalTargetContour,targetContour); vtkSmartPointer magFilter = vtkSmartPointer::New(); if(targetContour) { magFilter->SetInputConnection(localStorage->m_TargetExtractFilter->GetOutputPort()); } else { magFilter->SetInputConnection(localStorage->m_MappedExtractFilter->GetOutputPort()); } vtkSmartPointer appendFilter = vtkSmartPointer::New(); appendFilter->AddInputConnection(magFilter->GetOutputPort()); appendFilter->AddInputConnection(magFilter->GetOutputPort()); if(targetContour) { appendFilter->AddInputConnection(localStorage->m_MappedExtractFilter->GetOutputPort()); } else { appendFilter->AddInputConnection(localStorage->m_TargetExtractFilter->GetOutputPort()); } appendFilter->Update(); localStorage->m_EvaluationImage = appendFilter->GetOutput(); } void mitk::RegEvaluationMapper2D::PrepareDifference( LocalStorage * localStorage ) { vtkSmartPointer diffFilter = vtkSmartPointer::New(); vtkSmartPointer minFilter = vtkSmartPointer::New(); vtkSmartPointer maxFilter = vtkSmartPointer::New(); minFilter->SetInputConnection(0, localStorage->m_TargetExtractFilter->GetOutputPort()); minFilter->SetInputConnection(1, localStorage->m_MappedExtractFilter->GetOutputPort()); minFilter->SetOperationToMin(); maxFilter->SetInputConnection(0, localStorage->m_TargetExtractFilter->GetOutputPort()); maxFilter->SetInputConnection(1, localStorage->m_MappedExtractFilter->GetOutputPort()); maxFilter->SetOperationToMax(); diffFilter->SetInputConnection(0, maxFilter->GetOutputPort()); diffFilter->SetInputConnection(1, minFilter->GetOutputPort()); diffFilter->SetOperationToSubtract(); diffFilter->Update(); localStorage->m_EvaluationImage = diffFilter->GetOutput(); } void mitk::RegEvaluationMapper2D::PrepareWipe(mitk::DataNode* datanode, LocalStorage * localStorage, const Point2D& currentIndex2D) { mitk::RegEvalWipeStyleProperty::Pointer evalWipeStyleProp = mitk::RegEvalWipeStyleProperty::New(); datanode->GetProperty(evalWipeStyleProp, mitk::nodeProp_RegEvalWipeStyle); vtkSmartPointer wipedFilter = vtkSmartPointer::New(); wipedFilter->SetInputConnection(0, localStorage->m_TargetLevelWindowFilter->GetOutputPort()); wipedFilter->SetInputConnection(1, localStorage->m_MappedLevelWindowFilter->GetOutputPort()); wipedFilter->SetPosition(currentIndex2D[0], currentIndex2D[1]); if (evalWipeStyleProp->GetValueAsId() == 0) { wipedFilter->SetWipeToQuad(); } else if (evalWipeStyleProp->GetValueAsId() == 1) { wipedFilter->SetWipeToHorizontal(); } else if (evalWipeStyleProp->GetValueAsId() == 2) { wipedFilter->SetWipeToVertical(); } wipedFilter->Update(); localStorage->m_EvaluationImage = wipedFilter->GetOutput(); } void mitk::RegEvaluationMapper2D::PrepareCheckerBoard( mitk::DataNode* datanode, LocalStorage * localStorage ) { int checkerCount = 5; datanode->GetIntProperty(mitk::nodeProp_RegEvalCheckerCount,checkerCount); vtkSmartPointer checkerboardFilter = vtkSmartPointer::New(); checkerboardFilter->SetInputConnection(0, localStorage->m_TargetLevelWindowFilter->GetOutputPort()); checkerboardFilter->SetInputConnection(1, localStorage->m_MappedLevelWindowFilter->GetOutputPort()); checkerboardFilter->SetNumberOfDivisions(checkerCount, checkerCount, 1); checkerboardFilter->Update(); localStorage->m_EvaluationImage = checkerboardFilter->GetOutput(); } void mitk::RegEvaluationMapper2D::PrepareColorBlend( LocalStorage * localStorage ) { vtkSmartPointer appendFilter = vtkSmartPointer::New(); //red channel appendFilter->AddInputConnection(localStorage->m_MappedExtractFilter->GetOutputPort()); //green channel appendFilter->AddInputConnection(localStorage->m_MappedExtractFilter->GetOutputPort()); //blue channel appendFilter->AddInputConnection(localStorage->m_TargetExtractFilter->GetOutputPort()); appendFilter->Update(); localStorage->m_EvaluationImage = appendFilter->GetOutput(); } void mitk::RegEvaluationMapper2D::PrepareBlend( mitk::DataNode* datanode, LocalStorage * localStorage ) { int blendfactor = 50; datanode->GetIntProperty(mitk::nodeProp_RegEvalBlendFactor,blendfactor); vtkSmartPointer blendFilter = vtkSmartPointer::New(); blendFilter->AddInputConnection(localStorage->m_TargetExtractFilter->GetOutputPort()); blendFilter->AddInputConnection(localStorage->m_MappedExtractFilter->GetOutputPort()); blendFilter->SetWeight(0, (100 - blendfactor) / 100.); blendFilter->SetWeight(1,blendfactor/100.); blendFilter->Update(); localStorage->m_EvaluationImage = blendFilter->GetOutput(); } void mitk::RegEvaluationMapper2D::ApplyLevelWindow(mitk::BaseRenderer *renderer, const mitk::DataNode* dataNode, vtkMitkLevelWindowFilter* levelFilter) { LevelWindow levelWindow; dataNode->GetLevelWindow(levelWindow, renderer, "levelwindow"); levelFilter->GetLookupTable()->SetRange(levelWindow.GetLowerWindowBound(), levelWindow.GetUpperWindowBound()); mitk::LevelWindow opacLevelWindow; if (dataNode->GetLevelWindow(opacLevelWindow, renderer, "opaclevelwindow")) { //pass the opaque level window to the filter levelFilter->SetMinOpacity(opacLevelWindow.GetLowerWindowBound()); levelFilter->SetMaxOpacity(opacLevelWindow.GetUpperWindowBound()); } else { //no opaque level window levelFilter->SetMinOpacity(0.0); levelFilter->SetMaxOpacity(255.0); } } void mitk::RegEvaluationMapper2D::ApplyLookuptable(mitk::BaseRenderer* renderer, const mitk::DataNode* dataNode, vtkMitkLevelWindowFilter* levelFilter) { 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(dataNode->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. } levelFilter->SetLookupTable(usedLookupTable); } void mitk::RegEvaluationMapper2D::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::RegEvaluationMapper2D::Update(mitk::BaseRenderer* renderer) { bool visible = true; GetDataNode()->GetVisibility(visible, renderer, "visible"); if ( !visible ) { return; } mitk::Image* data = const_cast( this->GetTargetImage() ); 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()) //was the node modified? || (localStorage->m_LastUpdateTime < data->GetPipelineMTime()) //Was the data modified? || (localStorage->m_LastUpdateTime < renderer->GetCurrentWorldPlaneGeometryUpdateTime()) //was the geometry modified? || (localStorage->m_LastUpdateTime < renderer->GetCurrentWorldPlaneGeometry()->GetMTime()) || (localStorage->m_LastUpdateTime < node->GetPropertyList()->GetMTime()) //was a property modified? || (localStorage->m_LastUpdateTime < node->GetPropertyList(renderer)->GetMTime()) || (localStorage->m_LastUpdateTime < this->GetTargetNode()->GetMTime()) //was the target node modified? || (localStorage->m_LastUpdateTime < this->GetMovingNode()->GetMTime()) //was the moving node modified? || (localStorage->m_LastUpdateTime < this->GetTargetNode()->GetPropertyList()->GetMTime()) //was a target node property modified? || (localStorage->m_LastUpdateTime < this->GetTargetNode()->GetPropertyList(renderer)->GetMTime()) || (localStorage->m_LastUpdateTime < this->GetMovingNode()->GetPropertyList()->GetMTime()) //was a moving node property modified? || (localStorage->m_LastUpdateTime < this->GetMovingNode()->GetPropertyList(renderer)->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::RegEvaluationMapper2D::SetDefaultProperties(mitk::DataNode* node, mitk::BaseRenderer* renderer, bool overwrite) { mitk::RegEvaluationObject* regEval = dynamic_cast(node->GetData()); if(!regEval) { return; } // Properties common for both images and segmentations node->AddProperty( "depthOffset", mitk::FloatProperty::New( 0.0 ), renderer, overwrite ); if(regEval->GetTargetImage() && regEval->GetTargetImage()->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 ) ); // set to user configurable default value (see global options) 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(); mitk::LookupTableProperty::Pointer mitkLutProp = mitk::LookupTableProperty::New(); mitkLutProp->SetLookupTable(mitkLut); node->SetProperty("LookupTable", mitkLutProp); 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); node->AddProperty(mitk::nodeProp_RegEvalStyle, mitk::RegEvalStyleProperty::New(0), renderer, overwrite); node->AddProperty(mitk::nodeProp_RegEvalBlendFactor, mitk::IntProperty::New(50), renderer, overwrite); node->AddProperty(mitk::nodeProp_RegEvalCheckerCount, mitk::IntProperty::New(3), renderer, overwrite); node->AddProperty(mitk::nodeProp_RegEvalTargetContour, mitk::BoolProperty::New(true), renderer, overwrite); node->AddProperty(mitk::nodeProp_RegEvalWipeStyle, mitk::RegEvalWipeStyleProperty::New(0), renderer, overwrite); node->AddProperty(mitk::nodeProp_RegEvalCurrentPosition, mitk::GenericProperty::New(mitk::Point3D()), renderer, overwrite); Superclass::SetDefaultProperties(node, renderer, overwrite); } mitk::RegEvaluationMapper2D::LocalStorage* mitk::RegEvaluationMapper2D::GetLocalStorage(mitk::BaseRenderer* renderer) { return m_LSH.GetLocalStorage(renderer); } void mitk::RegEvaluationMapper2D::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 ) { vtkActor* 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::RegEvaluationMapper2D::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::RegEvaluationMapper2D::LocalStorage::~LocalStorage() { } mitk::RegEvaluationMapper2D::LocalStorage::LocalStorage() { m_TargetLevelWindowFilter = vtkSmartPointer::New(); m_MappedLevelWindowFilter = vtkSmartPointer::New(); m_TargetExtractFilter = vtkSmartPointer::New(); m_MappedExtractFilter = vtkSmartPointer::New(); //Do as much actions as possible in here to avoid double executions. m_Plane = vtkSmartPointer::New(); //m_Texture = vtkSmartPointer::New().GetPointer(); m_Texture = vtkSmartPointer::New().GetPointer(); m_DefaultLookupTable = vtkSmartPointer::New(); m_ColorLookupTable = vtkSmartPointer::New(); m_Mapper = vtkSmartPointer::New(); m_Actor = vtkSmartPointer::New(); m_Actors = vtkSmartPointer::New(); m_Reslicer = mitk::ExtractSliceFilter::New(); m_EvaluationImage = vtkSmartPointer::New(); m_EmptyPolyData = vtkSmartPointer::New(); mitk::LookupTable::Pointer mitkLUT = mitk::LookupTable::New(); //built a default lookuptable mitkLUT->SetType(mitk::LookupTable::GRAYSCALE); m_DefaultLookupTable = mitkLUT->GetVtkLookupTable(); mitkLUT->SetType(mitk::LookupTable::JET); 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/MatchPointRegistration/Rendering/mitkRegistrationWrapperMapperBase.cpp b/Modules/MatchPointRegistration/Rendering/mitkRegistrationWrapperMapperBase.cpp index 4bc59a9aca..88aa7c03c7 100644 --- a/Modules/MatchPointRegistration/Rendering/mitkRegistrationWrapperMapperBase.cpp +++ b/Modules/MatchPointRegistration/Rendering/mitkRegistrationWrapperMapperBase.cpp @@ -1,287 +1,282 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include #include #include #include #include #include #include #include #include #include #include #include "mitkMAPRegistrationWrapper.h" #include "mitkRegistrationWrapperMapperBase.h" #include "mitkRegVisColorStyleProperty.h" #include "mitkRegVisHelper.h" #include "mitkRegVisPropertyTags.h" mitk::MITKRegistrationWrapperMapperBase::MITKRegistrationWrapperMapperBase() { } mitk::MITKRegistrationWrapperMapperBase::~MITKRegistrationWrapperMapperBase() { } void mitk::MITKRegistrationWrapperMapperBase::GenerateDataForRenderer( mitk::BaseRenderer *renderer ) { mitk::DataNode::Pointer node = this->GetDataNode(); if (node.IsNull()) return; bool isVisible = true; node->GetVisibility(isVisible, renderer); if (!isVisible) return; RegWrapperLocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); //check if updates occured in the node or on the display bool outdatedRendererGeometry = RendererGeometryIsOutdated(renderer,localStorage->m_LastUpdateTime); if ( (localStorage->m_LastUpdateTime < node->GetMTime()) || (localStorage->m_LastUpdateTime < node->GetPropertyList()->GetMTime()) //was a property modified? || (localStorage->m_LastUpdateTime < node->GetPropertyList(renderer)->GetMTime()) || outdatedRendererGeometry) { MITK_DEBUG << "UPDATE NEEDED FOR _ " << renderer->GetName(); bool isGridActive = false; node->GetBoolProperty(mitk::nodeProp_RegVisGrid,isGridActive); bool isGlyphActive = false; node->GetBoolProperty(mitk::nodeProp_RegVisGlyph,isGlyphActive); bool isPointsActive = false; node->GetBoolProperty(mitk::nodeProp_RegVisPoints,isPointsActive); bool showStartGrid = false; node->GetBoolProperty(mitk::nodeProp_RegVisGridShowStart,showStartGrid); bool isGridActiveOutdated = mitk::PropertyIsOutdated(node,mitk::nodeProp_RegVisGrid,localStorage->m_LastUpdateTime); bool isGlyphActiveOutdated = mitk::PropertyIsOutdated(node,mitk::nodeProp_RegVisGlyph,localStorage->m_LastUpdateTime); bool isPointsActiveOutdated = mitk::PropertyIsOutdated(node,mitk::nodeProp_RegVisPoints,localStorage->m_LastUpdateTime); bool showStartGridOutdated = mitk::PropertyIsOutdated(node,mitk::nodeProp_RegVisGridShowStart,localStorage->m_LastUpdateTime); mitk::BaseData::Pointer baseData = node->GetData(); if (baseData.IsNull()) return; const mitk::MAPRegistrationWrapper* regWrapper = dynamic_cast(baseData.GetPointer()); if (regWrapper == nullptr) return; ////////////////////////////////////////////////////////////////////////// //1. Check the FOV and presentation styles bool outdatedFOV = mitk::GridIsOutdated(node,localStorage->m_LastUpdateTime); if (outdatedFOV ||isGridActiveOutdated || isGlyphActiveOutdated || isPointsActiveOutdated || outdatedRendererGeometry) { // we need to generate the grids/presentation again const map::core::RegistrationKernelBase<3,3>* regKernel= mitk::GetRelevantRegKernelOfNode(node); if(!regKernel) { mitkThrow() << "No reg kernel for visualization"; } mitk::BaseGeometry::ConstPointer gridDesc; unsigned int gridFrequ =5; if (!GetGeometryDescription(renderer,gridDesc, gridFrequ)) { return; }; if(isGridActive) { localStorage->m_DeformedGridData = mitk::Generate3DDeformationGrid(gridDesc, gridFrequ, regKernel); localStorage->m_StartGridData = mitk::Generate3DDeformationGrid(gridDesc,gridFrequ); localStorage->m_DeformedGridMapper->SetInputData(localStorage->m_DeformedGridData); localStorage->m_StartGridMapper->SetInputData(localStorage->m_StartGridData); } else if (isGlyphActive) { localStorage->m_DeformedGridData = mitk::Generate3DDeformationGlyph(gridDesc, regKernel); localStorage->m_StartGridData = nullptr; localStorage->m_DeformedGridMapper->SetInputData(localStorage->m_DeformedGridData); } else { mitkThrow() << "No reg kernel visualization style activated."; } } ////////////////////////////////////////////////////////////////////////// //2.Check if the mappers or actors must be modified bool isColorStyleOutdated = mitk::PropertyIsOutdated(node,mitk::nodeProp_RegVisColorStyle,localStorage->m_LastUpdateTime); bool isColorUniOutdated = mitk::PropertyIsOutdated(node,mitk::nodeProp_RegVisColorUni,localStorage->m_LastUpdateTime); bool isColor1Outdated = mitk::PropertyIsOutdated(node,mitk::nodeProp_RegVisColor1Value,localStorage->m_LastUpdateTime); bool isColor2Outdated = mitk::PropertyIsOutdated(node,mitk::nodeProp_RegVisColor2Value,localStorage->m_LastUpdateTime); bool isColor3Outdated = mitk::PropertyIsOutdated(node,mitk::nodeProp_RegVisColor3Value,localStorage->m_LastUpdateTime); bool isColor4Outdated = mitk::PropertyIsOutdated(node,mitk::nodeProp_RegVisColor4Value,localStorage->m_LastUpdateTime); bool isColor2MagOutdated = mitk::PropertyIsOutdated(node,mitk::nodeProp_RegVisColor2Magnitude,localStorage->m_LastUpdateTime); bool isColor3MagOutdated = mitk::PropertyIsOutdated(node,mitk::nodeProp_RegVisColor3Magnitude,localStorage->m_LastUpdateTime); bool isColor4MagOutdated = mitk::PropertyIsOutdated(node,mitk::nodeProp_RegVisColor4Magnitude,localStorage->m_LastUpdateTime); bool isColorInterpolateOutdated = mitk::PropertyIsOutdated(node,mitk::nodeProp_RegVisColorInterpolate,localStorage->m_LastUpdateTime); if(isColorStyleOutdated || isColorUniOutdated || isColor1Outdated || isColor2Outdated || isColor2MagOutdated || isColor3Outdated || isColor3MagOutdated || isColor4Outdated || isColor4MagOutdated || isColorInterpolateOutdated) { localStorage->m_DeformedGridMapper->ScalarVisibilityOn(); localStorage->m_DeformedGridMapper->SetScalarModeToUsePointData(); localStorage->m_DeformedGridMapper->SelectColorArray( "VectorMagnitude" ); mitk::RegVisColorStyleProperty* colorStyleProp = nullptr; node->GetProperty(colorStyleProp, mitk::nodeProp_RegVisColorStyle); float color1[3] = {0.0,0.0,0.0}; node->GetColor( color1, nullptr, mitk::nodeProp_RegVisColor1Value ); float color2[3] = {0.25,0.25,0.25}; node->GetColor( color2, nullptr, mitk::nodeProp_RegVisColor2Value ); float color3[3] = {0.5,0.5,0.5}; node->GetColor( color3, nullptr, mitk::nodeProp_RegVisColor3Value ); float color4[3] = {1.0,1.0,1.0}; node->GetColor( color4, nullptr, mitk::nodeProp_RegVisColor4Value ); double mag2 = 0; node->GetPropertyValue(mitk::nodeProp_RegVisColor2Magnitude, mag2); double mag3 = 0; node->GetPropertyValue(mitk::nodeProp_RegVisColor3Magnitude, mag3); double mag4 = 0; node->GetPropertyValue(mitk::nodeProp_RegVisColor4Magnitude, mag4); bool interpolate = true; node->GetBoolProperty(mitk::nodeProp_RegVisColorInterpolate,interpolate); //default :color by vector magnitude localStorage->m_DeformedGridMapper->SelectColorArray( "VectorMagnitude" ); localStorage->m_DeformedGridMapper->SetUseLookupTableScalarRange(true); localStorage->m_LUT = vtkSmartPointer::New(); if (!colorStyleProp || colorStyleProp->GetValueAsId()==0) { //uni color mode float temprgb[3] = {1.0,1.0,1.0}; node->GetColor( temprgb, nullptr, mitk::nodeProp_RegVisColorUni ); localStorage->m_LUT->AddRGBSegment(0.0,temprgb[0],temprgb[1],temprgb[2],1.0,temprgb[0],temprgb[1],temprgb[2]); localStorage->m_LUT->Build(); localStorage->m_DeformedGridMapper->SetLookupTable(localStorage->m_LUT); } else { localStorage->m_LUT->AddRGBPoint(0.0,color1[0],color1[1],color1[2]); localStorage->m_LUT->AddRGBPoint(mag2,color2[0],color2[1],color2[2]); localStorage->m_LUT->AddRGBPoint(mag3,color3[0],color3[1],color3[2]); localStorage->m_LUT->AddRGBPoint(mag4,color4[0],color4[1],color4[2]); if (!interpolate) { localStorage->m_LUT->AddRGBPoint(0.99*mag2,color1[0],color1[1],color1[2]); localStorage->m_LUT->AddRGBPoint(0.99*mag3,color2[0],color2[1],color2[2]); localStorage->m_LUT->AddRGBPoint(0.99*mag4,color3[0],color3[1],color3[2]); }; } localStorage->m_LUT->Build(); localStorage->m_DeformedGridMapper->SetLookupTable(localStorage->m_LUT); localStorage->m_DeformedGridMapper->Update(); } bool isGridStartColorOutdated = mitk::PropertyIsOutdated(node,mitk::nodeProp_RegVisGridStartColor,localStorage->m_LastUpdateTime); if(isGridStartColorOutdated) { localStorage->m_StartGridMapper->ScalarVisibilityOn(); localStorage->m_StartGridMapper->SetScalarModeToUsePointFieldData(); float temprgb[3]; if (node->GetColor( temprgb, nullptr, mitk::nodeProp_RegVisGridStartColor )) { double trgb[3] = { (double) temprgb[0], (double) temprgb[1], (double) temprgb[2] }; localStorage->m_StartGridActor->GetProperty()->SetColor(trgb); } } ////////////////////////////////////////////////////////////////////////// //3. Check if Assembly must be updated if(isGridActiveOutdated||isGlyphActiveOutdated||isPointsActiveOutdated||showStartGridOutdated) { localStorage->m_RegAssembly = vtkSmartPointer::New(); if (isGridActive) { localStorage->m_RegAssembly->AddPart(localStorage->m_DeformedGridActor); if (showStartGrid) { localStorage->m_RegAssembly->AddPart(localStorage->m_StartGridActor); } } else if (isGlyphActive) { localStorage->m_RegAssembly->AddPart(localStorage->m_DeformedGridActor); } } localStorage->m_LastUpdateTime.Modified(); } } void mitk::MITKRegistrationWrapperMapperBase::SetDefaultProperties(mitk::DataNode* node, mitk::BaseRenderer* renderer, bool overwrite) { Superclass::SetDefaultProperties(node, renderer, overwrite); } vtkProp* mitk::MITKRegistrationWrapperMapperBase::GetVtkProp(mitk::BaseRenderer *renderer) { return m_LSH.GetLocalStorage(renderer)->m_RegAssembly; } -void mitk::MITKRegistrationWrapperMapperBase::SetVtkMapperImmediateModeRendering(vtkMapper *) -{ - -} - mitk::MITKRegistrationWrapperMapperBase::RegWrapperLocalStorage::RegWrapperLocalStorage() { m_DeformedGridActor = vtkSmartPointer::New(); m_DeformedGridMapper = vtkSmartPointer::New(); m_DeformedGridActor->SetMapper(m_DeformedGridMapper); m_StartGridActor = vtkSmartPointer::New(); m_StartGridMapper = vtkSmartPointer::New(); m_StartGridActor->SetMapper(m_StartGridMapper); m_RegAssembly = vtkSmartPointer::New(); m_LUT = vtkSmartPointer::New(); m_DeformedGridData = nullptr; m_StartGridData = nullptr; } diff --git a/Modules/MatchPointRegistration/Rendering/mitkRegistrationWrapperMapperBase.h b/Modules/MatchPointRegistration/Rendering/mitkRegistrationWrapperMapperBase.h index 47bb764464..a9bfbcbeaf 100644 --- a/Modules/MatchPointRegistration/Rendering/mitkRegistrationWrapperMapperBase.h +++ b/Modules/MatchPointRegistration/Rendering/mitkRegistrationWrapperMapperBase.h @@ -1,101 +1,100 @@ /*=================================================================== 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 _MITK_MITK_REGISTRATION_WRAPPER_MAPPER_BASE_H #define _MITK_MITK_REGISTRATION_WRAPPER_MAPPER_BASE_H #include #include #include #include "MitkMatchPointRegistrationExports.h" class vtkPropAssembly; class vtkPolyDataMapper; class vtkPolyData; class vtkColorTransferFunction; class vtkActor; namespace mitk { /**Base class for all mapper that visualize a registration object.*/ class MITKRegistrationWrapperMapperBase : public VtkMapper { public: mitkClassMacro(MITKRegistrationWrapperMapperBase, VtkMapper); //========== essential implementation for mapper ========== vtkProp *GetVtkProp(mitk::BaseRenderer *renderer) override; static void SetDefaultProperties(DataNode* node, BaseRenderer* renderer = nullptr, bool overwrite = false ); - static void SetVtkMapperImmediateModeRendering(vtkMapper *mapper); void GenerateDataForRenderer(mitk::BaseRenderer* renderer) override; //========================================================= virtual bool GetGeometryDescription(mitk::BaseRenderer *renderer, mitk::BaseGeometry::ConstPointer& gridDesc, unsigned int& gridFrequ) const = 0; virtual bool RendererGeometryIsOutdated(mitk::BaseRenderer *renderer, const itk::TimeStamp& time) const = 0; /**Internal class to store all informations and helper needed by a mapper to provide the render data for a certain renderer.*/ class MITKMATCHPOINTREGISTRATION_EXPORT RegWrapperLocalStorage : public mitk::Mapper::BaseLocalStorage { public: vtkSmartPointer m_DeformedGridData; vtkSmartPointer m_StartGridData; vtkSmartPointer m_DeformedGridActor; vtkSmartPointer m_DeformedGridMapper; vtkSmartPointer m_StartGridActor; vtkSmartPointer m_StartGridMapper; vtkSmartPointer m_RegAssembly; vtkSmartPointer m_LUT; /** \brief Timestamp of last update of stored data. */ itk::TimeStamp m_LastUpdateTime; /** \brief Constructor of the local storage. Do as much actions as possible in here to avoid double executions. */ RegWrapperLocalStorage(); ~RegWrapperLocalStorage() override { } }; /** \brief This member holds all three LocalStorages for the 3D render window(s). */ mitk::LocalStorageHandler m_LSH; protected: MITKRegistrationWrapperMapperBase(); ~MITKRegistrationWrapperMapperBase() override; private: }; } // end namespace mitk #endif /* MITKRegistrationWrapperMapperBase_H_HEADER_INCLUDED */ diff --git a/Modules/Multilabel/mitkLabelSetImageVtkMapper2D.cpp b/Modules/Multilabel/mitkLabelSetImageVtkMapper2D.cpp index 2f0eabe5d7..ecb7f9cf43 100644 --- a/Modules/Multilabel/mitkLabelSetImageVtkMapper2D.cpp +++ b/Modules/Multilabel/mitkLabelSetImageVtkMapper2D.cpp @@ -1,653 +1,653 @@ /*=================================================================== 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 "mitkLabelSetImageVtkMapper2D.h" // MITK #include #include #include #include #include #include #include #include #include #include #include #include #include #include // MITK Rendering #include "vtkMitkLevelWindowFilter.h" #include "vtkMitkThickSlicesFilter.h" #include "vtkNeverTranslucentTexture.h" // VTK #include #include #include #include #include #include #include #include #include #include #include #include //#include // ITK #include #include mitk::LabelSetImageVtkMapper2D::LabelSetImageVtkMapper2D() { } mitk::LabelSetImageVtkMapper2D::~LabelSetImageVtkMapper2D() { } vtkProp *mitk::LabelSetImageVtkMapper2D::GetVtkProp(mitk::BaseRenderer *renderer) { // return the actor corresponding to the renderer return m_LSH.GetLocalStorage(renderer)->m_Actors; } mitk::LabelSetImageVtkMapper2D::LocalStorage *mitk::LabelSetImageVtkMapper2D::GetLocalStorage( mitk::BaseRenderer *renderer) { return m_LSH.GetLocalStorage(renderer); } void mitk::LabelSetImageVtkMapper2D::GenerateDataForRenderer(mitk::BaseRenderer *renderer) { LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); mitk::DataNode *node = this->GetDataNode(); auto *image = dynamic_cast(node->GetData()); assert(image && image->IsInitialized()); // check if there is a valid worldGeometry const PlaneGeometry *worldGeometry = renderer->GetCurrentWorldPlaneGeometry(); if ((worldGeometry == nullptr) || (!worldGeometry->IsValid()) || (!worldGeometry->HasReferenceGeometry())) return; image->Update(); int numberOfLayers = image->GetNumberOfLayers(); int activeLayer = image->GetActiveLayer(); float opacity = 1.0f; node->GetOpacity(opacity, renderer, "opacity"); if (numberOfLayers != localStorage->m_NumberOfLayers) { localStorage->m_NumberOfLayers = numberOfLayers; localStorage->m_ReslicedImageVector.clear(); localStorage->m_ReslicerVector.clear(); localStorage->m_LayerTextureVector.clear(); localStorage->m_LevelWindowFilterVector.clear(); localStorage->m_LayerMapperVector.clear(); localStorage->m_LayerActorVector.clear(); localStorage->m_Actors = vtkSmartPointer::New(); for (int lidx = 0; lidx < numberOfLayers; ++lidx) { localStorage->m_ReslicedImageVector.push_back(vtkSmartPointer::New()); localStorage->m_ReslicerVector.push_back(mitk::ExtractSliceFilter::New()); localStorage->m_LayerTextureVector.push_back(vtkSmartPointer::New()); localStorage->m_LevelWindowFilterVector.push_back(vtkSmartPointer::New()); localStorage->m_LayerMapperVector.push_back(vtkSmartPointer::New()); localStorage->m_LayerActorVector.push_back(vtkSmartPointer::New()); // do not repeat the texture (the image) localStorage->m_LayerTextureVector[lidx]->RepeatOff(); // set corresponding mappers for the actors localStorage->m_LayerActorVector[lidx]->SetMapper(localStorage->m_LayerMapperVector[lidx]); localStorage->m_Actors->AddPart(localStorage->m_LayerActorVector[lidx]); } localStorage->m_Actors->AddPart(localStorage->m_OutlineShadowActor); localStorage->m_Actors->AddPart(localStorage->m_OutlineActor); } // 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 for (int lidx = 0; lidx < numberOfLayers; ++lidx) { localStorage->m_ReslicedImageVector[lidx] = nullptr; localStorage->m_LayerMapperVector[lidx]->SetInputData(localStorage->m_EmptyPolyData); localStorage->m_OutlineActor->SetVisibility(false); localStorage->m_OutlineShadowActor->SetVisibility(false); } return; } for (int lidx = 0; lidx < numberOfLayers; ++lidx) { mitk::Image *layerImage = nullptr; // set main input for ExtractSliceFilter if (lidx == activeLayer) layerImage = image; else layerImage = image->GetLayerImage(lidx); localStorage->m_ReslicerVector[lidx]->SetInput(layerImage); localStorage->m_ReslicerVector[lidx]->SetWorldGeometry(worldGeometry); localStorage->m_ReslicerVector[lidx]->SetTimeStep(this->GetTimestep()); // set the transformation of the image to adapt reslice axis localStorage->m_ReslicerVector[lidx]->SetResliceTransformByGeometry( layerImage->GetTimeGeometry()->GetGeometryForTimeStep(this->GetTimestep())); // is the geometry of the slice based on the image image or the worldgeometry? bool inPlaneResampleExtentByGeometry = false; node->GetBoolProperty("in plane resample extent by geometry", inPlaneResampleExtentByGeometry, renderer); localStorage->m_ReslicerVector[lidx]->SetInPlaneResampleExtentByGeometry(inPlaneResampleExtentByGeometry); localStorage->m_ReslicerVector[lidx]->SetInterpolationMode(ExtractSliceFilter::RESLICE_NEAREST); localStorage->m_ReslicerVector[lidx]->SetVtkOutputRequest(true); // this is needed when thick mode was enabled before. These variables have to be reset to default values localStorage->m_ReslicerVector[lidx]->SetOutputDimensionality(2); localStorage->m_ReslicerVector[lidx]->SetOutputSpacingZDirection(1.0); localStorage->m_ReslicerVector[lidx]->SetOutputExtentZDirection(0, 0); // Bounds information for reslicing (only required if reference geometry is present) // this used for generating a vtkPLaneSource with the right size double sliceBounds[6]; sliceBounds[0] = 0.0; sliceBounds[1] = 0.0; sliceBounds[2] = 0.0; sliceBounds[3] = 0.0; sliceBounds[4] = 0.0; sliceBounds[5] = 0.0; localStorage->m_ReslicerVector[lidx]->GetClippedPlaneBounds(sliceBounds); // setup the textured plane this->GeneratePlane(renderer, sliceBounds); // get the spacing of the slice localStorage->m_mmPerPixel = localStorage->m_ReslicerVector[lidx]->GetOutputSpacing(); localStorage->m_ReslicerVector[lidx]->Modified(); // start the pipeline with updating the largest possible, needed if the geometry of the image has changed localStorage->m_ReslicerVector[lidx]->UpdateLargestPossibleRegion(); localStorage->m_ReslicedImageVector[lidx] = localStorage->m_ReslicerVector[lidx]->GetVtkOutput(); const auto *planeGeometry = dynamic_cast(worldGeometry); 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(layerImage->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 imageLayer localStorage->m_LevelWindowFilterVector[lidx]->SetClippingBounds(textureClippingBounds); localStorage->m_LevelWindowFilterVector[lidx]->SetLookupTable( image->GetLabelSet(lidx)->GetLookupTable()->GetVtkLookupTable()); // do not use a VTK lookup table (we do that ourselves in m_LevelWindowFilter) - localStorage->m_LayerTextureVector[lidx]->MapColorScalarsThroughLookupTableOff(); + localStorage->m_LayerTextureVector[lidx]->SetColorModeToDirectScalars(); // connect the imageLayer with the levelwindow filter localStorage->m_LevelWindowFilterVector[lidx]->SetInputData(localStorage->m_ReslicedImageVector[lidx]); // connect the texture with the output of the levelwindow filter // check for texture interpolation property bool textureInterpolation = false; node->GetBoolProperty("texture interpolation", textureInterpolation, renderer); // set the interpolation modus according to the property localStorage->m_LayerTextureVector[lidx]->SetInterpolate(textureInterpolation); localStorage->m_LayerTextureVector[lidx]->SetInputConnection( localStorage->m_LevelWindowFilterVector[lidx]->GetOutputPort()); this->TransformActor(renderer); // set the plane as input for the mapper localStorage->m_LayerMapperVector[lidx]->SetInputConnection(localStorage->m_Plane->GetOutputPort()); // set the texture for the actor localStorage->m_LayerActorVector[lidx]->SetTexture(localStorage->m_LayerTextureVector[lidx]); localStorage->m_LayerActorVector[lidx]->GetProperty()->SetOpacity(opacity); } mitk::Label* activeLabel = image->GetActiveLabel(activeLayer); if (nullptr != activeLabel) { bool contourActive = false; node->GetBoolProperty("labelset.contour.active", contourActive, renderer); if (contourActive && activeLabel->GetVisible()) //contour rendering { //generate contours/outlines localStorage->m_OutlinePolyData = this->CreateOutlinePolyData(renderer, localStorage->m_ReslicedImageVector[activeLayer], activeLabel->GetValue()); localStorage->m_OutlineActor->SetVisibility(true); localStorage->m_OutlineShadowActor->SetVisibility(true); const mitk::Color& color = activeLabel->GetColor(); localStorage->m_OutlineActor->GetProperty()->SetColor(color.GetRed(), color.GetGreen(), color.GetBlue()); localStorage->m_OutlineShadowActor->GetProperty()->SetColor(0, 0, 0); float contourWidth(2.0); node->GetFloatProperty("labelset.contour.width", contourWidth, renderer); localStorage->m_OutlineActor->GetProperty()->SetLineWidth(contourWidth); localStorage->m_OutlineShadowActor->GetProperty()->SetLineWidth(contourWidth * 1.5); localStorage->m_OutlineActor->GetProperty()->SetOpacity(opacity); localStorage->m_OutlineShadowActor->GetProperty()->SetOpacity(opacity); localStorage->m_OutlineMapper->SetInputData(localStorage->m_OutlinePolyData); return; } } localStorage->m_OutlineActor->SetVisibility(false); localStorage->m_OutlineShadowActor->SetVisibility(false); } bool mitk::LabelSetImageVtkMapper2D::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; } vtkSmartPointer mitk::LabelSetImageVtkMapper2D::CreateOutlinePolyData(mitk::BaseRenderer *renderer, vtkImageData *image, int pixelValue) { LocalStorage *localStorage = this->GetLocalStorage(renderer); // get the min and max index values of each direction int *extent = image->GetExtent(); int xMin = extent[0]; int xMax = extent[1]; int yMin = extent[2]; int yMax = extent[3]; int *dims = image->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 = this->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(image->GetScalarPointer()); while (y <= yMax) { // if the current pixel value is set to something if ((currentPixel) && (*currentPixel == pixelValue)) { // 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) != pixelValue) { // 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) != pixelValue) { // 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) != pixelValue) { // 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) != pixelValue) { // 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::LabelSetImageVtkMapper2D::ApplyColor(mitk::BaseRenderer *renderer, const mitk::Color &color) { LocalStorage *localStorage = this->GetLocalStorage(renderer); localStorage->m_OutlineActor->GetProperty()->SetColor(color.GetRed(), color.GetGreen(), color.GetBlue()); localStorage->m_OutlineShadowActor->GetProperty()->SetColor(0, 0, 0); } void mitk::LabelSetImageVtkMapper2D::ApplyOpacity(mitk::BaseRenderer *renderer, int layer) { LocalStorage *localStorage = this->GetLocalStorage(renderer); float opacity = 1.0f; this->GetDataNode()->GetOpacity(opacity, renderer, "opacity"); localStorage->m_LayerActorVector[layer]->GetProperty()->SetOpacity(opacity); localStorage->m_OutlineActor->GetProperty()->SetOpacity(opacity); localStorage->m_OutlineShadowActor->GetProperty()->SetOpacity(opacity); } void mitk::LabelSetImageVtkMapper2D::ApplyLookuptable(mitk::BaseRenderer *renderer, int layer) { LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); auto *input = dynamic_cast(this->GetDataNode()->GetData()); localStorage->m_LevelWindowFilterVector[layer]->SetLookupTable( input->GetLabelSet(layer)->GetLookupTable()->GetVtkLookupTable()); } void mitk::LabelSetImageVtkMapper2D::Update(mitk::BaseRenderer *renderer) { bool visible = true; const DataNode *node = this->GetDataNode(); node->GetVisibility(visible, renderer, "visible"); if (!visible) return; auto *image = dynamic_cast(node->GetData()); if (image == nullptr || image->IsInitialized() == false) return; // Calculate time step of the image data for the specified renderer (integer value) this->CalculateTimeStep(renderer); // Check if time step is valid const TimeGeometry *dataTimeGeometry = image->GetTimeGeometry(); if ((dataTimeGeometry == nullptr) || (dataTimeGeometry->CountTimeSteps() == 0) || (!dataTimeGeometry->IsValidTimeStep(this->GetTimestep()))) { return; } image->UpdateOutputInformation(); LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); // check if something important has changed and we need to re-render if ((localStorage->m_LastDataUpdateTime < image->GetMTime()) || (localStorage->m_LastDataUpdateTime < image->GetPipelineMTime()) || (localStorage->m_LastDataUpdateTime < renderer->GetCurrentWorldPlaneGeometryUpdateTime()) || (localStorage->m_LastDataUpdateTime < renderer->GetCurrentWorldPlaneGeometry()->GetMTime())) { this->GenerateDataForRenderer(renderer); localStorage->m_LastDataUpdateTime.Modified(); } else if ((localStorage->m_LastPropertyUpdateTime < node->GetPropertyList()->GetMTime()) || (localStorage->m_LastPropertyUpdateTime < node->GetPropertyList(renderer)->GetMTime()) || (localStorage->m_LastPropertyUpdateTime < image->GetPropertyList()->GetMTime())) { this->GenerateDataForRenderer(renderer); localStorage->m_LastPropertyUpdateTime.Modified(); } } // set the two points defining the textured plane according to the dimension and spacing void mitk::LabelSetImageVtkMapper2D::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::LabelSetImageVtkMapper2D::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; } void mitk::LabelSetImageVtkMapper2D::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_ReslicerVector[0]->GetResliceAxes(); // same for all layers trans->SetMatrix(matrix); for (int lidx = 0; lidx < localStorage->m_NumberOfLayers; ++lidx) { // transform the plane/contour (the actual actor) to the corresponding view (axial, coronal or saggital) localStorage->m_LayerActorVector[lidx]->SetUserTransform(trans); // transform the origin to center based coordinates, because MITK is center based. localStorage->m_LayerActorVector[lidx]->SetPosition( -0.5 * localStorage->m_mmPerPixel[0], -0.5 * localStorage->m_mmPerPixel[1], 0.0); } // same for outline actor localStorage->m_OutlineActor->SetUserTransform(trans); localStorage->m_OutlineActor->SetPosition( -0.5 * localStorage->m_mmPerPixel[0], -0.5 * localStorage->m_mmPerPixel[1], 0.0); // same for outline shadow actor localStorage->m_OutlineShadowActor->SetUserTransform(trans); localStorage->m_OutlineShadowActor->SetPosition( -0.5 * localStorage->m_mmPerPixel[0], -0.5 * localStorage->m_mmPerPixel[1], 0.0); } void mitk::LabelSetImageVtkMapper2D::SetDefaultProperties(mitk::DataNode *node, mitk::BaseRenderer *renderer, bool overwrite) { // add/replace the following properties node->SetProperty("opacity", FloatProperty::New(1.0f), renderer); node->SetProperty("binary", BoolProperty::New(false), renderer); mitk::RenderingModeProperty::Pointer renderingModeProperty = mitk::RenderingModeProperty::New(RenderingModeProperty::LOOKUPTABLE_LEVELWINDOW_COLOR); node->SetProperty("Image Rendering.Mode", renderingModeProperty, renderer); mitk::LevelWindow levelwindow(32767.5, 65535); mitk::LevelWindowProperty::Pointer levWinProp = mitk::LevelWindowProperty::New(levelwindow); levWinProp->SetLevelWindow(levelwindow); node->SetProperty("levelwindow", levWinProp, renderer); node->SetProperty("labelset.contour.active", BoolProperty::New(true), renderer); node->SetProperty("labelset.contour.width", FloatProperty::New(2.0), renderer); Superclass::SetDefaultProperties(node, renderer, overwrite); } mitk::LabelSetImageVtkMapper2D::LocalStorage::~LocalStorage() { } mitk::LabelSetImageVtkMapper2D::LocalStorage::LocalStorage() { // Do as much actions as possible in here to avoid double executions. m_Plane = vtkSmartPointer::New(); m_Actors = vtkSmartPointer::New(); m_OutlinePolyData = vtkSmartPointer::New(); m_EmptyPolyData = vtkSmartPointer::New(); m_OutlineActor = vtkSmartPointer::New(); m_OutlineMapper = vtkSmartPointer::New(); m_OutlineShadowActor = vtkSmartPointer::New(); m_NumberOfLayers = 0; m_OutlineActor->SetMapper(m_OutlineMapper); m_OutlineShadowActor->SetMapper(m_OutlineMapper); m_OutlineActor->SetVisibility(false); m_OutlineShadowActor->SetVisibility(false); } diff --git a/Modules/Segmentation/Rendering/mitkContourSetVtkMapper3D.cpp b/Modules/Segmentation/Rendering/mitkContourSetVtkMapper3D.cpp index a68eeb1b04..1c4549f422 100644 --- a/Modules/Segmentation/Rendering/mitkContourSetVtkMapper3D.cpp +++ b/Modules/Segmentation/Rendering/mitkContourSetVtkMapper3D.cpp @@ -1,163 +1,161 @@ /*=================================================================== 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 "mitkContourSetVtkMapper3D.h" #include "mitkColorProperty.h" #include "mitkDataNode.h" #include "mitkProperties.h" #include "mitkVtkPropRenderer.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include mitk::ContourSetVtkMapper3D::ContourSetVtkMapper3D() { m_VtkPolyDataMapper = vtkPolyDataMapper::New(); m_Actor = vtkActor::New(); m_Actor->SetMapper(m_VtkPolyDataMapper); m_ContourSet = vtkPolyData::New(); m_TubeFilter = vtkTubeFilter::New(); } mitk::ContourSetVtkMapper3D::~ContourSetVtkMapper3D() { if (m_VtkPolyDataMapper) m_VtkPolyDataMapper->Delete(); ; if (m_TubeFilter) m_TubeFilter->Delete(); ; if (m_ContourSet) m_ContourSet->Delete(); ; if (m_Actor) m_Actor->Delete(); ; } vtkProp *mitk::ContourSetVtkMapper3D::GetVtkProp(mitk::BaseRenderer * /*renderer*/) { return m_Actor; } void mitk::ContourSetVtkMapper3D::GenerateDataForRenderer(mitk::BaseRenderer *renderer) { bool visible = true; GetDataNode()->GetVisibility(visible, renderer, "visible"); if (!visible) { m_Actor->VisibilityOff(); return; } m_Actor->VisibilityOn(); if (renderer->GetCurrentWorldPlaneGeometryUpdateTime() > 0UL && renderer->GetCurrentWorldPlaneGeometryUpdateTime() < this->GetInput()->GetMTime()) { m_ContourSet = vtkPolyData::New(); vtkPoints *points = vtkPoints::New(); vtkCellArray *lines = vtkCellArray::New(); mitk::ContourSet::Pointer input = const_cast(this->GetInput()); mitk::ContourSet::ContourVectorType contourVec = input->GetContours(); auto contourIt = contourVec.begin(); vtkIdType firstPointIndex = 0, lastPointIndex = 0; vtkIdType ptIndex = 0; while (contourIt != contourVec.end()) { auto *nextContour = (mitk::Contour *)(*contourIt).second; Contour::InputType idx = nextContour->GetContourPath()->StartOfInput(); Contour::InputType end = nextContour->GetContourPath()->EndOfInput(); if (end > 50000) end = 0; mitk::Contour::PointsContainerPointer contourPoints = nextContour->GetPoints(); mitk::Contour::PointsContainerIterator pointsIt = contourPoints->Begin(); unsigned int counter = 0; firstPointIndex = ptIndex; while (pointsIt != contourPoints->End()) { if (counter % 2 == 0) { Contour::BoundingBoxType::PointType point; point = pointsIt.Value(); points->InsertPoint(ptIndex, point[0], point[1], point[2]); if (ptIndex > firstPointIndex) { vtkIdType cell[2] = {ptIndex - 1, ptIndex}; lines->InsertNextCell((vtkIdType)2, cell); } lastPointIndex = ptIndex; ptIndex++; } pointsIt++; idx += 1; } if (nextContour->GetClosed()) { vtkIdType cell[2] = {lastPointIndex, firstPointIndex}; lines->InsertNextCell((vtkIdType)2, cell); } contourIt++; } m_ContourSet->SetPoints(points); m_ContourSet->SetLines(lines); m_TubeFilter->SetInputData(m_ContourSet); m_TubeFilter->SetRadius(1); m_TubeFilter->Update(); m_VtkPolyDataMapper->SetInputConnection(m_TubeFilter->GetOutputPort()); double rgba[4] = {0.0f, 1.0f, 0.0f, 0.6f}; m_Actor->GetProperty()->SetColor(rgba); m_Actor->SetMapper(m_VtkPolyDataMapper); } - - SetVtkMapperImmediateModeRendering(m_VtkPolyDataMapper); } const mitk::ContourSet *mitk::ContourSetVtkMapper3D::GetInput() { return static_cast(GetDataNode()->GetData()); } diff --git a/Modules/Segmentation/Rendering/mitkContourVtkMapper3D.cpp b/Modules/Segmentation/Rendering/mitkContourVtkMapper3D.cpp index 4570490565..4a5e5f56be 100644 --- a/Modules/Segmentation/Rendering/mitkContourVtkMapper3D.cpp +++ b/Modules/Segmentation/Rendering/mitkContourVtkMapper3D.cpp @@ -1,180 +1,178 @@ /*=================================================================== 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 "mitkContourVtkMapper3D.h" #include "mitkColorProperty.h" #include "mitkContour.h" #include "mitkDataNode.h" #include "mitkProperties.h" #include "mitkVtkPropRenderer.h" #include #include #include #pragma GCC diagnostic ignored "-Wstrict-aliasing" #include #pragma GCC diagnostic warning "-Wstrict-aliasing" #include #include #include #include #include #include #include #include #include #include mitk::ContourVtkMapper3D::ContourVtkMapper3D() { m_VtkPolyDataMapper = vtkPolyDataMapper::New(); m_VtkPointList = vtkAppendPolyData::New(); m_Actor = vtkActor::New(); m_Actor->SetMapper(m_VtkPolyDataMapper); m_TubeFilter = vtkTubeFilter::New(); } mitk::ContourVtkMapper3D::~ContourVtkMapper3D() { if (m_VtkPolyDataMapper) m_VtkPolyDataMapper->Delete(); if (m_TubeFilter) m_TubeFilter->Delete(); if (m_VtkPointList) m_VtkPointList->Delete(); if (m_Contour) m_Contour->Delete(); if (m_Actor) m_Actor->Delete(); } vtkProp *mitk::ContourVtkMapper3D::GetVtkProp(mitk::BaseRenderer * /*renderer*/) { return m_Actor; } void mitk::ContourVtkMapper3D::GenerateDataForRenderer(mitk::BaseRenderer *renderer) { bool visible = true; GetDataNode()->GetVisibility(visible, renderer, "visible"); if (!visible) { m_Actor->VisibilityOff(); return; } m_Actor->VisibilityOn(); m_Contour = vtkPolyData::New(); mitk::Contour::Pointer input = const_cast(this->GetInput()); bool makeContour = true; if (makeContour) { vtkSmartPointer points = vtkSmartPointer::New(); vtkSmartPointer lines = vtkSmartPointer::New(); int numPts = input->GetNumberOfPoints(); if (numPts > 200000) numPts = 200000; mitk::Contour::PathPointer path = input->GetContourPath(); mitk::Contour::PathType::InputType cstart = path->StartOfInput(); mitk::Contour::PathType::InputType cend = path->EndOfInput(); mitk::Contour::PathType::InputType cstep = (cend - cstart + 1) / numPts; mitk::Contour::PathType::InputType ccur; vtkIdType ptIndex = 0; vtkIdType lastPointIndex = 0; mitk::Contour::PointsContainerPointer contourPoints = input->GetPoints(); double vtkpoint[3]; int i; float pointSize = 2; this->GetDataNode()->GetFloatProperty("spheres size", pointSize); bool showPoints = true; this->GetDataNode()->GetBoolProperty("show points", showPoints); if (showPoints) { m_VtkPointList = vtkAppendPolyData::New(); } for (i = 0, ccur = cstart; i < numPts; ++i, ccur += cstep) { itk2vtk(path->Evaluate(ccur), vtkpoint); points->InsertPoint(ptIndex, vtkpoint); if (ptIndex > 0) { vtkIdType cell[2] = {ptIndex - 1, ptIndex}; lines->InsertNextCell((vtkIdType)2, cell); } lastPointIndex = ptIndex; ++ptIndex; if (showPoints) { vtkSmartPointer sphere = vtkSmartPointer::New(); sphere->SetRadius(pointSize); sphere->SetCenter(vtkpoint); m_VtkPointList->AddInputConnection(sphere->GetOutputPort()); sphere->Update(); } } if (input->GetClosed()) { vtkIdType cell[2] = {lastPointIndex, 0}; lines->InsertNextCell((vtkIdType)2, cell); } m_Contour->SetPoints(points); m_Contour->SetLines(lines); m_TubeFilter->SetInputData(m_Contour); m_TubeFilter->SetRadius(pointSize / 2.0f); m_TubeFilter->SetNumberOfSides(8); m_TubeFilter->Update(); if (showPoints) { m_VtkPointList->AddInputConnection(m_TubeFilter->GetOutputPort()); m_VtkPolyDataMapper->SetInputConnection(m_VtkPointList->GetOutputPort()); } else { m_VtkPolyDataMapper->SetInputConnection(m_TubeFilter->GetOutputPort()); } double rgba[4] = {0.0f, 1.0f, 0.0f, 0.6f}; m_Actor->GetProperty()->SetColor(rgba); m_Actor->SetMapper(m_VtkPolyDataMapper); } - - SetVtkMapperImmediateModeRendering(m_VtkPolyDataMapper); } const mitk::Contour *mitk::ContourVtkMapper3D::GetInput() { return static_cast(GetDataNode()->GetData()); } diff --git a/Plugins/org.mitk.gui.qt.imagecropper/src/internal/QmitkImageCropper.h b/Plugins/org.mitk.gui.qt.imagecropper/src/internal/QmitkImageCropper.h index 5f63c1dab6..6c70c8fe40 100644 --- a/Plugins/org.mitk.gui.qt.imagecropper/src/internal/QmitkImageCropper.h +++ b/Plugins/org.mitk.gui.qt.imagecropper/src/internal/QmitkImageCropper.h @@ -1,180 +1,179 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision$ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #ifndef QmitkImageCropper_h #define QmitkImageCropper_h #include #ifdef WIN32 #pragma warning( disable : 4250 ) #endif #include -#include "QVTKWidget.h" #include "QmitkRegisterClasses.h" #include #include "itkCommand.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "ui_ImageCropperControls.h" #include "usServiceRegistration.h" /*! @brief QmitkImageCropperView \warning This class is not yet documented. Use "git blame" and ask the author to provide basic documentation. \sa QmitkFunctionality \ingroup ${plugin_target}_internal */ class QmitkImageCropper : public QmitkAbstractView { // this is needed for all Qt objects that should have a Qt meta-object // (everything that derives from QObject and wants to have signal/slots) private: Q_OBJECT public: /*! @brief Constructor. Called by SampleApp (or other apps that use functionalities) */ QmitkImageCropper(QObject *parent = 0); virtual ~QmitkImageCropper(); static const std::string VIEW_ID; virtual void CreateQtPartControl(QWidget *parent); virtual void SetFocus() override; /*! @brief Creates the Qt connections needed */ QWidget* GetControls(); /// @brief Called when the user clicks the GUI button protected slots: /*! * @brief Creates a new bounding object */ virtual void DoCreateNewBoundingObject(); /*! * @brief Whenever Crop button is pressed, issue a cropping action */ void DoCropping(); /*! * @brief Whenever Mask button is pressed, issue a masking action */ void DoMasking(); /*! * @brief Dis- or enable the advanced setting section */ void OnAdvancedSettingsButtonToggled(); /*! * @brief Updates current selection of the bounding object */ void OnDataSelectionChanged(const mitk::DataNode* node); /*! * @brief Sets the scalar value for outside pixels in case of masking */ void OnSliderValueChanged(int slidervalue); protected: /*! @brief called by QmitkFunctionality when DataManager's selection has changed */ void OnSelectionChanged(berry::IWorkbenchPart::Pointer part, const QList& nodes) override; /*! @brief called by QmitkFunctionality when DataNode is removed from DataManager */ void NodeRemoved(const mitk::DataNode* node) override; /*! @brief Sets the selected bounding object as current bounding object and set up interactor */ void OnComboBoxSelectionChanged(const mitk::DataNode* node); /*! * @brief Initializes a new bounding shape using the selected image geometry. */ mitk::Geometry3D::Pointer InitializeWithImageGeometry(mitk::BaseGeometry::Pointer geometry); void CreateBoundingShapeInteractor(bool rotationEnabled); private: /*! * The parent QWidget */ QWidget* m_ParentWidget; /*! * @brief A pointer to the node of the image to be cropped. */ mitk::WeakPointer m_ImageNode; /*! * @brief The cuboid used for cropping. */ mitk::GeometryData::Pointer m_CroppingObject; /*! * @brief Tree node of the cuboid used for cropping. */ mitk::DataNode::Pointer m_CroppingObjectNode; /*! * @brief Interactor for moving and scaling the cuboid */ mitk::BoundingShapeInteractor::Pointer m_BoundingShapeInteractor; void ProcessImage(bool crop); QList m_BoundingObjectNames; /*! * @brief Resets GUI to default */ void setDefaultGUI(); QString getAdaptedBoundingObjectName(const QString& name) const; // cropping parameter mitk::ScalarType m_CropOutsideValue; bool m_Advanced; bool m_Active; bool m_ScrollEnabled; Ui::ImageCropperControls m_Controls; }; #endif // QmitkImageCropper_h diff --git a/Plugins/org.mitk.gui.qt.moviemaker/src/internal/QmitkScreenshotMaker.h b/Plugins/org.mitk.gui.qt.moviemaker/src/internal/QmitkScreenshotMaker.h index 66b6a70b0a..d5028f4715 100644 --- a/Plugins/org.mitk.gui.qt.moviemaker/src/internal/QmitkScreenshotMaker.h +++ b/Plugins/org.mitk.gui.qt.moviemaker/src/internal/QmitkScreenshotMaker.h @@ -1,134 +1,132 @@ /*=================================================================== 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. ===================================================================*/ #if !defined(QMITK_ScreenshotMaker_H__INCLUDED) #define QMITK_ScreenshotMaker_H__INCLUDED #include #include #include "mitkCameraRotationController.h" #include "mitkStepper.h" #include "mitkMultiStepper.h" #include "mitkMovieGenerator.h" #include "itkCommand.h" -#include "QVTKWidget.h" #include "vtkEventQtSlotConnect.h" #include "vtkRenderWindow.h" #include "mitkVtkPropRenderer.h" #include "ui_QmitkScreenshotMakerControls.h" //#include "../MovieMakerDll.h" //class QmitkMovieMakerControls; class QmitkStepperAdapter; class vtkCamera; class QTimer; class QTime; /** * \brief View for creating movies (AVIs) */ class QmitkScreenshotMaker: public QmitkAbstractView, public mitk::IRenderWindowPartListener { Q_OBJECT public: /** \brief Constructor. */ QmitkScreenshotMaker(QObject *parent=0, const char *name=0); /** \brief Destructor. */ virtual ~QmitkScreenshotMaker(); /** \brief Method for creating the widget containing the application * controls, like sliders, buttons etc. */ virtual void CreateQtPartControl(QWidget *parent) override; // virtual QWidget * CreateControlWidget(QWidget *parent); /// /// Sets the focus to an internal widget. /// virtual void SetFocus() override; /** \brief Method for creating the connections of main and control widget. */ virtual void CreateConnections(); /** \brief Method for creating an QAction object, i.e. button & menu entry. * @param parent the parent QWidget */ // virtual QAction * CreateAction(QActionGroup *parent); /// /// Called when a RenderWindowPart becomes available. /// virtual void RenderWindowPartActivated(mitk::IRenderWindowPart* renderWindowPart) override; /// /// Called when a RenderWindowPart becomes unavailable. /// virtual void RenderWindowPartDeactivated(mitk::IRenderWindowPart* renderWindowPart) override; signals: protected slots: void GenerateScreenshot(); void GenerateMultiplanarScreenshots(); void Generate3DHighresScreenshot(); void GenerateMultiplanar3DHighresScreenshot(); void View1(); void View2(); void View3(); void SelectBackgroundColor(); protected: QObject *parentWidget; QWidget* m_Parent; - QVTKWidget * widget; vtkEventQtSlotConnect * connections; vtkRenderWindow * renderWindow; mitk::VtkPropRenderer::Pointer m_PropRenderer; Ui::QmitkScreenshotMakerControls* m_Controls; private: virtual void OnSelectionChanged(berry::IWorkbenchPart::Pointer part, const QList& nodes) override; vtkCamera* GetCam(); void GenerateHR3DAtlasScreenshots(QString fileName, QString filter = ""); void GenerateMultiplanarScreenshots(QString fileName); /*! \brief taking a screenshot "from" the specified renderer \param magnificationFactor specifying the quality of the screenshot (the magnification of the actual RenderWindow size) \param fileName file location and name where the screenshot should be saved */ void TakeScreenshot(vtkRenderer* renderer, unsigned int magnificationFactor, QString fileName, QString filter = ""); QColor m_BackgroundColor; mitk::DataNode* m_SelectedNode; QString m_LastPath; QString m_LastFile; QString m_PNGExtension = "PNG File (*.png)"; QString m_JPGExtension = "JPEG File (*.jpg)"; }; #endif // !defined(QMITK_ScreenshotMaker_H__INCLUDED)