Commit 9b8bc06c authored by lassoan's avatar lassoan
Browse files

ENH: Use widget for showing slice intersections

This allows slice intersection rotation and more flexible keyboard/mouse shortcut configuration in views. This commit only updates slice view.

Slice intersection translation/rotation shortcuts:
- Ctrl+Alt+LeftClick-and-drag rotates orthogonal slice views.
- Shift+Alt+LeftClick-and-drag moves slice intersection position (similar to Shift+MouseMove).

git-svn-id: http://svn.slicer.org/Slicer4/trunk@28010 3bd1e089-480b-0410-8dfb-8563597acbee
parent c6cfa1b6
......@@ -105,6 +105,12 @@ set(KIT_SRCS
vtkSliceViewInteractorStyle.cxx
vtkThreeDViewInteractorStyle.cxx
# Widgets
vtkMRMLAbstractWidget.cxx
vtkMRMLAbstractWidgetRepresentation.cxx
vtkSliceIntersectionWidget.cxx
vtkSliceIntersectionRepresentation2D.cxx
# Proxy classes
vtkMRMLLightBoxRendererManagerProxy.cxx
)
......
......@@ -108,6 +108,32 @@ void vtkMRMLAbstractSliceViewDisplayableManager::ConvertDeviceToXYZ(
xyz[2] = z;
}
//---------------------------------------------------------------------------
void vtkMRMLAbstractSliceViewDisplayableManager::ConvertDeviceToXYZ(
vtkRenderer * renderer, vtkMRMLSliceNode * sliceNode,
double x, double y, double xyz[3])
{
if (xyz == NULL || renderer == NULL || sliceNode == NULL)
{
return;
}
double windowWidth = renderer->GetRenderWindow()->GetSize()[0];
double windowHeight = renderer->GetRenderWindow()->GetSize()[1];
int numberOfColumns = sliceNode->GetLayoutGridColumns();
int numberOfRows = sliceNode->GetLayoutGridRows();
float tempX = x / windowWidth;
float tempY = (windowHeight - 1 - y) / windowHeight;
float z = floor(tempY*numberOfRows)*numberOfColumns + floor(tempX*numberOfColumns);
xyz[0] = x - renderer->GetOrigin()[0];
xyz[1] = y - renderer->GetOrigin()[1];
xyz[2] = z;
}
//---------------------------------------------------------------------------
void vtkMRMLAbstractSliceViewDisplayableManager::ConvertRASToXYZ(double ras[3], double xyz[3])
{
......
......@@ -57,6 +57,12 @@ public:
static void ConvertDeviceToXYZ(vtkRenderWindowInteractor * interactor,
vtkMRMLSliceNode * sliceNode, double x, double y, double xyz[3]);
/// Convenience function allowing to convert device coordinates (display) to XYZ coordinates (viewport).
/// Parameter \a xyz is double[3]
static void ConvertDeviceToXYZ(vtkRenderer * renderer,
vtkMRMLSliceNode * sliceNode, double x, double y, double xyz[3]);
/// Convert RAS to XYZ coordinates (viewport).
/// Parameters \a ras and \a xyz are double[3]. \a xyz[2] is the lightbox id.
/// \sa ConvertRASToXYZ(vtkMRMLSliceNode * sliceNode, double ras[3], double xyz[3])
......
/*=========================================================================
Copyright (c) ProxSim ltd., Kwun Tong, Hong Kong. All Rights Reserved.
See COPYRIGHT.txt
or http://www.slicer.org/copyright/copyright.txt for details.
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
This file was originally developed by Davide Punzo, punzodavide@hotmail.it,
and development was supported by ProxSim ltd.
=========================================================================*/
#include "vtkMRMLAbstractWidget.h"
#include "vtkMRMLApplicationLogic.h"
#include "vtkMRMLInteractionEventData.h"
#include "vtkMRMLInteractionNode.h"
#include "vtkMRMLScene.h"
#include "vtkMRMLSliceCompositeNode.h"
#include "vtkMRMLSliceNode.h"
#include "vtkMRMLSliceLogic.h"
#include "vtkMRMLAbstractWidgetRepresentation.h"
// VTK includes
#include "vtkRenderer.h"
#include "vtkRenderWindow.h"
#include "vtkWidgetEventTranslator.h"
//----------------------------------------------------------------------
vtkMRMLAbstractWidget::vtkMRMLAbstractWidget()
{
this->ApplicationLogic = NULL;
this->Renderer = NULL;
this->WidgetRep = NULL;
this->WidgetState = vtkMRMLAbstractWidget::WidgetStateIdle;
}
//----------------------------------------------------------------------
vtkMRMLAbstractWidget::~vtkMRMLAbstractWidget()
{
this->SetRenderer(nullptr);
}
//----------------------------------------------------------------------
void vtkMRMLAbstractWidget::SetEventTranslation(int widgetState, unsigned long interactionEvent, int modifiers, unsigned long widgetEvent)
{
if (widgetState >= this->EventTranslators.size())
{
this->EventTranslators.resize(widgetState +1);
}
vtkWidgetEventTranslator* translator = this->EventTranslators[widgetState];
if (!translator)
{
this->EventTranslators[widgetState] = vtkSmartPointer<vtkWidgetEventTranslator>::New();
translator = this->EventTranslators[widgetState];
}
vtkNew<vtkMRMLInteractionEventData> ed;
ed->SetType(interactionEvent);
ed->SetModifiers(modifiers);
translator->SetTranslation(interactionEvent, ed, widgetEvent);
}
//----------------------------------------------------------------------
void vtkMRMLAbstractWidget::SetEventTranslation(unsigned long interactionEvent, int modifiers, unsigned long widgetEvent)
{
this->SetEventTranslation(WidgetStateAny, interactionEvent, modifiers, widgetEvent);
}
//----------------------------------------------------------------------
void vtkMRMLAbstractWidget::SetEventTranslationClickAndDrag(int widgetState, unsigned long startInteractionEvent,
int modifiers, int widgetStateDragging,
unsigned long widgetStartEvent, unsigned long widgetEndEvent)
{
unsigned long endInteractionEvent = WidgetEventNone;
switch (startInteractionEvent)
{
case vtkCommand::LeftButtonPressEvent: endInteractionEvent = vtkCommand::LeftButtonReleaseEvent; break;
case vtkCommand::MiddleButtonPressEvent: endInteractionEvent = vtkCommand::MiddleButtonReleaseEvent; break;
case vtkCommand::RightButtonPressEvent: endInteractionEvent = vtkCommand::RightButtonReleaseEvent; break;
}
this->SetEventTranslation(widgetState, startInteractionEvent, modifiers, widgetStartEvent);
this->SetEventTranslation(widgetStateDragging, vtkCommand::MouseMoveEvent, vtkEvent::AnyModifier, WidgetEventMouseMove);
this->SetEventTranslation(widgetStateDragging, endInteractionEvent, vtkEvent::AnyModifier, widgetEndEvent);
}
//----------------------------------------------------------------------
void vtkMRMLAbstractWidget::SetKeyboardEventTranslation(
int widgetState, int modifier, char keyCode,
int repeatCount, const char* keySym, unsigned long widgetEvent)
{
if (widgetState >= this->EventTranslators.size())
{
this->EventTranslators.resize(widgetState +1);
}
vtkWidgetEventTranslator* translator = this->EventTranslators[widgetState];
if (!translator)
{
this->EventTranslators[widgetState] = vtkSmartPointer<vtkWidgetEventTranslator>::New();
translator = this->EventTranslators[widgetState];
}
translator->SetTranslation(vtkCommand::KeyPressEvent, modifier, keyCode,
repeatCount, keySym, widgetEvent);
}
//----------------------------------------------------------------------
void vtkMRMLAbstractWidget::SetKeyboardEventTranslation(
int modifier, char keyCode,
int repeatCount, const char* keySym, unsigned long widgetEvent)
{
this->SetKeyboardEventTranslation(WidgetStateAny, modifier, keyCode,
repeatCount, keySym, widgetEvent);
}
//-------------------------------------------------------------------------
void vtkMRMLAbstractWidget::SetRepresentation(vtkMRMLAbstractWidgetRepresentation *rep)
{
if (rep == this->WidgetRep)
{
// no change
return;
}
if (this->WidgetRep)
{
if (this->Renderer)
{
this->WidgetRep->SetRenderer(nullptr);
this->Renderer->RemoveViewProp(this->WidgetRep);
}
}
this->WidgetRep = rep;
if (this->Renderer != nullptr && this->WidgetRep != nullptr)
{
this->WidgetRep->SetRenderer(this->Renderer);
this->Renderer->AddViewProp(this->WidgetRep);
}
}
//-------------------------------------------------------------------------
vtkMRMLAbstractWidgetRepresentation* vtkMRMLAbstractWidget::GetRepresentation()
{
return this->WidgetRep;
}
//-------------------------------------------------------------------------
void vtkMRMLAbstractWidget::UpdateFromMRML(vtkMRMLNode* caller, unsigned long event, void *callData/*=NULL*/)
{
if (!this->WidgetRep)
{
return;
}
this->WidgetRep->UpdateFromMRML(caller, event, callData);
}
//----------------------------------------------------------------------
void vtkMRMLAbstractWidget::PrintSelf(ostream& os, vtkIndent indent)
{
this->Superclass::PrintSelf(os, indent);
os << indent << "WidgetState: " << this->WidgetState << endl;
}
//-----------------------------------------------------------------------------
unsigned long vtkMRMLAbstractWidget::TranslateInteractionEventToWidgetEvent(
vtkMRMLInteractionEventData* eventData)
{
if (!eventData)
{
return WidgetEventNone;
}
// Try to process with a state-specific translator
if (this->WidgetState < this->EventTranslators.size())
{
vtkWidgetEventTranslator* translator = this->EventTranslators[this->WidgetState];
if (translator)
{
unsigned long widgetEvent = this->TranslateInteractionEventToWidgetEvent(translator, eventData);
if (widgetEvent != WidgetEventNone)
{
return widgetEvent;
}
}
}
// Try to process with the state-independent translator
unsigned long widgetEvent = WidgetEventNone;
if (WidgetStateAny < this->EventTranslators.size())
{
vtkWidgetEventTranslator* translator = this->EventTranslators[WidgetStateAny];
if (translator)
{
// There is an event translator for this state
widgetEvent = this->TranslateInteractionEventToWidgetEvent(translator, eventData);
}
}
return widgetEvent;
}
//-----------------------------------------------------------------------------
unsigned long vtkMRMLAbstractWidget::TranslateInteractionEventToWidgetEvent(
vtkWidgetEventTranslator* translator, vtkMRMLInteractionEventData* eventData)
{
unsigned long widgetEvent = WidgetEventNone;
if (!eventData)
{
return widgetEvent;
}
if (eventData->GetType() == vtkCommand::KeyPressEvent)
{
// We package keypress events information into event data,
// unpack it for the event translator
int modifier = eventData->GetModifiers();
char keyCode = eventData->GetKeyCode();
int repeatCount = eventData->GetKeyRepeatCount();
const char* keySym = nullptr;
if (!eventData->GetKeySym().empty())
{
keySym = eventData->GetKeySym().c_str();
}
// If neither the ctrl nor the shift keys are pressed, give
// NoModifier a preference over AnyModifer.
if (modifier == vtkEvent::AnyModifier)
{
widgetEvent = translator->GetTranslation(vtkCommand::KeyPressEvent,
vtkEvent::NoModifier, keyCode, repeatCount, keySym);
}
if (widgetEvent == WidgetEventNone)
{
widgetEvent = translator->GetTranslation(vtkCommand::KeyPressEvent,
modifier, keyCode, repeatCount, keySym);
}
}
else
{
widgetEvent = translator->GetTranslation(eventData->GetType(), eventData);
}
return widgetEvent;
}
//-----------------------------------------------------------------------------
bool vtkMRMLAbstractWidget::CanProcessInteractionEvent(vtkMRMLInteractionEventData* eventData, double &distance2)
{
return false;
}
//-----------------------------------------------------------------------------
bool vtkMRMLAbstractWidget::ProcessInteractionEvent(vtkMRMLInteractionEventData* eventData)
{
return false;
}
//-----------------------------------------------------------------------------
void vtkMRMLAbstractWidget::Leave()
{
this->SetWidgetState(WidgetStateIdle);
}
//-------------------------------------------------------------------------
int vtkMRMLAbstractWidget::GetCursor()
{
if (this->WidgetState == WidgetStateIdle)
{
return VTK_CURSOR_DEFAULT;
}
else
{
return VTK_CURSOR_HAND;
}
}
//-------------------------------------------------------------------------
bool vtkMRMLAbstractWidget::GetGrabFocus()
{
// we need to grab focus when interactively dragging points
return this->GetInteractive();
}
//-------------------------------------------------------------------------
bool vtkMRMLAbstractWidget::GetInteractive()
{
switch (this->WidgetState)
{
case WidgetStateTranslate:
case WidgetStateScale:
case WidgetStateRotate:
return true;
default:
return false;
}
}
//-------------------------------------------------------------------------
bool vtkMRMLAbstractWidget::GetNeedToRender()
{
if (!this->WidgetRep)
{
return false;
}
return this->WidgetRep->GetNeedToRender();
}
//-------------------------------------------------------------------------
void vtkMRMLAbstractWidget::NeedToRenderOff()
{
if (!this->WidgetRep)
{
return;
}
this->WidgetRep->NeedToRenderOff();
}
//----------------------------------------------------------------------
void vtkMRMLAbstractWidget::SetRenderer(vtkRenderer* renderer)
{
if (renderer == this->Renderer)
{
return;
}
if (this->Renderer != nullptr && this->WidgetRep != nullptr)
{
this->Renderer->RemoveViewProp(this->WidgetRep);
}
this->Renderer = renderer;
if (this->WidgetRep != nullptr && this->Renderer != nullptr)
{
this->WidgetRep->SetRenderer(this->Renderer);
this->Renderer->AddViewProp(this->WidgetRep);
}
}
//---------------------------------------------------------------------------
const char* vtkMRMLAbstractWidget::GetAssociatedNodeID(vtkMRMLInteractionEventData* vtkNotUsed(eventData))
{
if (!this->WidgetRep)
{
return nullptr;
}
// is there a volume in the background?
vtkMRMLSliceNode* sliceNode = vtkMRMLSliceNode::SafeDownCast(this->WidgetRep->GetViewNode());
if (!sliceNode)
{
// this only works for slice views for now
return nullptr;
}
// find the slice composite node in the scene with the matching layout name
vtkMRMLApplicationLogic *mrmlAppLogic = this->GetMRMLApplicationLogic();
if (!mrmlAppLogic)
{
return nullptr;
}
vtkMRMLSliceLogic *sliceLogic = mrmlAppLogic->GetSliceLogic(sliceNode);
if (!sliceLogic)
{
return nullptr;
}
vtkMRMLSliceCompositeNode* sliceCompositeNode = sliceLogic->GetSliceCompositeNode(sliceNode);
if (!sliceCompositeNode)
{
return nullptr;
}
if (sliceCompositeNode->GetBackgroundVolumeID())
{
return sliceCompositeNode->GetBackgroundVolumeID();
}
else if (sliceCompositeNode->GetForegroundVolumeID())
{
return sliceCompositeNode->GetForegroundVolumeID();
}
else if (sliceCompositeNode->GetLabelVolumeID())
{
return sliceCompositeNode->GetLabelVolumeID();
}
return nullptr;
}
//---------------------------------------------------------------------------
void vtkMRMLAbstractWidget::SetMRMLApplicationLogic(vtkMRMLApplicationLogic* applicationLogic)
{
this->ApplicationLogic = applicationLogic;
}
//---------------------------------------------------------------------------
vtkMRMLApplicationLogic* vtkMRMLAbstractWidget::GetMRMLApplicationLogic()
{
return this->ApplicationLogic;
}
//---------------------------------------------------------------------------
vtkMRMLInteractionNode* vtkMRMLAbstractWidget::GetInteractionNode()
{
if (!this->WidgetRep)
{
return nullptr;
}
vtkMRMLAbstractViewNode* viewNode = this->WidgetRep->GetViewNode();
if (!viewNode)
{
return nullptr;
}
return viewNode->GetInteractionNode();
}
/*=========================================================================
Copyright (c) ProxSim ltd., Kwun Tong, Hong Kong. All Rights Reserved.
See COPYRIGHT.txt
or http://www.slicer.org/copyright/copyright.txt for details.
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
This file was originally developed by Davide Punzo, punzodavide@hotmail.it,
and development was supported by ProxSim ltd.
=========================================================================*/
/**
* @class vtkMRMLAbstractWidget
* @brief Process interaction events to update state of MRML widget nodes
*
* vtkMRMLAbstractWidget is the abstract class that handles interaction events
* received from the displayable manager. To decide which widget gets the chance
* to process an interaction event, this class does not use VTK picking manager,
* but interactor style class queries displayable managers about what events they can
* process, displayable manager queries its widgets, and based on the returned
* information, interactor style selects a displayable manager and the displayable
* manager selects a widget.
*
* vtkAbstractWidget uses vtkSlicerWidgetEventTranslator to translate VTK
* interaction events (defined in vtkCommand.h) into widget events (defined in
* vtkMRMLAbstractWidget.h and subclasses). This class allows modification
* of event bindings.
*
* @sa
* vtkSlicerWidgetRepresentation vtkSlicerWidgetEventTranslator
*
*/
#ifndef vtkMRMLAbstractWidget_h
#define vtkMRMLAbstractWidget_h
#include "vtkMRMLDisplayableManagerExport.h"
#include "vtkObject.h"
#include "vtkSmartPointer.h"
#include <vector>
class vtkMRMLAbstractViewNode;
class vtkMRMLApplicationLogic;
class vtkMRMLInteractionEventData;
class vtkMRMLInteractionNode;
class vtkMRMLNode;
class vtkMRMLAbstractWidgetRepresentation;
class vtkRenderer;
class vtkWidgetEventTranslator;
class VTK_MRML_DISPLAYABLEMANAGER_EXPORT vtkMRMLAbstractWidget : public vtkObject
{
public:
/// Standard methods for a VTK class.
vtkTypeMacro(vtkMRMLAbstractWidget, vtkObject);
virtual void PrintSelf(ostream& os, vtkIndent indent) VTK_OVERRIDE;
virtual void SetMRMLApplicationLogic(vtkMRMLApplicationLogic* applicationLogic);
vtkMRMLApplicationLogic* GetMRMLApplicationLogic();
/// Set the representation.
/// The widget takes over the ownership of this actor.
virtual void SetRepresentation(vtkMRMLAbstractWidgetRepresentation *r);
/// Get the representation
virtual vtkMRMLAbstractWidgetRepresentation *GetRepresentation();
/// Build the actors of the representation with the info stored in the MRML scene
virtual void UpdateFromMRML(vtkMRMLNode* caller, unsigned long event, void *callData = NULL);
/// Convenient method to change what state the widget is in.
vtkSetMacro(WidgetState,int);
/// Convenient method to determine the state of the method
vtkGetMacro(WidgetState,int);
/// The state of the widget
enum
{
WidgetStateAny, // this state is used for referring to any widget state (for defining event translations)
WidgetStateIdle, // mouse pointer is outside the widget, click does not do anything
WidgetStateOnWidget, // mouse pointer is over the widget, clicking will add a point or manipulate the line
WidgetStateTranslate, // mouse move transforms the entire widget
WidgetStateRotate, // mouse move transforms the entire widget
WidgetStateScale, // mouse move transforms the entire widget
WidgetStateUser // this is a starting index that can be used for widget-specific states
};
/// Widget events
enum WidgetEvents
{
WidgetEventNone,
WidgetEventMouseMove,
WidgetEventTranslateStart,
WidgetEventTranslateEnd,
WidgetEventRotateStart,
WidgetEventRotateEnd,
WidgetEventScaleStart,
WidgetEventScaleEnd,
// MRML events
WidgetEventPick, // generates a MRML Pick event (e.g., on left click)
WidgetEventJumpCursor, // jumps cursor to the selected position