diff --git a/Modules/ContourModel/Rendering/mitkContourModelMapper3D.cpp b/Modules/ContourModel/Rendering/mitkContourModelMapper3D.cpp index 03fa9641fc..e6b499da4b 100644 --- a/Modules/ContourModel/Rendering/mitkContourModelMapper3D.cpp +++ b/Modules/ContourModel/Rendering/mitkContourModelMapper3D.cpp @@ -1,233 +1,233 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include <mitkContourModelMapper3D.h> #include <vtkCellArray.h> #include <vtkPoints.h> #include <vtkProperty.h> mitk::ContourModelMapper3D::ContourModelMapper3D() { } mitk::ContourModelMapper3D::~ContourModelMapper3D() { } const mitk::ContourModel *mitk::ContourModelMapper3D::GetInput(void) { // convient way to get the data from the dataNode return static_cast<const mitk::ContourModel *>(GetDataNode()->GetData()); } vtkProp *mitk::ContourModelMapper3D::GetVtkProp(mitk::BaseRenderer *renderer) { // return the actor corresponding to the renderer return m_LSH.GetLocalStorage(renderer)->m_Actor; } void mitk::ContourModelMapper3D::GenerateDataForRenderer(mitk::BaseRenderer *renderer) { /* First convert the contourModel to vtkPolyData, then tube filter it and * set it input for our mapper */ LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); auto *inputContour = static_cast<mitk::ContourModel *>(GetDataNode()->GetData()); localStorage->m_OutlinePolyData = this->CreateVtkPolyDataFromContour(inputContour); this->ApplyContourProperties(renderer); // tube filter the polyData localStorage->m_TubeFilter->SetInputData(localStorage->m_OutlinePolyData); float lineWidth(1.0); if (this->GetDataNode()->GetFloatProperty("contour.3D.width", lineWidth, renderer)) { localStorage->m_TubeFilter->SetRadius(lineWidth); } else { localStorage->m_TubeFilter->SetRadius(0.5); } localStorage->m_TubeFilter->CappingOn(); localStorage->m_TubeFilter->SetNumberOfSides(10); localStorage->m_TubeFilter->Update(); localStorage->m_Mapper->SetInputConnection(localStorage->m_TubeFilter->GetOutputPort()); } void mitk::ContourModelMapper3D::Update(mitk::BaseRenderer *renderer) { bool visible = true; GetDataNode()->GetVisibility(visible, renderer, "visible"); auto *data = static_cast<mitk::ContourModel *>(GetDataNode()->GetData()); if (data == nullptr) { return; } // Calculate time step of the input data for the specified renderer (integer value) this->CalculateTimeStep(renderer); LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); // Check if time step is valid const TimeGeometry *dataTimeGeometry = data->GetTimeGeometry(); if ((dataTimeGeometry == nullptr) || (dataTimeGeometry->CountTimeSteps() == 0) || - (!dataTimeGeometry->IsValidTimePoint(renderer->GetTime())) || (this->GetTimestep() == -1)) + (!dataTimeGeometry->IsValidTimePoint(renderer->GetTime())) || (this->GetTimestep() == TIMESTEP_INVALID)) { // clear the rendered polydata localStorage->m_Mapper->SetInputData(vtkSmartPointer<vtkPolyData>::New()); return; } const DataNode *node = this->GetDataNode(); data->UpdateOutputInformation(); // 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(); } vtkSmartPointer<vtkPolyData> mitk::ContourModelMapper3D::CreateVtkPolyDataFromContour(mitk::ContourModel *inputContour) { const auto timestep = this->GetTimestep(); // the points to draw vtkSmartPointer<vtkPoints> points = vtkSmartPointer<vtkPoints>::New(); // the lines to connect the points vtkSmartPointer<vtkCellArray> lines = vtkSmartPointer<vtkCellArray>::New(); // Create a polydata to store everything in vtkSmartPointer<vtkPolyData> polyData = vtkSmartPointer<vtkPolyData>::New(); // iterate over the control points auto current = inputContour->IteratorBegin(timestep); auto next = inputContour->IteratorBegin(timestep); if (next != inputContour->IteratorEnd(timestep)) { next++; auto end = inputContour->IteratorEnd(timestep); while (next != end) { mitk::ContourModel::VertexType *currentControlPoint = *current; mitk::ContourModel::VertexType *nextControlPoint = *next; if (!(currentControlPoint->Coordinates[0] == nextControlPoint->Coordinates[0] && currentControlPoint->Coordinates[1] == nextControlPoint->Coordinates[1] && currentControlPoint->Coordinates[2] == nextControlPoint->Coordinates[2])) { vtkIdType p1 = points->InsertNextPoint(currentControlPoint->Coordinates[0], currentControlPoint->Coordinates[1], currentControlPoint->Coordinates[2]); vtkIdType p2 = points->InsertNextPoint( nextControlPoint->Coordinates[0], nextControlPoint->Coordinates[1], nextControlPoint->Coordinates[2]); // add the line between both contorlPoints lines->InsertNextCell(2); lines->InsertCellPoint(p1); lines->InsertCellPoint(p2); } current++; next++; } if (inputContour->IsClosed(timestep)) { // If the contour is closed add a line from the last to the first control point mitk::ContourModel::VertexType *firstControlPoint = *(inputContour->IteratorBegin(timestep)); mitk::ContourModel::VertexType *lastControlPoint = *(--(inputContour->IteratorEnd(timestep))); if (lastControlPoint->Coordinates[0] != firstControlPoint->Coordinates[0] || lastControlPoint->Coordinates[1] != firstControlPoint->Coordinates[1] || lastControlPoint->Coordinates[2] != firstControlPoint->Coordinates[2]) { vtkIdType p2 = points->InsertNextPoint( lastControlPoint->Coordinates[0], lastControlPoint->Coordinates[1], lastControlPoint->Coordinates[2]); vtkIdType p1 = points->InsertNextPoint( firstControlPoint->Coordinates[0], firstControlPoint->Coordinates[1], firstControlPoint->Coordinates[2]); // add the line to the cellArray lines->InsertNextCell(2); lines->InsertCellPoint(p1); lines->InsertCellPoint(p2); } } // Add the points to the dataset polyData->SetPoints(points); // Add the lines to the dataset polyData->SetLines(lines); } return polyData; } void mitk::ContourModelMapper3D::ApplyContourProperties(mitk::BaseRenderer *renderer) { LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); mitk::ColorProperty::Pointer colorprop = dynamic_cast<mitk::ColorProperty *>(GetDataNode()->GetProperty("contour.color", renderer)); if (colorprop) { // set the color of the contour double red = colorprop->GetColor().GetRed(); double green = colorprop->GetColor().GetGreen(); double blue = colorprop->GetColor().GetBlue(); localStorage->m_Actor->GetProperty()->SetColor(red, green, blue); } } /*+++++++++++++++++++ LocalStorage part +++++++++++++++++++++++++*/ mitk::ContourModelMapper3D::LocalStorage *mitk::ContourModelMapper3D::GetLocalStorage(mitk::BaseRenderer *renderer) { return m_LSH.GetLocalStorage(renderer); } mitk::ContourModelMapper3D::LocalStorage::LocalStorage() { m_Mapper = vtkSmartPointer<vtkPolyDataMapper>::New(); m_Actor = vtkSmartPointer<vtkActor>::New(); m_OutlinePolyData = vtkSmartPointer<vtkPolyData>::New(); m_TubeFilter = vtkSmartPointer<vtkTubeFilter>::New(); // set the mapper for the actor m_Actor->SetMapper(m_Mapper); } void mitk::ContourModelMapper3D::SetDefaultProperties(mitk::DataNode *node, mitk::BaseRenderer *renderer, bool overwrite) { node->AddProperty("color", ColorProperty::New(1.0, 0.0, 0.0), renderer, overwrite); node->AddProperty("contour.3D.width", mitk::FloatProperty::New(0.5), renderer, overwrite); Superclass::SetDefaultProperties(node, renderer, overwrite); } diff --git a/Modules/ContourModel/Rendering/mitkContourModelSetMapper3D.cpp b/Modules/ContourModel/Rendering/mitkContourModelSetMapper3D.cpp index cab6397f83..fa22fc0cbb 100644 --- a/Modules/ContourModel/Rendering/mitkContourModelSetMapper3D.cpp +++ b/Modules/ContourModel/Rendering/mitkContourModelSetMapper3D.cpp @@ -1,232 +1,232 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include <mitkContourModelSetMapper3D.h> #include "mitkSurface.h" #include <vtkCellArray.h> #include <vtkPoints.h> #include <vtkPolyLine.h> #include <vtkProperty.h> mitk::ContourModelSetMapper3D::ContourModelSetMapper3D() { } mitk::ContourModelSetMapper3D::~ContourModelSetMapper3D() { } const mitk::ContourModelSet *mitk::ContourModelSetMapper3D::GetInput(void) { // convenient way to get the data from the dataNode return static_cast<const mitk::ContourModelSet *>(GetDataNode()->GetData()); } vtkProp *mitk::ContourModelSetMapper3D::GetVtkProp(mitk::BaseRenderer *renderer) { // return the actor corresponding to the renderer return m_LSH.GetLocalStorage(renderer)->m_Assembly; } void mitk::ContourModelSetMapper3D::GenerateDataForRenderer(mitk::BaseRenderer *renderer) { /* First convert the contourModel to vtkPolyData, then tube filter it and * set it input for our mapper */ LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); auto *contourModelSet = dynamic_cast<ContourModelSet *>(this->GetDataNode()->GetData()); if (contourModelSet != nullptr) { vtkSmartPointer<vtkPoints> points = vtkSmartPointer<vtkPoints>::New(); vtkSmartPointer<vtkCellArray> cells = vtkSmartPointer<vtkCellArray>::New(); vtkIdType baseIndex = 0; auto it = contourModelSet->Begin(); auto end = contourModelSet->End(); while (it != end) { ContourModel *contourModel = it->GetPointer(); auto vertIt = contourModel->Begin(); auto vertEnd = contourModel->End(); while (vertIt != vertEnd) { points->InsertNextPoint((*vertIt)->Coordinates[0], (*vertIt)->Coordinates[1], (*vertIt)->Coordinates[2]); ++vertIt; } vtkSmartPointer<vtkPolyLine> line = vtkSmartPointer<vtkPolyLine>::New(); vtkIdList *pointIds = line->GetPointIds(); vtkIdType numPoints = contourModel->GetNumberOfVertices(); pointIds->SetNumberOfIds(numPoints + 1); for (vtkIdType i = 0; i < numPoints; ++i) pointIds->SetId(i, baseIndex + i); pointIds->SetId(numPoints, baseIndex); cells->InsertNextCell(line); baseIndex += numPoints; ++it; } vtkSmartPointer<vtkPolyData> polyData = vtkSmartPointer<vtkPolyData>::New(); polyData->SetPoints(points); polyData->SetLines(cells); vtkSmartPointer<vtkPolyDataMapper> mapper = vtkSmartPointer<vtkPolyDataMapper>::New(); vtkSmartPointer<vtkActor> actor = vtkSmartPointer<vtkActor>::New(); actor->SetMapper(mapper); mapper->SetInputData(polyData); localStorage->m_Assembly->AddPart(actor); } this->ApplyContourProperties(renderer); this->ApplyContourModelSetProperties(renderer); } void mitk::ContourModelSetMapper3D::Update(mitk::BaseRenderer *renderer) { bool visible = true; GetDataNode()->GetVisibility(visible, renderer, "visible"); auto *data = static_cast<mitk::ContourModel *>(GetDataNode()->GetData()); if (data == nullptr) { return; } // Calculate time step of the input data for the specified renderer (integer value) this->CalculateTimeStep(renderer); LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); - if (this->GetTimestep() == -1) + if (this->GetTimestep() == TIMESTEP_INVALID) { return; } const DataNode *node = this->GetDataNode(); data->UpdateOutputInformation(); // 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(); } vtkSmartPointer<vtkPolyData> mitk::ContourModelSetMapper3D::CreateVtkPolyDataFromContour( mitk::ContourModel *inputContour, mitk::BaseRenderer *renderer) { const auto timestep = this->GetTimestep(); LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); localStorage->m_contourToPolyData->SetInput(inputContour); localStorage->m_contourToPolyData->Update(); vtkSmartPointer<vtkPolyData> polyData = vtkSmartPointer<vtkPolyData>::New(); polyData = localStorage->m_contourToPolyData->GetOutput()->GetVtkPolyData(timestep); return polyData; } void mitk::ContourModelSetMapper3D::ApplyContourModelSetProperties(BaseRenderer *renderer) { LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); DataNode *dataNode = this->GetDataNode(); if (dataNode != nullptr) { float lineWidth = 1; dataNode->GetFloatProperty("contour.3D.width", lineWidth, renderer); vtkSmartPointer<vtkPropCollection> collection = vtkSmartPointer<vtkPropCollection>::New(); localStorage->m_Assembly->GetActors(collection); collection->InitTraversal(); for (vtkIdType i = 0; i < collection->GetNumberOfItems(); i++) { vtkActor::SafeDownCast(collection->GetNextProp())->GetProperty()->SetLineWidth(lineWidth); } } } void mitk::ContourModelSetMapper3D::ApplyContourProperties(mitk::BaseRenderer *renderer) { LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); mitk::ColorProperty::Pointer colorprop = dynamic_cast<mitk::ColorProperty *>(GetDataNode()->GetProperty("contour.color", renderer)); if (colorprop) { // set the color of the contour double red = colorprop->GetColor().GetRed(); double green = colorprop->GetColor().GetGreen(); double blue = colorprop->GetColor().GetBlue(); vtkSmartPointer<vtkPropCollection> collection = vtkSmartPointer<vtkPropCollection>::New(); localStorage->m_Assembly->GetActors(collection); collection->InitTraversal(); for (vtkIdType i = 0; i < collection->GetNumberOfItems(); i++) { vtkActor::SafeDownCast(collection->GetNextProp())->GetProperty()->SetColor(red, green, blue); } } } /*+++++++++++++++++++ LocalStorage part +++++++++++++++++++++++++*/ mitk::ContourModelSetMapper3D::LocalStorage *mitk::ContourModelSetMapper3D::GetLocalStorage( mitk::BaseRenderer *renderer) { return m_LSH.GetLocalStorage(renderer); } mitk::ContourModelSetMapper3D::LocalStorage::LocalStorage() { m_Assembly = vtkSmartPointer<vtkAssembly>::New(); m_contourToPolyData = mitk::ContourModelToSurfaceFilter::New(); } void mitk::ContourModelSetMapper3D::SetDefaultProperties(mitk::DataNode *node, mitk::BaseRenderer *renderer, bool overwrite) { node->AddProperty("color", ColorProperty::New(1.0, 0.0, 0.0), renderer, overwrite); node->AddProperty("contour.3D.width", mitk::FloatProperty::New(0.5), renderer, overwrite); Superclass::SetDefaultProperties(node, renderer, overwrite); } diff --git a/Modules/Core/include/mitkMapper.h b/Modules/Core/include/mitkMapper.h index 17ee2d7719..d6c60ff7b0 100644 --- a/Modules/Core/include/mitkMapper.h +++ b/Modules/Core/include/mitkMapper.h @@ -1,211 +1,211 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef mitkMapper_h #define mitkMapper_h #include "mitkBaseRenderer.h" #include "mitkCommon.h" #include "mitkLevelWindow.h" #include "mitkLocalStorageHandler.h" #include "mitkVtkPropRenderer.h" #include <MitkCoreExports.h> #include <itkObject.h> #include <itkWeakPointer.h> class vtkWindow; class vtkProp; namespace mitk { class BaseRenderer; class BaseData; class DataNode; /** \brief Base class of all mappers, Vtk as well as OpenGL mappers * * By the help of mappers, the input data is transformed to tangible primitives, * such as surfaces, points, lines, etc. * This is the base class of all mappers, Vtk as well as OpenGL mappers. * Subclasses of mitk::Mapper control the creation of rendering primitives * that interface to the graphics library (e.g., OpenGL, vtk). * * \todo Should Mapper be a subclass of ImageSource? * \ingroup Mapper */ class MITKCORE_EXPORT Mapper : public itk::Object { public: mitkClassMacroItkParent(Mapper, itk::Object); /** \brief Set the DataNode containing the data to map */ itkSetObjectMacro(DataNode, DataNode); /** \brief Get the DataNode containing the data to map. * Method only returns valid DataNode Pointer if the mapper belongs to a data node. * Otherwise, the returned DataNode Pointer might be invalid. */ virtual DataNode *GetDataNode() const; /**\brief Get the data to map * * Returns the mitk::BaseData object associated with this mapper. * \return the mitk::BaseData associated with this mapper. * \deprecatedSince{2013_03} Use GetDataNode()->GetData() instead to access the data */ DEPRECATED(BaseData *GetData() const); /** \brief Convenience access method for color properties (instances of * ColorProperty) * \return \a true property was found * \deprecatedSince{2013_03} Use GetDataNode()->GetColor(...) instead to get the color */ DEPRECATED(virtual bool GetColor(float rgb[3], BaseRenderer *renderer, const char *name = "color") const); /** \brief Convenience access method for visibility properties (instances * of BoolProperty) * \return \a true property was found * \sa IsVisible * \deprecatedSince{2013_03} Use GetDataNode()->GetVisibility(...) instead to get the visibility */ DEPRECATED(virtual bool GetVisibility(bool &visible, BaseRenderer *renderer, const char *name = "visible") const); /** \brief Convenience access method for opacity properties (instances of * FloatProperty) * \return \a true property was found * \deprecatedSince{2013_03} Use GetDataNode()->GetOpacity(...) instead to get the opacity */ DEPRECATED(virtual bool GetOpacity(float &opacity, BaseRenderer *renderer, const char *name = "opacity") const); /** \brief Convenience access method for color properties (instances of * LevelWindoProperty) * \return \a true property was found * \deprecatedSince{2013_03} Use GetDataNode->GetLevelWindow(...) instead to get the levelwindow */ DEPRECATED(virtual bool GetLevelWindow(LevelWindow &levelWindow, BaseRenderer *renderer, const char *name = "levelwindow") const); /** \brief Convenience access method for visibility properties (instances * of BoolProperty). Return value is the visibility. Default is * visible==true, i.e., true is returned even if the property (\a * propertyKey) is not found. * * Thus, the return value has a different meaning than in the * GetVisibility method! * \sa GetVisibility * \deprecatedSince{2013_03} Use GetDataNode()->GetVisibility(...) instead */ DEPRECATED(virtual bool IsVisible(BaseRenderer *renderer, const char *name = "visible") const); /** \brief Returns whether this is an vtk-based mapper * \deprecatedSince{2013_03} All mappers of superclass VTKMapper are vtk based, use a dynamic_cast instead */ virtual bool IsVtkBased() const { return true; } /** \brief Calls the time step of the input data for the specified renderer and checks * whether the time step is valid and calls method GenerateDataForRenderer() */ virtual void Update(BaseRenderer *renderer); /** \brief Responsible for calling the appropriate render functions. * To be implemented in sub-classes. */ virtual void MitkRender(mitk::BaseRenderer *renderer, mitk::VtkPropRenderer::RenderType type) = 0; /** * \brief Apply specific color and opacity properties read from the PropertyList. * Reimplemented in GLmapper (does not use the actor) and the VtkMapper class. * The function is called by the individual mapper (mostly in the ApplyProperties() or ApplyAllProperties() * method). */ virtual void ApplyColorAndOpacityProperties(mitk::BaseRenderer *renderer, vtkActor *actor = nullptr) = 0; /** \brief Set default values of properties used by this mapper * to \a node * * \param node The node for which the properties are set * \param overwrite overwrite existing properties (default: \a false) * \param renderer defines which property list of node is used * (default: \a nullptr, i.e. default property list) */ static void SetDefaultProperties(DataNode *node, BaseRenderer *renderer = nullptr, bool overwrite = false); /** \brief Returns the current time step as calculated from the renderer */ - int GetTimestep() const { return m_TimeStep; } + TimeStepType GetTimestep() const { return m_TimeStep; } /** Returns true if this Mapper currently allows for Level-of-Detail rendering. * This reflects whether this Mapper currently invokes StartEvent, EndEvent, and * ProgressEvent on BaseRenderer. */ virtual bool IsLODEnabled(BaseRenderer * /*renderer*/) const { return false; } protected: /** \brief explicit constructor which disallows implicit conversions */ explicit Mapper(); /** \brief virtual destructor in order to derive from this class */ ~Mapper() override; /** \brief Generate the data needed for rendering (independent of a specific renderer) * \deprecatedSince{2013_03} Use GenerateDataForRenderer(BaseRenderer* renderer) instead. */ DEPRECATED(virtual void GenerateData()) {} /** \brief Generate the data needed for rendering into \a renderer */ virtual void GenerateDataForRenderer(BaseRenderer * /* renderer */) {} /** \brief Updates the time step, which is sometimes needed in subclasses */ virtual void CalculateTimeStep(BaseRenderer *renderer); /** \brief Reset the mapper (i.e., make sure that nothing is displayed) if no * valid data is present. In most cases the reimplemented function * disables the according actors (toggling visibility off) * * To be implemented in sub-classes. */ virtual void ResetMapper(BaseRenderer * /*renderer*/) {} mitk::DataNode *m_DataNode; private: /** \brief The current time step of the dataset to be rendered, * for use in subclasses. * The current timestep can be accessed via the GetTimestep() method. */ - int m_TimeStep; + TimeStepType m_TimeStep; /** \brief copy constructor */ Mapper(const Mapper &); /** \brief assignment operator */ Mapper &operator=(const Mapper &); public: /** \brief Base class for mapper specific rendering resources. */ class MITKCORE_EXPORT BaseLocalStorage { public: BaseLocalStorage() = default; virtual ~BaseLocalStorage() = default; BaseLocalStorage(const BaseLocalStorage &) = delete; BaseLocalStorage & operator=(const BaseLocalStorage &) = delete; bool IsGenerateDataRequired(mitk::BaseRenderer *renderer, mitk::Mapper *mapper, mitk::DataNode *dataNode) const; inline void UpdateGenerateDataTime() { m_LastGenerateDataTime.Modified(); } inline itk::TimeStamp &GetLastGenerateDataTime() { return m_LastGenerateDataTime; } protected: /** \brief timestamp of last update of stored data */ itk::TimeStamp m_LastGenerateDataTime; }; }; } // namespace mitk #endif diff --git a/Modules/Core/include/mitkTimeGeometry.h b/Modules/Core/include/mitkTimeGeometry.h index de6de74c97..cfbd8c732e 100644 --- a/Modules/Core/include/mitkTimeGeometry.h +++ b/Modules/Core/include/mitkTimeGeometry.h @@ -1,349 +1,351 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef mitkTimeGeometry_h #define mitkTimeGeometry_h // ITK #include <itkObject.h> // MITK #include "mitkOperationActor.h" #include <MitkCoreExports.h> #include <mitkBaseGeometry.h> #include <mitkCommon.h> namespace mitk { typedef mitk::ScalarType TimePointType; typedef std::size_t TimeStepType; + static const TimeStepType TIMESTEP_INVALID = -1; + /** * \brief Manages the geometries of a data object for each time step * * This class is an abstract class. The concrete implementation * depends on the way the different time steps are managed. * * The time is defined either by a time step or a time point. Time steps * are non-negative integers starting from 0. A time point is a ScalarType value * which gives the passed time since start in ms. Be aware that the starting * point is not fixed so it is possible that the same time point defines two * different time depending on the start time of the used time geometry. * * \addtogroup geometry */ class MITKCORE_EXPORT TimeGeometry : public itk::Object, public OperationActor { protected: TimeGeometry(); ~TimeGeometry() override; /** * \brief Contains a bounding box which includes all time steps */ BoundingBox::Pointer m_BoundingBox; /** * \brief Makes a deep copy of the current object */ LightObject::Pointer InternalClone() const override; public: mitkClassMacroItkParent(TimeGeometry, itk::Object); itkCloneMacro(Self); itkCreateAnotherMacro(Self); /** * \brief Returns the number of time steps. * * Returns the number of time steps for which * geometries are saved. The number of time steps * is also the upper bound of the time steps. The * minimum time steps is always 0. */ virtual TimeStepType CountTimeSteps() const = 0; /** * \brief Returns the first time point for which the object is valid. * * Returns the first valid time point for this geometry. If only one * time steps available it usually goes from -max to +max. The time point * is given in ms. */ virtual TimePointType GetMinimumTimePoint() const = 0; /** * \brief Returns the last time point for which the object is valid * * Gives the last time point for which a valid geometry is saved in * this time geometry. The time point is given in ms. */ virtual TimePointType GetMaximumTimePoint() const = 0; /** * \brief Returns the first time point for which the object is valid. * * Returns the first valid time point for the given TimeStep. The time point * is given in ms. */ virtual TimePointType GetMinimumTimePoint(TimeStepType step) const = 0; /** * \brief Returns the last time point for which the object is valid * * Gives the last time point for the Geometry specified by the given TimeStep. The time point is given in ms. */ virtual TimePointType GetMaximumTimePoint(TimeStepType step) const = 0; /** * \brief Get the time bounds (in ms) */ virtual TimeBounds GetTimeBounds() const = 0; /** * \brief Get the time bounds for the given TimeStep (in ms) */ virtual TimeBounds GetTimeBounds(TimeStepType step) const = 0; /** * \brief Tests if a given time point is covered by this object * * Returns true if a geometry can be returned for the given time * point and falls if not. The time point must be given in ms. */ virtual bool IsValidTimePoint(TimePointType timePoint) const = 0; /** * \brief Test for the given time step if a geometry is available * * Returns true if a geometry is defined for the given time step. * Otherwise false is returned. * The time step is defined as positive number. */ virtual bool IsValidTimeStep(TimeStepType timeStep) const = 0; /** * \brief Converts a time step to a time point * * Converts a time step to a time point in a way that * the new time point indicates the same geometry as the time step. * If the original time steps does not point to a valid geometry, * a time point is calculated that also does not point to a valid * geometry, but no exception is raised. */ virtual TimePointType TimeStepToTimePoint(TimeStepType timeStep) const = 0; /** * \brief Converts a time point to the corresponding time step * * Converts a time point to a time step in a way that * the new time step indicates the same geometry as the time point. * If a negative invalid time point is given always time step 0 is * returned. If an positive invalid time step is given an invalid * time step will be returned. */ virtual TimeStepType TimePointToTimeStep(TimePointType timePoint) const = 0; /** * \brief Returns the geometry of a specific time point * * Returns the geometry which defines the given time point. If * the given time point is invalid an null-pointer is returned. * * The pointer to the returned geometry may point to the saved * geometry but this is not necessarily the case. So a change to * the returned geometry may or may not afflict the geometry for the * time point or all time points depending on the used implementation * of TimeGeometry. */ virtual BaseGeometry::Pointer GetGeometryForTimePoint(TimePointType timePoint) const = 0; /** * \brief Returns the geometry which corresponds to the given time step * * Returns the geometry which defines the given time step. If * the given time step is invalid an null-pointer is returned. * * The pointer to the returned geometry may point to the saved * geometry but this is not necessarily the case. So a change to * the returned geometry may or may not afflict the geometry for the * time step or all time steps depending on the used implementation * of TimeGeometry. */ virtual BaseGeometry::Pointer GetGeometryForTimeStep(TimeStepType timeStep) const = 0; /** * \brief Returns a clone of the geometry of a specific time point * * If an invalid time step is given (e.g. no geometry is defined for this time step) * a null-pointer will be returned. */ virtual BaseGeometry::Pointer GetGeometryCloneForTimeStep(TimeStepType timeStep) const = 0; /** * \brief Sets the geometry for a given time step * * Sets the geometry for the given time steps. This may also afflects other * time steps, depending on the implementation of TimeGeometry. */ virtual void SetTimeStepGeometry(BaseGeometry *geometry, TimeStepType timeStep) = 0; /** * \brief Expands to the given number of time steps * * Expands to the given number of time steps. Each new created time * step is filled with an empty geometry. * Shrinking is not supported! */ virtual void Expand(TimeStepType size) = 0; /** * \brief Replaces the geometry instances with clones ot the passed geometry. * * Replaces the geometries of all time steps with clones of the passed * geometry. Replacement strategy depends on the implementation of TimeGeometry * sub class. * @remark The time points itself stays untouched. Use this method if you want * to change the spatial properties of a TimeGeometry and preserve the time * "grid". */ virtual void ReplaceTimeStepGeometries(const BaseGeometry *geometry) = 0; /** * \brief Tests if all necessary information are set and the object is valid */ virtual bool IsValid() const = 0; /** * \brief Get the position of the corner number \a id (in world coordinates) * * See SetImageGeometry for how a corner is defined on images. */ Point3D GetCornerPointInWorld(int id) const; /** * \brief Get the position of a corner (in world coordinates) * * See SetImageGeometry for how a corner is defined on images. */ Point3D GetCornerPointInWorld(bool xFront = true, bool yFront = true, bool zFront = true) const; /** * \brief Get the center of the bounding-box in mm */ Point3D GetCenterInWorld() const; /** * \brief Get the squared length of the diagonal of the bounding-box in mm */ double GetDiagonalLength2InWorld() const; /** * \brief Get the length of the diagonal of the bounding-box in mm */ double GetDiagonalLengthInWorld() const; /** * \brief Test whether the point \a p (world coordinates in mm) is inside the bounding box */ bool IsWorldPointInside(const mitk::Point3D &p) const; /** * \brief Updates the bounding box to cover the area used in all time steps * * The bounding box is updated by this method. The new bounding box * covers an area which includes all bounding boxes during * all times steps. */ void UpdateBoundingBox(); /** * \brief Returns a bounding box that covers all time steps */ BoundingBox *GetBoundingBoxInWorld() const { return m_BoundingBox; } /** * \brief Returns the world bounds of the object that cover all time steps */ BoundingBox::BoundsArrayType GetBoundsInWorld() const { return m_BoundingBox->GetBounds(); } /** * \brief Returns the Extend of the bounding in the given direction */ ScalarType GetExtentInWorld(unsigned int direction) const; /** * \brief Initializes the TimeGeometry */ virtual void Initialize(); /** * \brief Updates the geometry */ void Update(); /** * \brief Updates everything except the Bounding box * * This class should be overwritten by child classes. * The method is called when Update() is required. */ virtual void UpdateWithoutBoundingBox(){}; /** * \brief Executes the given operation on all time steps */ void ExecuteOperation(Operation *op) override; void PrintSelf(std::ostream &os, itk::Indent indent) const override; }; // end class TimeGeometry /** * @brief Equal A function comparing two instances of TimeGeometry for being identical. * * @ingroup MITKTestingAPI * * The function compares two instances of TimeGeometries in all their aspects. * * The parameter eps is a tolerance value for all methods which are internally used for comparison. * If you want to use different tolerance values for different parts of the geometry, feel free to use * the other comparison methods and write your own implementation of Equal. * * @param rightHandSide Compare this against leftHandSide. * @param leftHandSide Compare this against rightHandSide. * @param eps Tolerance for comparison. You can use mitk::eps in most cases. * @param verbose Flag indicating if the user wants detailed console output or not. * * @return True, if all comparison are true. False in any other case. */ MITKCORE_EXPORT bool Equal(const mitk::TimeGeometry &leftHandSide, const mitk::TimeGeometry &rightHandSide, ScalarType eps, bool verbose); /** * @brief Compare two instances of TimeGeometry * * @ingroup MITKTestingAPI * * The function compares two instances of TimeGeometries in all their aspects. * * The parameter eps is a tolerance value for all methods which are internally used for comparison. * If you want to use different tolerance values for different parts of the geometry, feel free to use * the other comparison methods and write your own implementation of Equal. * * @param leftHandSide Compare this against rightHandSide. * @param rightHandSide Compare this against leftHandSide. * @param coordinateEps Tolerance for comparison of all spatial and temporal aspects (spacing, origin and grid alignment, time points). * You can use mitk::eps in most cases. * @param directionEps Tolerance for comparison of all directional aspects (axis). You can use mitk::eps in most cases. * @param verbose Flag indicating if the user wants detailed console output or not. * * @return True, if all comparisons are true. False in any other case. */ MITKCORE_EXPORT bool Equal(const mitk::TimeGeometry& leftHandSide, const mitk::TimeGeometry& rightHandSide, ScalarType coordinateEps, ScalarType directionEps, bool verbose); } // end namespace MITK #endif diff --git a/Modules/Core/src/Rendering/mitkBaseRenderer.cpp b/Modules/Core/src/Rendering/mitkBaseRenderer.cpp index f0c4d9b9d3..37d9ac5b90 100644 --- a/Modules/Core/src/Rendering/mitkBaseRenderer.cpp +++ b/Modules/Core/src/Rendering/mitkBaseRenderer.cpp @@ -1,780 +1,780 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkBaseRenderer.h" #include "mitkBaseRendererHelper.h" #include "mitkMapper.h" #include "mitkResliceMethodProperty.h" // Geometries #include "mitkSlicedGeometry3D.h" #include "mitkVtkLayerController.h" #include "mitkInteractionConst.h" #include "mitkProperties.h" #include "mitkWeakPointerProperty.h" // VTK #include <vtkCamera.h> #include <vtkLinearTransform.h> #include <vtkActor.h> #include <vtkPolyDataMapper.h> #include <vtkProperty.h> namespace mitk { itkEventMacroDefinition(RendererResetEvent, itk::AnyEvent); } mitk::BaseRenderer::BaseRendererMapType mitk::BaseRenderer::baseRendererMap; mitk::BaseRenderer *mitk::BaseRenderer::GetInstance(vtkRenderWindow *renWin) { for (auto mapit = baseRendererMap.begin(); mapit != baseRendererMap.end(); ++mapit) { if ((*mapit).first == renWin) return (*mapit).second; } return nullptr; } void mitk::BaseRenderer::AddInstance(vtkRenderWindow *renWin, BaseRenderer *baseRenderer) { if (renWin == nullptr || baseRenderer == nullptr) return; // ensure that no BaseRenderer is managed twice mitk::BaseRenderer::RemoveInstance(renWin); baseRendererMap.insert(BaseRendererMapType::value_type(renWin, baseRenderer)); } void mitk::BaseRenderer::RemoveInstance(vtkRenderWindow *renWin) { auto mapit = baseRendererMap.find(renWin); if (mapit != baseRendererMap.end()) baseRendererMap.erase(mapit); } mitk::BaseRenderer *mitk::BaseRenderer::GetByName(const std::string &name) { for (auto mapit = baseRendererMap.begin(); mapit != baseRendererMap.end(); ++mapit) { if ((*mapit).second->m_Name == name) return (*mapit).second; } return nullptr; } vtkRenderWindow *mitk::BaseRenderer::GetRenderWindowByName(const std::string &name) { for (auto mapit = baseRendererMap.begin(); mapit != baseRendererMap.end(); ++mapit) { if ((*mapit).second->m_Name == name) return (*mapit).first; } return nullptr; } mitk::BaseRenderer::BaseRendererMapType mitk::BaseRenderer::GetSpecificRenderWindows(MapperSlotId mapper) { BaseRendererMapType allRenderWindows; for (auto mapit = baseRendererMap.begin(); mapit != baseRendererMap.end(); ++mapit) { if (mapper == mapit->second->GetMapperID()) { allRenderWindows.insert(BaseRendererMapType::value_type(mapit->first, mapit->second)); } } return allRenderWindows; } mitk::BaseRenderer::BaseRendererMapType mitk::BaseRenderer::GetAll2DRenderWindows() { return GetSpecificRenderWindows(BaseRenderer::Standard2D); } mitk::BaseRenderer::BaseRendererMapType mitk::BaseRenderer::GetAll3DRenderWindows() { return GetSpecificRenderWindows(BaseRenderer::Standard3D); } mitk::BaseRenderer::BaseRenderer(const char *name, vtkRenderWindow *renWin) : m_RenderWindow(nullptr), m_VtkRenderer(nullptr), m_MapperID(StandardMapperSlot::Standard2D), m_DataStorage(nullptr), m_LastUpdateTime(0), m_CameraController(nullptr), m_CameraRotationController(nullptr), m_SliceNavigationController(nullptr), m_WorldTimeGeometry(nullptr), m_InteractionReferenceGeometry(nullptr), m_CurrentWorldGeometry(nullptr), m_CurrentWorldPlaneGeometry(nullptr), m_Slice(0), m_TimeStep(), m_CurrentWorldPlaneGeometryUpdateTime(), m_TimeStepUpdateTime(), m_KeepDisplayedRegion(true), m_ReferenceGeometryAligned(true), m_CurrentWorldPlaneGeometryData(nullptr), m_CurrentWorldPlaneGeometryNode(nullptr), m_CurrentWorldPlaneGeometryTransformTime(0), m_Name(name), m_EmptyWorldGeometry(true), m_NumberOfVisibleLODEnabledMappers(0) { m_Bounds[0] = 0; m_Bounds[1] = 0; m_Bounds[2] = 0; m_Bounds[3] = 0; m_Bounds[4] = 0; m_Bounds[5] = 0; if (name != nullptr) { m_Name = name; } else { m_Name = "unnamed renderer"; itkWarningMacro(<< "Created unnamed renderer. Bad for serialization. Please choose a name."); } if (renWin != nullptr) { m_RenderWindow = renWin; m_RenderWindow->Register(nullptr); } else { itkWarningMacro(<< "Created mitkBaseRenderer without vtkRenderWindow present."); } // instances.insert( this ); // adding this BaseRenderer to the List of all BaseRenderer m_BindDispatcherInteractor = new mitk::BindDispatcherInteractor(GetName()); WeakPointerProperty::Pointer rendererProp = WeakPointerProperty::New((itk::Object *)this); m_CurrentWorldPlaneGeometry = mitk::PlaneGeometry::New(); m_CurrentWorldPlaneGeometryData = mitk::PlaneGeometryData::New(); m_CurrentWorldPlaneGeometryData->SetPlaneGeometry(m_CurrentWorldPlaneGeometry); m_CurrentWorldPlaneGeometryNode = mitk::DataNode::New(); m_CurrentWorldPlaneGeometryNode->SetData(m_CurrentWorldPlaneGeometryData); m_CurrentWorldPlaneGeometryNode->GetPropertyList()->SetProperty("renderer", rendererProp); m_CurrentWorldPlaneGeometryNode->GetPropertyList()->SetProperty("layer", IntProperty::New(1000)); m_CurrentWorldPlaneGeometryNode->SetProperty("reslice.thickslices", mitk::ResliceMethodProperty::New()); m_CurrentWorldPlaneGeometryNode->SetProperty("reslice.thickslices.num", mitk::IntProperty::New(1)); m_CurrentWorldPlaneGeometryTransformTime = m_CurrentWorldPlaneGeometryNode->GetVtkTransform()->GetMTime(); m_SliceNavigationController = mitk::SliceNavigationController::New(); m_SliceNavigationController->SetRenderer(this); m_SliceNavigationController->ConnectGeometrySendEvent(this); m_SliceNavigationController->ConnectGeometryUpdateEvent(this); m_SliceNavigationController->ConnectGeometrySliceEvent(this); auto* timeNavigationController = RenderingManager::GetInstance()->GetTimeNavigationController(); timeNavigationController->ConnectTimeEvent(this); m_CameraRotationController = mitk::CameraRotationController::New(); m_CameraRotationController->SetRenderWindow(m_RenderWindow); m_CameraRotationController->AcquireCamera(); m_CameraController = mitk::CameraController::New(); m_CameraController->SetRenderer(this); m_VtkRenderer = vtkRenderer::New(); m_VtkRenderer->SetMaximumNumberOfPeels(16); if (AntiAliasing::FastApproximate == RenderingManager::GetInstance()->GetAntiAliasing()) m_VtkRenderer->UseFXAAOn(); if (nullptr == mitk::VtkLayerController::GetInstance(m_RenderWindow)) mitk::VtkLayerController::AddInstance(m_RenderWindow, m_VtkRenderer); mitk::VtkLayerController::GetInstance(m_RenderWindow)->InsertSceneRenderer(m_VtkRenderer); } mitk::BaseRenderer::~BaseRenderer() { if (m_VtkRenderer != nullptr) { m_VtkRenderer->Delete(); m_VtkRenderer = nullptr; } if (m_CameraController.IsNotNull()) m_CameraController->SetRenderer(nullptr); mitk::VtkLayerController::RemoveInstance(m_RenderWindow); RemoveAllLocalStorages(); m_DataStorage = nullptr; if (m_BindDispatcherInteractor != nullptr) { delete m_BindDispatcherInteractor; } if (m_RenderWindow != nullptr) { m_RenderWindow->Delete(); m_RenderWindow = nullptr; } auto* timeNavigationController = RenderingManager::GetInstance()->GetTimeNavigationController(); timeNavigationController->Disconnect(this); } void mitk::BaseRenderer::RemoveAllLocalStorages() { this->InvokeEvent(RendererResetEvent()); std::list<mitk::BaseLocalStorageHandler *>::iterator it; for (it = m_RegisteredLocalStorageHandlers.begin(); it != m_RegisteredLocalStorageHandlers.end(); ++it) (*it)->ClearLocalStorage(this, false); m_RegisteredLocalStorageHandlers.clear(); } void mitk::BaseRenderer::RegisterLocalStorageHandler(mitk::BaseLocalStorageHandler *lsh) { m_RegisteredLocalStorageHandlers.push_back(lsh); } void mitk::BaseRenderer::UnregisterLocalStorageHandler(mitk::BaseLocalStorageHandler *lsh) { m_RegisteredLocalStorageHandlers.remove(lsh); } void mitk::BaseRenderer::SetDataStorage(DataStorage *storage) { if (storage != m_DataStorage && storage != nullptr) { m_DataStorage = storage; m_BindDispatcherInteractor->SetDataStorage(m_DataStorage); this->Modified(); } } mitk::Dispatcher::Pointer mitk::BaseRenderer::GetDispatcher() const { return m_BindDispatcherInteractor->GetDispatcher(); } void mitk::BaseRenderer::Resize(int w, int h) { m_RenderWindow->SetSize(w, h); } void mitk::BaseRenderer::InitRenderer(vtkRenderWindow *renderwindow) { if (m_RenderWindow != renderwindow) { if (m_RenderWindow != nullptr) { m_RenderWindow->Delete(); } m_RenderWindow = renderwindow; if (m_RenderWindow != nullptr) { m_RenderWindow->Register(nullptr); } } RemoveAllLocalStorages(); if (m_CameraController.IsNotNull()) { m_CameraController->SetRenderer(this); } } void mitk::BaseRenderer::InitSize(int w, int h) { m_RenderWindow->SetSize(w, h); } void mitk::BaseRenderer::SetWorldTimeGeometry(const mitk::TimeGeometry* geometry) { if (m_WorldTimeGeometry == geometry) { return; } m_WorldTimeGeometry = geometry; this->UpdateCurrentGeometries(); } void mitk::BaseRenderer::SetInteractionReferenceGeometry(const TimeGeometry* geometry) { if (m_InteractionReferenceGeometry == geometry) { return; } m_InteractionReferenceGeometry = geometry; this->UpdateCurrentGeometries(); } void mitk::BaseRenderer::SetSlice(unsigned int slice) { if (m_Slice == slice) { return; } m_Slice = slice; this->UpdateCurrentGeometries(); } void mitk::BaseRenderer::SetTimeStep(unsigned int timeStep) { if (m_TimeStep == timeStep) { return; } m_TimeStep = timeStep; m_TimeStepUpdateTime.Modified(); this->UpdateCurrentGeometries(); } mitk::TimeStepType mitk::BaseRenderer::GetTimeStep(const mitk::BaseData* data) const { if ((data == nullptr) || (data->IsInitialized() == false)) { - return -1; + return TIMESTEP_INVALID; } return data->GetTimeGeometry()->TimePointToTimeStep(GetTime()); } mitk::ScalarType mitk::BaseRenderer::GetTime() const { if (m_WorldTimeGeometry.IsNull()) { return 0; } else { ScalarType timeInMS = m_WorldTimeGeometry->TimeStepToTimePoint(GetTimeStep()); if (timeInMS == itk::NumericTraits<mitk::ScalarType>::NonpositiveMin()) return 0; else return timeInMS; } } void mitk::BaseRenderer::SetGeometry(const itk::EventObject& geometrySendEvent) { const auto* sendEvent = dynamic_cast<const SliceNavigationController::GeometrySendEvent*>(&geometrySendEvent); if (nullptr == sendEvent) { return; } SetWorldTimeGeometry(sendEvent->GetTimeGeometry()); } void mitk::BaseRenderer::UpdateGeometry(const itk::EventObject& geometryUpdateEvent) { const auto* updateEvent = dynamic_cast<const SliceNavigationController::GeometryUpdateEvent*>(&geometryUpdateEvent); if (nullptr == updateEvent) { return; } if (m_CurrentWorldGeometry.IsNull()) { return; } const auto* slicedWorldGeometry = dynamic_cast<const SlicedGeometry3D*>(m_CurrentWorldGeometry.GetPointer()); if (slicedWorldGeometry) { PlaneGeometry* geometry2D = slicedWorldGeometry->GetPlaneGeometry(m_Slice); SetCurrentWorldPlaneGeometry(geometry2D); // calls Modified() } } void mitk::BaseRenderer::SetGeometrySlice(const itk::EventObject& geometrySliceEvent) { const auto* sliceEvent = dynamic_cast<const SliceNavigationController::GeometrySliceEvent*>(&geometrySliceEvent); if (nullptr == sliceEvent) { return; } this->SetSlice(sliceEvent->GetPos()); } void mitk::BaseRenderer::SetGeometryTime(const itk::EventObject& geometryTimeEvent) { const auto* timeEvent = dynamic_cast<const TimeNavigationController::TimeEvent *>(&geometryTimeEvent); if (nullptr == timeEvent) { return; } this->SetTimeStep(timeEvent->GetTimeStep()); } void mitk::BaseRenderer::SendUpdateSlice() { m_CurrentWorldPlaneGeometryUpdateTime.Modified(); } void mitk::BaseRenderer::SetMapperID(MapperSlotId id) { if (m_MapperID != id) { bool useDepthPeeling = Standard3D == id; m_VtkRenderer->SetUseDepthPeeling(useDepthPeeling); m_VtkRenderer->SetUseDepthPeelingForVolumes(useDepthPeeling); m_MapperID = id; this->Modified(); } } int* mitk::BaseRenderer::GetSize() const { return m_RenderWindow->GetSize(); } int* mitk::BaseRenderer::GetViewportSize() const { return m_VtkRenderer->GetSize(); } const double* mitk::BaseRenderer::GetBounds() const { return m_Bounds; } void mitk::BaseRenderer::RequestUpdate() { SetConstrainZoomingAndPanning(true); RenderingManager::GetInstance()->RequestUpdate(m_RenderWindow); } void mitk::BaseRenderer::ForceImmediateUpdate() { RenderingManager::GetInstance()->ForceImmediateUpdate(m_RenderWindow); } unsigned int mitk::BaseRenderer::GetNumberOfVisibleLODEnabledMappers() const { return m_NumberOfVisibleLODEnabledMappers; } void mitk::BaseRenderer::SetSliceNavigationController(mitk::SliceNavigationController *SlicenavigationController) { if (SlicenavigationController == nullptr) return; // copy worldgeometry SlicenavigationController->SetInputWorldTimeGeometry(SlicenavigationController->GetCreatedWorldGeometry()); SlicenavigationController->Update(); // set new m_SliceNavigationController = SlicenavigationController; m_SliceNavigationController->SetRenderer(this); if (m_SliceNavigationController.IsNotNull()) { m_SliceNavigationController->ConnectGeometrySendEvent(this); m_SliceNavigationController->ConnectGeometryUpdateEvent(this); m_SliceNavigationController->ConnectGeometrySliceEvent(this); } } void mitk::BaseRenderer::DisplayToWorld(const Point2D& displayPoint, Point3D& worldIndex) const { if (m_MapperID == BaseRenderer::Standard2D) { double display[3], * world; // For the right z-position in display coordinates, take the focal point, convert it to display and use it for // correct depth. double* displayCoord; double cameraFP[4]; // Get camera focal point and position. Convert to display (screen) // coordinates. We need a depth value for z-buffer. this->GetVtkRenderer()->GetActiveCamera()->GetFocalPoint(cameraFP); cameraFP[3] = 0.0; this->GetVtkRenderer()->SetWorldPoint(cameraFP[0], cameraFP[1], cameraFP[2], cameraFP[3]); this->GetVtkRenderer()->WorldToDisplay(); displayCoord = this->GetVtkRenderer()->GetDisplayPoint(); // now convert the display point to world coordinates display[0] = displayPoint[0]; display[1] = displayPoint[1]; display[2] = displayCoord[2]; this->GetVtkRenderer()->SetDisplayPoint(display); this->GetVtkRenderer()->DisplayToWorld(); world = this->GetVtkRenderer()->GetWorldPoint(); for (int i = 0; i < 3; i++) { worldIndex[i] = world[i] / world[3]; } } else if (m_MapperID == BaseRenderer::Standard3D) { // Seems to be the same code as above, but subclasses may contain different implementations. PickWorldPoint(displayPoint, worldIndex); } return; } void mitk::BaseRenderer::DisplayToPlane(const Point2D &displayPoint, Point2D &planePointInMM) const { if (m_MapperID == BaseRenderer::Standard2D) { Point3D worldPoint; this->DisplayToWorld(displayPoint, worldPoint); m_CurrentWorldPlaneGeometry->Map(worldPoint, planePointInMM); } else if (m_MapperID == BaseRenderer::Standard3D) { MITK_WARN << "No conversion possible with 3D mapper."; return; } return; } void mitk::BaseRenderer::WorldToDisplay(const Point3D &worldIndex, Point2D &displayPoint) const { double world[4], *display; world[0] = worldIndex[0]; world[1] = worldIndex[1]; world[2] = worldIndex[2]; world[3] = 1.0; this->GetVtkRenderer()->SetWorldPoint(world); this->GetVtkRenderer()->WorldToDisplay(); display = this->GetVtkRenderer()->GetDisplayPoint(); displayPoint[0] = display[0]; displayPoint[1] = display[1]; return; } void mitk::BaseRenderer::WorldToView(const mitk::Point3D &worldIndex, mitk::Point2D &viewPoint) const { double world[4], *view; world[0] = worldIndex[0]; world[1] = worldIndex[1]; world[2] = worldIndex[2]; world[3] = 1.0; this->GetVtkRenderer()->SetWorldPoint(world); this->GetVtkRenderer()->WorldToView(); view = this->GetVtkRenderer()->GetViewPoint(); this->GetVtkRenderer()->ViewToNormalizedViewport(view[0], view[1], view[2]); viewPoint[0] = view[0] * this->GetViewportSize()[0]; viewPoint[1] = view[1] * this->GetViewportSize()[1]; return; } void mitk::BaseRenderer::PlaneToDisplay(const Point2D &planePointInMM, Point2D &displayPoint) const { Point3D worldPoint; m_CurrentWorldPlaneGeometry->Map(planePointInMM, worldPoint); this->WorldToDisplay(worldPoint, displayPoint); return; } void mitk::BaseRenderer::PlaneToView(const Point2D &planePointInMM, Point2D &viewPoint) const { Point3D worldPoint; m_CurrentWorldPlaneGeometry->Map(planePointInMM, worldPoint); this->WorldToView(worldPoint,viewPoint); return; } double mitk::BaseRenderer::GetScaleFactorMMPerDisplayUnit() const { if (this->GetMapperID() == BaseRenderer::Standard2D) { // GetParallelScale returns half of the height of the render window in mm. // Divided by the half size of the Display size in pixel givest the mm per pixel. return this->GetVtkRenderer()->GetActiveCamera()->GetParallelScale() * 2.0 / GetViewportSize()[1]; } else return 1.0; } mitk::Point2D mitk::BaseRenderer::GetDisplaySizeInMM() const { Point2D dispSizeInMM; dispSizeInMM[0] = GetSizeX() * GetScaleFactorMMPerDisplayUnit(); dispSizeInMM[1] = GetSizeY() * GetScaleFactorMMPerDisplayUnit(); return dispSizeInMM; } mitk::Point2D mitk::BaseRenderer::GetViewportSizeInMM() const { Point2D dispSizeInMM; dispSizeInMM[0] = GetViewportSize()[0] * GetScaleFactorMMPerDisplayUnit(); dispSizeInMM[1] = GetViewportSize()[1] * GetScaleFactorMMPerDisplayUnit(); return dispSizeInMM; } mitk::Point2D mitk::BaseRenderer::GetOriginInMM() const { Point2D originPx; originPx[0] = m_VtkRenderer->GetOrigin()[0]; originPx[1] = m_VtkRenderer->GetOrigin()[1]; Point2D displayGeometryOriginInMM; DisplayToPlane(originPx, displayGeometryOriginInMM); // top left of the render window (Origin) return displayGeometryOriginInMM; } void mitk::BaseRenderer::SetConstrainZoomingAndPanning(bool constrain) { m_ConstrainZoomingAndPanning = constrain; if (m_ConstrainZoomingAndPanning) { this->GetCameraController()->AdjustCameraToPlane(); } } void mitk::BaseRenderer::UpdateCurrentGeometries() { m_ReferenceGeometryAligned = true; if (m_WorldTimeGeometry.IsNull()) { // simply mark the base renderer as modified Modified(); return; } if (m_TimeStep >= m_WorldTimeGeometry->CountTimeSteps()) { m_TimeStep = m_WorldTimeGeometry->CountTimeSteps() - 1; } auto slicedWorldGeometry = dynamic_cast<SlicedGeometry3D*>(m_WorldTimeGeometry->GetGeometryForTimeStep(m_TimeStep).GetPointer()); if (slicedWorldGeometry != nullptr) { if (m_Slice >= slicedWorldGeometry->GetSlices()) { m_Slice = slicedWorldGeometry->GetSlices() - 1; } SetCurrentWorldGeometry(slicedWorldGeometry); SetCurrentWorldPlaneGeometry(slicedWorldGeometry->GetPlaneGeometry(m_Slice)); m_ReferenceGeometryAligned = BaseRendererHelper::IsRendererGeometryAlignedWithGeometry(this, m_InteractionReferenceGeometry); } } void mitk::BaseRenderer::SetCurrentWorldPlaneGeometry(const mitk::PlaneGeometry* geometry2d) { if (m_CurrentWorldPlaneGeometry == geometry2d) { return; } m_CurrentWorldPlaneGeometry = geometry2d->Clone(); m_CurrentWorldPlaneGeometryData->SetPlaneGeometry(m_CurrentWorldPlaneGeometry); m_CurrentWorldPlaneGeometryUpdateTime.Modified(); Modified(); } void mitk::BaseRenderer::SetCurrentWorldGeometry(const mitk::BaseGeometry* geometry) { if (m_CurrentWorldGeometry == geometry) { return; } m_CurrentWorldGeometry = geometry; if (geometry == nullptr) { m_Bounds[0] = 0; m_Bounds[1] = 0; m_Bounds[2] = 0; m_Bounds[3] = 0; m_Bounds[4] = 0; m_Bounds[5] = 0; m_EmptyWorldGeometry = true; return; } BoundingBox::Pointer boundingBox = m_CurrentWorldGeometry->CalculateBoundingBoxRelativeToTransform(nullptr); const BoundingBox::BoundsArrayType& worldBounds = boundingBox->GetBounds(); m_Bounds[0] = worldBounds[0]; m_Bounds[1] = worldBounds[1]; m_Bounds[2] = worldBounds[2]; m_Bounds[3] = worldBounds[3]; m_Bounds[4] = worldBounds[4]; m_Bounds[5] = worldBounds[5]; if (boundingBox->GetDiagonalLength2() <= mitk::eps) { m_EmptyWorldGeometry = true; } else { m_EmptyWorldGeometry = false; } } void mitk::BaseRenderer::PrintSelf(std::ostream &os, itk::Indent indent) const { os << indent << " MapperID: " << m_MapperID << std::endl; os << indent << " Slice: " << m_Slice << std::endl; os << indent << " TimeStep: " << m_TimeStep << std::endl; os << indent << " CurrentWorldPlaneGeometry: "; if (m_CurrentWorldPlaneGeometry.IsNull()) os << "nullptr" << std::endl; else m_CurrentWorldPlaneGeometry->Print(os, indent); os << indent << " CurrentWorldPlaneGeometryUpdateTime: " << m_CurrentWorldPlaneGeometryUpdateTime << std::endl; os << indent << " CurrentWorldPlaneGeometryTransformTime: " << m_CurrentWorldPlaneGeometryTransformTime << std::endl; Superclass::PrintSelf(os, indent); } diff --git a/Modules/Multilabel/mitkLabelSetImageVtkMapper2D.cpp b/Modules/Multilabel/mitkLabelSetImageVtkMapper2D.cpp index 7a4210ee50..70893567b1 100644 --- a/Modules/Multilabel/mitkLabelSetImageVtkMapper2D.cpp +++ b/Modules/Multilabel/mitkLabelSetImageVtkMapper2D.cpp @@ -1,755 +1,764 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkLabelSetImageVtkMapper2D.h" // MITK #include <mitkAbstractTransformGeometry.h> #include <mitkDataNode.h> #include <mitkImageSliceSelector.h> #include <mitkPlaneGeometry.h> #include <mitkProperties.h> #include <mitkVectorProperty.h> // MITK Rendering #include "vtkNeverTranslucentTexture.h" // VTK #include <vtkCamera.h> #include <vtkImageData.h> #include <vtkImageReslice.h> #include <vtkLookupTable.h> #include <vtkPlaneSource.h> #include <vtkPolyData.h> #include <vtkPolyDataMapper.h> #include <vtkImageMapToColors.h> namespace { itk::ModifiedTimeType PropertyTimeStampIsNewer(const mitk::IPropertyProvider* provider, mitk::BaseRenderer* renderer, const std::string& propName, itk::ModifiedTimeType refMT) { const std::string context = renderer != nullptr ? renderer->GetName() : ""; auto prop = provider->GetConstProperty(propName, context); if (prop != nullptr) { return prop->GetTimeStamp() > refMT; } return false; } } 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::GenerateLookupTable(mitk::BaseRenderer* renderer) { LocalStorage* localStorage = m_LSH.GetLocalStorage(renderer); mitk::DataNode* node = this->GetDataNode(); auto* image = dynamic_cast<mitk::LabelSetImage*>(node->GetData()); assert(image && image->IsInitialized()); localStorage->m_LabelLookupTable = image->GetLookupTable()->Clone(); const auto labelValues = image->GetAllLabelValues(); std::string propertyName = "org.mitk.multilabel.labels.highlighted"; mitk::IntVectorProperty::Pointer prop = dynamic_cast<mitk::IntVectorProperty*>(node->GetNonConstProperty(propertyName)); if (nullptr != prop) { const auto highlightedLabelValues = prop->GetValue(); if (!highlightedLabelValues.empty()) { auto lookUpTable = localStorage->m_LabelLookupTable->GetVtkLookupTable(); auto highlightEnd = highlightedLabelValues.cend(); double rgba[4]; for (const auto& value : labelValues) { lookUpTable->GetTableValue(value, rgba); if (highlightEnd == std::find(highlightedLabelValues.begin(), highlightedLabelValues.end(), value)) { //make all none highlighted values more transparent rgba[3] *= 0.3; } else { if (rgba[3] != 0) { //if highlighted values are visible set them to opaque to pop out rgba[3] = 1.; } else { //if highlighted values are invisible the opacity is increased a bit //to give a visual hint that the are highlighted but also invisible. //e.g. needed to see a difference if you change the visibility of //a highlighted label in the MultiLabelInspector rgba[3] = 0.4; } } lookUpTable->SetTableValue(value, rgba); } localStorage->m_LabelLookupTable->Modified(); } } } namespace { std::vector<mitk::LabelSetImage::GroupIndexType> GetOutdatedGroups(const mitk::LabelSetImageVtkMapper2D::LocalStorage* ls, const mitk::LabelSetImage* seg) { const auto nrOfGroups = seg->GetNumberOfLayers(); std::vector<mitk::LabelSetImage::GroupIndexType> result; for (mitk::LabelSetImage::GroupIndexType groupID = 0; groupID < nrOfGroups; ++groupID) { const auto groupImage = seg->GetGroupImage(groupID); if (groupImage->GetMTime() > ls->m_LastDataUpdateTime || groupImage->GetPipelineMTime() > ls->m_LastDataUpdateTime || ls->m_GroupImageIDs.size() <= groupID || groupImage != ls->m_GroupImageIDs[groupID]) { result.push_back(groupID); } } return result; } } void mitk::LabelSetImageVtkMapper2D::GenerateDataForRenderer(mitk::BaseRenderer *renderer) { LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); mitk::DataNode *node = this->GetDataNode(); auto *segmentation = dynamic_cast<mitk::LabelSetImage *>(node->GetData()); assert(segmentation && segmentation->IsInitialized()); bool isLookupModified = localStorage->m_LabelLookupTable.IsNull() || (localStorage->m_LabelLookupTable->GetMTime() < segmentation->GetLookupTable()->GetMTime()) || PropertyTimeStampIsNewer(node, renderer, "org.mitk.multilabel.labels.highlighted", localStorage->m_LabelLookupTable->GetMTime()) || PropertyTimeStampIsNewer(node, renderer, "opacity", localStorage->m_LabelLookupTable->GetMTime()); if (isLookupModified) { this->GenerateLookupTable(renderer); } - auto outdatedGroups = GetOutdatedGroups(localStorage, segmentation); - bool isGeometryModified = (localStorage->m_LastDataUpdateTime < renderer->GetCurrentWorldPlaneGeometryUpdateTime()) || (localStorage->m_LastDataUpdateTime < renderer->GetCurrentWorldPlaneGeometry()->GetMTime()); bool rendererGeometryIsValid = true; // check if there is a valid worldGeometry if (isGeometryModified) { const PlaneGeometry* worldGeometry = renderer->GetCurrentWorldPlaneGeometry(); rendererGeometryIsValid = worldGeometry != nullptr && worldGeometry->IsValid() && worldGeometry->HasReferenceGeometry(); isGeometryModified = rendererGeometryIsValid && localStorage->m_WorldPlane.IsNotNull() && !Equal(*worldGeometry, *(localStorage->m_WorldPlane.GetPointer())); localStorage->m_WorldPlane = rendererGeometryIsValid ? worldGeometry->Clone() : nullptr; } - bool hasValidContent = rendererGeometryIsValid && RenderingGeometryIntersectsImage(localStorage->m_WorldPlane, segmentation->GetSlicedGeometry()); + const bool hasValidContent = rendererGeometryIsValid && RenderingGeometryIntersectsImage(localStorage->m_WorldPlane, segmentation->GetSlicedGeometry()); + const bool contentBecameValid = hasValidContent && !localStorage->m_HasValidContent; + const bool contentBecameInvalid = !hasValidContent && localStorage->m_HasValidContent; - if (!hasValidContent && localStorage->m_HasValidContent) + if (contentBecameInvalid) { // 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 (unsigned int lidx = 0; lidx < localStorage->m_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); } localStorage->m_LastDataUpdateTime.Modified(); } - if (isGeometryModified || (hasValidContent && !localStorage->m_HasValidContent)) + localStorage->m_HasValidContent = hasValidContent; + if (!hasValidContent) + { + // early out if there is no intersection of the current rendering geometry + // and the geometry of the image that is to be rendered. + return; + } + + std::vector<mitk::LabelSetImage::GroupIndexType> outdatedGroups; + auto currentTimestep = this->GetTimestep(); + if (isGeometryModified || contentBecameValid || localStorage->m_LastTimeStep!= currentTimestep) { //if geometry is outdated or we have valid content again // -> all groups need regeneration outdatedGroups.resize(segmentation->GetNumberOfLayers()); std::iota(outdatedGroups.begin(), outdatedGroups.end(), 0); } - - localStorage->m_HasValidContent = hasValidContent; - - if (!hasValidContent) + else { - // early out if there is no intersection of the current rendering geometry - // and the geometry of the image that is to be rendered. - return; + outdatedGroups = GetOutdatedGroups(localStorage, segmentation); } if (!outdatedGroups.empty()) { this->GenerateImageSlice(renderer, outdatedGroups); } + localStorage->m_LastTimeStep = currentTimestep; + + float opacity = 1.0f; node->GetOpacity(opacity, renderer, "opacity"); if (isLookupModified) { //if lookup table is modified all groups need a new color mapping outdatedGroups.resize(segmentation->GetNumberOfLayers()); std::iota(outdatedGroups.begin(), outdatedGroups.end(), 0); } for (const auto groupID: outdatedGroups) { localStorage->m_LayerImageMapToColors[groupID]->SetLookupTable(localStorage->m_LabelLookupTable->GetVtkLookupTable()); localStorage->m_LayerImageMapToColors[groupID]->SetInputData(localStorage->m_ReslicedImageVector[groupID]); localStorage->m_LayerImageMapToColors[groupID]->Update(); // 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[groupID]->SetInterpolate(textureInterpolation); localStorage->m_LayerTextureVector[groupID]->SetInputConnection( localStorage->m_LayerImageMapToColors[groupID]->GetOutputPort()); this->TransformActor(renderer); // set the plane as input for the mapper localStorage->m_LayerMapperVector[groupID]->SetInputConnection(localStorage->m_Plane->GetOutputPort()); // set the texture for the actor localStorage->m_LayerActorVector[groupID]->SetTexture(localStorage->m_LayerTextureVector[groupID]); localStorage->m_LayerActorVector[groupID]->GetProperty()->SetOpacity(opacity); } auto activeLayer = segmentation->GetActiveLayer(); bool activeGroupIsOutdated = std::find(outdatedGroups.begin(), outdatedGroups.end(), activeLayer) != outdatedGroups.end(); if (activeGroupIsOutdated || PropertyTimeStampIsNewer(node, renderer, "opacity", localStorage->m_LastActiveLabelUpdateTime.GetMTime()) || PropertyTimeStampIsNewer(node, renderer, "labelset.contour.active", localStorage->m_LastActiveLabelUpdateTime.GetMTime()) || PropertyTimeStampIsNewer(node, renderer, "labelset.contour.width", localStorage->m_LastActiveLabelUpdateTime.GetMTime()) ) { this->GenerateActiveLabelOutline(renderer); } } void mitk::LabelSetImageVtkMapper2D::GenerateImageSlice(mitk::BaseRenderer* renderer, const std::vector<mitk::LabelSetImage::GroupIndexType>& outdatedGroupIDs) { LocalStorage* localStorage = m_LSH.GetLocalStorage(renderer); mitk::DataNode* node = this->GetDataNode(); auto* segmentation = dynamic_cast<mitk::LabelSetImage*>(node->GetData()); assert(segmentation && segmentation->IsInitialized()); segmentation->Update(); const auto numberOfLayers = segmentation->GetNumberOfLayers(); if (numberOfLayers != localStorage->m_NumberOfLayers) { if (numberOfLayers > localStorage->m_NumberOfLayers) { for (unsigned int lidx = localStorage->m_NumberOfLayers; lidx < numberOfLayers; ++lidx) { localStorage->m_GroupImageIDs.push_back(nullptr); localStorage->m_ReslicedImageVector.push_back(vtkSmartPointer<vtkImageData>::New()); localStorage->m_ReslicerVector.push_back(mitk::ExtractSliceFilter::New()); localStorage->m_LayerTextureVector.push_back(vtkSmartPointer<vtkNeverTranslucentTexture>::New()); localStorage->m_LayerMapperVector.push_back(vtkSmartPointer<vtkPolyDataMapper>::New()); localStorage->m_LayerActorVector.push_back(vtkSmartPointer<vtkActor>::New()); localStorage->m_LayerImageMapToColors.push_back(vtkSmartPointer<vtkImageMapToColors>::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]); } } else { localStorage->m_GroupImageIDs.resize(numberOfLayers); localStorage->m_ReslicedImageVector.resize(numberOfLayers); localStorage->m_ReslicerVector.resize(numberOfLayers); localStorage->m_LayerTextureVector.resize(numberOfLayers); localStorage->m_LayerMapperVector.resize(numberOfLayers); localStorage->m_LayerActorVector.resize(numberOfLayers); localStorage->m_LayerImageMapToColors.resize(numberOfLayers); } localStorage->m_NumberOfLayers = numberOfLayers; localStorage->m_Actors = vtkSmartPointer<vtkPropAssembly>::New(); for (unsigned int lidx = 0; lidx < numberOfLayers; ++lidx) { localStorage->m_Actors->AddPart(localStorage->m_LayerActorVector[lidx]); } localStorage->m_Actors->AddPart(localStorage->m_OutlineShadowActor); localStorage->m_Actors->AddPart(localStorage->m_OutlineActor); } for (const auto groupID : outdatedGroupIDs) { const auto groupImage = segmentation->GetGroupImage(groupID); localStorage->m_GroupImageIDs[groupID] = groupImage; localStorage->m_ReslicerVector[groupID]->SetInput(groupImage); localStorage->m_ReslicerVector[groupID]->SetWorldGeometry(localStorage->m_WorldPlane); localStorage->m_ReslicerVector[groupID]->SetTimeStep(this->GetTimestep()); // set the transformation of the image to adapt reslice axis localStorage->m_ReslicerVector[groupID]->SetResliceTransformByGeometry( groupImage->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[groupID]->SetInPlaneResampleExtentByGeometry(inPlaneResampleExtentByGeometry); localStorage->m_ReslicerVector[groupID]->SetInterpolationMode(ExtractSliceFilter::RESLICE_NEAREST); localStorage->m_ReslicerVector[groupID]->SetVtkOutputRequest(true); // this is needed when thick mode was enabled before. These variables have to be reset to default values localStorage->m_ReslicerVector[groupID]->SetOutputDimensionality(2); localStorage->m_ReslicerVector[groupID]->SetOutputSpacingZDirection(1.0); localStorage->m_ReslicerVector[groupID]->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[groupID]->GetClippedPlaneBounds(sliceBounds); // setup the textured plane this->GeneratePlane(renderer, sliceBounds); // get the spacing of the slice localStorage->m_mmPerPixel = localStorage->m_ReslicerVector[groupID]->GetOutputSpacing(); localStorage->m_ReslicerVector[groupID]->Modified(); // start the pipeline with updating the largest possible, needed if the geometry of the image has changed localStorage->m_ReslicerVector[groupID]->UpdateLargestPossibleRegion(); localStorage->m_ReslicedImageVector[groupID] = localStorage->m_ReslicerVector[groupID]->GetVtkOutput(); } localStorage->m_LastDataUpdateTime.Modified(); } void mitk::LabelSetImageVtkMapper2D::GenerateActiveLabelOutline(mitk::BaseRenderer* renderer) { LocalStorage* localStorage = m_LSH.GetLocalStorage(renderer); mitk::DataNode* node = this->GetDataNode(); auto* image = dynamic_cast<mitk::LabelSetImage*>(node->GetData()); int activeLayer = image->GetActiveLayer(); float opacity = 1.0f; node->GetOpacity(opacity, renderer, "opacity"); mitk::Label* activeLabel = image->GetActiveLabel(); bool contourActive = false; node->GetBoolProperty("labelset.contour.active", contourActive, renderer); if (nullptr != activeLabel && contourActive && activeLabel->GetVisible()) { //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); } else { localStorage->m_OutlineActor->SetVisibility(false); localStorage->m_OutlineShadowActor->SetVisibility(false); } localStorage->m_LastActiveLabelUpdateTime.Modified(); } bool mitk::LabelSetImageVtkMapper2D::RenderingGeometryIntersectsImage(const PlaneGeometry *renderingGeometry, const BaseGeometry *imageGeometry) const { // 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<vtkPolyData> 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<vtkPoints> points = vtkSmartPointer<vtkPoints>::New(); // the points to draw vtkSmartPointer<vtkCellArray> lines = vtkSmartPointer<vtkCellArray>::New(); // the lines to connect the points // We take the pointer to the first pixel of the image auto *currentPixel = static_cast<mitk::Label::PixelType *>(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<vtkPolyData> polyData = vtkSmartPointer<vtkPolyData>::New(); // Add the points to the dataset polyData->SetPoints(points); // Add the lines to the dataset polyData->SetLines(lines); return polyData; } 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<mitk::LabelSetImage *>(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_LabelLookupTable.IsNull() || (localStorage->m_LabelLookupTable->GetMTime() < image->GetLookupTable()->GetMTime()) || (localStorage->m_LastDataUpdateTime < image->GetMTime()) || (localStorage->m_LastDataUpdateTime < image->GetPipelineMTime()) || (localStorage->m_LastDataUpdateTime < renderer->GetCurrentWorldPlaneGeometryUpdateTime()) || (localStorage->m_LastDataUpdateTime < renderer->GetCurrentWorldPlaneGeometry()->GetMTime()) || (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(); } 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 sagittal) 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 sagittal vtkSmartPointer<vtkTransform> trans = vtkSmartPointer<vtkTransform>::New(); vtkSmartPointer<vtkMatrix4x4> matrix = localStorage->m_ReslicerVector[0]->GetResliceAxes(); // same for all layers trans->SetMatrix(matrix); for (unsigned int lidx = 0; lidx < localStorage->m_NumberOfLayers; ++lidx) { // transform the plane/contour (the actual actor) to the corresponding view (axial, coronal or sagittal) 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); 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<vtkPlaneSource>::New(); m_Actors = vtkSmartPointer<vtkPropAssembly>::New(); m_OutlinePolyData = vtkSmartPointer<vtkPolyData>::New(); m_EmptyPolyData = vtkSmartPointer<vtkPolyData>::New(); m_OutlineActor = vtkSmartPointer<vtkActor>::New(); m_OutlineMapper = vtkSmartPointer<vtkPolyDataMapper>::New(); m_OutlineShadowActor = vtkSmartPointer<vtkActor>::New(); m_HasValidContent = false; m_NumberOfLayers = 0; m_mmPerPixel = nullptr; + m_LastTimeStep = 0; m_OutlineActor->SetMapper(m_OutlineMapper); m_OutlineShadowActor->SetMapper(m_OutlineMapper); m_OutlineActor->SetVisibility(false); m_OutlineShadowActor->SetVisibility(false); } diff --git a/Modules/Multilabel/mitkLabelSetImageVtkMapper2D.h b/Modules/Multilabel/mitkLabelSetImageVtkMapper2D.h index c213b7fff5..4defba4876 100644 --- a/Modules/Multilabel/mitkLabelSetImageVtkMapper2D.h +++ b/Modules/Multilabel/mitkLabelSetImageVtkMapper2D.h @@ -1,231 +1,233 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef mitkLabelSetImageVtkMapper2D_h #define mitkLabelSetImageVtkMapper2D_h // MITK #include "MitkMultilabelExports.h" #include "mitkCommon.h" // MITK Rendering #include "mitkBaseRenderer.h" #include "mitkExtractSliceFilter.h" #include "mitkLabelSetImage.h" #include "mitkVtkMapper.h" // VTK #include <vtkSmartPointer.h> class vtkActor; class vtkPolyDataMapper; class vtkPlaneSource; class vtkImageData; class vtkLookupTable; class vtkImageReslice; class vtkPoints; class vtkMitkThickSlicesFilter; class vtkPolyData; class vtkNeverTranslucentTexture; class vtkImageMapToColors; namespace mitk { /** \brief Mapper to resample and display 2D slices of a 3D labelset image. * * Properties that can be set for labelset images and influence this mapper are: * * - \b "labelset.contour.active": (BoolProperty) whether to show only the active label as a contour or not * - \b "labelset.contour.width": (FloatProperty) line width of the contour * The default properties are: * - \b "labelset.contour.active", mitk::BoolProperty::New( true ), renderer, overwrite ) * - \b "labelset.contour.width", mitk::FloatProperty::New( 2.0 ), renderer, overwrite ) * \ingroup Mapper */ class MITKMULTILABEL_EXPORT LabelSetImageVtkMapper2D : public VtkMapper { public: /** Standard class typedefs. */ mitkClassMacro(LabelSetImageVtkMapper2D, VtkMapper); /** Method for creation through the object factory. */ itkNewMacro(Self); /** \brief Get the Image to map */ const mitk::Image *GetInput(void); /** \brief Checks whether this mapper needs to update itself and generate * data. */ void Update(mitk::BaseRenderer *renderer) override; //### methods of MITK-VTK rendering pipeline vtkProp *GetVtkProp(mitk::BaseRenderer *renderer) override; //### end of methods of MITK-VTK rendering pipeline /** \brief Internal class holding the mapper, actor, etc. for each of the 3 2D render windows */ /** * To render axial, coronal, and sagittal, the mapper is called three times. * For performance reasons, the corresponding data for each view is saved in the * internal helper class LocalStorage. This allows rendering n views with just * 1 mitkMapper using n vtkMapper. * */ class MITKMULTILABEL_EXPORT LocalStorage : public mitk::Mapper::BaseLocalStorage { public: vtkSmartPointer<vtkPropAssembly> m_Actors; /** Vector containing the pointer of the currently used group images. * IMPORTANT: This member must not be used to access any data. * Its purpose is to allow checking if the order of the groups has changed * in order to adapt the pipe line accordingly*/ std::vector<const Image*> m_GroupImageIDs; std::vector<vtkSmartPointer<vtkActor>> m_LayerActorVector; std::vector<vtkSmartPointer<vtkPolyDataMapper>> m_LayerMapperVector; std::vector<vtkSmartPointer<vtkImageData>> m_ReslicedImageVector; std::vector<vtkSmartPointer<vtkImageMapToColors>> m_LayerImageMapToColors; std::vector<vtkSmartPointer<vtkNeverTranslucentTexture>> m_LayerTextureVector; vtkSmartPointer<vtkPolyData> m_EmptyPolyData; vtkSmartPointer<vtkPlaneSource> m_Plane; std::vector<mitk::ExtractSliceFilter::Pointer> m_ReslicerVector; vtkSmartPointer<vtkPolyData> m_OutlinePolyData; /** \brief An actor for the outline */ vtkSmartPointer<vtkActor> m_OutlineActor; /** \brief An actor for the outline shadow*/ vtkSmartPointer<vtkActor> m_OutlineShadowActor; /** \brief A mapper for the outline */ vtkSmartPointer<vtkPolyDataMapper> m_OutlineMapper; /** \brief Timestamp of last update of stored data. */ itk::TimeStamp m_LastDataUpdateTime; /** \brief Timestamp of last update of a property. */ itk::TimeStamp m_LastPropertyUpdateTime; /** \brief Timestamp of last update of a property. */ itk::TimeStamp m_LastActiveLabelUpdateTime; /** \brief mmPerPixel relation between pixel and mm. (World spacing).*/ mitk::ScalarType *m_mmPerPixel; /** look up table for label colors. */ mitk::LookupTable::Pointer m_LabelLookupTable; mitk::PlaneGeometry::Pointer m_WorldPlane; bool m_HasValidContent; + mitk::TimeStepType m_LastTimeStep; + unsigned int m_NumberOfLayers; /** \brief Default constructor of the local storage. */ LocalStorage(); /** \brief Default destructor of the local storage. */ ~LocalStorage() override; }; /** \brief The LocalStorageHandler holds all (three) LocalStorages for the three 2D render windows. */ mitk::LocalStorageHandler<LocalStorage> m_LSH; /** \brief Get the LocalStorage corresponding to the current renderer. */ LocalStorage *GetLocalStorage(mitk::BaseRenderer *renderer); /** \brief Set the default properties for general image rendering. */ static void SetDefaultProperties(mitk::DataNode *node, mitk::BaseRenderer *renderer = nullptr, bool overwrite = false); /** \brief This method switches between different rendering modes (e.g. use a lookup table or a transfer function). * Detailed documentation about the modes can be found here: \link mitk::RenderingModeProperty \endlink */ void ApplyRenderingMode(mitk::BaseRenderer *renderer); protected: /** \brief Transforms the actor to the actual position in 3D. * \param renderer The current renderer corresponding to the render window. */ void TransformActor(mitk::BaseRenderer *renderer); /** \brief Generates a plane according to the size of the resliced image in milimeters. * * In VTK a vtkPlaneSource is defined through three points. The origin and two * points defining the axes of the plane (see VTK documentation). The origin is * set to (xMin; yMin; Z), where xMin and yMin are the minimal bounds of the * resliced image in space. Z is relevant for blending and the layer property. * The center of the plane (C) is also the center of the view plane (cf. the image above). * * \note For the standard MITK view with three 2D render windows showing three * different slices, three such planes are generated. All these planes are generated * in the XY-plane (even if they depict a YZ-slice of the volume). * */ void GeneratePlane(mitk::BaseRenderer *renderer, double planeBounds[6]); /** \brief Generates a vtkPolyData object containing the outline of a given binary slice. \param renderer Pointer to the renderer containing the needed information \param image \param pixelValue \note This code is based on code from the iil library. */ vtkSmartPointer<vtkPolyData> CreateOutlinePolyData(mitk::BaseRenderer *renderer, vtkImageData *image, int pixelValue = 1); /** Default constructor */ LabelSetImageVtkMapper2D(); /** Default deconstructor */ ~LabelSetImageVtkMapper2D() override; /** \brief Does the actual resampling, without rendering the image yet. * All the data is generated inside this method. The vtkProp (or Actor) * is filled with content (i.e. the resliced image). * * After generation, a 4x4 transformation matrix(t) of the current slice is obtained * from the vtkResliceImage object via GetReslicesAxis(). This matrix is * applied to each textured plane (actor->SetUserTransform(t)) to transform everything * to the actual 3D position (cf. the following image). * * \image html cameraPositioning3D.png * */ void GenerateDataForRenderer(mitk::BaseRenderer *renderer) override; void GenerateImageSlice(mitk::BaseRenderer* renderer, const std::vector<mitk::LabelSetImage::GroupIndexType>& outdatedGroupIDs); void GenerateActiveLabelOutline(mitk::BaseRenderer* renderer); /** \brief Generates the look up table that should be used. */ void GenerateLookupTable(mitk::BaseRenderer* renderer); /** \brief This method uses the vtkCamera clipping range and the layer property * to calcualte the depth of the object (e.g. image or contour). The depth is used * to keep the correct order for the final VTK rendering.*/ float CalculateLayerDepth(mitk::BaseRenderer *renderer); /** * \brief Calculates whether the given rendering geometry intersects the * given SlicedGeometry3D. * * This method checks if the given Geometry2D intersects the given * SlicedGeometry3D. It calculates the distance of the Geometry2D to all * 8 cornerpoints of the SlicedGeometry3D. If all distances have the same * sign (all positive or all negative) there is no intersection. * If the distances have different sign, there is an intersection. **/ bool RenderingGeometryIntersectsImage(const PlaneGeometry *renderingGeometry, const BaseGeometry* imageGeometry) const; }; } // namespace mitk #endif