diff --git a/Modules/BoundingShape/src/Interactions/mitkBoundingShapeInteractor.cpp b/Modules/BoundingShape/src/Interactions/mitkBoundingShapeInteractor.cpp index fb124d07b0..73fa0190cd 100644 --- a/Modules/BoundingShape/src/Interactions/mitkBoundingShapeInteractor.cpp +++ b/Modules/BoundingShape/src/Interactions/mitkBoundingShapeInteractor.cpp @@ -1,613 +1,613 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "../DataManagement/mitkBoundingShapeUtil.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #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 { class BoundingShapeInteractor::Impl { public: Impl() : ScrollEnabled(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 ScrollEnabled; Point3D InitialPickedWorldPoint; Point3D LastPickedWorldPoint; Point2D InitialPickedDisplayPoint; std::vector Handles; Handle ActiveHandle; Geometry3D::Pointer OriginalGeometry; bool RotationEnabled; std::map DisplayInteractorConfigs; }; } 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(newInputNode->GetProperty(selectedColorPropertyName)); mitk::ColorProperty::Pointer deselectedColor = dynamic_cast(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->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(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(this->GetDataNode()->GetData()); int timeStep = interactionEvent->GetSender()->GetTimeStep(this->GetDataNode()->GetData()); mitk::BaseGeometry::Pointer geometry = geometryData->GetGeometry(timeStep); std::vector 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 InteractionPositionEvent *positionEvent = dynamic_cast(interactionEvent); if (positionEvent == nullptr) return false; GeometryData::Pointer geometryData = dynamic_cast(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 imageTransform = geometry->GetVtkTransform()->GetMatrix(); Point3D center = geometry->GetCenter(); auto translation = vtkSmartPointer::New(); auto transform = vtkSmartPointer::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 InteractionPositionEvent *positionEvent = dynamic_cast(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(this->GetDataNode()->GetData()); BaseGeometry::Pointer geometry = geometryData->GetUpdatedTimeGeometry()->GetGeometryForTimeStep(timeStep); std::vector cornerPoints = GetCornerPoints(geometry, true); interactionEvent->GetSender()->WorldToDisplay(boundingBoxCenter, displayCenterPoint); double scale = interactionEvent->GetSender()->GetScaleFactorMMPerDisplayUnit(); // GetDisplaySizeInMM mitk::DoubleProperty::Pointer handleSizeProperty = dynamic_cast(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(); interactionEvent->GetSender()->GetRenderingManager()->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 *interactionEvent) { this->DisableCrosshairNavigation(); DataNode::Pointer node = this->GetDataNode(); if (node.IsNull()) return; mitk::ColorProperty::Pointer selectedColor = dynamic_cast(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(); interactionEvent->GetSender()->GetRenderingManager()->RequestUpdateAll(); return; } void mitk::BoundingShapeInteractor::DeselectHandles(StateMachineAction *, InteractionEvent *interactionEvent) { this->DisableCrosshairNavigation(); 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(); interactionEvent->GetSender()->GetRenderingManager()->RequestUpdateAll(); return; } void mitk::BoundingShapeInteractor::SelectObject(StateMachineAction *, InteractionEvent *) { this->DisableCrosshairNavigation(); // 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(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 *interactionEvent) { this->EnableCrosshairNavigation(); // 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(node->GetProperty(deselectedColorPropertyName)); if (deselectedColor.IsNotNull()) { node->GetPropertyList()->SetProperty("color", deselectedColor); } this->GetDataNode()->GetData()->UpdateOutputInformation(); // Geometry is up-to-date this->GetDataNode()->GetData()->Modified(); interactionEvent->GetSender()->GetRenderingManager()->RequestUpdateAll(); return; } void mitk::BoundingShapeInteractor::InitInteraction(StateMachineAction *, InteractionEvent *interactionEvent) { InitMembers(interactionEvent); } bool mitk::BoundingShapeInteractor::InitMembers(InteractionEvent *interactionEvent) { InteractionPositionEvent *positionEvent = dynamic_cast(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) { InteractionPositionEvent *positionEvent = dynamic_cast(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) { InteractionPositionEvent *positionEvent = dynamic_cast(interactionEvent); if (positionEvent == nullptr) return; GeometryData::Pointer geometryData = dynamic_cast(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 faces = m_Impl->ActiveHandle.GetFaceIndices(); auto pointscontainer = mitk::BoundingBox::PointsContainer::New(); // calculate cornerpoints from geometry plus visualization offset std::vector cornerPoints = GetCornerPoints(geometry, true); unsigned int num = 0; for (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 (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 at 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 (abs(BBmin[0] - BBmax[0]) > 0.01 && abs(BBmin[1] - BBmax[1]) > 0.01 && - abs(BBmin[2] - BBmax[2]) > 0.01) // TODO: check if the extent is greater than zero + 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); EnableCrosshairNavigation(); // update rendering mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void mitk::BoundingShapeInteractor::EnableCrosshairNavigation() { // enable the crosshair navigation // Re-enabling InteractionEventObservers that have been previously disabled for legacy handling of Tools // in new interaction framework for (std::map::iterator it = m_Impl->DisplayInteractorConfigs.begin(); it != m_Impl->DisplayInteractorConfigs.end(); ++it) { if (it->first) { mitk::DisplayInteractor *displayInteractor = static_cast( us::GetModuleContext()->GetService(it->first)); if (displayInteractor != nullptr) { // here the regular configuration is loaded again displayInteractor->SetEventConfig(it->second); // MITK_INFO << "restore config"; } } } m_Impl->DisplayInteractorConfigs.clear(); m_Impl->ScrollEnabled = true; } void mitk::BoundingShapeInteractor::DisableCrosshairNavigation() { // dont deactivate twice, else we will clutter the config list ... if (m_Impl->ScrollEnabled == false) 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->DisplayInteractorConfigs.clear(); std::vector> listEventObserver = us::GetModuleContext()->GetServiceReferences(); for (std::vector>::iterator it = listEventObserver.begin(); it != listEventObserver.end(); ++it) { mitk::DisplayInteractor *displayInteractor = dynamic_cast(us::GetModuleContext()->GetService(*it)); if (displayInteractor != nullptr) { // remember the original configuration m_Impl->DisplayInteractorConfigs.insert(std::make_pair(*it, displayInteractor->GetEventConfig())); // here the alternative configuration is loaded displayInteractor->SetEventConfig("DisplayConfigMITKNoCrosshair.xml"); // MITK_INFO << "change config"; } } m_Impl->ScrollEnabled = false; } diff --git a/Modules/BoundingShape/src/Rendering/mitkBoundingShapeVtkMapper2D.cpp b/Modules/BoundingShape/src/Rendering/mitkBoundingShapeVtkMapper2D.cpp index 2f5f5363d8..be47fd4ced 100644 --- a/Modules/BoundingShape/src/Rendering/mitkBoundingShapeVtkMapper2D.cpp +++ b/Modules/BoundingShape/src/Rendering/mitkBoundingShapeVtkMapper2D.cpp @@ -1,462 +1,462 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for detailocalStorage. ===================================================================*/ #include "../DataManagement/mitkBoundingShapeUtil.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static vtkSmartPointer CreateHandle() { auto handle = vtkSmartPointer::New(); handle->SetPhiResolution(8); handle->SetThetaResolution(16); return handle; } 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 HandlePropertyList; mitk::LocalStorageHandler LocalStorageHandler; }; } mitk::BoundingShapeVtkMapper2D::LocalStorage::LocalStorage() : m_Actor(vtkSmartPointer::New()), m_HandleActor(vtkSmartPointer::New()), m_SelectedHandleActor(vtkSmartPointer::New()), m_Mapper(vtkSmartPointer::New()), m_HandleMapper(vtkSmartPointer::New()), m_SelectedHandleMapper(vtkSmartPointer::New()), m_Cutter(vtkSmartPointer::New()), m_CuttingPlane(vtkSmartPointer::New()), m_PropAssembly(vtkSmartPointer::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(CreateHandle()); 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; 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); } 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 (abs(scale - localStorage->m_ZoomFactor) > 0.001) + 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(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 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 result0[3], result1[3], result2[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, result0); 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, result1); 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, result2); vtkMath::Normalize(result0); vtkMath::Normalize(result1); vtkMath::Normalize(result2); // 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 imageTransform = geometry->GetVtkTransform()->GetMatrix(); auto translation = vtkSmartPointer::New(); translation->Translate(center[0] - imageTransform->GetElement(0, 3), center[1] - imageTransform->GetElement(1, 3), center[2] - imageTransform->GetElement(2, 3)); auto transform = vtkSmartPointer::New(); transform->SetMatrix(imageTransform); transform->PostMultiply(); transform->Concatenate(translation); transform->Update(); cube->Update(); auto transformFilter = vtkSmartPointer::New(); transformFilter->SetInputData(cube->GetOutput()); transformFilter->SetTransform(transform); transformFilter->Update(); cube->Delete(); vtkSmartPointer 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 normal[3]; normal[0] = planeGeometry->GetNormal()[0]; normal[1] = planeGeometry->GetNormal()[1]; normal[2] = planeGeometry->GetNormal()[2]; // MITK_INFO << "normal1 " << normal[0] << " " << normal[1] << " " << normal[2]; localStorage->m_CuttingPlane->SetOrigin(origin); localStorage->m_CuttingPlane->SetNormal(normal); // 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(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 = renderer->GetDisplaySizeInMM(); double handleSize = ((displaySize[0] + displaySize[1]) / 2.0) * initialHandleSize; auto appendPoly = vtkSmartPointer::New(); unsigned int i = 0; // add handles and their assigned properties to the local storage mitk::IntProperty::Pointer activeHandleId = dynamic_cast(node->GetProperty("Bounding Shape.Active Handle ID")); bool visible = false; bool selected = false; for (auto handle : localStorage->m_Handles) { Point3D handleCenter = m_Impl->HandlePropertyList[i].GetPosition(); handle->SetRadius(handleSize); handle->SetCenter(handleCenter[0], handleCenter[1], handleCenter[2]); vtkMath::Normalize(normal); double angle = vtkMath::DegreesFromRadians(acos(vtkMath::Dot(normal, result0))); double angle1 = vtkMath::DegreesFromRadians(acos(vtkMath::Dot(normal, result1))); double angle2 = vtkMath::DegreesFromRadians(acos(vtkMath::Dot(normal, result2))); // show handles only if the corresponding face is aligned to the render window - if ((((abs(angle - 0) < 0.001) || (abs(angle - 180) < 0.001)) && i != 0 && i != 1) || - (((abs(angle1 - 0) < 0.001) || (abs(angle1 - 180) < 0.001)) && i != 2 && i != 3) || - (((abs(angle2 - 0) < 0.001) || (abs(angle2 - 180) < 0.001)) && i != 4 && i != 5)) + if ((((std::abs(angle - 0) < 0.001) || (std::abs(angle - 180) < 0.001)) && i != 0 && i != 1) || + (((std::abs(angle1 - 0) < 0.001) || (std::abs(angle1 - 180) < 0.001)) && i != 2 && i != 3) || + (((std::abs(angle2 - 0) < 0.001) || (std::abs(angle2 - 180) < 0.001)) && i != 4 && i != 5)) { if (activeHandleId == nullptr) { appendPoly->AddInputConnection(handle->GetOutputPort()); } else { if ((activeHandleId->GetValue() != m_Impl->HandlePropertyList[i].GetIndex())) { appendPoly->AddInputConnection(handle->GetOutputPort()); } else { handle->Update(); localStorage->m_SelectedHandleMapper->SetInputData(handle->GetOutput()); localStorage->m_SelectedHandleActor->VisibilityOn(); selected = true; } } visible = true; } i++; } if (visible) { appendPoly->Update(); } else { localStorage->m_HandleActor->VisibilityOff(); localStorage->m_SelectedHandleActor->VisibilityOff(); } auto stripper = vtkSmartPointer::New(); stripper->SetInputData(localStorage->m_Cutter->GetOutput()); stripper->Update(); auto cutPolyData = vtkSmartPointer::New(); cutPolyData->SetPoints(stripper->GetOutput()->GetPoints()); cutPolyData->SetPolys(stripper->GetOutput()->GetLines()); localStorage->m_Actor->GetMapper()->SetInputDataObject(cutPolyData); mitk::ColorProperty::Pointer selectedColor = dynamic_cast(node->GetProperty("color")); if (selectedColor != nullptr) { mitk::Color color = selectedColor->GetColor(); localStorage->m_Actor->GetProperty()->SetColor(color[0], color[1], color[2]); } 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 *) { } diff --git a/Modules/BoundingShape/src/Rendering/mitkBoundingShapeVtkMapper3D.cpp b/Modules/BoundingShape/src/Rendering/mitkBoundingShapeVtkMapper3D.cpp index 6d62e984b1..42279a3831 100644 --- a/Modules/BoundingShape/src/Rendering/mitkBoundingShapeVtkMapper3D.cpp +++ b/Modules/BoundingShape/src/Rendering/mitkBoundingShapeVtkMapper3D.cpp @@ -1,339 +1,339 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkBoundingShapeVtkMapper3D.h" #include "../DataManagement/mitkBoundingShapeUtil.h" #include #include #include #include #include #include #include #include #include #include namespace mitk { class BoundingShapeVtkMapper3D::Impl { class LocalStorage : public Mapper::BaseLocalStorage { public: LocalStorage(); ~LocalStorage(); LocalStorage(const LocalStorage &) = delete; LocalStorage &operator=(const LocalStorage &) = delete; std::vector> Handles; vtkSmartPointer Actor; vtkSmartPointer HandleActor; vtkSmartPointer SelectedHandleActor; vtkSmartPointer 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 HandlePropertyList; mitk::LocalStorageHandler LocalStorageHandler; }; } mitk::BoundingShapeVtkMapper3D::Impl::LocalStorage::LocalStorage() : Actor(vtkSmartPointer::New()), HandleActor(vtkSmartPointer::New()), SelectedHandleActor(vtkSmartPointer::New()), PropAssembly(vtkSmartPointer::New()) { for (int i = 0; i < 6; i++) Handles.push_back(vtkSmartPointer::New()); } mitk::BoundingShapeVtkMapper3D::Impl::LocalStorage::~LocalStorage() { } void mitk::BoundingShapeVtkMapper3D::SetDefaultProperties(DataNode *node, BaseRenderer *renderer, bool overwrite) { Superclass::SetDefaultProperties(node, renderer, overwrite); } mitk::BoundingShapeVtkMapper3D::BoundingShapeVtkMapper3D() : m_Impl(new Impl) { } mitk::BoundingShapeVtkMapper3D::~BoundingShapeVtkMapper3D() { delete m_Impl; } void mitk::BoundingShapeVtkMapper3D::ApplyColorAndOpacityProperties(BaseRenderer *renderer, vtkActor *actor) { Superclass::ApplyColorAndOpacityProperties(renderer, actor); } 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 (abs(distance - m_Impl->DistanceFromCam) > mitk::eps) + 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 GeometryData *geometryData = dynamic_cast(dataNode->GetData()); if (geometryData == nullptr) return; mitk::BaseGeometry::Pointer geometry = geometryData->GetGeometry(); mitk::Vector3D spacing = geometry->GetSpacing(); // calculate cornerpoints from geometry std::vector 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 imageTransform = geometry->GetVtkTransform()->GetMatrix(); auto translation = vtkSmartPointer::New(); translation->Translate(center[0] - imageTransform->GetElement(0, 3), center[1] - imageTransform->GetElement(1, 3), center[2] - imageTransform->GetElement(2, 3)); auto transform = vtkSmartPointer::New(); transform->SetMatrix(imageTransform); transform->PostMultiply(); transform->Concatenate(translation); transform->Update(); cube->Update(); auto transformFilter = vtkSmartPointer::New(); transformFilter->SetInputData(cube->GetOutput()); transformFilter->SetTransform(transform); transformFilter->Update(); cube->Delete(); vtkSmartPointer polydata = transformFilter->GetPolyDataOutput(); if (polydata == nullptr) { localStorage->Actor->VisibilityOff(); return; } mitk::DoubleProperty::Pointer handleSizeProperty = dynamic_cast(this->GetDataNode()->GetProperty("Bounding Shape.Handle Size Factor")); ScalarType initialHandleSize; if (handleSizeProperty != nullptr) initialHandleSize = handleSizeProperty->GetValue(); else initialHandleSize = 1.0 / 40.0; 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::New(); auto appendPoly = vtkSmartPointer::New(); mitk::IntProperty::Pointer activeHandleId = dynamic_cast(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->SetRadius(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::New(); mapper->SetInputData(polydata); auto handlemapper = vtkSmartPointer::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(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/Modules/Core/include/mitkProportionalTimeGeometry.h b/Modules/Core/include/mitkProportionalTimeGeometry.h index 18e9e92bec..316187197f 100644 --- a/Modules/Core/include/mitkProportionalTimeGeometry.h +++ b/Modules/Core/include/mitkProportionalTimeGeometry.h @@ -1,263 +1,263 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef ProportionalTimeGeometry_h #define ProportionalTimeGeometry_h // MITK #include #include #include #include namespace mitk { /** * \brief Organizes geometries over proportional time steps * * For this TimeGeometry implementation it is assumed that * the durations of the time steps are equidistant, e.g. * the durations of the time steps in one ProportionalTimeGeometry * are the same. The geometries of the time steps are independent, * and not linked to each other. Since the timeBounds of the * geometries are different for each time step it is not possible * to set the same geometry to different time steps. Instead * copies should be used. * * \addtogroup geometry */ class MITKCORE_EXPORT ProportionalTimeGeometry : public TimeGeometry { public: mitkClassMacro(ProportionalTimeGeometry, TimeGeometry); ProportionalTimeGeometry(); typedef ProportionalTimeGeometry self; itkFactorylessNewMacro(Self) itkCloneMacro(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 override; /** * \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 override; /** * \brief Returns the last time point for which the object is valid * * Gives the last time point for which a valid geometrie is saved in * this time geometry. The time point is given in ms. */ virtual TimePointType GetMaximumTimePoint() const override; /** * \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 override; /** * \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 override; /** * \brief Get the time bounds (in ms) */ virtual TimeBounds GetTimeBounds() const override; /** * \brief Get the time bounds for the given TimeStep (in ms) */ virtual TimeBounds GetTimeBounds(TimeStepType step) const override; /** * \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 override; /** * \brief Test for the given time step if a geometry is availible * * Returns true if a geometry is defined for the given time step. * Otherwise false is returned. * The time step is defined as positiv number. */ virtual bool IsValidTimeStep(TimeStepType timeStep) const override; /** * \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 override; /** * \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 negativ invalid time point is given always time step 0 is * returned. If an positiv invalid time step is given an invalid * time step will be returned. */ virtual TimeStepType TimePointToTimeStep(TimePointType timePoint) const override; /** * \brief Returns the geometry which corresponds to the given time step * * Returns a clone of the geometry which defines the given time step. If * the given time step is invalid an null-pointer is returned. */ virtual BaseGeometry::Pointer GetGeometryCloneForTimeStep(TimeStepType timeStep) const override; /** * \brief Returns the geometry which corresponds to the given time point * * Returns the geometry which defines the given time point. If * the given time point is invalid an null-pointer is returned. * * If the returned geometry is changed this will affect the saved * geometry. */ virtual BaseGeometry::Pointer GetGeometryForTimePoint(TimePointType timePoint) const override; /** * \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. * * If the returned geometry is changed this will affect the saved * geometry. */ virtual BaseGeometry::Pointer GetGeometryForTimeStep(TimeStepType timeStep) const override; /** * \brief Tests if all necessary informations are set and the object is valid */ virtual bool IsValid() const override; /** * \brief Initilizes a new object with one time steps which contains an empty geometry. */ virtual void Initialize() override; /** * \brief Expands the time geometry to the given number of time steps. * * Initializes the new time steps with empty geometries if no timesteps * in the geometry so far. Otherwise fills the new times steps with * clones of the first time step. * Shrinking is not supported. */ virtual void Expand(TimeStepType size) override; /** * \brief Sets the geometry for the given time step * * This method does not afflict other time steps, since the geometry for * each time step is saved individually. */ virtual void SetTimeStepGeometry(BaseGeometry *geometry, TimeStepType timeStep) override; /** * \brief Replaces the geometry instances with clones of 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); + virtual void ReplaceTimeStepGeometries(const BaseGeometry *geometry) override; /** * \brief Makes a deep copy of the current object */ virtual itk::LightObject::Pointer InternalClone() const override; itkGetConstMacro(FirstTimePoint, TimePointType); itkSetMacro(FirstTimePoint, TimePointType); itkGetConstMacro(StepDuration, TimePointType); itkSetMacro(StepDuration, TimePointType); // void SetGeometryForTimeStep(TimeStepType timeStep, BaseGeometry& geometry); void ClearAllGeometries(); // void AddGeometry(BaseGeometry geometry); void ReserveSpaceForGeometries(TimeStepType numberOfGeometries); /** * \brief Initializes the TimeGeometry with equally time Step geometries * * Saves a copy for each time step. */ void Initialize(const BaseGeometry *geometry, TimeStepType timeSteps); /** * \brief Initialize the TimeGeometry with empty BaseGeometry */ void Initialize(TimeStepType timeSteps); virtual void PrintSelf(std::ostream &os, itk::Indent indent) const override; protected: virtual ~ProportionalTimeGeometry(); std::vector m_GeometryVector; TimePointType m_FirstTimePoint; TimePointType m_StepDuration; }; // end class ProportialTimeGeometry /** * @brief Equal A function comparing two ProportionalTimeGeometries for being identical. * * @ingroup MITKTestingAPI * * The function compares two instances of ProportionalTimeGeometries 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::ProportionalTimeGeometry &leftHandSide, const mitk::ProportionalTimeGeometry &rightHandSide, ScalarType eps, bool verbose); } // end namespace MITK #endif // ProportionalTimeGeometry_h