diff --git a/Modules/ROI/files.cmake b/Modules/ROI/files.cmake index 8526fe310a..9880797ab8 100644 --- a/Modules/ROI/files.cmake +++ b/Modules/ROI/files.cmake @@ -1,13 +1,15 @@ set(H_FILES include/mitkROI.h include/mitkROIMapper2D.h include/mitkROIMapper3D.h + src/mitkROIMapperHelper.h src/mitkROIObjectFactory.h ) set(CPP_FILES mitkROI.cpp mitkROIMapper2D.cpp mitkROIMapper3D.cpp + mitkROIMapperHelper.cpp mitkROIObjectFactory.cpp ) diff --git a/Modules/ROI/include/mitkROIMapper2D.h b/Modules/ROI/include/mitkROIMapper2D.h index b7443b3c2f..0e8323592d 100644 --- a/Modules/ROI/include/mitkROIMapper2D.h +++ b/Modules/ROI/include/mitkROIMapper2D.h @@ -1,69 +1,66 @@ /*============================================================================ 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 mitkROIMapper2D_h #define mitkROIMapper2D_h #include <mitkLocalStorageHandler.h> #include <mitkVtkMapper.h> #include <MitkROIExports.h> template <class T> class vtkSmartPointer; class vtkPropAssembly; -class vtkCaptionActor2D; namespace mitk { class MITKROI_EXPORT ROIMapper2D : public VtkMapper { class LocalStorage : public Mapper::BaseLocalStorage { public: LocalStorage(); ~LocalStorage() override; vtkPropAssembly* GetPropAssembly() const; void SetPropAssembly(vtkPropAssembly* propAssembly); const PlaneGeometry* GetLastPlaneGeometry() const; void SetLastPlaneGeometry(const PlaneGeometry* planeGeometry); protected: vtkSmartPointer<vtkPropAssembly> m_PropAssembly; PlaneGeometry::ConstPointer m_LastPlaneGeometry; }; public: static void SetDefaultProperties(DataNode* node, BaseRenderer* renderer = nullptr, bool override = false); mitkClassMacro(ROIMapper2D, VtkMapper) itkFactorylessNewMacro(Self) vtkProp *GetVtkProp(mitk::BaseRenderer *renderer) override; protected: ROIMapper2D(); ~ROIMapper2D() override; void GenerateDataForRenderer(BaseRenderer* renderer) override; void ApplyColorAndOpacityProperties(BaseRenderer* renderer, vtkActor* actor) override; private: - vtkSmartPointer<vtkCaptionActor2D> CreateCaptionActor(const std::string& caption, double* attachmentPoint, vtkProperty* property, const BaseRenderer* renderer) const; - LocalStorageHandler<LocalStorage> m_LocalStorageHandler; }; } #endif diff --git a/Modules/ROI/src/mitkROIMapper2D.cpp b/Modules/ROI/src/mitkROIMapper2D.cpp index 562c736917..c7784d6969 100644 --- a/Modules/ROI/src/mitkROIMapper2D.cpp +++ b/Modules/ROI/src/mitkROIMapper2D.cpp @@ -1,240 +1,210 @@ /*============================================================================ 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 <mitkROIMapper2D.h> -#include <mitkROI.h> -#include <vtkCaptionActor2D.h> +#include "mitkROIMapperHelper.h" + #include <vtkCubeSource.h> #include <vtkPlane.h> #include <vtkPolyDataMapper.h> #include <vtkPolyDataPlaneCutter.h> #include <vtkPropAssembly.h> -#include <vtkSmartPointer.h> -#include <vtkTextActor.h> -#include <vtkTextProperty.h> -// Implemented in mitkROIMapper3D.cpp -extern void ApplyIndividualProperties(const mitk::IPropertyProvider* properties, vtkActor* actor); +#include <boost/algorithm/string.hpp> + +#include <regex> namespace { std::string GetName(const mitk::ROI::Element& roi) { auto property = roi.GetConstProperty("name"); if (property.IsNotNull()) { auto nameProperty = dynamic_cast<const mitk::StringProperty*>(property.GetPointer()); if (nameProperty != nullptr) return nameProperty->GetValue(); } return ""; } mitk::Point3D GetBottomLeftPoint(vtkPoints* points, mitk::BaseRenderer* renderer) { mitk::Point3D point = points->GetPoint(0); mitk::Point2D bottomLeftDisplayPoint; renderer->WorldToDisplay(point, bottomLeftDisplayPoint); auto numPoints = points->GetNumberOfPoints(); mitk::Point2D displayPoint; for (decltype(numPoints) i = 1; i < numPoints; ++i) { point.FillPoint(points->GetPoint(i)); renderer->WorldToDisplay(point, displayPoint); bottomLeftDisplayPoint[0] = std::min(bottomLeftDisplayPoint[0], displayPoint[0]); bottomLeftDisplayPoint[1] = std::min(bottomLeftDisplayPoint[1], displayPoint[1]); } renderer->DisplayToWorld(bottomLeftDisplayPoint, point); return point; } } mitk::ROIMapper2D::LocalStorage::LocalStorage() : m_PropAssembly(vtkSmartPointer<vtkPropAssembly>::New()) { } mitk::ROIMapper2D::LocalStorage::~LocalStorage() { } vtkPropAssembly* mitk::ROIMapper2D::LocalStorage::GetPropAssembly() const { return m_PropAssembly; } void mitk::ROIMapper2D::LocalStorage::SetPropAssembly(vtkPropAssembly* propAssembly) { m_PropAssembly = propAssembly; } const mitk::PlaneGeometry* mitk::ROIMapper2D::LocalStorage::GetLastPlaneGeometry() const { return m_LastPlaneGeometry; } void mitk::ROIMapper2D::LocalStorage::SetLastPlaneGeometry(const PlaneGeometry* planeGeometry) { m_LastPlaneGeometry = planeGeometry; } void mitk::ROIMapper2D::SetDefaultProperties(DataNode* node, BaseRenderer* renderer, bool override) { - // Implemented in ROIMapper3D + Superclass::SetDefaultProperties(node, renderer, override); + ROIMapperHelper::SetDefaultProperties(node, renderer, override); } mitk::ROIMapper2D::ROIMapper2D() { } mitk::ROIMapper2D::~ROIMapper2D() { } void mitk::ROIMapper2D::GenerateDataForRenderer(BaseRenderer* renderer) { const auto* dataNode = this->GetDataNode(); if (dataNode == nullptr) return; auto* localStorage = m_LocalStorageHandler.GetLocalStorage(renderer); const auto* planeGeometry = renderer->GetCurrentWorldPlaneGeometry(); if (localStorage->GetLastPlaneGeometry() != nullptr && localStorage->GetLastPlaneGeometry()->IsOnPlane(planeGeometry) && localStorage->GetLastGenerateDataTime() >= dataNode->GetMTime()) { return; } localStorage->SetLastPlaneGeometry(planeGeometry->Clone()); const auto* data = dynamic_cast<ROI*>(this->GetData()); if (data == nullptr) return; auto propAssembly = vtkSmartPointer<vtkPropAssembly>::New(); if (dataNode->IsVisible(renderer)) { const auto* geometry = data->GetGeometry(); const auto halfSpacing = geometry->GetSpacing() * 0.5f; auto plane = vtkSmartPointer<vtkPlane>::New(); plane->SetOrigin(planeGeometry->GetOrigin().data()); plane->SetNormal(planeGeometry->GetNormal().data()); for (const auto& roi : *data) { Point3D min; geometry->IndexToWorld(roi.Min, min); min -= halfSpacing; Point3D max; geometry->IndexToWorld(roi.Max, max); max += halfSpacing; auto cube = vtkSmartPointer<vtkCubeSource>::New(); cube->SetBounds(min[0], max[0], min[1], max[1], min[2], max[2]); auto cutter = vtkSmartPointer<vtkPolyDataPlaneCutter>::New(); cutter->SetInputConnection(cube->GetOutputPort()); cutter->SetPlane(plane); cutter->Update(); auto* slicePolyData = cutter->GetOutput(); if (slicePolyData->GetNumberOfLines() == 0) continue; auto mapper = vtkSmartPointer<vtkPolyDataMapper>::New(); mapper->SetInputConnection(cutter->GetOutputPort()); auto actor = vtkSmartPointer<vtkActor>::New(); actor->SetMapper(mapper); this->ApplyColorAndOpacityProperties(renderer, actor); - ApplyIndividualProperties(roi.Properties, actor); + ROIMapperHelper::ApplyIndividualProperties(roi.Properties, actor); propAssembly->AddPart(actor); - auto name = GetName(roi); - - if (!name.empty()) + if (std::string caption; dataNode->GetStringProperty("caption", caption, renderer)) { - auto bottomLeftPoint = GetBottomLeftPoint(slicePolyData->GetPoints(), renderer); - auto captionActor = CreateCaptionActor(name, bottomLeftPoint.data(), actor->GetProperty(), renderer); + caption = ROIMapperHelper::ParseCaption(caption, roi); + + if (!caption.empty()) + { + auto bottomLeftPoint = GetBottomLeftPoint(slicePolyData->GetPoints(), renderer); + auto captionActor = ROIMapperHelper::CreateCaptionActor(caption, bottomLeftPoint, actor->GetProperty(), dataNode, renderer); - propAssembly->AddPart(captionActor); + propAssembly->AddPart(captionActor); + } } } } localStorage->SetPropAssembly(propAssembly); localStorage->UpdateGenerateDataTime(); } void mitk::ROIMapper2D::ApplyColorAndOpacityProperties(BaseRenderer* renderer, vtkActor* actor) { auto* property = actor->GetProperty(); float opacity = 1.0f; this->GetOpacity(opacity, renderer); property->SetOpacity(opacity); } vtkProp* mitk::ROIMapper2D::GetVtkProp(BaseRenderer* renderer) { return m_LocalStorageHandler.GetLocalStorage(renderer)->GetPropAssembly(); } - -vtkSmartPointer<vtkCaptionActor2D> mitk::ROIMapper2D::CreateCaptionActor(const std::string& caption, double* attachmentPoint, vtkProperty* property, const BaseRenderer* renderer) const -{ - auto captionActor = vtkSmartPointer<vtkCaptionActor2D>::New(); - captionActor->SetPosition(property->GetLineWidth() * 0.5, property->GetLineWidth() * 0.5); - captionActor->GetTextActor()->SetTextScaleModeToNone(); - captionActor->SetAttachmentPoint(attachmentPoint); - captionActor->SetCaption(caption.c_str()); - captionActor->BorderOff(); - captionActor->LeaderOff(); - - auto* textProperty = captionActor->GetCaptionTextProperty(); - textProperty->SetColor(property->GetColor()); - textProperty->SetOpacity(property->GetOpacity()); - textProperty->ShadowOff(); - - auto* dataNode = this->GetDataNode(); - - int fontSize = 16; - dataNode->GetIntProperty("font.size", fontSize, renderer); - textProperty->SetFontSize(fontSize); - - bool bold = false; - dataNode->GetBoolProperty("font.bold", bold, renderer); - textProperty->SetBold(bold); - - bool italic = false; - dataNode->GetBoolProperty("font.italic", italic, renderer); - textProperty->SetItalic(italic); - - return captionActor; -} diff --git a/Modules/ROI/src/mitkROIMapper3D.cpp b/Modules/ROI/src/mitkROIMapper3D.cpp index 605eb7aafe..f68813ac34 100644 --- a/Modules/ROI/src/mitkROIMapper3D.cpp +++ b/Modules/ROI/src/mitkROIMapper3D.cpp @@ -1,171 +1,123 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include <mitkROIMapper3D.h> #include <mitkROI.h> +#include "mitkROIMapperHelper.h" + #include <vtkActor.h> #include <vtkCubeSource.h> #include <vtkPolyDataMapper.h> #include <vtkSmartPointer.h> -// Used by both ROIMapper2D and ROIMapper3D -void ApplyIndividualProperties(const mitk::IPropertyProvider* properties, vtkActor* actor) -{ - actor->GetProperty()->SetRepresentationToWireframe(); - actor->GetProperty()->LightingOff(); - - auto property = properties->GetConstProperty("color"); - - if (property.IsNotNull()) - { - auto colorProperty = dynamic_cast<const mitk::ColorProperty*>(property.GetPointer()); - - if (colorProperty != nullptr) - { - const auto color = colorProperty->GetColor(); - actor->GetProperty()->SetColor(color[0], color[1], color[2]); - } - } - - property = properties->GetConstProperty("opacity"); - - if (property.IsNotNull()) - { - auto opacityProperty = dynamic_cast<const mitk::FloatProperty*>(property.GetPointer()); - - if (opacityProperty != nullptr) - { - const auto opacity = opacityProperty->GetValue(); - actor->GetProperty()->SetOpacity(actor->GetProperty()->GetOpacity() * opacity); - } - } - - property = properties->GetConstProperty("lineWidth"); - - if (property.IsNotNull()) - { - auto lineWidthProperty = dynamic_cast<const mitk::FloatProperty*>(property.GetPointer()); - - if (lineWidthProperty != nullptr) - { - const auto lineWidth = lineWidthProperty->GetValue(); - actor->GetProperty()->SetLineWidth(lineWidth); - } - } -} - mitk::ROIMapper3D::LocalStorage::LocalStorage() : m_PropAssembly(vtkSmartPointer<vtkPropAssembly>::New()) { } mitk::ROIMapper3D::LocalStorage::~LocalStorage() { } vtkPropAssembly* mitk::ROIMapper3D::LocalStorage::GetPropAssembly() const { return m_PropAssembly; } void mitk::ROIMapper3D::LocalStorage::SetPropAssembly(vtkPropAssembly* propAssembly) { m_PropAssembly = propAssembly; } void mitk::ROIMapper3D::SetDefaultProperties(DataNode* node, BaseRenderer* renderer, bool override) { Superclass::SetDefaultProperties(node, renderer, override); - - node->AddProperty("opacity", mitk::FloatProperty::New(1.0f), renderer, override); - node->AddProperty("font.bold", mitk::BoolProperty::New(false), renderer, override); - node->AddProperty("font.italic", mitk::BoolProperty::New(false), renderer, override); - node->AddProperty("font.size", mitk::IntProperty::New(16), renderer, override); + ROIMapperHelper::SetDefaultProperties(node, renderer, override); } mitk::ROIMapper3D::ROIMapper3D() { } mitk::ROIMapper3D::~ROIMapper3D() { } void mitk::ROIMapper3D::GenerateDataForRenderer(BaseRenderer* renderer) { const auto* dataNode = this->GetDataNode(); if (dataNode == nullptr) return; auto* localStorage = m_LocalStorageHandler.GetLocalStorage(renderer); if (localStorage->GetLastGenerateDataTime() >= dataNode->GetMTime()) return; const auto* data = dynamic_cast<ROI*>(this->GetData()); if (data == nullptr) return; auto propAssembly = vtkSmartPointer<vtkPropAssembly>::New(); if (dataNode->IsVisible(renderer)) { const auto* geometry = data->GetGeometry(); const auto halfSpacing = geometry->GetSpacing() * 0.5f; for (const auto& roi : *data) { Point3D min; geometry->IndexToWorld(roi.Min, min); min -= halfSpacing; Point3D max; geometry->IndexToWorld(roi.Max, max); max += halfSpacing; auto cube = vtkSmartPointer<vtkCubeSource>::New(); cube->SetBounds(min[0], max[0], min[1], max[1], min[2], max[2]); cube->Update(); auto mapper = vtkSmartPointer<vtkPolyDataMapper>::New(); mapper->SetInputConnection(cube->GetOutputPort()); auto actor = vtkSmartPointer<vtkActor>::New(); actor->SetMapper(mapper); this->ApplyColorAndOpacityProperties(renderer, actor); - ApplyIndividualProperties(roi.Properties, actor); + ROIMapperHelper::ApplyIndividualProperties(roi.Properties, actor); propAssembly->AddPart(actor); } } localStorage->SetPropAssembly(propAssembly); localStorage->UpdateGenerateDataTime(); } void mitk::ROIMapper3D::ApplyColorAndOpacityProperties(BaseRenderer* renderer, vtkActor* actor) { auto* property = actor->GetProperty(); float opacity = 1.0f; this->GetOpacity(opacity, renderer); property->SetOpacity(opacity); } vtkProp* mitk::ROIMapper3D::GetVtkProp(BaseRenderer* renderer) { return m_LocalStorageHandler.GetLocalStorage(renderer)->GetPropAssembly(); } diff --git a/Modules/ROI/src/mitkROIMapperHelper.cpp b/Modules/ROI/src/mitkROIMapperHelper.cpp new file mode 100644 index 0000000000..cbf5b99bff --- /dev/null +++ b/Modules/ROI/src/mitkROIMapperHelper.cpp @@ -0,0 +1,130 @@ +/*============================================================================ + +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 "mitkROIMapperHelper.h" + +#include <vtkTextActor.h> +#include <vtkTextProperty.h> + +#include <boost/algorithm/string.hpp> + +#include <regex> + +void mitk::ROIMapperHelper::ApplyIndividualProperties(const IPropertyProvider* properties, vtkActor* actor) +{ + auto* property = actor->GetProperty(); + + property->SetRepresentationToWireframe(); + property->LightingOff(); + + if (auto colorProperty = GetConstProperty<ColorProperty>("color", properties); colorProperty != nullptr) + { + const auto color = colorProperty->GetColor(); + property->SetColor(color[0], color[1], color[2]); + } + + if (auto opacityProperty = GetConstProperty<FloatProperty>("opacity", properties); opacityProperty != nullptr) + { + const auto opacity = opacityProperty->GetValue(); + property->SetOpacity(property->GetOpacity() * opacity); + } + + if (auto lineWidthProperty = GetConstProperty<FloatProperty>("lineWidth", properties); lineWidthProperty != nullptr) + { + const auto lineWidth = lineWidthProperty->GetValue(); + property->SetLineWidth(lineWidth); + } +} + +vtkSmartPointer<vtkCaptionActor2D> mitk::ROIMapperHelper::CreateCaptionActor(const std::string& caption, const Point3D& attachmentPoint, vtkProperty* property, const DataNode* dataNode, const BaseRenderer* renderer) +{ + auto captionActor = vtkSmartPointer<vtkCaptionActor2D>::New(); + captionActor->SetPosition(property->GetLineWidth() * 0.5, property->GetLineWidth() * 0.5); + captionActor->GetTextActor()->SetTextScaleModeToNone(); + captionActor->SetAttachmentPoint(attachmentPoint[0], attachmentPoint[1], attachmentPoint[2]); + captionActor->SetCaption(caption.c_str()); + captionActor->BorderOff(); + captionActor->LeaderOff(); + + auto* textProperty = captionActor->GetCaptionTextProperty(); + textProperty->SetColor(property->GetColor()); + textProperty->SetOpacity(property->GetOpacity()); + textProperty->ShadowOff(); + + int fontSize = 16; + dataNode->GetIntProperty("font.size", fontSize, renderer); + textProperty->SetFontSize(fontSize); + + bool bold = false; + dataNode->GetBoolProperty("font.bold", bold, renderer); + textProperty->SetBold(bold); + + bool italic = false; + dataNode->GetBoolProperty("font.italic", italic, renderer); + textProperty->SetItalic(italic); + + return captionActor; +} + +std::string mitk::ROIMapperHelper::ParseCaption(const std::string& captionTemplate, const ROI::Element& roi) +{ + std::regex regex(R"(\{([^}]*)\})"); // Anything between curly braces (considered as placeholder). + + auto start = captionTemplate.cbegin(); + bool hasPlaceholders = false; + std::string caption; + std::smatch match; + + // Iterate through the caption template and substitute all + // placeholders with corresponding data or property values. + + while (std::regex_search(start, captionTemplate.cend(), match, regex)) + { + hasPlaceholders = true; + + caption.append(match.prefix().first, match.prefix().second); + + if (match[1] == "ID") + { + caption.append(std::to_string(roi.ID)); + } + else + { + auto property = roi.GetConstProperty(match[1]); + + if (property.IsNotNull()) + caption.append(property->GetValueAsString()); + } + + start = match.suffix().first; + } + + if (match.suffix().matched) + caption.append(match.suffix().first, match.suffix().second); + + if (hasPlaceholders) + { + boost::trim(caption); + return caption; + } + + return captionTemplate; +} + +void mitk::ROIMapperHelper::SetDefaultProperties(DataNode* node, BaseRenderer* renderer, bool override) +{ + node->AddProperty("opacity", FloatProperty::New(1.0f), renderer, override); + node->AddProperty("font.bold", BoolProperty::New(false), renderer, override); + node->AddProperty("font.italic", BoolProperty::New(false), renderer, override); + node->AddProperty("font.size", IntProperty::New(16), renderer, override); + node->AddProperty("caption", StringProperty::New("{ID}\n{name}"), renderer, override); +} diff --git a/Modules/ROI/src/mitkROIMapperHelper.h b/Modules/ROI/src/mitkROIMapperHelper.h new file mode 100644 index 0000000000..48161c27ad --- /dev/null +++ b/Modules/ROI/src/mitkROIMapperHelper.h @@ -0,0 +1,48 @@ +/*============================================================================ + +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 mitkROIMapperHelper_h +#define mitkROIMapperHelper_h + +#include <mitkBaseRenderer.h> +#include <mitkROI.h> + +#include <vtkCaptionActor2D.h> +#include <vtkProperty.h> +#include <vtkSmartPointer.h> + +namespace mitk +{ + namespace ROIMapperHelper + { + void ApplyIndividualProperties(const IPropertyProvider* properties, vtkActor* actor); + + vtkSmartPointer<vtkCaptionActor2D> CreateCaptionActor(const std::string& caption, const Point3D& attachmentPoint, vtkProperty* property, const DataNode* dataNode, const BaseRenderer* renderer); + + std::string ParseCaption(const std::string& captionTemplate, const ROI::Element& roi); + + void SetDefaultProperties(DataNode* node, BaseRenderer* renderer, bool override); + + template <class T> + const T* GetConstProperty(const std::string& propertyKey, const mitk::IPropertyProvider* properties) + { + auto property = properties->GetConstProperty(propertyKey); + + if (property.IsNotNull()) + return dynamic_cast<const T*>(property.GetPointer()); + + return nullptr; + } + } +} + +#endif