diff --git a/Modules/BoundingShape/src/Interactions/mitkBoundingShapeInteractor.cpp b/Modules/BoundingShape/src/Interactions/mitkBoundingShapeInteractor.cpp index 9c61cf79ae..27a7210bb2 100644 --- a/Modules/BoundingShape/src/Interactions/mitkBoundingShapeInteractor.cpp +++ b/Modules/BoundingShape/src/Interactions/mitkBoundingShapeInteractor.cpp @@ -1,605 +1,605 @@ /*============================================================================ 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 "../DataManagement/mitkBoundingShapeUtil.h" #include <mitkBoundingShapeInteractor.h> #include <mitkDisplayActionEventBroadcast.h> #include <mitkInteractionConst.h> #include <mitkInteractionEventObserver.h> #include <mitkInteractionKeyEvent.h> #include <mitkInteractionPositionEvent.h> #include <mitkMouseWheelEvent.h> #include <vtkCamera.h> #include <vtkInteractorObserver.h> #include <vtkInteractorStyle.h> #include <vtkPointData.h> #include <vtkRenderWindowInteractor.h> #include <vtkSmartPointer.h> #include "usGetModuleContext.h" #include "usModuleRegistry.h" // Properties to allow the user to interact with the base data const char *selectedColorPropertyName = "Bounding Shape.Selected Color"; const char *deselectedColorPropertyName = "Bounding Shape.Deselected Color"; const char *activeHandleIdPropertyName = "Bounding Shape.Active Handle ID"; const char *boundingShapePropertyName = "Bounding Shape"; namespace mitk { itkEventMacroDefinition(BoundingShapeInteractionEvent, itk::AnyEvent); class BoundingShapeInteractor::Impl { public: Impl() : OriginalInteractionEnabled(false), RotationEnabled(false) { Point3D initialPoint; initialPoint.Fill(0.0); for (int i = 0; i < 6; ++i) Handles.push_back(Handle(initialPoint, i, GetHandleIndices(i))); } ~Impl() {} bool OriginalInteractionEnabled; Point3D InitialPickedWorldPoint; Point3D LastPickedWorldPoint; Point2D InitialPickedDisplayPoint; std::vector<Handle> Handles; Handle ActiveHandle; Geometry3D::Pointer OriginalGeometry; bool RotationEnabled; std::map<us::ServiceReferenceU, mitk::EventConfig> DisplayInteractionConfigs; }; } mitk::BoundingShapeInteractor::BoundingShapeInteractor() : m_Impl(new Impl) { } mitk::BoundingShapeInteractor::~BoundingShapeInteractor() { this->RestoreNodeProperties(); delete m_Impl; } void mitk::BoundingShapeInteractor::ConnectActionsAndFunctions() { // **Conditions** that can be used in the state machine, to ensure that certain conditions are met, before actually // executing an action CONNECT_CONDITION("isHoveringOverObject", CheckOverObject); CONNECT_CONDITION("isHoveringOverHandles", CheckOverHandles); // **Function** in the statemachine patterns also referred to as **Actions** CONNECT_FUNCTION("selectObject", SelectObject); CONNECT_FUNCTION("deselectObject", DeselectObject); CONNECT_FUNCTION("deselectHandles", DeselectHandles); CONNECT_FUNCTION("initInteraction", InitInteraction); CONNECT_FUNCTION("translateObject", TranslateObject); CONNECT_FUNCTION("selectHandle", SelectHandle); CONNECT_FUNCTION("scaleObject", ScaleObject); // CONNECT_FUNCTION("rotateObject",RotateObject); } // RotateObject(StateMachineAction*, InteractionEvent* interactionEvent) // void mitk::BoundingShapeInteractor::RotateGeometry(mitk::ScalarType angle, int rotationaxis, mitk::BaseGeometry* // geometry) //{ // mitk::Vector3D rotationAxis = geometry->GetAxisVector(rotationaxis); // float pointX = 0.0f; // float pointY = 0.0f; // float pointZ = 0.0f; // mitk::Point3D pointOfRotation; // pointOfRotation.Fill(0.0); // this->GetDataNode()->GetFloatProperty(anchorPointX, pointX); // this->GetDataNode()->GetFloatProperty(anchorPointY, pointY); // this->GetDataNode()->GetFloatProperty(anchorPointZ, pointZ); // pointOfRotation[0] = pointX; // pointOfRotation[1] = pointY; // pointOfRotation[2] = pointZ; // // mitk::RotationOperation* doOp = new mitk::RotationOperation(OpROTATE, pointOfRotation, rotationAxis, angle); // // geometry->ExecuteOperation(doOp); // delete doOp; //} void mitk::BoundingShapeInteractor::SetRotationEnabled(bool rotationEnabled) { m_Impl->RotationEnabled = rotationEnabled; } void mitk::BoundingShapeInteractor::DataNodeChanged() { mitk::DataNode::Pointer newInputNode = this->GetDataNode(); if (newInputNode == nullptr) return; // add color properties mitk::ColorProperty::Pointer selectedColor = dynamic_cast<mitk::ColorProperty *>(newInputNode->GetProperty(selectedColorPropertyName)); mitk::ColorProperty::Pointer deselectedColor = dynamic_cast<mitk::ColorProperty *>(newInputNode->GetProperty(deselectedColorPropertyName)); if (selectedColor.IsNull()) newInputNode->AddProperty(selectedColorPropertyName, mitk::ColorProperty::New(0.0, 1.0, 0.0)); if (deselectedColor.IsNull()) - newInputNode->AddProperty(deselectedColorPropertyName, mitk::ColorProperty::New(1.0, 1.0, 1.0)); + newInputNode->AddProperty(deselectedColorPropertyName, mitk::ColorProperty::New(1.0, 0.0, 0.0)); newInputNode->SetProperty(boundingShapePropertyName, mitk::BoolProperty::New(true)); newInputNode->AddProperty(activeHandleIdPropertyName, mitk::IntProperty::New(-1)); newInputNode->SetProperty("layer", mitk::IntProperty::New(101)); newInputNode->SetBoolProperty("fixedLayer", mitk::BoolProperty::New(true)); newInputNode->SetBoolProperty("pickable", true); mitk::ColorProperty::Pointer initialColor = dynamic_cast<mitk::ColorProperty *>(newInputNode->GetProperty(deselectedColorPropertyName)); if (initialColor.IsNotNull()) { newInputNode->SetColor(initialColor->GetColor()); } mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void mitk::BoundingShapeInteractor::HandlePositionChanged(const InteractionEvent *interactionEvent, Point3D ¢er) { GeometryData::Pointer geometryData = dynamic_cast<GeometryData *>(this->GetDataNode()->GetData()); int timeStep = interactionEvent->GetSender()->GetTimeStep(this->GetDataNode()->GetData()); mitk::BaseGeometry::Pointer geometry = geometryData->GetGeometry(timeStep); std::vector<Point3D> cornerPoints = GetCornerPoints(geometry, true); if (m_Impl->Handles.size() == 6) { // set handle positions Point3D pointLeft = CalcAvgPoint(cornerPoints[5], cornerPoints[6]); Point3D pointRight = CalcAvgPoint(cornerPoints[1], cornerPoints[2]); Point3D pointTop = CalcAvgPoint(cornerPoints[0], cornerPoints[6]); Point3D pointBottom = CalcAvgPoint(cornerPoints[7], cornerPoints[1]); Point3D pointFront = CalcAvgPoint(cornerPoints[2], cornerPoints[7]); Point3D pointBack = CalcAvgPoint(cornerPoints[4], cornerPoints[1]); m_Impl->Handles[0].SetPosition(pointLeft); m_Impl->Handles[1].SetPosition(pointRight); m_Impl->Handles[2].SetPosition(pointTop); m_Impl->Handles[3].SetPosition(pointBottom); m_Impl->Handles[4].SetPosition(pointFront); m_Impl->Handles[5].SetPosition(pointBack); // calculate center based on half way of the distance between two opposing cornerpoints center = CalcAvgPoint(cornerPoints[7], cornerPoints[0]); } } void mitk::BoundingShapeInteractor::SetDataNode(DataNode *node) { this->RestoreNodeProperties(); // if there is another node set, restore it's color if (node == nullptr) return; DataInteractor::SetDataNode(node); // calls DataNodeChanged internally this->DataNodeChanged(); } bool mitk::BoundingShapeInteractor::CheckOverObject(const InteractionEvent *interactionEvent) { const auto *positionEvent = dynamic_cast<const InteractionPositionEvent *>(interactionEvent); if (positionEvent == nullptr) return false; GeometryData::Pointer geometryData = dynamic_cast<GeometryData *>(this->GetDataNode()->GetData()); int timeStep = interactionEvent->GetSender()->GetTimeStep(this->GetDataNode()->GetData()); BaseGeometry::Pointer geometry = geometryData->GetGeometry(timeStep); // calculates translation based on offset+extent not on the transformation matrix (because the cube is located in the // center not in the origin) vtkSmartPointer<vtkMatrix4x4> imageTransform = geometry->GetVtkTransform()->GetMatrix(); Point3D center = geometry->GetCenter(); auto translation = vtkSmartPointer<vtkTransform>::New(); auto transform = vtkSmartPointer<vtkTransform>::New(); translation->Translate(center[0] - imageTransform->GetElement(0, 3), center[1] - imageTransform->GetElement(1, 3), center[2] - imageTransform->GetElement(2, 3)); transform->SetMatrix(imageTransform); transform->PostMultiply(); transform->Concatenate(translation); transform->Update(); mitk::Vector3D extent; for (unsigned int i = 0; i < 3; ++i) extent[i] = (geometry->GetExtent(i)); Point3D currentWorldPosition; Point2D currentDisplayPosition = positionEvent->GetPointerPositionOnScreen(); interactionEvent->GetSender()->DisplayToWorld(currentDisplayPosition, currentWorldPosition); ScalarType transformedPosition[4]; transformedPosition[0] = currentWorldPosition[0]; transformedPosition[1] = currentWorldPosition[1]; transformedPosition[2] = currentWorldPosition[2]; transformedPosition[3] = 1; // transform point from world to object coordinates transform->GetInverse()->TransformPoint(transformedPosition, transformedPosition); // check if the world point is within bounds bool isInside = (transformedPosition[0] >= (-extent[0] / 2.0)) && (transformedPosition[0] <= (extent[0] / 2.0)) && (transformedPosition[1] >= (-extent[1] / 2.0)) && (transformedPosition[1] <= (extent[1] / 2.0)) && (transformedPosition[2] >= (-extent[2] / 2.0)) && (transformedPosition[2] <= (extent[2] / 2.0)); return isInside; } bool mitk::BoundingShapeInteractor::CheckOverHandles(const InteractionEvent *interactionEvent) { Point3D boundingBoxCenter; HandlePositionChanged(interactionEvent, boundingBoxCenter); const auto *positionEvent = dynamic_cast<const InteractionPositionEvent *>(interactionEvent); if (positionEvent == nullptr) return false; Point2D displayCenterPoint; // to do: change to actual time step (currently not necessary because geometry remains the same for each timestep int timeStep = 0; GeometryData::Pointer geometryData = dynamic_cast<GeometryData *>(this->GetDataNode()->GetData()); BaseGeometry::Pointer geometry = geometryData->GetUpdatedTimeGeometry()->GetGeometryForTimeStep(timeStep); std::vector<Point3D> cornerPoints = GetCornerPoints(geometry, true); interactionEvent->GetSender()->WorldToDisplay(boundingBoxCenter, displayCenterPoint); double scale = interactionEvent->GetSender()->GetScaleFactorMMPerDisplayUnit(); // GetDisplaySizeInMM mitk::DoubleProperty::Pointer handleSizeProperty = dynamic_cast<mitk::DoubleProperty *>(this->GetDataNode()->GetProperty("Bounding Shape.Handle Size Factor")); ScalarType initialHandleSize; if (handleSizeProperty != nullptr) initialHandleSize = handleSizeProperty->GetValue(); else initialHandleSize = 1.0 / 40.0; mitk::Point2D displaysize = interactionEvent->GetSender()->GetDisplaySizeInMM(); ScalarType handlesize = ((displaysize[0] + displaysize[1]) / 2.0) * initialHandleSize; unsigned int handleNum = 0; for (auto &handle : m_Impl->Handles) { Point2D centerpoint; interactionEvent->GetSender()->WorldToDisplay(handle.GetPosition(), centerpoint); Point2D currentDisplayPosition = positionEvent->GetPointerPositionOnScreen(); if ((currentDisplayPosition.EuclideanDistanceTo(centerpoint) < (handlesize / scale)) && (currentDisplayPosition.EuclideanDistanceTo(displayCenterPoint) > (handlesize / scale))) // check if mouse is hovering over center point { handle.SetActive(true); m_Impl->ActiveHandle = handle; this->GetDataNode()->GetPropertyList()->SetProperty(activeHandleIdPropertyName, mitk::IntProperty::New(handleNum++)); this->GetDataNode()->GetData()->Modified(); RenderingManager::GetInstance()->RequestUpdateAll(); return true; } else { handleNum++; handle.SetActive(false); } this->GetDataNode()->GetPropertyList()->SetProperty(activeHandleIdPropertyName, mitk::IntProperty::New(-1)); } return false; } void mitk::BoundingShapeInteractor::SelectHandle(StateMachineAction *, InteractionEvent *) { this->DisableOriginalInteraction(); DataNode::Pointer node = this->GetDataNode(); if (node.IsNull()) return; mitk::ColorProperty::Pointer selectedColor = dynamic_cast<mitk::ColorProperty *>(node->GetProperty(deselectedColorPropertyName)); if (selectedColor.IsNotNull()) { this->GetDataNode()->GetPropertyList()->SetProperty("color", selectedColor); } this->GetDataNode()->GetData()->UpdateOutputInformation(); // Geometry is up-to-date this->GetDataNode()->GetData()->Modified(); RenderingManager::GetInstance()->RequestUpdateAll(); return; } void mitk::BoundingShapeInteractor::DeselectHandles(StateMachineAction *, InteractionEvent *) { this->DisableOriginalInteraction(); DataNode::Pointer node = this->GetDataNode(); if (node.IsNull()) return; this->GetDataNode()->GetPropertyList()->SetProperty(activeHandleIdPropertyName, mitk::IntProperty::New(-1)); this->GetDataNode()->GetData()->UpdateOutputInformation(); // Geometry is up-to-date this->GetDataNode()->GetData()->Modified(); RenderingManager::GetInstance()->RequestUpdateAll(); return; } void mitk::BoundingShapeInteractor::SelectObject(StateMachineAction *, InteractionEvent *) { this->DisableOriginalInteraction(); // disable crosshair interaction and scolling if user is hovering over the object DataNode::Pointer node = this->GetDataNode(); if (node.IsNull()) return; mitk::ColorProperty::Pointer selectedColor = dynamic_cast<mitk::ColorProperty *>(node->GetProperty(selectedColorPropertyName)); if (selectedColor.IsNotNull()) { node->GetPropertyList()->SetProperty("color", selectedColor); } this->GetDataNode()->GetData()->UpdateOutputInformation(); // Geometry is up-to-date this->GetDataNode()->GetData()->Modified(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); return; } void mitk::BoundingShapeInteractor::DeselectObject(StateMachineAction *, InteractionEvent *) { this->EnableOriginalInteraction(); // enable crosshair interaction and scolling if user is hovering over the object DataNode::Pointer node = this->GetDataNode(); if (node.IsNull()) return; mitk::ColorProperty::Pointer deselectedColor = dynamic_cast<mitk::ColorProperty *>(node->GetProperty(deselectedColorPropertyName)); if (deselectedColor.IsNotNull()) { node->GetPropertyList()->SetProperty("color", deselectedColor); } this->GetDataNode()->GetData()->UpdateOutputInformation(); // Geometry is up-to-date this->GetDataNode()->GetData()->Modified(); RenderingManager::GetInstance()->RequestUpdateAll(); return; } void mitk::BoundingShapeInteractor::InitInteraction(StateMachineAction *, InteractionEvent *interactionEvent) { InitMembers(interactionEvent); } bool mitk::BoundingShapeInteractor::InitMembers(InteractionEvent *interactionEvent) { auto *positionEvent = dynamic_cast<InteractionPositionEvent *>(interactionEvent); if (positionEvent == nullptr) return false; // get initial position coordinates m_Impl->InitialPickedDisplayPoint = positionEvent->GetPointerPositionOnScreen(); m_Impl->InitialPickedWorldPoint = positionEvent->GetPositionInWorld(); m_Impl->LastPickedWorldPoint = positionEvent->GetPositionInWorld(); return true; } void mitk::BoundingShapeInteractor::TranslateObject(StateMachineAction *, InteractionEvent *interactionEvent) { auto *positionEvent = dynamic_cast<InteractionPositionEvent *>(interactionEvent); if (positionEvent == nullptr) return; int timeStep = interactionEvent->GetSender()->GetTimeStep(this->GetDataNode()->GetData()); mitk::BaseGeometry::Pointer geometry = this->GetDataNode()->GetData()->GetUpdatedTimeGeometry()->GetGeometryForTimeStep(timeStep); Vector3D spacing = geometry->GetSpacing(); Point3D currentPickedPoint; interactionEvent->GetSender()->DisplayToWorld(positionEvent->GetPointerPositionOnScreen(), currentPickedPoint); Vector3D interactionMove; // pixel aligned shifting of the bounding box interactionMove[0] = std::round((currentPickedPoint[0] - m_Impl->LastPickedWorldPoint[0]) / spacing[0]) * spacing[0]; interactionMove[1] = std::round((currentPickedPoint[1] - m_Impl->LastPickedWorldPoint[1]) / spacing[1]) * spacing[1]; interactionMove[2] = std::round((currentPickedPoint[2] - m_Impl->LastPickedWorldPoint[2]) / spacing[2]) * spacing[2]; if ((interactionMove[0] + interactionMove[1] + interactionMove[2]) != 0.0) // only update current position if a movement occured { m_Impl->LastPickedWorldPoint = currentPickedPoint; geometry->SetOrigin(geometry->GetOrigin() + interactionMove); this->GetDataNode()->GetData()->UpdateOutputInformation(); // Geometry is up-to-date this->GetDataNode()->GetData()->Modified(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } return; } void mitk::BoundingShapeInteractor::ScaleObject(StateMachineAction *, InteractionEvent *interactionEvent) { auto *positionEvent = dynamic_cast<InteractionPositionEvent *>(interactionEvent); if (positionEvent == nullptr) return; GeometryData::Pointer geometryData = dynamic_cast<GeometryData *>(this->GetDataNode()->GetData()); Point3D handlePickedPoint = m_Impl->ActiveHandle.GetPosition(); Point3D currentPickedPoint; interactionEvent->GetSender()->DisplayToWorld(positionEvent->GetPointerPositionOnScreen(), currentPickedPoint); int timeStep = interactionEvent->GetSender()->GetTimeStep(this->GetDataNode()->GetData()); mitk::BaseGeometry::Pointer geometry = geometryData->GetGeometry(timeStep); Vector3D spacing = geometry->GetSpacing(); // pixel aligned bounding box Vector3D interactionMove; interactionMove[0] = (currentPickedPoint[0] - m_Impl->LastPickedWorldPoint[0]); interactionMove[1] = (currentPickedPoint[1] - m_Impl->LastPickedWorldPoint[1]); interactionMove[2] = (currentPickedPoint[2] - m_Impl->LastPickedWorldPoint[2]); std::vector<int> faces = m_Impl->ActiveHandle.GetFaceIndices(); auto pointscontainer = mitk::BoundingBox::PointsContainer::New(); // calculate cornerpoints from geometry plus visualization offset std::vector<Point3D> cornerPoints = GetCornerPoints(geometry, true); unsigned int num = 0; for (const auto &point : cornerPoints) { pointscontainer->InsertElement(num++, point); } // calculate center based on half way of the distance between two opposing cornerpoints mitk::Point3D center = CalcAvgPoint(cornerPoints[7], cornerPoints[0]); Vector3D faceNormal; faceNormal[0] = handlePickedPoint[0] - center[0]; faceNormal[1] = handlePickedPoint[1] - center[1]; faceNormal[2] = handlePickedPoint[2] - center[2]; Vector3D faceShift = ((faceNormal * interactionMove) / (faceNormal.GetNorm() * faceNormal.GetNorm())) * faceNormal; // calculate cornerpoints from geometry without visualization offset to update actual geometry cornerPoints = GetCornerPoints(geometry, false); num = 0; for (const auto &point : cornerPoints) { pointscontainer->InsertElement(num++, point); } bool positionChangeThreshold = true; for (int numFaces = 0; numFaces < 8; numFaces++) // estimate the corresponding face and shift its assigned points { if ((numFaces != faces[0]) && (numFaces != faces[1]) && (numFaces != faces[2]) && (numFaces != faces[3])) { Point3D point = pointscontainer->GetElement(numFaces); if (m_Impl->RotationEnabled) // apply if geometry is rotated and a pixel aligned shift is not possible { point[0] += faceShift[0]; point[1] += faceShift[1]; point[2] += faceShift[2]; } else // shift pixelwise { point[0] += std::round(faceShift[0] / spacing[0]) * spacing[0]; point[1] += std::round(faceShift[1] / spacing[1]) * spacing[1]; point[2] += std::round(faceShift[2] / spacing[2]) * spacing[2]; } if (point == pointscontainer->GetElement(numFaces)) positionChangeThreshold = false; else m_Impl->LastPickedWorldPoint = point; pointscontainer->InsertElement(numFaces, point); } } if (positionChangeThreshold) // update only if bounding box is shifted at least by one pixel { auto inverse = mitk::AffineTransform3D::New(); geometry->GetIndexToWorldTransform()->GetInverse(inverse); for (unsigned int pointid = 0; pointid < 8; pointid++) { pointscontainer->InsertElement(pointid, inverse->TransformPoint(pointscontainer->GetElement(pointid))); } auto bbox = mitk::BoundingBox::New(); bbox->SetPoints(pointscontainer); bbox->ComputeBoundingBox(); mitk::Point3D BBmin = bbox->GetMinimum(); mitk::Point3D BBmax = bbox->GetMaximum(); if (std::abs(BBmin[0] - BBmax[0]) > 0.01 && std::abs(BBmin[1] - BBmax[1]) > 0.01 && std::abs(BBmin[2] - BBmax[2]) > 0.01) // TODO: check if the extent is greater than zero { geometry->SetBounds(bbox->GetBounds()); geometry->Modified(); this->GetDataNode()->GetData()->UpdateOutputInformation(); // Geometry is up-to-date this->GetDataNode()->GetData()->Modified(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } return; } void mitk::BoundingShapeInteractor::RestoreNodeProperties() { mitk::DataNode::Pointer inputNode = this->GetDataNode(); if (inputNode.IsNull()) return; mitk::ColorProperty::Pointer color = (mitk::ColorProperty::New(1.0, 1.0, 1.0)); if (color.IsNotNull()) { inputNode->GetPropertyList()->SetProperty("color", color); } inputNode->SetProperty("layer", mitk::IntProperty::New(99)); inputNode->SetProperty(boundingShapePropertyName, mitk::BoolProperty::New(false)); inputNode->GetPropertyList()->DeleteProperty(activeHandleIdPropertyName); EnableOriginalInteraction(); // update rendering mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void mitk::BoundingShapeInteractor::EnableOriginalInteraction() { // Re-enabling InteractionEventObservers that have been previously disabled for legacy handling of Tools // in new interaction framework for (const auto& displayInteractionConfig : m_Impl->DisplayInteractionConfigs) { if (displayInteractionConfig.first) { auto displayActionEventBroadcast = static_cast<mitk::DisplayActionEventBroadcast *>( us::GetModuleContext()->GetService<mitk::InteractionEventObserver>(displayInteractionConfig.first)); if (nullptr != displayActionEventBroadcast) { // here the regular configuration is loaded again displayActionEventBroadcast->SetEventConfig(displayInteractionConfig.second); } } } m_Impl->DisplayInteractionConfigs.clear(); m_Impl->OriginalInteractionEnabled = true; } void mitk::BoundingShapeInteractor::DisableOriginalInteraction() { // dont deactivate twice, else we will clutter the config list ... if (false == m_Impl->OriginalInteractionEnabled) return; // As a legacy solution the display interaction of the new interaction framework is disabled here to avoid conflicts // with tools // Note: this only affects InteractionEventObservers (formerly known as Listeners) all DataNode specific interaction // will still be enabled m_Impl->DisplayInteractionConfigs.clear(); auto eventObservers = us::GetModuleContext()->GetServiceReferences<mitk::InteractionEventObserver>(); for (const auto& eventObserver : eventObservers) { auto *displayActionEventBroadcast = dynamic_cast<mitk::DisplayActionEventBroadcast *>( us::GetModuleContext()->GetService<mitk::InteractionEventObserver>(eventObserver)); if (nullptr != displayActionEventBroadcast) { // remember the original configuration m_Impl->DisplayInteractionConfigs.insert(std::make_pair(eventObserver, displayActionEventBroadcast->GetEventConfig())); // here the alternative configuration is loaded displayActionEventBroadcast->AddEventConfig("DisplayConfigBlockLMB.xml"); } } m_Impl->OriginalInteractionEnabled = false; } diff --git a/Modules/BoundingShape/src/Rendering/mitkBoundingShapeVtkMapper2D.cpp b/Modules/BoundingShape/src/Rendering/mitkBoundingShapeVtkMapper2D.cpp index f2a3cf330c..5114a48201 100644 --- a/Modules/BoundingShape/src/Rendering/mitkBoundingShapeVtkMapper2D.cpp +++ b/Modules/BoundingShape/src/Rendering/mitkBoundingShapeVtkMapper2D.cpp @@ -1,457 +1,462 @@ /*============================================================================ 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 "../DataManagement/mitkBoundingShapeUtil.h" #include <mitkBaseProperty.h> #include <mitkBoundingShapeVtkMapper2D.h> #include <vtkActor2D.h> #include <vtkAppendPolyData.h> #include <vtkCoordinate.h> #include <vtkMath.h> #include <vtkPointData.h> #include <vtkPolyData.h> #include <vtkPolyDataMapper2D.h> #include <vtkProperty2D.h> #include <vtkStripper.h> #include <vtkTransformFilter.h> #include <vtkTransformPolyDataFilter.h> namespace mitk { class BoundingShapeVtkMapper2D::Impl { public: Impl() { Point3D initialPoint; initialPoint.Fill(0); for (int i = 0; i < 6; ++i) HandlePropertyList.push_back(Handle(initialPoint, i, GetHandleIndices(i))); } std::vector<Handle> HandlePropertyList; mitk::LocalStorageHandler<LocalStorage> LocalStorageHandler; }; } mitk::BoundingShapeVtkMapper2D::LocalStorage::LocalStorage() : m_Actor(vtkSmartPointer<vtkActor>::New()), m_HandleActor(vtkSmartPointer<vtkActor2D>::New()), m_SelectedHandleActor(vtkSmartPointer<vtkActor2D>::New()), m_Mapper(vtkSmartPointer<vtkPolyDataMapper>::New()), m_HandleMapper(vtkSmartPointer<vtkPolyDataMapper2D>::New()), m_SelectedHandleMapper(vtkSmartPointer<vtkPolyDataMapper2D>::New()), m_Cutter(vtkSmartPointer<vtkCutter>::New()), m_CuttingPlane(vtkSmartPointer<vtkPlane>::New()), m_LastSliceNumber(0), m_PropAssembly(vtkSmartPointer<vtkPropAssembly>::New()), m_ZoomFactor(1.0) { m_Actor->SetMapper(m_Mapper); - m_Actor->GetProperty()->SetOpacity(0.3); m_Actor->VisibilityOn(); m_HandleActor->SetMapper(m_HandleMapper); m_HandleActor->VisibilityOn(); m_SelectedHandleActor->VisibilityOn(); m_SelectedHandleActor->GetProperty()->SetColor(0, 1.0, 0); m_SelectedHandleActor->SetMapper(m_SelectedHandleMapper); vtkCoordinate *tcoord = vtkCoordinate::New(); tcoord->SetCoordinateSystemToWorld(); m_SelectedHandleMapper->SetTransformCoordinate(tcoord); tcoord->Delete(); m_Cutter->SetCutFunction(m_CuttingPlane); for (int i = 0; i < 6; ++i) m_Handles.push_back(vtkSmartPointer<vtkCubeSource>::New()); m_PropAssembly->AddPart(m_Actor); m_PropAssembly->AddPart(m_HandleActor); m_PropAssembly->VisibilityOn(); } bool mitk::BoundingShapeVtkMapper2D::LocalStorage::IsUpdateRequired(mitk::BaseRenderer *renderer, mitk::Mapper *mapper, mitk::DataNode *dataNode) { const mitk::PlaneGeometry *worldGeometry = renderer->GetCurrentWorldPlaneGeometry(); if (m_LastGenerateDataTime < worldGeometry->GetMTime()) return true; unsigned int sliceNumber = renderer->GetSlice(); if (m_LastSliceNumber != sliceNumber) return true; if (mapper && m_LastGenerateDataTime < mapper->GetMTime()) return true; if (dataNode) { if (m_LastGenerateDataTime < dataNode->GetMTime()) return true; mitk::BaseData *data = dataNode->GetData(); if (data && m_LastGenerateDataTime < data->GetMTime()) return true; } return false; } mitk::BoundingShapeVtkMapper2D::LocalStorage::~LocalStorage() { } void mitk::BoundingShapeVtkMapper2D::Update(mitk::BaseRenderer *renderer) { this->GenerateDataForRenderer(renderer); } void mitk::BoundingShapeVtkMapper2D::SetDefaultProperties(DataNode *node, BaseRenderer *renderer, bool overwrite) { Superclass::SetDefaultProperties(node, renderer, overwrite); + node->AddProperty("opacity", FloatProperty::New(0.2f), renderer, overwrite); } mitk::BoundingShapeVtkMapper2D::BoundingShapeVtkMapper2D() : m_Impl(new Impl) { } mitk::BoundingShapeVtkMapper2D::~BoundingShapeVtkMapper2D() { delete m_Impl; } void mitk::BoundingShapeVtkMapper2D::GenerateDataForRenderer(BaseRenderer *renderer) { const DataNode::Pointer node = GetDataNode(); if (node == nullptr) return; LocalStorage *localStorage = m_Impl->LocalStorageHandler.GetLocalStorage(renderer); // either update if GeometryData was modified or if the zooming was performed bool needGenerateData = localStorage->IsUpdateRequired( renderer, this, GetDataNode()); // true; // localStorage->GetLastGenerateDataTime() < node->GetMTime() || // localStorage->GetLastGenerateDataTime() < node->GetData()->GetMTime(); // //localStorage->IsGenerateDataRequired(renderer, this, GetDataNode()); double scale = renderer->GetScaleFactorMMPerDisplayUnit(); if (std::abs(scale - localStorage->m_ZoomFactor) > 0.001) { localStorage->m_ZoomFactor = scale; needGenerateData = true; } if (needGenerateData) { bool visible = true; GetDataNode()->GetVisibility(visible, renderer, "visible"); if (!visible) { localStorage->m_Actor->VisibilityOff(); return; } GeometryData::Pointer shape = static_cast<GeometryData *>(node->GetData()); if (shape == nullptr) return; mitk::BaseGeometry::Pointer geometry = shape->GetGeometry(); mitk::Vector3D spacing = geometry->GetSpacing(); // calculate cornerpoints and extent from geometry with visualization offset std::vector<Point3D> cornerPoints = GetCornerPoints(geometry, true); Point3D p0 = cornerPoints[0]; Point3D p1 = cornerPoints[1]; Point3D p2 = cornerPoints[2]; Point3D p4 = cornerPoints[4]; Point3D extent; extent[0] = sqrt((p0[0] - p4[0]) * (p0[0] - p4[0]) + (p0[1] - p4[1]) * (p0[1] - p4[1]) + (p0[2] - p4[2]) * (p0[2] - p4[2])); extent[1] = sqrt((p0[0] - p2[0]) * (p0[0] - p2[0]) + (p0[1] - p2[1]) * (p0[1] - p2[1]) + (p0[2] - p2[2]) * (p0[2] - p2[2])); extent[2] = sqrt((p0[0] - p1[0]) * (p0[0] - p1[0]) + (p0[1] - p1[1]) * (p0[1] - p1[1]) + (p0[2] - p1[2]) * (p0[2] - p1[2])); // calculate center based on half way of the distance between two opposing cornerpoints mitk::Point3D center = CalcAvgPoint(cornerPoints[7], cornerPoints[0]); if (m_Impl->HandlePropertyList.size() == 6) { // set handle positions Point3D pointLeft = CalcAvgPoint(cornerPoints[5], cornerPoints[6]); Point3D pointRight = CalcAvgPoint(cornerPoints[1], cornerPoints[2]); Point3D pointTop = CalcAvgPoint(cornerPoints[0], cornerPoints[6]); Point3D pointBottom = CalcAvgPoint(cornerPoints[7], cornerPoints[1]); Point3D pointFront = CalcAvgPoint(cornerPoints[2], cornerPoints[7]); Point3D pointBack = CalcAvgPoint(cornerPoints[4], cornerPoints[1]); m_Impl->HandlePropertyList[0].SetPosition(pointLeft); m_Impl->HandlePropertyList[1].SetPosition(pointRight); m_Impl->HandlePropertyList[2].SetPosition(pointTop); m_Impl->HandlePropertyList[3].SetPosition(pointBottom); m_Impl->HandlePropertyList[4].SetPosition(pointFront); m_Impl->HandlePropertyList[5].SetPosition(pointBack); } // caculate face normals double cubeFaceNormal0[3], cubeFaceNormal1[3], cubeFaceNormal2[3]; double a[3], b[3]; a[0] = (cornerPoints[5][0] - cornerPoints[6][0]); a[1] = (cornerPoints[5][1] - cornerPoints[6][1]); a[2] = (cornerPoints[5][2] - cornerPoints[6][2]); b[0] = (cornerPoints[5][0] - cornerPoints[4][0]); b[1] = (cornerPoints[5][1] - cornerPoints[4][1]); b[2] = (cornerPoints[5][2] - cornerPoints[4][2]); vtkMath::Cross(a, b, cubeFaceNormal0); a[0] = (cornerPoints[0][0] - cornerPoints[6][0]); a[1] = (cornerPoints[0][1] - cornerPoints[6][1]); a[2] = (cornerPoints[0][2] - cornerPoints[6][2]); b[0] = (cornerPoints[0][0] - cornerPoints[2][0]); b[1] = (cornerPoints[0][1] - cornerPoints[2][1]); b[2] = (cornerPoints[0][2] - cornerPoints[2][2]); vtkMath::Cross(a, b, cubeFaceNormal1); a[0] = (cornerPoints[2][0] - cornerPoints[7][0]); a[1] = (cornerPoints[2][1] - cornerPoints[7][1]); a[2] = (cornerPoints[2][2] - cornerPoints[7][2]); b[0] = (cornerPoints[2][0] - cornerPoints[6][0]); b[1] = (cornerPoints[2][1] - cornerPoints[6][1]); b[2] = (cornerPoints[2][2] - cornerPoints[6][2]); vtkMath::Cross(a, b, cubeFaceNormal2); vtkMath::Normalize(cubeFaceNormal0); vtkMath::Normalize(cubeFaceNormal1); vtkMath::Normalize(cubeFaceNormal2); // create cube for rendering bounding box auto cube = vtkCubeSource::New(); cube->SetXLength(extent[0] / spacing[0]); cube->SetYLength(extent[1] / spacing[1]); cube->SetZLength(extent[2] / spacing[2]); // calculates translation based on offset+extent not on the transformation matrix vtkSmartPointer<vtkMatrix4x4> imageTransform = geometry->GetVtkTransform()->GetMatrix(); auto translation = vtkSmartPointer<vtkTransform>::New(); translation->Translate(center[0] - imageTransform->GetElement(0, 3), center[1] - imageTransform->GetElement(1, 3), center[2] - imageTransform->GetElement(2, 3)); auto transform = vtkSmartPointer<vtkTransform>::New(); transform->SetMatrix(imageTransform); transform->PostMultiply(); transform->Concatenate(translation); transform->Update(); cube->Update(); auto transformFilter = vtkSmartPointer<vtkTransformFilter>::New(); transformFilter->SetInputData(cube->GetOutput()); transformFilter->SetTransform(transform); transformFilter->Update(); cube->Delete(); vtkSmartPointer<vtkPolyData> polydata = transformFilter->GetPolyDataOutput(); if (polydata == nullptr || (polydata->GetNumberOfPoints() < 1)) { localStorage->m_Actor->VisibilityOff(); localStorage->m_HandleActor->VisibilityOff(); localStorage->m_SelectedHandleActor->VisibilityOff(); return; } // estimate current image plane to decide whether the cube is visible or not const PlaneGeometry *planeGeometry = renderer->GetCurrentWorldPlaneGeometry(); if ((planeGeometry == nullptr) || (!planeGeometry->IsValid()) || (!planeGeometry->HasReferenceGeometry())) return; double origin[3]; origin[0] = planeGeometry->GetOrigin()[0]; origin[1] = planeGeometry->GetOrigin()[1]; origin[2] = planeGeometry->GetOrigin()[2]; double displayPlaneNormal[3]; displayPlaneNormal[0] = planeGeometry->GetNormal()[0]; displayPlaneNormal[1] = planeGeometry->GetNormal()[1]; displayPlaneNormal[2] = planeGeometry->GetNormal()[2]; vtkMath::Normalize(displayPlaneNormal); localStorage->m_CuttingPlane->SetOrigin(origin); localStorage->m_CuttingPlane->SetNormal(displayPlaneNormal); // add cube polydata to local storage localStorage->m_Cutter->SetInputData(polydata); localStorage->m_Cutter->SetGenerateCutScalars(1); localStorage->m_Cutter->Update(); if (localStorage->m_PropAssembly->GetParts()->IsItemPresent(localStorage->m_HandleActor)) localStorage->m_PropAssembly->RemovePart(localStorage->m_HandleActor); if (localStorage->m_PropAssembly->GetParts()->IsItemPresent(localStorage->m_Actor)) localStorage->m_PropAssembly->RemovePart(localStorage->m_Actor); vtkCoordinate *tcoord = vtkCoordinate::New(); tcoord->SetCoordinateSystemToWorld(); localStorage->m_HandleMapper->SetTransformCoordinate(tcoord); tcoord->Delete(); if (localStorage->m_Cutter->GetOutput()->GetNumberOfPoints() > 0) // if plane is visible in the renderwindow { mitk::DoubleProperty::Pointer handleSizeProperty = dynamic_cast<mitk::DoubleProperty *>(this->GetDataNode()->GetProperty("Bounding Shape.Handle Size Factor")); ScalarType initialHandleSize; if (handleSizeProperty != nullptr) initialHandleSize = handleSizeProperty->GetValue(); else initialHandleSize = 0.02; mitk::Point2D displaySize = renderer->GetDisplaySizeInMM(); double handleSize = ((displaySize[0] + displaySize[1]) / 2.0) * initialHandleSize; auto appendPoly = vtkSmartPointer<vtkAppendPolyData>::New(); unsigned int handleIdx = 0; // add handles and their assigned properties to the local storage mitk::IntProperty::Pointer activeHandleId = dynamic_cast<mitk::IntProperty *>(node->GetProperty("Bounding Shape.Active Handle ID")); double angle0 = std::abs(vtkMath::DegreesFromRadians(vtkMath::AngleBetweenVectors(displayPlaneNormal, cubeFaceNormal0))); if (angle0 > 179.0) angle0 -= 180.0; double angle1 = std::abs(vtkMath::DegreesFromRadians(vtkMath::AngleBetweenVectors(displayPlaneNormal, cubeFaceNormal1))); if (angle1 > 179.0) angle1 -= 180.0; double angle2 = std::abs(vtkMath::DegreesFromRadians(vtkMath::AngleBetweenVectors(displayPlaneNormal, cubeFaceNormal2))); if (angle2 > 179.0) angle2 -= 180.0; bool visible = false; bool selected = false; for (auto& handle : localStorage->m_Handles) { Point3D handleCenter = m_Impl->HandlePropertyList[handleIdx].GetPosition(); handle->SetXLength(handleSize); handle->SetYLength(handleSize); handle->SetZLength(handleSize); handle->SetCenter(handleCenter[0], handleCenter[1], handleCenter[2]); // show handles only if the corresponding face is aligned to the render window if ( (handleIdx != 0 && handleIdx != 1 && std::abs(angle0) < 0.1) || // handles 0 and 1 (handleIdx != 2 && handleIdx != 3 && std::abs(angle1) < 0.1) || // handles 2 and 3 (handleIdx != 4 && handleIdx != 5 && std::abs(angle2) < 0.1) ) // handles 4 and 5 { if (activeHandleId == nullptr) { appendPoly->AddInputConnection(handle->GetOutputPort()); } else { if ((activeHandleId->GetValue() != m_Impl->HandlePropertyList[handleIdx].GetIndex())) { appendPoly->AddInputConnection(handle->GetOutputPort()); } else { handle->Update(); localStorage->m_SelectedHandleMapper->SetInputData(handle->GetOutput()); localStorage->m_SelectedHandleActor->VisibilityOn(); selected = true; } } visible = true; } ++handleIdx; } if (visible) { appendPoly->Update(); } else { localStorage->m_HandleActor->VisibilityOff(); localStorage->m_SelectedHandleActor->VisibilityOff(); } auto stripper = vtkSmartPointer<vtkStripper>::New(); stripper->SetInputData(localStorage->m_Cutter->GetOutput()); stripper->Update(); auto cutPolyData = vtkSmartPointer<vtkPolyData>::New(); cutPolyData->SetPoints(stripper->GetOutput()->GetPoints()); cutPolyData->SetPolys(stripper->GetOutput()->GetLines()); localStorage->m_Actor->GetMapper()->SetInputDataObject(cutPolyData); - mitk::ColorProperty::Pointer selectedColor = dynamic_cast<mitk::ColorProperty *>(node->GetProperty("color")); - if (selectedColor != nullptr) - { - mitk::Color color = selectedColor->GetColor(); - localStorage->m_Actor->GetProperty()->SetColor(color[0], color[1], color[2]); - } + + this->ApplyColorAndOpacityProperties(renderer, localStorage->m_Actor); if (activeHandleId != nullptr) { localStorage->m_HandleActor->GetProperty()->SetColor(1, 0, 0); } else { localStorage->m_HandleActor->GetProperty()->SetColor(1, 1, 1); } localStorage->m_HandleActor->GetMapper()->SetInputDataObject(appendPoly->GetOutput()); // add parts to the overall storage localStorage->m_PropAssembly->AddPart(localStorage->m_Actor); localStorage->m_PropAssembly->AddPart(localStorage->m_HandleActor); if (selected) { localStorage->m_PropAssembly->AddPart(localStorage->m_SelectedHandleActor); } localStorage->m_PropAssembly->VisibilityOn(); localStorage->m_Actor->VisibilityOn(); localStorage->m_HandleActor->VisibilityOn(); } else { localStorage->m_PropAssembly->VisibilityOff(); localStorage->m_Actor->VisibilityOff(); localStorage->m_HandleActor->VisibilityOff(); localStorage->m_SelectedHandleActor->VisibilityOff(); localStorage->UpdateGenerateDataTime(); } localStorage->UpdateGenerateDataTime(); } } vtkProp *mitk::BoundingShapeVtkMapper2D::GetVtkProp(BaseRenderer *renderer) { return m_Impl->LocalStorageHandler.GetLocalStorage(renderer)->m_PropAssembly; } -void mitk::BoundingShapeVtkMapper2D::ApplyColorAndOpacityProperties(BaseRenderer *, vtkActor *) +void mitk::BoundingShapeVtkMapper2D::ApplyColorAndOpacityProperties(BaseRenderer *renderer, vtkActor *actor) { + auto* property = actor->GetProperty(); + + std::array<float, 3> color = { 1.0, 0.0, 0.0 }; + this->GetDataNode()->GetColor(color.data(), renderer); + property->SetColor(color[0], color[1], color[2]); + + float opacity = 0.2f; + this->GetDataNode()->GetOpacity(opacity, renderer); + property->SetOpacity(opacity); } diff --git a/Modules/BoundingShape/src/Rendering/mitkBoundingShapeVtkMapper3D.cpp b/Modules/BoundingShape/src/Rendering/mitkBoundingShapeVtkMapper3D.cpp index ce0053e4e0..b07e626fa2 100644 --- a/Modules/BoundingShape/src/Rendering/mitkBoundingShapeVtkMapper3D.cpp +++ b/Modules/BoundingShape/src/Rendering/mitkBoundingShapeVtkMapper3D.cpp @@ -1,336 +1,333 @@ /*============================================================================ 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 "mitkBoundingShapeVtkMapper3D.h" #include "../DataManagement/mitkBoundingShapeUtil.h" #include <mitkBaseProperty.h> #include <vtkAppendPolyData.h> #include <vtkCamera.h> #include <vtkCubeSource.h> #include <vtkDataSetMapper.h> #include <vtkMath.h> #include <vtkPolyData.h> #include <vtkPolyDataMapper.h> #include <vtkTransformFilter.h> namespace mitk { class BoundingShapeVtkMapper3D::Impl { class LocalStorage : public Mapper::BaseLocalStorage { public: LocalStorage(); ~LocalStorage() override; LocalStorage(const LocalStorage &) = delete; LocalStorage &operator=(const LocalStorage &) = delete; std::vector<vtkSmartPointer<vtkCubeSource>> Handles; vtkSmartPointer<vtkActor> Actor; vtkSmartPointer<vtkActor> HandleActor; vtkSmartPointer<vtkActor> SelectedHandleActor; vtkSmartPointer<vtkPropAssembly> PropAssembly; }; public: Impl() : DistanceFromCam(1.0) { Point3D initialPoint; initialPoint.Fill(0); for (int i = 0; i < 6; ++i) HandlePropertyList.push_back(Handle(initialPoint, i, GetHandleIndices(i))); } double DistanceFromCam; std::vector<Handle> HandlePropertyList; mitk::LocalStorageHandler<LocalStorage> LocalStorageHandler; }; } mitk::BoundingShapeVtkMapper3D::Impl::LocalStorage::LocalStorage() : Actor(vtkSmartPointer<vtkActor>::New()), HandleActor(vtkSmartPointer<vtkActor>::New()), SelectedHandleActor(vtkSmartPointer<vtkActor>::New()), PropAssembly(vtkSmartPointer<vtkPropAssembly>::New()) { for (int i = 0; i < 6; i++) Handles.push_back(vtkSmartPointer<vtkCubeSource>::New()); } mitk::BoundingShapeVtkMapper3D::Impl::LocalStorage::~LocalStorage() { } void mitk::BoundingShapeVtkMapper3D::SetDefaultProperties(DataNode *node, BaseRenderer *renderer, bool overwrite) { Superclass::SetDefaultProperties(node, renderer, overwrite); + node->AddProperty("opacity", FloatProperty::New(0.2f), renderer, overwrite); } mitk::BoundingShapeVtkMapper3D::BoundingShapeVtkMapper3D() : m_Impl(new Impl) { } mitk::BoundingShapeVtkMapper3D::~BoundingShapeVtkMapper3D() { delete m_Impl; } -void mitk::BoundingShapeVtkMapper3D::ApplyColorAndOpacityProperties(BaseRenderer*, vtkActor*) +void mitk::BoundingShapeVtkMapper3D::ApplyColorAndOpacityProperties(BaseRenderer *renderer, vtkActor *actor) { - //Superclass::ApplyColorAndOpacityProperties(renderer, actor); + auto* property = actor->GetProperty(); + + std::array<float, 3> color = { 1.0, 0.0, 0.0 }; + this->GetDataNode()->GetColor(color.data(), renderer); + property->SetColor(color[0], color[1], color[2]); + + float opacity = 0.2f; + this->GetDataNode()->GetOpacity(opacity, renderer); + property->SetOpacity(opacity); } void mitk::BoundingShapeVtkMapper3D::ApplyBoundingShapeProperties(BaseRenderer *renderer, vtkActor *actor) { if (actor == nullptr) return; auto dataNode = this->GetDataNode(); if (dataNode == nullptr) return; bool isVisible = false; dataNode->GetBoolProperty("Bounding Shape.3D Rendering", isVisible, renderer); actor->SetVisibility(isVisible); float lineWidth = 1.0f; dataNode->GetFloatProperty("Bounding Shape.Line.Width", lineWidth, renderer); auto property = actor->GetProperty(); property->SetLineWidth(lineWidth); } void mitk::BoundingShapeVtkMapper3D::GenerateDataForRenderer(BaseRenderer *renderer) { auto dataNode = this->GetDataNode(); if (dataNode == nullptr) return; vtkCamera *camera = renderer->GetVtkRenderer()->GetActiveCamera(); auto localStorage = m_Impl->LocalStorageHandler.GetLocalStorage(renderer); bool needGenerateData = localStorage->GetLastGenerateDataTime() < dataNode->GetMTime(); double distance = camera->GetDistance(); if (std::abs(distance - m_Impl->DistanceFromCam) > mitk::eps) { m_Impl->DistanceFromCam = distance; needGenerateData = true; } if (needGenerateData) { bool isVisible = true; dataNode->GetVisibility(isVisible, renderer); if (!isVisible) { localStorage->Actor->VisibilityOff(); return; } // set the input-object at time t for the mapper auto *geometryData = dynamic_cast<GeometryData *>(dataNode->GetData()); if (geometryData == nullptr) return; mitk::BaseGeometry::Pointer geometry = geometryData->GetGeometry(); mitk::Vector3D spacing = geometry->GetSpacing(); // calculate cornerpoints from geometry std::vector<Point3D> cornerPoints = GetCornerPoints(geometry, true); Point3D p0 = cornerPoints[0]; Point3D p1 = cornerPoints[1]; Point3D p2 = cornerPoints[2]; Point3D p4 = cornerPoints[4]; Point3D extent; extent[0] = sqrt((p0[0] - p4[0]) * (p0[0] - p4[0]) + (p0[1] - p4[1]) * (p0[1] - p4[1]) + (p0[2] - p4[2]) * (p0[2] - p4[2])); extent[1] = sqrt((p0[0] - p2[0]) * (p0[0] - p2[0]) + (p0[1] - p2[1]) * (p0[1] - p2[1]) + (p0[2] - p2[2]) * (p0[2] - p2[2])); extent[2] = sqrt((p0[0] - p1[0]) * (p0[0] - p1[0]) + (p0[1] - p1[1]) * (p0[1] - p1[1]) + (p0[2] - p1[2]) * (p0[2] - p1[2])); // calculate center based on half way of the distance between two opposing cornerpoints mitk::Point3D center = CalcAvgPoint(cornerPoints[7], cornerPoints[0]); if (m_Impl->HandlePropertyList.size() == 6) { // set handle positions Point3D pointLeft = CalcAvgPoint(cornerPoints[5], cornerPoints[6]); Point3D pointRight = CalcAvgPoint(cornerPoints[1], cornerPoints[2]); Point3D pointTop = CalcAvgPoint(cornerPoints[0], cornerPoints[6]); Point3D pointBottom = CalcAvgPoint(cornerPoints[7], cornerPoints[1]); Point3D pointFront = CalcAvgPoint(cornerPoints[2], cornerPoints[7]); Point3D pointBack = CalcAvgPoint(cornerPoints[4], cornerPoints[1]); m_Impl->HandlePropertyList[0].SetPosition(pointLeft); m_Impl->HandlePropertyList[1].SetPosition(pointRight); m_Impl->HandlePropertyList[2].SetPosition(pointTop); m_Impl->HandlePropertyList[3].SetPosition(pointBottom); m_Impl->HandlePropertyList[4].SetPosition(pointFront); m_Impl->HandlePropertyList[5].SetPosition(pointBack); } auto cube = vtkCubeSource::New(); cube->SetXLength(extent[0] / spacing[0]); cube->SetYLength(extent[1] / spacing[1]); cube->SetZLength(extent[2] / spacing[2]); // calculates translation based on offset+extent not on the transformation matrix vtkSmartPointer<vtkMatrix4x4> imageTransform = geometry->GetVtkTransform()->GetMatrix(); auto translation = vtkSmartPointer<vtkTransform>::New(); translation->Translate(center[0] - imageTransform->GetElement(0, 3), center[1] - imageTransform->GetElement(1, 3), center[2] - imageTransform->GetElement(2, 3)); auto transform = vtkSmartPointer<vtkTransform>::New(); transform->SetMatrix(imageTransform); transform->PostMultiply(); transform->Concatenate(translation); transform->Update(); cube->Update(); auto transformFilter = vtkSmartPointer<vtkTransformFilter>::New(); transformFilter->SetInputData(cube->GetOutput()); transformFilter->SetTransform(transform); transformFilter->Update(); cube->Delete(); vtkSmartPointer<vtkPolyData> polydata = transformFilter->GetPolyDataOutput(); if (polydata == nullptr) { localStorage->Actor->VisibilityOff(); return; } mitk::DoubleProperty::Pointer handleSizeProperty = dynamic_cast<mitk::DoubleProperty *>(this->GetDataNode()->GetProperty("Bounding Shape.Handle Size Factor")); ScalarType initialHandleSize; if (handleSizeProperty != nullptr) initialHandleSize = handleSizeProperty->GetValue(); else initialHandleSize = 0.02; double handlesize = ((camera->GetDistance() * std::tan(vtkMath::RadiansFromDegrees(camera->GetViewAngle()))) / 2.0) * initialHandleSize; if (localStorage->PropAssembly->GetParts()->IsItemPresent(localStorage->HandleActor)) localStorage->PropAssembly->RemovePart(localStorage->HandleActor); if (localStorage->PropAssembly->GetParts()->IsItemPresent(localStorage->Actor)) localStorage->PropAssembly->RemovePart(localStorage->Actor); auto selectedhandlemapper = vtkSmartPointer<vtkPolyDataMapper>::New(); auto appendPoly = vtkSmartPointer<vtkAppendPolyData>::New(); mitk::IntProperty::Pointer activeHandleId = dynamic_cast<mitk::IntProperty *>(dataNode->GetProperty("Bounding Shape.Active Handle ID")); int i = 0; for (auto &handle : localStorage->Handles) { Point3D handlecenter = m_Impl->HandlePropertyList[i].GetPosition(); handle->SetCenter(handlecenter[0], handlecenter[1], handlecenter[2]); handle->SetXLength(handlesize); handle->SetYLength(handlesize); handle->SetZLength(handlesize); handle->Update(); if (activeHandleId == nullptr) { appendPoly->AddInputConnection(handle->GetOutputPort()); } else { if (activeHandleId->GetValue() != m_Impl->HandlePropertyList[i].GetIndex()) { appendPoly->AddInputConnection(handle->GetOutputPort()); } else { selectedhandlemapper->SetInputData(handle->GetOutput()); localStorage->SelectedHandleActor->SetMapper(selectedhandlemapper); localStorage->SelectedHandleActor->GetProperty()->SetColor(0, 1, 0); localStorage->SelectedHandleActor->GetMapper()->SetInputDataObject(handle->GetOutput()); localStorage->PropAssembly->AddPart(localStorage->SelectedHandleActor); } } i++; } appendPoly->Update(); auto mapper = vtkSmartPointer<vtkPolyDataMapper>::New(); mapper->SetInputData(polydata); auto handlemapper = vtkSmartPointer<vtkPolyDataMapper>::New(); handlemapper->SetInputData(appendPoly->GetOutput()); localStorage->Actor->SetMapper(mapper); localStorage->Actor->GetMapper()->SetInputDataObject(polydata); - localStorage->Actor->GetProperty()->SetOpacity(0.3); - - mitk::ColorProperty::Pointer selectedColor = dynamic_cast<mitk::ColorProperty *>(dataNode->GetProperty("color")); - if (selectedColor != nullptr) - { - mitk::Color color = selectedColor->GetColor(); - localStorage->Actor->GetProperty()->SetColor(color[0], color[1], color[2]); - } localStorage->HandleActor->SetMapper(handlemapper); if (activeHandleId == nullptr) { localStorage->HandleActor->GetProperty()->SetColor(1, 1, 1); } else { localStorage->HandleActor->GetProperty()->SetColor(1, 0, 0); } localStorage->HandleActor->GetMapper()->SetInputDataObject(appendPoly->GetOutput()); this->ApplyColorAndOpacityProperties(renderer, localStorage->Actor); this->ApplyBoundingShapeProperties(renderer, localStorage->Actor); - - this->ApplyColorAndOpacityProperties(renderer, localStorage->HandleActor); this->ApplyBoundingShapeProperties(renderer, localStorage->HandleActor); - - this->ApplyColorAndOpacityProperties(renderer, localStorage->SelectedHandleActor); this->ApplyBoundingShapeProperties(renderer, localStorage->SelectedHandleActor); // apply properties read from the PropertyList // this->ApplyProperties(localStorage->m_Actor, renderer); // this->ApplyProperties(localStorage->m_HandleActor, renderer); // this->ApplyProperties(localStorage->m_SelectedHandleActor, renderer); localStorage->Actor->VisibilityOn(); localStorage->HandleActor->VisibilityOn(); localStorage->SelectedHandleActor->VisibilityOn(); localStorage->PropAssembly->AddPart(localStorage->Actor); localStorage->PropAssembly->AddPart(localStorage->HandleActor); localStorage->PropAssembly->VisibilityOn(); localStorage->UpdateGenerateDataTime(); } } vtkProp *mitk::BoundingShapeVtkMapper3D::GetVtkProp(BaseRenderer *renderer) { return m_Impl->LocalStorageHandler.GetLocalStorage(renderer)->PropAssembly; } diff --git a/Plugins/org.mitk.gui.qt.imagecropper/src/internal/QmitkImageCropperView.cpp b/Plugins/org.mitk.gui.qt.imagecropper/src/internal/QmitkImageCropperView.cpp index 235f9dd175..c8ae768dbd 100644 --- a/Plugins/org.mitk.gui.qt.imagecropper/src/internal/QmitkImageCropperView.cpp +++ b/Plugins/org.mitk.gui.qt.imagecropper/src/internal/QmitkImageCropperView.cpp @@ -1,512 +1,510 @@ /*============================================================================ 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 "QmitkImageCropperView.h" #include <mitkBoundingShapeCropper.h> #include <mitkImageStatisticsHolder.h> #include <mitkInteractionConst.h> #include <mitkITKImageImport.h> #include <mitkLabelSetImage.h> #include <mitkNodePredicateDataType.h> #include <mitkNodePredicateAnd.h> #include <mitkNodePredicateNot.h> #include <mitkNodePredicateProperty.h> #include <mitkNodePredicateFunction.h> #include <mitkRenderingManager.h> #include <usModuleRegistry.h> #include <QMessageBox> const std::string QmitkImageCropperView::VIEW_ID = "org.mitk.views.qmitkimagecropper"; QmitkImageCropperView::QmitkImageCropperView(QObject *) : m_ParentWidget(nullptr) , m_BoundingShapeInteractor(nullptr) , m_CropOutsideValue(0) { CreateBoundingShapeInteractor(false); } QmitkImageCropperView::~QmitkImageCropperView() { //disable interactor if (m_BoundingShapeInteractor != nullptr) { m_BoundingShapeInteractor->SetDataNode(nullptr); m_BoundingShapeInteractor->EnableInteraction(false); } } void QmitkImageCropperView::CreateQtPartControl(QWidget *parent) { // create GUI widgets from the Qt Designer's .ui file m_Controls.setupUi(parent); m_Controls.imageSelectionWidget->SetDataStorage(GetDataStorage()); m_Controls.imageSelectionWidget->SetNodePredicate( mitk::NodePredicateAnd::New(mitk::TNodePredicateDataType<mitk::Image>::New(), mitk::NodePredicateNot::New(mitk::NodePredicateProperty::New("helper object")))); m_Controls.imageSelectionWidget->SetSelectionIsOptional(true); m_Controls.imageSelectionWidget->SetAutoSelectNewNodes(true); m_Controls.imageSelectionWidget->SetEmptyInfo(QString("Please select an image node")); m_Controls.imageSelectionWidget->SetPopUpTitel(QString("Select image node")); connect(m_Controls.imageSelectionWidget, &QmitkSingleNodeSelectionWidget::CurrentSelectionChanged, this, &QmitkImageCropperView::OnImageSelectionChanged); m_Controls.boundingBoxSelectionWidget->SetDataStorage(GetDataStorage()); m_Controls.boundingBoxSelectionWidget->SetNodePredicate(mitk::NodePredicateAnd::New( mitk::TNodePredicateDataType<mitk::GeometryData>::New(), mitk::NodePredicateNot::New(mitk::NodePredicateProperty::New("helper object")))); m_Controls.boundingBoxSelectionWidget->SetSelectionIsOptional(true); m_Controls.boundingBoxSelectionWidget->SetAutoSelectNewNodes(true); m_Controls.boundingBoxSelectionWidget->SetEmptyInfo(QString("Please select a bounding box")); m_Controls.boundingBoxSelectionWidget->SetPopUpTitel(QString("Select bounding box node")); connect(m_Controls.boundingBoxSelectionWidget, &QmitkSingleNodeSelectionWidget::CurrentSelectionChanged, this, &QmitkImageCropperView::OnBoundingBoxSelectionChanged); connect(m_Controls.buttonCreateNewBoundingBox, SIGNAL(clicked()), this, SLOT(OnCreateNewBoundingBox())); connect(m_Controls.buttonCropping, SIGNAL(clicked()), this, SLOT(OnCropping())); connect(m_Controls.buttonMasking, SIGNAL(clicked()), this, SLOT(OnMasking())); auto lambda = [this]() { m_Controls.groupImageSettings->setVisible(!m_Controls.groupImageSettings->isVisible()); }; connect(m_Controls.buttonAdvancedSettings, &ctkExpandButton::clicked, this, lambda); connect(m_Controls.spinBoxOutsidePixelValue, SIGNAL(valueChanged(int)), this, SLOT(OnSliderValueChanged(int))); SetDefaultGUI(); m_ParentWidget = parent; this->OnImageSelectionChanged(m_Controls.imageSelectionWidget->GetSelectedNodes()); this->OnBoundingBoxSelectionChanged(m_Controls.boundingBoxSelectionWidget->GetSelectedNodes()); } void QmitkImageCropperView::OnImageSelectionChanged(QList<mitk::DataNode::Pointer>) { bool rotationEnabled = false; m_Controls.labelWarningRotation->setVisible(false); auto imageNode = m_Controls.imageSelectionWidget->GetSelectedNode(); if (imageNode.IsNull()) { SetDefaultGUI(); return; } auto image = dynamic_cast<mitk::Image*>(imageNode->GetData()); if (nullptr != image) { if (image->GetDimension() < 3) { QMessageBox::warning(nullptr, tr("Invalid image selected"), tr("ImageCropper only works with 3 or more dimensions."), QMessageBox::Ok, QMessageBox::NoButton, QMessageBox::NoButton); SetDefaultGUI(); return; } m_ParentWidget->setEnabled(true); m_Controls.buttonCreateNewBoundingBox->setEnabled(true); vtkSmartPointer<vtkMatrix4x4> imageMat = image->GetGeometry()->GetVtkMatrix(); // check whether the image geometry is rotated; if so, no pixel aligned cropping or masking can be performed if ((imageMat->GetElement(1, 0) == 0.0) && (imageMat->GetElement(0, 1) == 0.0) && (imageMat->GetElement(1, 2) == 0.0) && (imageMat->GetElement(2, 1) == 0.0) && (imageMat->GetElement(2, 0) == 0.0) && (imageMat->GetElement(0, 2) == 0.0)) { rotationEnabled = false; m_Controls.labelWarningRotation->setVisible(false); } else { rotationEnabled = true; m_Controls.labelWarningRotation->setStyleSheet(" QLabel { color: rgb(255, 0, 0) }"); m_Controls.labelWarningRotation->setVisible(true); } this->CreateBoundingShapeInteractor(rotationEnabled); if (itk::IOPixelEnum::SCALAR == image->GetPixelType().GetPixelType()) { // Might be changed with the upcoming new image statistics plugin //(recomputation might be very expensive for large images ;) ) auto statistics = image->GetStatistics(); auto minPixelValue = statistics->GetScalarValueMin(); auto maxPixelValue = statistics->GetScalarValueMax(); if (minPixelValue < std::numeric_limits<int>::min()) { minPixelValue = std::numeric_limits<int>::min(); } if (maxPixelValue > std::numeric_limits<int>::max()) { maxPixelValue = std::numeric_limits<int>::max(); } m_Controls.spinBoxOutsidePixelValue->setEnabled(true); m_Controls.spinBoxOutsidePixelValue->setMaximum(static_cast<int>(maxPixelValue)); m_Controls.spinBoxOutsidePixelValue->setMinimum(static_cast<int>(minPixelValue)); m_Controls.spinBoxOutsidePixelValue->setValue(static_cast<int>(minPixelValue)); } else { m_Controls.spinBoxOutsidePixelValue->setEnabled(false); } unsigned int dim = image->GetDimension(); if (dim < 2 || dim > 4) { m_ParentWidget->setEnabled(false); } if (m_Controls.boundingBoxSelectionWidget->GetSelectedNode().IsNotNull()) { m_Controls.buttonCropping->setEnabled(true); m_Controls.buttonMasking->setEnabled(true); m_Controls.buttonAdvancedSettings->setEnabled(true); m_Controls.groupImageSettings->setEnabled(true); } } } void QmitkImageCropperView::OnBoundingBoxSelectionChanged(QList<mitk::DataNode::Pointer>) { auto boundingBoxNode = m_Controls.boundingBoxSelectionWidget->GetSelectedNode(); if (boundingBoxNode.IsNull()) { SetDefaultGUI(); m_BoundingShapeInteractor->EnableInteraction(false); m_BoundingShapeInteractor->SetDataNode(nullptr); if (m_Controls.imageSelectionWidget->GetSelectedNode().IsNotNull()) { m_Controls.buttonCreateNewBoundingBox->setEnabled(true); } return; } auto boundingBox = dynamic_cast<mitk::GeometryData*>(boundingBoxNode->GetData()); if (nullptr != boundingBox) { // node newly selected boundingBoxNode->SetVisibility(true); m_BoundingShapeInteractor->EnableInteraction(true); m_BoundingShapeInteractor->SetDataNode(boundingBoxNode); mitk::RenderingManager::GetInstance()->InitializeViews(); if (m_Controls.imageSelectionWidget->GetSelectedNode().IsNotNull()) { m_Controls.buttonCropping->setEnabled(true); m_Controls.buttonMasking->setEnabled(true); m_Controls.buttonAdvancedSettings->setEnabled(true); m_Controls.groupImageSettings->setEnabled(true); } } } void QmitkImageCropperView::OnCreateNewBoundingBox() { auto imageNode = m_Controls.imageSelectionWidget->GetSelectedNode(); if (imageNode.IsNull()) { return; } if (nullptr == imageNode->GetData()) { return; } QString name = QString::fromStdString(imageNode->GetName() + " Bounding Box"); auto boundingShape = this->GetDataStorage()->GetNode(mitk::NodePredicateFunction::New([&name](const mitk::DataNode *node) { return 0 == node->GetName().compare(name.toStdString()); })); if (nullptr != boundingShape) { name = this->AdaptBoundingObjectName(name); } // get current timestep to support 3d+t images auto renderWindowPart = this->GetRenderWindowPart(mitk::WorkbenchUtil::IRenderWindowPartStrategy::OPEN); const mitk::TimePointType timePoint = renderWindowPart->GetSelectedTimePoint(); const auto imageGeometry = imageNode->GetData()->GetTimeGeometry()->GetGeometryForTimePoint(timePoint); auto boundingBox = mitk::GeometryData::New(); boundingBox->SetGeometry(static_cast<mitk::Geometry3D*>(this->InitializeWithImageGeometry(imageGeometry))); auto boundingBoxNode = mitk::DataNode::New(); boundingBoxNode->SetData(boundingBox); boundingBoxNode->SetProperty("name", mitk::StringProperty::New(name.toStdString())); - boundingBoxNode->SetProperty("color", mitk::ColorProperty::New(1.0, 1.0, 1.0)); - boundingBoxNode->SetProperty("opacity", mitk::FloatProperty::New(0.6)); boundingBoxNode->SetProperty("layer", mitk::IntProperty::New(99)); boundingBoxNode->AddProperty("Bounding Shape.Handle Size Factor", mitk::DoubleProperty::New(0.02)); boundingBoxNode->SetBoolProperty("pickable", true); if (!this->GetDataStorage()->Exists(boundingBoxNode)) { GetDataStorage()->Add(boundingBoxNode, imageNode); } m_Controls.boundingBoxSelectionWidget->SetCurrentSelectedNode(boundingBoxNode); } void QmitkImageCropperView::OnCropping() { this->ProcessImage(false); } void QmitkImageCropperView::OnMasking() { this->ProcessImage(true); } void QmitkImageCropperView::OnSliderValueChanged(int slidervalue) { m_CropOutsideValue = slidervalue; } void QmitkImageCropperView::CreateBoundingShapeInteractor(bool rotationEnabled) { if (m_BoundingShapeInteractor.IsNull()) { m_BoundingShapeInteractor = mitk::BoundingShapeInteractor::New(); m_BoundingShapeInteractor->LoadStateMachine("BoundingShapeInteraction.xml", us::ModuleRegistry::GetModule("MitkBoundingShape")); m_BoundingShapeInteractor->SetEventConfig("BoundingShapeMouseConfig.xml", us::ModuleRegistry::GetModule("MitkBoundingShape")); } m_BoundingShapeInteractor->SetRotationEnabled(rotationEnabled); } mitk::Geometry3D::Pointer QmitkImageCropperView::InitializeWithImageGeometry(const mitk::BaseGeometry* geometry) const { // convert a BaseGeometry into a Geometry3D (otherwise IO is not working properly) if (geometry == nullptr) mitkThrow() << "Geometry is not valid."; auto boundingGeometry = mitk::Geometry3D::New(); boundingGeometry->SetBounds(geometry->GetBounds()); boundingGeometry->SetImageGeometry(geometry->GetImageGeometry()); boundingGeometry->SetOrigin(geometry->GetOrigin()); boundingGeometry->SetSpacing(geometry->GetSpacing()); boundingGeometry->SetIndexToWorldTransform(geometry->GetIndexToWorldTransform()->Clone()); boundingGeometry->Modified(); return boundingGeometry; } void QmitkImageCropperView::ProcessImage(bool mask) { auto renderWindowPart = this->GetRenderWindowPart(mitk::WorkbenchUtil::IRenderWindowPartStrategy::OPEN); const auto timePoint = renderWindowPart->GetSelectedTimePoint(); auto imageNode = m_Controls.imageSelectionWidget->GetSelectedNode(); if (imageNode.IsNull()) { QMessageBox::information(nullptr, "Warning", "Please load and select an image before starting image processing."); return; } auto boundingBoxNode = m_Controls.boundingBoxSelectionWidget->GetSelectedNode(); if (boundingBoxNode.IsNull()) { QMessageBox::information(nullptr, "Warning", "Please load and select a cropping object before starting image processing."); return; } if (!imageNode->GetData()->GetTimeGeometry()->IsValidTimePoint(timePoint)) { QMessageBox::information(nullptr, "Warning", "Please select a time point that is within the time bounds of the selected image."); return; } const auto timeStep = imageNode->GetData()->GetTimeGeometry()->TimePointToTimeStep(timePoint); auto image = dynamic_cast<mitk::Image*>(imageNode->GetData()); auto boundingBox = dynamic_cast<mitk::GeometryData*>(boundingBoxNode->GetData()); if (nullptr != image && nullptr != boundingBox) { // Check if initial node name is already in box name std::string imagePrefix = ""; if (boundingBoxNode->GetName().find(imageNode->GetName()) != 0) { imagePrefix = imageNode->GetName() + "_"; } QString imageName; if (mask) { imageName = QString::fromStdString(imagePrefix + boundingBoxNode->GetName() + "_masked"); } else { imageName = QString::fromStdString(imagePrefix + boundingBoxNode->GetName() + "_cropped"); } if (m_Controls.checkBoxCropTimeStepOnly->isChecked()) { imageName = imageName + "_T" + QString::number(timeStep); } // image and bounding shape ok, set as input auto croppedImageNode = mitk::DataNode::New(); auto cutter = mitk::BoundingShapeCropper::New(); cutter->SetGeometry(boundingBox); // adjustable in advanced settings cutter->SetUseWholeInputRegion(mask); //either mask (mask=true) or crop (mask=false) cutter->SetOutsideValue(m_CropOutsideValue); cutter->SetUseCropTimeStepOnly(m_Controls.checkBoxCropTimeStepOnly->isChecked()); cutter->SetCurrentTimeStep(timeStep); // TODO: Add support for MultiLayer (right now only Mulitlabel support) auto labelsetImageInput = dynamic_cast<mitk::LabelSetImage*>(image); if (nullptr != labelsetImageInput) { cutter->SetInput(labelsetImageInput); // do the actual cutting try { cutter->Update(); } catch (const itk::ExceptionObject& e) { std::string message = std::string("The Cropping filter could not process because of: \n ") + e.GetDescription(); QMessageBox::warning(nullptr, tr("Cropping not possible!"), tr(message.c_str()), QMessageBox::Ok, QMessageBox::NoButton, QMessageBox::NoButton); return; } auto labelSetImage = mitk::LabelSetImage::New(); labelSetImage->InitializeByLabeledImage(cutter->GetOutput()); for (unsigned int i = 0; i < labelsetImageInput->GetNumberOfLayers(); i++) { labelSetImage->AddLabelSetToLayer(i, labelsetImageInput->GetLabelSet(i)); } croppedImageNode->SetData(labelSetImage); croppedImageNode->SetProperty("name", mitk::StringProperty::New(imageName.toStdString())); //add cropping result to the current data storage as child node to the image node if (!m_Controls.checkOverwriteImage->isChecked()) { if (!this->GetDataStorage()->Exists(croppedImageNode)) { this->GetDataStorage()->Add(croppedImageNode, imageNode); } } else // original image will be overwritten by the result image and the bounding box of the result is adjusted { imageNode->SetData(labelSetImage); imageNode->Modified(); // Adjust coordinate system by doing a reinit on auto tempDataStorage = mitk::DataStorage::SetOfObjects::New(); tempDataStorage->InsertElement(0, imageNode); // initialize the views to the bounding geometry auto bounds = this->GetDataStorage()->ComputeBoundingGeometry3D(tempDataStorage); mitk::RenderingManager::GetInstance()->InitializeViews(bounds); } } else { cutter->SetInput(image); // do the actual cutting try { cutter->Update(); } catch (const itk::ExceptionObject& e) { std::string message = std::string("The Cropping filter could not process because of: \n ") + e.GetDescription(); QMessageBox::warning(nullptr, tr("Cropping not possible!"), tr(message.c_str()), QMessageBox::Ok, QMessageBox::NoButton, QMessageBox::NoButton); return; } //add cropping result to the current data storage as child node to the image node if (!m_Controls.checkOverwriteImage->isChecked()) { croppedImageNode->SetData(cutter->GetOutput()); croppedImageNode->SetProperty("name", mitk::StringProperty::New(imageName.toStdString())); croppedImageNode->SetProperty("color", mitk::ColorProperty::New(1.0, 1.0, 1.0)); mitk::LevelWindow levelWindow; imageNode->GetLevelWindow(levelWindow); croppedImageNode->SetLevelWindow(levelWindow); if (!this->GetDataStorage()->Exists(croppedImageNode)) { this->GetDataStorage()->Add(croppedImageNode, imageNode); imageNode->SetVisibility(mask); // Give the user a visual clue that something happened when image was cropped } } else // original image will be overwritten by the result image and the bounding box of the result is adjusted { mitk::LevelWindow levelWindow; imageNode->GetLevelWindow(levelWindow); imageNode->SetData(cutter->GetOutput()); imageNode->SetLevelWindow(levelWindow); // Adjust coordinate system by doing a reinit on auto tempDataStorage = mitk::DataStorage::SetOfObjects::New(); tempDataStorage->InsertElement(0, imageNode); // initialize the views to the bounding geometry auto bounds = this->GetDataStorage()->ComputeBoundingGeometry3D(tempDataStorage); mitk::RenderingManager::GetInstance()->InitializeViews(bounds); } } } else { QMessageBox::information(nullptr, "Warning", "Please load and select an image before starting image processing."); } } void QmitkImageCropperView::SetDefaultGUI() { m_Controls.buttonCreateNewBoundingBox->setEnabled(false); m_Controls.buttonCropping->setEnabled(false); m_Controls.buttonMasking->setEnabled(false); m_Controls.buttonAdvancedSettings->setEnabled(false); m_Controls.groupImageSettings->setEnabled(false); m_Controls.groupImageSettings->setVisible(false); m_Controls.checkOverwriteImage->setChecked(false); m_Controls.checkBoxCropTimeStepOnly->setChecked(false); } QString QmitkImageCropperView::AdaptBoundingObjectName(const QString& name) const { unsigned int counter = 2; QString newName = QString("%1 %2").arg(name).arg(counter); while (nullptr != this->GetDataStorage()->GetNode(mitk::NodePredicateFunction::New([&newName](const mitk::DataNode *node) { return 0 == node->GetName().compare(newName.toStdString()); }))) { newName = QString("%1 %2").arg(name).arg(++counter); } return newName; }