fw4spl
fwVtkWheelWidget.cpp
1 /* ***** BEGIN LICENSE BLOCK *****
2  * FW4SPL - Copyright (C) IRCAD, 2017-2018.
3  * Distributed under the terms of the GNU Lesser General Public License (LGPL) as
4  * published by the Free Software Foundation.
5  * ****** END LICENSE BLOCK ****** */
6 
7 #include "fwRenderVTK/vtk/fwVtkWheelWidget.hpp"
8 
9 #include <fwServices/macros.hpp>
10 
11 #include <glm/geometric.hpp>
12 #include <glm/gtc/vec1.hpp> // hack, some glm functions won't compile without this.
13 #include <glm/vec2.hpp>
14 
15 #include <vtkActor2D.h>
16 #include <vtkCallbackCommand.h>
17 #include <vtkObjectFactory.h>
18 #include <vtkRenderer.h>
19 #include <vtkRenderWindow.h>
20 #include <vtkRenderWindowInteractor.h>
21 #include <vtkWidgetCallbackMapper.h>
22 #include <vtkWidgetEvent.h>
23 
24 vtkStandardNewMacro(fwVtkWheelWidget);
25 
26 //----------------------------------------------------------------------------------
27 
28 fwVtkWheelWidget::fwVtkWheelWidget()
29 {
30  // Set the initial state
31  this->WidgetState = fwVtkWheelWidget::Hovering;
32 
33  // Okay, define the events
34  this->CallbackMapper->SetCallbackMethod(vtkCommand::LeftButtonPressEvent,
35  vtkWidgetEvent::Select,
36  this, fwVtkWheelWidget::SelectAction);
37  this->CallbackMapper->SetCallbackMethod(vtkCommand::MouseMoveEvent,
38  vtkWidgetEvent::Move,
39  this, fwVtkWheelWidget::MoveAction);
40  this->CallbackMapper->SetCallbackMethod(vtkCommand::LeftButtonReleaseEvent,
41  vtkWidgetEvent::EndSelect,
42  this, fwVtkWheelWidget::EndSelectAction);
43 
44  // Default update callback, crashes the program.
45  m_wheelUpdateCallback = [] (double, double, double)
46  {
47  OSLM_FATAL("No update callback set.");
48  };
49 }
50 
51 //----------------------------------------------------------------------
52 
54 {
55  if( !this->WidgetRep )
56  {
57  this->WidgetRep = fwVtkWheelRepresentation::New();
58  }
59 }
60 
61 //----------------------------------------------------------------------------------
62 
63 void fwVtkWheelWidget::SetWheelUpdateCallback(std::function<void (double, double, double)> f)
64 {
65  m_wheelUpdateCallback = f;
66 }
67 
68 //----------------------------------------------------------------------------------
69 
70 void fwVtkWheelWidget::MoveAction(vtkAbstractWidget* w)
71 {
72  fwVtkWheelWidget* self = reinterpret_cast<fwVtkWheelWidget*>(w);
73 
74  int x = self->Interactor->GetEventPosition()[0];
75  int y = self->Interactor->GetEventPosition()[1];
76 
77  // Motion while selecting is ignored
78  bool renderRequired = false;
79 
80  fwVtkWheelRepresentation* widgetRep = dynamic_cast<fwVtkWheelRepresentation*>(self->WidgetRep);
81 
82  SLM_ASSERT("Widget representation is not a wheel.", widgetRep);
83 
84  if ( self->WidgetState == fwVtkWheelWidget::Selecting )
85  {
86  const auto& actPos = widgetRep->GetWheelActor()->GetPosition();
87  const int* viewportSize = self->GetRepresentation()->GetRenderer()->GetRenderWindow()->GetSize();
88 
89  x = ::glm::clamp(x, 0, viewportSize[0]);
90  y = ::glm::clamp(y, 0, viewportSize[1]);
91 
92  widgetRep->GetWheelActor()->SetPosition(x - self->m_initMouseX + actPos[0], y - self->m_initMouseY + actPos[1]);
93  self->m_initMouseX = x;
94  self->m_initMouseY = y;
95 
96  const ::glm::dvec2 center = widgetRep->GetCenterInScreenSpace();
97  const double orientation = widgetRep->GetOrientation();
98  self->m_wheelUpdateCallback(center.x, center.y, orientation);
99  self->EventCallbackCommand->SetAbortFlag(1);
100 
101  renderRequired = true;
102  }
103  else if( self->WidgetState == fwVtkWheelWidget::Rotating)
104  {
105  const ::glm::dvec2 center = widgetRep->GetCenterInScreenSpace();
106 
107  const auto v1 = ::glm::normalize(::glm::dvec2(self->m_initMouseX - center.x, self->m_initMouseY - center.y));
108  const auto v2 = ::glm::normalize(::glm::dvec2(x - center.x, y - center.y));
109 
110  const double vectProd = v1.x * v2.y - v1.y * v2.x;
111  const double vectDot = ::glm::dot(v1, v2);
112  const double angleSin = std::asin(vectProd);
113  const double angleCos = std::acos(vectDot);
114 
115  const double angle = ::glm::sign(angleSin) * angleCos;
116 
117  widgetRep->SetOrientation(self->m_initOrientation + angle);
118 
119  const double orientation = widgetRep->GetOrientation();
120  self->m_wheelUpdateCallback(center.x, center.y, orientation);
121  self->EventCallbackCommand->SetAbortFlag(1);
122 
123  renderRequired = true;
124  }
125  else if ( self->WidgetState == fwVtkWheelWidget::Hovering )
126  {
127  widgetRep->SetHovering( widgetRep->isOnWheel(x, y) || widgetRep->isInCenter(x, y) );
128  renderRequired = true;
129  }
130 
131  if( renderRequired )
132  {
133  self->Render();
134  }
135 }
136 
137 //----------------------------------------------------------------------------------
138 
139 void fwVtkWheelWidget::SelectAction(vtkAbstractWidget* w)
140 {
141  fwVtkWheelWidget* self = reinterpret_cast<fwVtkWheelWidget*>(w);
142 
143  const int x = self->Interactor->GetEventPosition()[0];
144  const int y = self->Interactor->GetEventPosition()[1];
145 
146  const fwVtkWheelRepresentation* widgetRep = dynamic_cast<fwVtkWheelRepresentation*>(self->WidgetRep);
147 
148  SLM_ASSERT("Widget representation is not a wheel.", widgetRep);
149 
150  if(widgetRep->isInCenter(x, y))
151  {
152  self->WidgetState = ::fwVtkWheelWidget::Selecting;
153  self->m_initMouseX = x;
154  self->m_initMouseY = y;
155  self->EventCallbackCommand->SetAbortFlag(1);
156  }
157  else if(widgetRep->isOnWheel(x, y))
158  {
159  self->WidgetState = ::fwVtkWheelWidget::Rotating;
160  self->m_initMouseX = x;
161  self->m_initMouseY = y;
162  self->m_initOrientation = widgetRep->GetOrientation();
163  self->EventCallbackCommand->SetAbortFlag(1);
164  }
165 
166  self->Render();
167 }
168 
169 //----------------------------------------------------------------------------------
170 
171 void fwVtkWheelWidget::EndSelectAction(vtkAbstractWidget* w)
172 {
173  fwVtkWheelWidget* self = reinterpret_cast<fwVtkWheelWidget*>(w);
174 
175  self->WidgetState = fwVtkWheelWidget::Hovering;
176 
177  self->Render();
178 }
179 
180 //----------------------------------------------------------------------------------
181 
182 void fwVtkWheelWidget::PrintSelf(ostream&, vtkIndent)
183 {
184  SLM_WARN("Not implemented.");
185 }
186 
187 //----------------------------------------------------------------------------------
188 
190 {
191  this->WidgetRep = rep;
192 }
193 
194 //----------------------------------------------------------------------------------
195 
197 {
198  return dynamic_cast<fwVtkWheelRepresentation*>(this->WidgetRep);
199 }
static FWRENDERVTK_API fwVtkWheelRepresentation * New()
Calls the constructor. Initializes wheel geometry and actor.
double GetOrientation() const
Returns the orientation in radians.
FWRENDERVTK_API void SetRepresentation(fwVtkWheelRepresentation *rep)
Set associated representation.
Implements the interactions with the wheel widget.
FWRENDERVTK_API void PrintSelf(ostream &os, vtkIndent indent) VTK_OVERRIDE
Not implemented.
#define SLM_WARN(message)
Definition: spyLog.hpp:261
vtkActor2D * GetWheelActor() const
Get the actor holding the wheel.
FWRENDERVTK_API::glm::dvec2 GetCenterInScreenSpace() const
Returns the center in viewport coordinates.
FWRENDERVTK_API void SetOrientation(double orientation)
Set the wheel orientation, expressed in radians.
FWRENDERVTK_API fwVtkWheelRepresentation * GetRepresentation() const
Get the representation.
#define SLM_ASSERT(message, cond)
work like &#39;assert&#39; from &#39;cassert&#39;, with in addition a message logged by spylog (with FATAL loglevel) ...
Definition: spyLog.hpp:308
#define OSLM_FATAL(message)
Definition: spyLog.hpp:285
FWRENDERVTK_API void SetHovering(bool hover)
Set the wheel hovering.
FWRENDERVTK_API void CreateDefaultRepresentation() VTK_OVERRIDE
Creates a wheel representation as default.
FWRENDERVTK_API bool isOnWheel(int X, int Y) const
Check if the (X, Y) screen position is on the wheel.
FWRENDERVTK_API bool isInCenter(int X, int Y) const
Check if the (X, Y) screen position is inside the wheel center.
Representation of a wheel widget.