Skip to content

Commit

Permalink
ENH: Add jump to handle action for interaction handles
Browse files Browse the repository at this point in the history
This commit enables the jump to handle action when left-clicking on an interaction handle.

For Transform nodes:
  - Jump is only available when clicking on the center of transformation.

For Markups nodes:
  - Jump is available for any handle type.

Re #7570
  • Loading branch information
Sunderlandkyl authored and lassoan committed Feb 20, 2024
1 parent 325fdbd commit ae08d0d
Show file tree
Hide file tree
Showing 5 changed files with 125 additions and 42 deletions.
81 changes: 71 additions & 10 deletions Libs/MRML/DisplayableManager/vtkMRMLInteractionWidget.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@

// MRML includes
#include <vtkMRMLDisplayNode.h>
#include <vtkMRMLScene.h>
#include <vtkMRMLSliceNode.h>

//----------------------------------------------------------------------
Expand All @@ -48,22 +49,25 @@ vtkMRMLInteractionWidget::vtkMRMLInteractionWidget()
this->SetEventTranslation(WidgetStateIdle, vtkCommand::MouseMoveEvent, vtkEvent::AnyModifier, WidgetEventMouseMove);
this->SetEventTranslation(WidgetStateIdle, vtkCommand::Move3DEvent, vtkEvent::AnyModifier, WidgetEventMouseMove);

// Handle interactions
// Translation
this->SetEventTranslationClickAndDrag(WidgetStateOnTranslationHandle, vtkCommand::LeftButtonPressEvent, vtkEvent::NoModifier,
WidgetStateTranslate, WidgetEventTranslateStart, WidgetEventTranslateEnd);

// Rotation
this->SetEventTranslationClickAndDrag(WidgetStateOnRotationHandle, vtkCommand::LeftButtonPressEvent, vtkEvent::NoModifier,
WidgetStateRotate, WidgetEventRotateStart, WidgetEventRotateEnd);

this->SetEventTranslationClickAndDrag(WidgetStateOnScaleHandle, vtkCommand::LeftButtonPressEvent, vtkEvent::AltModifier,
WidgetStateUniformScale, WidgetEventUniformScaleStart, WidgetEventUniformScaleEnd);

// Scale
this->SetEventTranslationClickAndDrag(WidgetStateOnScaleHandle, vtkCommand::LeftButtonPressEvent, vtkEvent::NoModifier,
WidgetStateScale, WidgetEventScaleStart, WidgetEventScaleEnd);

unsigned int interactionHandleStates[] = { WidgetStateOnTranslationHandle, WidgetStateOnRotationHandle, WidgetStateOnScaleHandle };
for (unsigned int interactionHandleState : interactionHandleStates)
// Uniform scale
this->SetEventTranslationClickAndDrag(WidgetStateOnScaleHandle, vtkCommand::LeftButtonPressEvent, vtkEvent::AltModifier,
WidgetStateUniformScale, WidgetEventUniformScaleStart, WidgetEventUniformScaleEnd);

