diff --git a/Modules/Gizmo/src/mitkGizmoInteractor.cpp b/Modules/Gizmo/src/mitkGizmoInteractor.cpp index 67d7328900..10f7332b3a 100644 --- a/Modules/Gizmo/src/mitkGizmoInteractor.cpp +++ b/Modules/Gizmo/src/mitkGizmoInteractor.cpp @@ -1,461 +1,457 @@ /*=================================================================== 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 "mitkGizmoInteractor.h" #include "mitkGizmoMapper2D.h" // MITK includes #include #include #include #include #include #include #include #include #include #include // VTK includes #include #include #include #include #include #include #include #include #include mitk::GizmoInteractor::GizmoInteractor() { m_ColorForHighlight[0] = 1.0; m_ColorForHighlight[1] = 0.5; m_ColorForHighlight[2] = 0.0; m_ColorForHighlight[3] = 1.0; // TODO if we want to get this configurable, the this is the recipe: // - make the 2D mapper add corresponding properties to control "enabled" and "color" // - make the interactor evaluate those properties // - in an ideal world, modify the state machine on the fly and skip mouse move handling } mitk::GizmoInteractor::~GizmoInteractor() { } void mitk::GizmoInteractor::ConnectActionsAndFunctions() { CONNECT_CONDITION("PickedHandle", HasPickedHandle); CONNECT_FUNCTION("DecideInteraction", DecideInteraction); CONNECT_FUNCTION("MoveAlongAxis", MoveAlongAxis); CONNECT_FUNCTION("RotateAroundAxis", RotateAroundAxis); CONNECT_FUNCTION("MoveFreely", MoveFreely); CONNECT_FUNCTION("ScaleEqually", ScaleEqually); CONNECT_FUNCTION("FeedUndoStack", FeedUndoStack); } void mitk::GizmoInteractor::SetGizmoNode(DataNode *node) { DataInteractor::SetDataNode(node); m_Gizmo = dynamic_cast(node->GetData()); // setup picking from just this object m_Picker.clear(); } void mitk::GizmoInteractor::SetManipulatedObjectNode(DataNode *node) { if (node && node->GetData()) { m_ManipulatedObjectGeometry = node->GetData()->GetGeometry(); } } bool mitk::GizmoInteractor::HasPickedHandle(const InteractionEvent *interactionEvent) { auto positionEvent = dynamic_cast(interactionEvent); if (positionEvent == nullptr || m_Gizmo.IsNull() || m_ManipulatedObjectGeometry.IsNull() || interactionEvent->GetSender()->GetRenderWindow()->GetNeverRendered()) { return false; } if (interactionEvent->GetSender()->GetMapperID() == BaseRenderer::Standard2D) { m_PickedHandle = PickFrom2D(positionEvent); } else { m_PickedHandle = PickFrom3D(positionEvent); } UpdateHandleHighlight(); return m_PickedHandle != Gizmo::NoHandle; } void mitk::GizmoInteractor::DecideInteraction(StateMachineAction *, InteractionEvent *interactionEvent) { assert(m_PickedHandle != Gizmo::NoHandle); auto positionEvent = dynamic_cast(interactionEvent); if (positionEvent == nullptr) { return; } // if something relevant was picked, we calculate a number of // important points and axes for the upcoming geometry manipulations // note initial state m_InitialClickPosition2D = positionEvent->GetPointerPositionOnScreen(); m_InitialClickPosition3D = positionEvent->GetPositionInWorld(); auto renderer = positionEvent->GetSender()->GetVtkRenderer(); renderer->SetWorldPoint(m_InitialClickPosition3D[0], m_InitialClickPosition3D[1], m_InitialClickPosition3D[2], 0); renderer->WorldToDisplay(); m_InitialClickPosition2DZ = renderer->GetDisplayPoint()[2]; m_InitialGizmoCenter3D = m_Gizmo->GetCenter(); positionEvent->GetSender()->WorldToDisplay(m_InitialGizmoCenter3D, m_InitialGizmoCenter2D); m_InitialManipulatedObjectGeometry = m_ManipulatedObjectGeometry->Clone(); switch ( m_PickedHandle ) { case Gizmo::MoveAlongAxisX: case Gizmo::RotateAroundAxisX: case Gizmo::ScaleX: m_AxisOfMovement = m_InitialManipulatedObjectGeometry->GetAxisVector(0); break; case Gizmo::MoveAlongAxisY: case Gizmo::RotateAroundAxisY: case Gizmo::ScaleY: m_AxisOfMovement = m_InitialManipulatedObjectGeometry->GetAxisVector(1); break; case Gizmo::MoveAlongAxisZ: case Gizmo::RotateAroundAxisZ: case Gizmo::ScaleZ: m_AxisOfMovement = m_InitialManipulatedObjectGeometry->GetAxisVector(2); break; default: break; } m_AxisOfMovement.Normalize(); m_AxisOfRotation = m_AxisOfMovement; // for translation: test whether the user clicked into the "object's real" axis direction // or into the other one Vector3D intendedAxis = m_InitialClickPosition3D - m_InitialGizmoCenter3D; if ( intendedAxis * m_AxisOfMovement < 0 ) { m_AxisOfMovement *= -1.0; } // for rotation: test whether the axis of rotation is more looking in the direction // of the camera or in the opposite vtkCamera *camera = renderer->GetActiveCamera(); vtkVector3d cameraDirection(camera->GetDirectionOfProjection()); double angle_rad = vtkMath::AngleBetweenVectors(cameraDirection.GetData(), m_AxisOfRotation.GetDataPointer()); if ( angle_rad < vtkMath::Pi() / 2.0 ) { m_AxisOfRotation *= -1.0; } InternalEvent::Pointer decision; switch (m_PickedHandle) { case Gizmo::MoveAlongAxisX: case Gizmo::MoveAlongAxisY: case Gizmo::MoveAlongAxisZ: decision = InternalEvent::New(interactionEvent->GetSender(), this, "StartTranslationAlongAxis"); break; case Gizmo::RotateAroundAxisX: case Gizmo::RotateAroundAxisY: case Gizmo::RotateAroundAxisZ: decision = InternalEvent::New(interactionEvent->GetSender(), this, "StartRotationAroundAxis"); break; case Gizmo::MoveFreely: decision = InternalEvent::New(interactionEvent->GetSender(), this, "MoveFreely"); break; case Gizmo::ScaleX: case Gizmo::ScaleY: case Gizmo::ScaleZ: decision = InternalEvent::New(interactionEvent->GetSender(), this, "ScaleEqually"); break; default: break; } interactionEvent->GetSender()->GetDispatcher()->QueueEvent(decision); } void mitk::GizmoInteractor::MoveAlongAxis(StateMachineAction *, InteractionEvent *interactionEvent) { auto positionEvent = dynamic_cast(interactionEvent); if (positionEvent == nullptr) { return; } - Vector2D axisVector2D = m_InitialClickPosition2D - m_InitialGizmoCenter2D; - axisVector2D.Normalize(); - - Vector2D movement2D = positionEvent->GetPointerPositionOnScreen() - m_InitialClickPosition2D; - double relativeMovement = movement2D * axisVector2D; // projection - - Vector3D movement3D = relativeMovement * m_AxisOfMovement; + Vector3D mouseMovement3D = positionEvent->GetPositionInWorld() - m_InitialClickPosition3D; + double projectedMouseMovement3D = mouseMovement3D * m_AxisOfMovement; + Vector3D movement3D = projectedMouseMovement3D * m_AxisOfMovement; ApplyTranslationToManipulatedObject(movement3D); RenderingManager::GetInstance()->ForceImmediateUpdateAll(); } void mitk::GizmoInteractor::RotateAroundAxis(StateMachineAction *, InteractionEvent *interactionEvent) { auto positionEvent = dynamic_cast(interactionEvent); if (positionEvent == nullptr) { return; } Vector2D originalVector = m_InitialClickPosition2D - m_InitialGizmoCenter2D; Vector2D currentVector = positionEvent->GetPointerPositionOnScreen() - m_InitialGizmoCenter2D; originalVector.Normalize(); currentVector.Normalize(); double angle_rad = std::atan2(currentVector[1], currentVector[0]) - std::atan2(originalVector[1], originalVector[0]); ApplyRotationToManipulatedObject(vtkMath::DegreesFromRadians(angle_rad)); RenderingManager::GetInstance()->ForceImmediateUpdateAll(); } void mitk::GizmoInteractor::MoveFreely(StateMachineAction *, InteractionEvent *interactionEvent) { auto positionEvent = dynamic_cast(interactionEvent); if (positionEvent == nullptr) { return; } Point2D currentPosition2D = positionEvent->GetPointerPositionOnScreen(); // re-use the initial z value to really move parallel to the camera plane auto renderer = positionEvent->GetSender()->GetVtkRenderer(); renderer->SetDisplayPoint(currentPosition2D[0], currentPosition2D[1], m_InitialClickPosition2DZ); renderer->DisplayToWorld(); vtkVector3d worldPointVTK(renderer->GetWorldPoint()); Point3D worldPointITK(worldPointVTK.GetData()); Vector3D movementITK(worldPointITK - m_InitialClickPosition3D); ApplyTranslationToManipulatedObject(movementITK); RenderingManager::GetInstance()->ForceImmediateUpdateAll(); } void mitk::GizmoInteractor::ScaleEqually(StateMachineAction *, InteractionEvent *interactionEvent) { auto positionEvent = dynamic_cast(interactionEvent); if (positionEvent == nullptr) { return; } Point2D currentPosition2D = positionEvent->GetPointerPositionOnScreen(); double relativeSize = (currentPosition2D - m_InitialGizmoCenter2D).GetNorm() / (m_InitialClickPosition2D - m_InitialGizmoCenter2D).GetNorm(); ApplyEqualScalingToManipulatedObject(relativeSize); RenderingManager::GetInstance()->ForceImmediateUpdateAll(); } void mitk::GizmoInteractor::ApplyTranslationToManipulatedObject(const Vector3D &translation) { assert(m_ManipulatedObjectGeometry.IsNotNull()); auto manipulatedGeometry = m_InitialManipulatedObjectGeometry->Clone(); m_FinalDoOperation.reset(new PointOperation(OpMOVE, translation)); if (m_UndoEnabled) { m_FinalUndoOperation.reset(new PointOperation(OpMOVE, -translation)); } manipulatedGeometry->ExecuteOperation(m_FinalDoOperation.get()); m_ManipulatedObjectGeometry->SetIdentity(); m_ManipulatedObjectGeometry->Compose(manipulatedGeometry->GetIndexToWorldTransform()); } void mitk::GizmoInteractor::ApplyEqualScalingToManipulatedObject(double scalingFactor) { assert(m_ManipulatedObjectGeometry.IsNotNull()); auto manipulatedGeometry = m_InitialManipulatedObjectGeometry->Clone(); m_FinalDoOperation.reset(new ScaleOperation(OpSCALE, scalingFactor - 1.0, m_InitialGizmoCenter3D)); if (m_UndoEnabled) { m_FinalUndoOperation.reset(new ScaleOperation(OpSCALE, -(scalingFactor - 1.0), m_InitialGizmoCenter3D)); } manipulatedGeometry->ExecuteOperation(m_FinalDoOperation.get()); m_ManipulatedObjectGeometry->SetIdentity(); m_ManipulatedObjectGeometry->Compose(manipulatedGeometry->GetIndexToWorldTransform()); } void mitk::GizmoInteractor::ApplyRotationToManipulatedObject(double angle_deg) { assert(m_ManipulatedObjectGeometry.IsNotNull()); auto manipulatedGeometry = m_InitialManipulatedObjectGeometry->Clone(); m_FinalDoOperation.reset(new RotationOperation(OpROTATE, m_InitialGizmoCenter3D, m_AxisOfRotation, angle_deg)); if (m_UndoEnabled) { m_FinalUndoOperation.reset(new RotationOperation(OpROTATE, m_InitialGizmoCenter3D, m_AxisOfRotation, -angle_deg)); } manipulatedGeometry->ExecuteOperation(m_FinalDoOperation.get()); m_ManipulatedObjectGeometry->SetIdentity(); m_ManipulatedObjectGeometry->Compose(manipulatedGeometry->GetIndexToWorldTransform()); } void mitk::GizmoInteractor::FeedUndoStack(StateMachineAction *, InteractionEvent *) { if (m_UndoEnabled) { OperationEvent *operationEvent = new OperationEvent(m_ManipulatedObjectGeometry, // OperationEvent will destroy operations! // --> release() and not get() m_FinalDoOperation.release(), m_FinalUndoOperation.release(), "Direct geometry manipulation"); mitk::OperationEvent::IncCurrObjectEventId(); // save each modification individually m_UndoController->SetOperationEvent(operationEvent); } } mitk::Gizmo::HandleType mitk::GizmoInteractor::PickFrom2D(const InteractionPositionEvent *positionEvent) { BaseRenderer *renderer = positionEvent->GetSender(); auto mapper = GetDataNode()->GetMapper(BaseRenderer::Standard2D); auto gizmo_mapper = dynamic_cast(mapper); auto &picker = m_Picker[renderer]; if (picker == nullptr) { picker = vtkSmartPointer::New(); picker->SetTolerance(0.005); if (gizmo_mapper) { // doing this each time is bizarre picker->AddPickList(gizmo_mapper->GetVtkProp(renderer)); picker->PickFromListOn(); } } auto displayPosition = positionEvent->GetPointerPositionOnScreen(); picker->Pick(displayPosition[0], displayPosition[1], 0, positionEvent->GetSender()->GetVtkRenderer()); vtkIdType pickedPointID = picker->GetPointId(); if (pickedPointID == -1) { return Gizmo::NoHandle; } vtkPolyData *polydata = gizmo_mapper->GetVtkPolyData(renderer); if (polydata && polydata->GetPointData() && polydata->GetPointData()->GetScalars()) { double dataValue = polydata->GetPointData()->GetScalars()->GetTuple1(pickedPointID); return m_Gizmo->GetHandleFromPointDataValue(dataValue); } return Gizmo::NoHandle; } mitk::Gizmo::HandleType mitk::GizmoInteractor::PickFrom3D(const InteractionPositionEvent *positionEvent) { BaseRenderer *renderer = positionEvent->GetSender(); auto &picker = m_Picker[renderer]; if (picker == nullptr) { picker = vtkSmartPointer::New(); picker->SetTolerance(0.005); auto mapper = GetDataNode()->GetMapper(BaseRenderer::Standard3D); auto vtk_mapper = dynamic_cast(mapper); if (vtk_mapper) { // doing this each time is bizarre picker->AddPickList(vtk_mapper->GetVtkProp(renderer)); picker->PickFromListOn(); } } auto displayPosition = positionEvent->GetPointerPositionOnScreen(); picker->Pick(displayPosition[0], displayPosition[1], 0, positionEvent->GetSender()->GetVtkRenderer()); vtkIdType pickedPointID = picker->GetPointId(); if (pickedPointID == -1) { return Gizmo::NoHandle; } // _something_ picked return m_Gizmo->GetHandleFromPointID(pickedPointID); } void mitk::GizmoInteractor::UpdateHandleHighlight() { if (m_HighlightedHandle != m_PickedHandle) { auto node = GetDataNode(); if (node == nullptr) return; auto base_prop = node->GetProperty("LookupTable"); if (base_prop == nullptr) return; auto lut_prop = dynamic_cast(base_prop); if (lut_prop == nullptr) return; auto lut = lut_prop->GetLookupTable(); if (lut == nullptr) return; // Table size is expected to constructed as one entry per gizmo-part enum value assert(lut->GetVtkLookupTable()->GetNumberOfTableValues() > std::max(m_PickedHandle, m_HighlightedHandle)); // Reset previously overwritten color if (m_HighlightedHandle != Gizmo::NoHandle) { lut->SetTableValue(m_HighlightedHandle, m_ColorReplacedByHighlight); } // Overwrite currently highlighted color if (m_PickedHandle != Gizmo::NoHandle) { lut->GetTableValue(m_PickedHandle, m_ColorReplacedByHighlight); lut->SetTableValue(m_PickedHandle, m_ColorForHighlight); } // Mark node modified to allow repaint node->Modified(); RenderingManager::GetInstance()->RequestUpdateAll(RenderingManager::REQUEST_UPDATE_ALL); m_HighlightedHandle = m_PickedHandle; } } diff --git a/Modules/Gizmo/src/mitkGizmoMapper2D.cpp b/Modules/Gizmo/src/mitkGizmoMapper2D.cpp index d0703db556..b7ad640c67 100644 --- a/Modules/Gizmo/src/mitkGizmoMapper2D.cpp +++ b/Modules/Gizmo/src/mitkGizmoMapper2D.cpp @@ -1,341 +1,343 @@ /*=================================================================== 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 "mitkGizmoMapper2D.h" #include "mitkGizmo.h" // MITK includes #include +#include #include #include #include #include // VTK includes #include #include #include #include #include #include #include #include #include mitk::GizmoMapper2D::LocalStorage::LocalStorage() { m_VtkPolyDataMapper = vtkSmartPointer::New(); m_Actor = vtkSmartPointer::New(); m_Actor->SetMapper(m_VtkPolyDataMapper); } const mitk::Gizmo *mitk::GizmoMapper2D::GetInput() { return static_cast(GetDataNode()->GetData()); } void mitk::GizmoMapper2D::ResetMapper(mitk::BaseRenderer *renderer) { LocalStorage *ls = m_LSH.GetLocalStorage(renderer); ls->m_Actor->VisibilityOff(); } namespace { //! Helper method: will assign given value to all points in given polydata object. void AssignScalarValueTo(vtkPolyData *polydata, char value) { vtkSmartPointer pointData = vtkSmartPointer::New(); int numberOfPoints = polydata->GetNumberOfPoints(); pointData->SetNumberOfComponents(1); pointData->SetNumberOfTuples(numberOfPoints); pointData->FillComponent(0, value); polydata->GetPointData()->SetScalars(pointData); } //! Helper method: will create a vtkPolyData representing a disk //! around center, inside the plane defined by viewRight and viewUp, //! and with the given radius. vtkSmartPointer Create2DDisk(mitk::Vector3D viewRight, mitk::Vector3D viewUp, mitk::Point3D center, double radius) { // build the axis itself (as a tube around the line defining the axis) vtkSmartPointer disk = vtkSmartPointer::New(); mitk::Vector3D ringPointer; unsigned int numberOfRingPoints = 36; vtkSmartPointer ringPoints = vtkSmartPointer::New(); vtkSmartPointer ringPoly = vtkSmartPointer::New(); ringPoly->InsertNextCell(numberOfRingPoints + 1); for (unsigned int segment = 0; segment < numberOfRingPoints; ++segment) { double x = std::cos((double)(segment) / (double)numberOfRingPoints * 2.0 * vtkMath::Pi()); double y = std::sin((double)(segment) / (double)numberOfRingPoints * 2.0 * vtkMath::Pi()); ringPointer = viewRight * x + viewUp * y; ringPoints->InsertPoint(segment, (center + ringPointer * radius).GetDataPointer()); ringPoly->InsertCellPoint(segment); } ringPoly->InsertCellPoint(0); disk->SetPoints(ringPoints); disk->SetPolys(ringPoly); return disk; } //! Helper method: will create a vtkPolyData representing a 2D arrow //! that is oriented from arrowStart to arrowTip, orthogonal //! to camera direction. The arrow tip will contain scalar values //! of vertexValueScale, the arrow shaft will contain scalar values //! of vertexValueMove. Those values are used for picking during interaction. vtkSmartPointer Create2DArrow(mitk::Vector3D cameraDirection, mitk::Point3D arrowStart, mitk::Point3D arrowTip, int vertexValueMove, int vertexValueScale) { mitk::Vector3D arrowDirection = arrowTip - arrowStart; mitk::Vector3D arrowOrthogonal = itk::CrossProduct(cameraDirection, arrowDirection); arrowOrthogonal.Normalize(); double triangleFraction = 0.2; vtkSmartPointer arrow = vtkSmartPointer::New(); vtkSmartPointer points = vtkSmartPointer::New(); // shaft : points 0, 1 points->InsertPoint(0, arrowStart.GetDataPointer()); points->InsertPoint(1, (arrowStart + arrowDirection * (1.0 - triangleFraction)).GetDataPointer()); // tip : points 2, 3, 4 points->InsertPoint(2, arrowTip.GetDataPointer()); points->InsertPoint(3, (arrowStart + (1.0 - triangleFraction) * arrowDirection + arrowOrthogonal * (0.5 * triangleFraction * arrowDirection.GetNorm())) .GetDataPointer()); points->InsertPoint(4, (arrowStart + (1.0 - triangleFraction) * arrowDirection - arrowOrthogonal * (0.5 * triangleFraction * arrowDirection.GetNorm())) .GetDataPointer()); arrow->SetPoints(points); // define line connection for shaft vtkSmartPointer lines = vtkSmartPointer::New(); vtkIdType shaftLinePoints[] = {0, 1}; lines->InsertNextCell(2, shaftLinePoints); arrow->SetLines(lines); // define polygon for triangle vtkSmartPointer polys = vtkSmartPointer::New(); vtkIdType tipLinePoints[] = {2, 3, 4}; polys->InsertNextCell(3, tipLinePoints); arrow->SetPolys(polys); // assign scalar values vtkSmartPointer pointData = vtkSmartPointer::New(); pointData->SetNumberOfComponents(1); pointData->SetNumberOfTuples(5); pointData->FillComponent(0, vertexValueScale); pointData->SetTuple1(0, vertexValueMove); pointData->SetTuple1(1, vertexValueMove); arrow->GetPointData()->SetScalars(pointData); return arrow; } } vtkPolyData *mitk::GizmoMapper2D::GetVtkPolyData(mitk::BaseRenderer *renderer) { return m_LSH.GetLocalStorage(renderer)->m_VtkPolyDataMapper->GetInput(); } void mitk::GizmoMapper2D::GenerateDataForRenderer(mitk::BaseRenderer *renderer) { auto gizmo = GetInput(); auto node = GetDataNode(); LocalStorage *ls = m_LSH.GetLocalStorage(renderer); // check if something important has changed and we need to re-render if ((ls->m_LastUpdateTime >= node->GetMTime()) && (ls->m_LastUpdateTime >= gizmo->GetPipelineMTime()) && + (ls->m_LastUpdateTime >= renderer->GetCameraController()->GetMTime()) && (ls->m_LastUpdateTime >= renderer->GetCurrentWorldPlaneGeometryUpdateTime()) && (ls->m_LastUpdateTime >= renderer->GetCurrentWorldPlaneGeometry()->GetMTime()) && (ls->m_LastUpdateTime >= node->GetPropertyList()->GetMTime()) && (ls->m_LastUpdateTime >= node->GetPropertyList(renderer)->GetMTime())) { return; } ls->m_LastUpdateTime.Modified(); // some special handling around visibility: let two properties steer // visibility in 2D instead of many renderer specific "visible" properties bool visible2D = true; this->GetDataNode()->GetBoolProperty("show in 2D", visible2D); if (!visible2D && renderer->GetMapperID() == BaseRenderer::Standard2D) { ls->m_Actor->VisibilityOff(); return; } else { ls->m_Actor->VisibilityOn(); } auto camera = renderer->GetVtkRenderer()->GetActiveCamera(); auto plane = renderer->GetCurrentWorldPlaneGeometry(); Point3D gizmoCenterView = plane->ProjectPointOntoPlane(gizmo->GetCenter()); Vector3D viewUp; camera->GetViewUp(viewUp.GetDataPointer()); Vector3D cameraDirection; camera->GetDirectionOfProjection(cameraDirection.GetDataPointer()); Vector3D viewRight = itk::CrossProduct(viewUp, cameraDirection); auto appender = vtkSmartPointer::New(); - double diagonal = std::min(renderer->GetSizeX(), renderer->GetSizeY()); - double arrowLength = 0.1 * diagonal; // fixed in relation to window size + double diagonal = std::min(renderer->GetSizeX(), renderer->GetSizeY()) * renderer->GetScaleFactorMMPerDisplayUnit(); + double arrowLength = 0.3 * diagonal; // fixed in relation to window size auto disk = Create2DDisk(viewRight, viewUp, gizmoCenterView - cameraDirection, 0.1 * arrowLength); AssignScalarValueTo(disk, Gizmo::MoveFreely); appender->AddInputData(disk); // loop over directions -1 and +1 for arrows for (double direction = -1.0; direction < 2.0; direction += 2.0) { auto axisX = Create2DArrow(cameraDirection, gizmoCenterView, plane->ProjectPointOntoPlane(gizmo->GetCenter() + (gizmo->GetAxisX() * arrowLength) * direction), Gizmo::MoveAlongAxisX, Gizmo::ScaleX); appender->AddInputData(axisX); auto axisY = Create2DArrow(cameraDirection, gizmoCenterView, plane->ProjectPointOntoPlane(gizmo->GetCenter() + (gizmo->GetAxisY() * arrowLength) * direction), Gizmo::MoveAlongAxisY, Gizmo::ScaleY); appender->AddInputData(axisY); auto axisZ = Create2DArrow(cameraDirection, gizmoCenterView, plane->ProjectPointOntoPlane(gizmo->GetCenter() + (gizmo->GetAxisZ() * arrowLength) * direction), Gizmo::MoveAlongAxisZ, Gizmo::ScaleZ); appender->AddInputData(axisZ); } ls->m_VtkPolyDataMapper->SetInputConnection(appender->GetOutputPort()); ApplyVisualProperties(renderer); } void mitk::GizmoMapper2D::ApplyVisualProperties(BaseRenderer *renderer) { LocalStorage *ls = m_LSH.GetLocalStorage(renderer); float lineWidth = 3.0f; ls->m_Actor->GetProperty()->SetLineWidth(lineWidth); mitk::LookupTableProperty::Pointer lookupTableProp; this->GetDataNode()->GetProperty(lookupTableProp, "LookupTable", renderer); if (lookupTableProp.IsNotNull()) { ls->m_VtkPolyDataMapper->SetLookupTable(lookupTableProp->GetLookupTable()->GetVtkLookupTable()); } bool scalarVisibility = false; this->GetDataNode()->GetBoolProperty("scalar visibility", scalarVisibility); ls->m_VtkPolyDataMapper->SetScalarVisibility((scalarVisibility ? 1 : 0)); if (scalarVisibility) { mitk::VtkScalarModeProperty *scalarMode; if (this->GetDataNode()->GetProperty(scalarMode, "scalar mode", renderer)) ls->m_VtkPolyDataMapper->SetScalarMode(scalarMode->GetVtkScalarMode()); else ls->m_VtkPolyDataMapper->SetScalarModeToDefault(); bool colorMode = false; this->GetDataNode()->GetBoolProperty("color mode", colorMode); ls->m_VtkPolyDataMapper->SetColorMode((colorMode ? 1 : 0)); double scalarsMin = 0; this->GetDataNode()->GetDoubleProperty("ScalarsRangeMinimum", scalarsMin, renderer); double scalarsMax = 1.0; this->GetDataNode()->GetDoubleProperty("ScalarsRangeMaximum", scalarsMax, renderer); ls->m_VtkPolyDataMapper->SetScalarRange(scalarsMin, scalarsMax); } } void mitk::GizmoMapper2D::SetDefaultProperties(mitk::DataNode *node, mitk::BaseRenderer *renderer /*= nullptr*/, bool /*= false*/) { node->SetProperty("color", ColorProperty::New(0.3, 0.3, 0.3)); // a little lighter than the // "plane widgets" of // QmitkStdMultiWidget node->SetProperty("scalar visibility", BoolProperty::New(true), renderer); node->SetProperty("ScalarsRangeMinimum", DoubleProperty::New(0), renderer); node->SetProperty("ScalarsRangeMaximum", DoubleProperty::New((int)Gizmo::NoHandle), renderer); double colorMoveFreely[] = {1, 0, 0, 1}; // RGBA double colorAxisX[] = {0.753, 0, 0, 1}; // colors copied from QmitkStdMultiWidget to double colorAxisY[] = {0, 0.69, 0, 1}; // look alike double colorAxisZ[] = {0, 0.502, 1, 1}; double colorInactive[] = {0.7, 0.7, 0.7, 1}; // build a nice color table vtkSmartPointer lut = vtkSmartPointer::New(); lut->SetNumberOfTableValues((int)Gizmo::NoHandle + 1); lut->SetTableRange(0, (int)Gizmo::NoHandle); lut->SetTableValue(Gizmo::MoveFreely, colorMoveFreely); lut->SetTableValue(Gizmo::MoveAlongAxisX, colorAxisX); lut->SetTableValue(Gizmo::MoveAlongAxisY, colorAxisY); lut->SetTableValue(Gizmo::MoveAlongAxisZ, colorAxisZ); lut->SetTableValue(Gizmo::RotateAroundAxisX, colorAxisX); lut->SetTableValue(Gizmo::RotateAroundAxisY, colorAxisY); lut->SetTableValue(Gizmo::RotateAroundAxisZ, colorAxisZ); lut->SetTableValue(Gizmo::ScaleX, colorAxisX); lut->SetTableValue(Gizmo::ScaleY, colorAxisY); lut->SetTableValue(Gizmo::ScaleZ, colorAxisZ); lut->SetTableValue(Gizmo::NoHandle, colorInactive); mitk::LookupTable::Pointer mlut = mitk::LookupTable::New(); mlut->SetVtkLookupTable(lut); mitk::LookupTableProperty::Pointer lutProp = mitk::LookupTableProperty::New(); lutProp->SetLookupTable(mlut); node->SetProperty("LookupTable", lutProp, renderer); node->SetProperty("helper object", BoolProperty::New(true), renderer); node->SetProperty("visible", BoolProperty::New(true), renderer); node->SetProperty("show in 2D", BoolProperty::New(true), renderer); // no "show in 3D" because this would require a specialized mapper for gizmos in 3D }