for (unsigned int interactionHandleState = WidgetStateInteraction_First; interactionHandleState < WidgetStateInteraction_Last; ++interactionHandleState)
{
// Jump slices
this->SetEventTranslation(interactionHandleState, vtkMRMLInteractionEventData::LeftButtonClickEvent, vtkEvent::NoModifier, WidgetEventJumpCursor);

// Context menu events
Expand Down Expand Up @@ -307,18 +311,17 @@ bool vtkMRMLInteractionWidget::ProcessWidgetMenuDisplayNodeTypeAndIndex(
}

//-------------------------------------------------------------------------
bool vtkMRMLInteractionWidget::ProcessEndMouseDrag(vtkMRMLInteractionEventData* vtkNotUsed(eventData))
bool vtkMRMLInteractionWidget::ProcessEndMouseDrag(vtkMRMLInteractionEventData* eventData)
{
if (!this->WidgetRep)
{
return false;
}

if ((this->WidgetState != vtkMRMLInteractionWidget::WidgetStateTranslate
if ( this->WidgetState != vtkMRMLInteractionWidget::WidgetStateTranslate
&& this->WidgetState != vtkMRMLInteractionWidget::WidgetStateScale
&& this->WidgetState != vtkMRMLInteractionWidget::WidgetStateUniformScale
&& this->WidgetState != vtkMRMLInteractionWidget::WidgetStateRotate
) || !this->WidgetRep)
&& this->WidgetState != vtkMRMLInteractionWidget::WidgetStateRotate)
{
return false;
}
Expand All @@ -338,6 +341,48 @@ bool vtkMRMLInteractionWidget::ProcessEndMouseDrag(vtkMRMLInteractionEventData*
}

this->EndWidgetInteraction();

// only claim this as processed if the mouse was moved (this allows the event to be interpreted as button click)
bool processedEvent = eventData->GetMouseMovedSinceButtonDown();
return processedEvent;
}

//-------------------------------------------------------------------------
bool vtkMRMLInteractionWidget::ProcessJumpCursor(vtkMRMLInteractionEventData* vtkNotUsed(eventData))
{
int type = this->GetActiveComponentType();
int index = this->GetActiveComponentIndex();
return this->JumpToHandlePosition(type, index);
}

//-------------------------------------------------------------------------
bool vtkMRMLInteractionWidget::JumpToHandlePosition(int type, int index)
{
vtkMRMLInteractionWidgetRepresentation* rep = vtkMRMLInteractionWidgetRepresentation::SafeDownCast(this->GetRepresentation());
if (!rep)
{
return false;
}

vtkMRMLAbstractViewNode* viewNode = rep->GetViewNode();
if (!viewNode)
{
return false;
}

vtkMRMLScene* scene = viewNode->GetScene();
if (!scene)
{
return false;
}

double jumpPosition_World[3] = { 0.0, 0.0, 0.0 };
rep->GetInteractionHandlePositionWorld(type, index, jumpPosition_World);

int viewGroup = rep->GetViewNode()->GetViewGroup();
vtkMRMLSliceNode::JumpAllSlices(scene, jumpPosition_World[0], jumpPosition_World[1], jumpPosition_World[2],
-1, viewGroup, vtkMRMLSliceNode::SafeDownCast(viewNode));

return true;
}

Expand All @@ -346,6 +391,11 @@ bool vtkMRMLInteractionWidget::ProcessInteractionEvent(vtkMRMLInteractionEventDa
{
unsigned long widgetEvent = this->TranslateInteractionEventToWidgetEvent(eventData);

if (this->ApplicationLogic)
{
this->ApplicationLogic->PauseRender();
}

bool processedEvent = false;
switch (widgetEvent)
{
Expand Down Expand Up @@ -379,12 +429,23 @@ bool vtkMRMLInteractionWidget::ProcessInteractionEvent(vtkMRMLInteractionEventDa
case WidgetEventUniformScaleEnd:
processedEvent = ProcessEndMouseDrag(eventData);
break;
case WidgetEventJumpCursor:
processedEvent = ProcessJumpCursor(eventData);
break;
default:
break;
}

if (!processedEvent)
{
processedEvent = this->ProcessButtonClickEvent(eventData);
}

if (this->ApplicationLogic)
{
this->ApplicationLogic->ResumeRender();
}

return processedEvent;
}

Expand Down
11 changes: 6 additions & 5 deletions Libs/MRML/DisplayableManager/vtkMRMLInteractionWidget.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,8 @@ class VTK_MRML_DISPLAYABLEMANAGER_EXPORT vtkMRMLInteractionWidget
/// Widget states
enum
{
WidgetStateOnTranslationHandle = WidgetStateUser, // hovering over a translation interaction handle
WidgetStateInteraction_First = WidgetStateUser,
WidgetStateOnTranslationHandle = WidgetStateInteraction_First, // hovering over a translation interaction handle
WidgetStateOnRotationHandle, // hovering over a rotation interaction handle
WidgetStateOnScaleHandle, // hovering over a scale interaction handle
WidgetStateInteraction_Last
Expand Down Expand Up @@ -127,6 +128,10 @@ class VTK_MRML_DISPLAYABLEMANAGER_EXPORT vtkMRMLInteractionWidget
virtual bool ProcessWidgetScaleStart(vtkMRMLInteractionEventData* eventData);
virtual bool ProcessWidgetUniformScaleStart(vtkMRMLInteractionEventData* eventData);
virtual bool ProcessEndMouseDrag(vtkMRMLInteractionEventData* eventData);
virtual bool ProcessJumpCursor(vtkMRMLInteractionEventData* eventData);

// Jump to the handle position for the given type and index. Returns true if successful.
virtual bool JumpToHandlePosition(int type, int index);

/// Get the closest point on the line defined by the interaction handle axis.
/// Input coordinates are in display coordinates, while output are in world coordinates.
Expand All @@ -146,10 +151,6 @@ class VTK_MRML_DISPLAYABLEMANAGER_EXPORT vtkMRMLInteractionWidget
private:
vtkMRMLInteractionWidget(const vtkMRMLInteractionWidget&) = delete;
void operator=(const vtkMRMLInteractionWidget&) = delete;

int TranslationHandleType{InteractionTranslationHandle};
int RotationHandleType {InteractionRotationHandle};
int ScaleHandleType {InteractionScaleHandle};
};

#endif
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,30 @@ class VTK_MRML_DISPLAYABLEMANAGER_EXPORT vtkMRMLInteractionWidgetRepresentation
vtkGetMacro(Interacting, bool);
vtkBooleanMacro(Interacting, bool);

/// Get the color of the specified handle
/// Type is specified using InteractionType enum
virtual void GetHandleColor(int type, int index, double color[4]);
/// Get the opacity of the specified handle
virtual double GetHandleOpacity(int type, int index);
/// Get the visibility of the specified handle
virtual bool GetHandleVisibility(int type, int index);
/// Get the type of glyph (Arrow, Circle, Ring, etc.) of the specified handle.
virtual int GetHandleGlyphType(int type, int index);
/// Get if the view scaling should be applied to the position of the handle.
virtual bool GetApplyScaleToPosition(int type, int index);
/// Get the position of the interaction handle in local coordinates
/// Type is specified using vtkMRMLInteractionDisplayNode::ComponentType
virtual void GetInteractionHandlePositionLocal(int type, int index, double position[3]);
/// Get the position of the interaction handle in world coordinates
/// Type is specified using vtkMRMLInteractionDisplayNode::ComponentType
virtual void GetInteractionHandlePositionWorld(int type, int index, double position[3]);

///@{
/// Update HandleToWorldTransform. This controls the position and orientation of the interaction handles.
virtual void UpdateHandleToWorldTransform();
virtual void UpdateHandleToWorldTransform(vtkTransform* handleToWorldTransform) = 0;
///@}

protected:
vtkMRMLInteractionWidgetRepresentation();
~vtkMRMLInteractionWidgetRepresentation() override;
Expand Down Expand Up @@ -260,36 +284,11 @@ class VTK_MRML_DISPLAYABLEMANAGER_EXPORT vtkMRMLInteractionWidgetRepresentation

/// Set the scale of the interaction handles in world coordinates
virtual void SetWidgetScale(double scale);
/// Get the color of the specified handle
/// Type is specified using InteractionType enum
virtual void GetHandleColor(int type, int index, double color[4]);
/// Get the opacity of the specified handle
virtual double GetHandleOpacity(int type, int index);
/// Get the visibility of the specified handle
virtual bool GetHandleVisibility(int type, int index);
/// Get the type of glyph (Arrow, Circle, Ring, etc.) of the specified handle.
virtual int GetHandleGlyphType(int type, int index);
/// Get if the view scaling should be applied to the position of the handle.
virtual bool GetApplyScaleToPosition(int type, int index);

/// Get the vector from the interaction handle to the camera in world coordinates.
/// In slice views and in 3D with parallel projection this is the same as the camera view direction.
virtual void GetHandleToCameraVectorWorld(double handlePosition_World[3], double normal_World[3]);

/// Get the position of the interaction handle in local coordinates
/// Type is specified using vtkMRMLInteractionDisplayNode::ComponentType
virtual void GetInteractionHandlePositionLocal(int type, int index, double position[3]);

/// Get the position of the interaction handle in world coordinates
/// Type is specified using vtkMRMLInteractionDisplayNode::ComponentType
virtual void GetInteractionHandlePositionWorld(int type, int index, double position[3]);

///@{
/// Update HandleToWorldTransform. This controls the position and orientation of the interaction handles.
virtual void UpdateHandleToWorldTransform();
virtual void UpdateHandleToWorldTransform(vtkTransform* handleToWorldTransform) = 0;
///@}

/// Orthogonalize the transform axes. The Z-axis will not be changed.
virtual void OrthoganalizeTransform(vtkTransform* transform);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,10 @@ bool vtkMRMLTransformHandleWidget::ProcessEndMouseDrag(vtkMRMLInteractionEventDa
}

this->EndWidgetInteraction();
return true;

// only claim this as processed if the mouse was moved (this allows the event to be interpreted as button click)
bool processedEvent = eventData->GetMouseMovedSinceButtonDown();
return processedEvent;
}

//-------------------------------------------------------------------------
Expand Down Expand Up @@ -393,3 +396,21 @@ void vtkMRMLTransformHandleWidget::TranslateTransformCenter(double eventPos[2])
vtkMath::Add(transform->TransformVectorAtPoint(origin_World, translationVector_World), centerOfTransformation, centerOfTransformation);
this->GetTransformNode()->SetCenterOfTransformation(centerOfTransformation);
}

//-------------------------------------------------------------------------
bool vtkMRMLTransformHandleWidget::ProcessJumpCursor(vtkMRMLInteractionEventData* eventData)
{
int type = this->GetActiveComponentType();
if (type != InteractionTranslationHandle)
{
return false;
}

int index = this->GetActiveComponentIndex();
if (index != 3)
{
return false;
}

return Superclass::ProcessJumpCursor(eventData);
}
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ class VTK_SLICER_TRANSFORMS_MODULE_MRMLDISPLAYABLEMANAGER_EXPORT vtkMRMLTransfor

enum
{
WidgetEventTranslateTransformCenterStart = WidgetStateInteraction_Last,
WidgetEventTranslateTransformCenterStart = WidgetEventInteraction_Last,
WidgetEventTranslateTransformCenterEnd,
};

Expand All @@ -86,6 +86,7 @@ class VTK_SLICER_TRANSFORMS_MODULE_MRMLDISPLAYABLEMANAGER_EXPORT vtkMRMLTransfor
bool ProcessMouseMove(vtkMRMLInteractionEventData* eventData) override;
bool ProcessEndMouseDrag(vtkMRMLInteractionEventData* eventData) override;
bool ProcessWidgetMenu(vtkMRMLInteractionEventData* eventData) override;
bool ProcessJumpCursor(vtkMRMLInteractionEventData* eventData) override;

virtual void TranslateTransformCenter(double eventPos[2]);

Expand Down

0 comments on commit ae08d0d

Please sign in to comment.