fw4spl
SNegatoSlicingInteractor.cpp
1 /* ***** BEGIN LICENSE BLOCK *****
2  * FW4SPL - Copyright (C) IRCAD, 2009-2017.
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 "visuVTKAdaptor/SNegatoSlicingInteractor.hpp"
8 
9 #include <fwCom/Signal.hpp>
10 #include <fwCom/Signal.hxx>
11 #include <fwCom/Signals.hpp>
12 #include <fwCom/Slot.hpp>
13 #include <fwCom/Slot.hxx>
14 #include <fwCom/Slots.hpp>
15 #include <fwCom/Slots.hxx>
16 
17 #include <fwData/Image.hpp>
18 #include <fwData/Integer.hpp>
19 #include <fwData/String.hpp>
20 #include <fwData/TransferFunction.hpp>
21 
22 #include <fwDataTools/fieldHelper/Image.hpp>
23 #include <fwDataTools/fieldHelper/MedicalImageHelpers.hpp>
24 
25 #include <fwRenderVTK/vtk/fwVtkCellPicker.hpp>
26 #include <fwRenderVTK/vtk/Helpers.hpp>
27 
28 #include <fwServices/macros.hpp>
29 
30 #include <vtkActor.h>
31 #include <vtkActorCollection.h>
32 #include <vtkAssemblyNode.h>
33 #include <vtkCellPicker.h>
34 #include <vtkCommand.h>
35 #include <vtkInteractorStyleImage.h>
36 #include <vtkProp3DCollection.h>
37 #include <vtkRenderWindowInteractor.h>
38 
40 
41 namespace visuVTKAdaptor
42 {
43 
44 #define START_SLICING_EVENT vtkCommand::MiddleButtonPressEvent
45 #define STOP_SLICING_EVENT vtkCommand::MiddleButtonReleaseEvent
46 
47 class NegatoSlicingCallback : public vtkCommand
48 {
49 public:
50  //------------------------------------------------------------------------------
51 
52  static NegatoSlicingCallback* New()
53  {
54  return new NegatoSlicingCallback();
55  }
56 
58  m_picker(nullptr),
59  m_localPicker(nullptr),
60  m_pickedProp(nullptr),
61  m_mouseMoveObserved(false)
62  {
63  this->PassiveObserverOff();
64  }
65 
67  {
68  }
69 
70  //------------------------------------------------------------------------------
71 
72  bool Pick(double pickPoint[3], double position[3])
73  {
74  SLM_ASSERT("m_picker should be set before picking.", m_picker);
75 
76  if ( m_picker->Pick( pickPoint, m_adaptor->getRenderer() ) )
77  {
78  m_picker->GetPickPosition(position);
79  return true;
80  }
81  return false;
82  }
83 
84  //------------------------------------------------------------------------------
85 
86  bool localPick(double pickPoint[3], double position[3])
87  {
88  SLM_ASSERT("m_localPicker should be set before picking.", m_localPicker);
89 
90  if ( m_localPicker->Pick( pickPoint, m_adaptor->getRenderer() ) )
91  {
92  m_localPicker->GetPickPosition(position);
93  return true;
94  }
95  return false;
96  }
97 
98  //------------------------------------------------------------------------------
99 
100  void startSlicing()
101  {
102  SLM_TRACE("vtkEvent: MiddleButtonPressEvent");
103  SLM_ASSERT("m_adaptor not instanced", m_adaptor);
104  SLM_ASSERT("m_picker not instanced", m_picker);
105 
106  double pickPoint[3];
107  double pickedPoint[3];
108 
109  int x, y;
110  m_adaptor->getInteractor()->GetEventPosition(x, y);
111  pickPoint[0] = x;
112  pickPoint[1] = y;
113  pickPoint[2] = 0;
114 
115  OSLM_TRACE(
116  "vtkEvent: MiddleButtonPressEvent: picking " << pickPoint[0] << ", " << pickPoint[1] << ", " <<
117  pickPoint[2]);
118 
119  if ( this->Pick(pickPoint, pickedPoint) )
120  {
121  SLM_TRACE("vtkEvent: MiddleButtonPressEvent:picked point");
122  SLM_ASSERT("Slicing has already begun", !m_mouseMoveObserved);
123  m_adaptor->getInteractor()->AddObserver(vtkCommand::MouseMoveEvent, this, 1.);
124  m_mouseMoveObserved = true;
125  SetAbortFlag(1);
126 
127  //m_pickedProp = m_picker->GetProp3D();
128  m_pickedProp = ::fwRenderVTK::vtk::getNearestPickedProp(m_picker, m_adaptor->getRenderer());
129  m_localPicker = fwVtkCellPicker::New();
130  m_localPicker->InitializePickList();
131  m_localPicker->PickFromListOn();
132  m_localPicker->AddPickList(m_pickedProp);
133 
134  double localPickedPoint[3];
135  this->localPick(pickPoint, localPickedPoint);
136 
137  m_adaptor->startSlicing(localPickedPoint);
138  }
139  }
140 
141  //------------------------------------------------------------------------------
142 
143  void stopSlicing()
144  {
145  SLM_TRACE("vtkEvent: MiddleButtonReleaseEvent");
146  SLM_ASSERT("m_adaptor not instanced", m_adaptor);
147  SLM_ASSERT("m_picker not instanced", m_picker);
148  SLM_ASSERT("Slicing doesn't begun", m_mouseMoveObserved);
149 
150  m_adaptor->getInteractor()->RemoveObservers(vtkCommand::MouseMoveEvent, this);
151  m_mouseMoveObserved = false;
152  m_adaptor->stopSlicing();
153  m_localPicker->Delete();
154  m_localPicker = nullptr;
155  m_pickedProp = nullptr;
156  }
157 
158  //------------------------------------------------------------------------------
159 
160  virtual void Execute( vtkObject* caller, unsigned long eventId, void*)
161  {
162  if(m_mouseMoveObserved && eventId == START_SLICING_EVENT)
163  {
164  SetAbortFlag(1); // To handle space bar pressed and middle click slicing at the same time
165  }
166 
167  if(m_mouseMoveObserved || !m_adaptor->getInteractor()->GetShiftKey())
168  {
169  if (eventId == START_SLICING_EVENT && !m_mouseMoveObserved)
170  {
171  startSlicing();
172  }
173 
174  else if(eventId == STOP_SLICING_EVENT && m_mouseMoveObserved)
175  {
176  stopSlicing();
177  }
178  else if (eventId == vtkCommand::MouseMoveEvent)
179  {
180  SLM_TRACE("vtkEvent: MouseMoveEvent");
181  SLM_ASSERT("m_mouseMoveObserved not instanced", m_mouseMoveObserved);
182 
183  int x, y;
184  double pickPoint[3];
185  double pickedPoint[3];
186 
187  m_adaptor->getInteractor()->GetEventPosition(x, y);
188  pickPoint[0] = x;
189  pickPoint[1] = y;
190  pickPoint[2] = 0;
191 
192  if ( this->localPick(pickPoint, pickedPoint))
193  {
194  m_adaptor->updateSlicing(pickedPoint);
195  }
196  }
197  else if (eventId == vtkCommand::KeyPressEvent && !m_adaptor->getInteractor()->GetControlKey())
198  {
199  vtkRenderWindowInteractor* rwi = vtkRenderWindowInteractor::SafeDownCast(caller);
200  char* keySym = rwi->GetKeySym();
201 
202  if ( std::string(keySym) == "A" || std::string(keySym) == "a" )
203  {
204  m_adaptor->pushSlice(-1, m_adaptor->getOrientation());
205  }
206  else if (std::string(keySym) == "Z" || std::string(keySym) == "z" )
207  {
208  m_adaptor->pushSlice(1, m_adaptor->getOrientation());
209  }
210  if ( std::string(keySym) == "T" || std::string(keySym) == "t" )
211  {
212  m_adaptor->pushSlice(-1, ::fwDataTools::helper::MedicalImageAdaptor::Z_AXIS);
213  }
214  else if (std::string(keySym) == "Y" || std::string(keySym) == "y" )
215  {
216  m_adaptor->pushSlice(1, ::fwDataTools::helper::MedicalImageAdaptor::Z_AXIS);
217  }
218  else if (std::string(keySym) == "G" || std::string(keySym) == "g" )
219  {
220  m_adaptor->pushSlice(-1, ::fwDataTools::helper::MedicalImageAdaptor::Y_AXIS);
221  }
222  else if (std::string(keySym) == "H" || std::string(keySym) == "h" )
223  {
224  m_adaptor->pushSlice(1, ::fwDataTools::helper::MedicalImageAdaptor::Y_AXIS);
225  }
226  else if (std::string(keySym) == "B" || std::string(keySym) == "b" )
227  {
228  m_adaptor->pushSlice(-1, ::fwDataTools::helper::MedicalImageAdaptor::X_AXIS);
229  }
230  else if (std::string(keySym) == "N" || std::string(keySym) == "n" )
231  {
232  m_adaptor->pushSlice(1, ::fwDataTools::helper::MedicalImageAdaptor::X_AXIS);
233  }
234  else if (std::string(keySym) == "space" && !m_mouseMoveObserved)
235  {
236  this->startSlicing();
237  }
238  }
239  else if(eventId == vtkCommand::KeyReleaseEvent)
240  {
241  vtkRenderWindowInteractor* rwi = vtkRenderWindowInteractor::SafeDownCast(caller);
242  char* keySym = rwi->GetKeySym();
243 
244  if (std::string(keySym) == "space" && m_mouseMoveObserved)
245  {
246  this->stopSlicing();
247  }
248  }
249  }
250  else if (m_adaptor->getInteractor()->GetShiftKey())
251  {
252  if (eventId == MouseWheelForwardEvent)
253  {
254  m_adaptor->pushSlice(1, m_adaptor->getOrientation());
255  }
256  else if (eventId == vtkCommand::MouseWheelBackwardEvent)
257  {
258  m_adaptor->pushSlice(-1, m_adaptor->getOrientation());
259  }
260  }
261  }
262 
263  //------------------------------------------------------------------------------
264 
265  void setAdaptor( SNegatoSlicingInteractor::sptr adaptor)
266  {
267  m_adaptor = adaptor;
268  }
269 
270  //------------------------------------------------------------------------------
271 
272  void setPicker( vtkAbstractPropPicker* picker)
273  {
274  m_picker = picker;
275  }
276 
277 protected:
278  SNegatoSlicingInteractor::sptr m_adaptor;
279  vtkAbstractPropPicker* m_picker;
280  vtkAbstractPropPicker* m_localPicker;
281  vtkProp* m_pickedProp;
282  bool m_mouseMoveObserved;
283 };
284 
285 static const ::fwCom::Slots::SlotKeyType s_UPDATE_SLICE_INDEX_SLOT = "updateSliceIndex";
286 static const ::fwCom::Slots::SlotKeyType s_UPDATE_SLICE_TYPE_SLOT = "updateSliceType";
287 
288 const ::fwCom::Signals::SignalKeyType SNegatoSlicingInteractor::s_SLICING_STARTED_SIG = "slicingStarted";
289 const ::fwCom::Signals::SignalKeyType SNegatoSlicingInteractor::s_SLICING_STOPPED_SIG = "slicingStopped";
290 
291 static const ::fwServices::IService::KeyType s_IMAGE_INOUT = "image";
292 
293 //-----------------------------------------------------------------------------
294 
295 SNegatoSlicingInteractor::SNegatoSlicingInteractor() noexcept :
296  m_vtkObserver(nullptr),
297  m_priority(.6f)
298 {
299  m_sigSlicingStarted = newSignal< SlicingStartedSignalType >(s_SLICING_STARTED_SIG);
300  m_sigSlicingStopped = newSignal< SlicingStoppedSignalType >(s_SLICING_STOPPED_SIG);
301 
302  newSlot(s_UPDATE_SLICE_INDEX_SLOT, &SNegatoSlicingInteractor::updateSliceIndex, this);
303  newSlot(s_UPDATE_SLICE_TYPE_SLOT, &SNegatoSlicingInteractor::updateSliceType, this);
304 }
305 
306 //-----------------------------------------------------------------------------
307 
308 SNegatoSlicingInteractor::~SNegatoSlicingInteractor() noexcept
309 {
310 }
311 
312 //-----------------------------------------------------------------------------
313 
315 {
316  this->configureParams();
317 
318  const ConfigType config = this->getConfigTree().get_child("config.<xmlattr>");
319 
320  const std::string orientation = config.get<std::string>("sliceIndex", "axial");
321  if(orientation == "axial" )
322  {
323  m_orientation = Z_AXIS;
324  }
325  else if(orientation == "frontal" )
326  {
327  m_orientation = Y_AXIS;
328  }
329  else if(orientation == "sagittal" )
330  {
331  m_orientation = X_AXIS;
332  }
333 }
334 
335 //-----------------------------------------------------------------------------
336 
338 {
339  this->initialize();
340 
341  NegatoSlicingCallback* observer = NegatoSlicingCallback::New();
342  observer->setAdaptor( SNegatoSlicingInteractor::dynamicCast(this->getSptr()) );
343  observer->setPicker(this->getPicker());
344 
345  m_vtkObserver = observer;
346 
347  this->getInteractor()->AddObserver(START_SLICING_EVENT, m_vtkObserver, m_priority);
348  this->getInteractor()->AddObserver(STOP_SLICING_EVENT, m_vtkObserver, m_priority);
349  this->getInteractor()->AddObserver(vtkCommand::KeyPressEvent, m_vtkObserver, m_priority);
350  this->getInteractor()->AddObserver(vtkCommand::KeyReleaseEvent, m_vtkObserver, m_priority);
351  this->getInteractor()->AddObserver(vtkCommand::MouseWheelForwardEvent, m_vtkObserver, m_priority);
352  this->getInteractor()->AddObserver(vtkCommand::MouseWheelBackwardEvent, m_vtkObserver, m_priority);
353 
354  ::fwData::Image::sptr image = this->getInOut< ::fwData::Image >(s_IMAGE_INOUT);
355  SLM_ASSERT("Missing image", image);
356  this->updateImageInfos(image);
357 }
358 
359 //-----------------------------------------------------------------------------
360 
362 {
363  ::fwData::Image::sptr image = this->getInOut< ::fwData::Image >(s_IMAGE_INOUT);
364  SLM_ASSERT("Missing image", image);
365  this->updateImageInfos(image);
366 }
367 
368 //-----------------------------------------------------------------------------
369 
371 {
372  this->getInteractor()->RemoveObservers(START_SLICING_EVENT, m_vtkObserver);
373  this->getInteractor()->RemoveObservers(STOP_SLICING_EVENT, m_vtkObserver);
374  this->getInteractor()->RemoveObservers(vtkCommand::KeyPressEvent, m_vtkObserver);
375  this->getInteractor()->RemoveObservers(vtkCommand::KeyReleaseEvent, m_vtkObserver);
376  this->getInteractor()->RemoveObservers(vtkCommand::MouseWheelForwardEvent, m_vtkObserver);
377  this->getInteractor()->RemoveObservers(vtkCommand::MouseWheelBackwardEvent, m_vtkObserver);
378  m_vtkObserver->Delete();
379  m_vtkObserver = nullptr;
380 }
381 
382 //-----------------------------------------------------------------------------
383 
384 void SNegatoSlicingInteractor::updateSliceIndex(int axial, int frontal, int sagittal)
385 {
386  m_axialIndex->value() = axial;
387  m_frontalIndex->value() = frontal;
388  m_sagittalIndex->value() = sagittal;
389 }
390 
391 //-----------------------------------------------------------------------------
392 
394 {
395  if( to == static_cast<int>(m_orientation) )
396  {
397  setOrientation( static_cast< Orientation >( from ));
398  }
399  else if(from == static_cast<int>(m_orientation))
400  {
401  setOrientation( static_cast< Orientation >( to ));
402  }
403 }
404 
405 //-----------------------------------------------------------------------------
406 
407 void SNegatoSlicingInteractor::startSlicing( double pickedPoint[3] )
408 {
409  ::fwData::Integer::sptr sliceIndex[3];
410  this->getSliceIndex(sliceIndex);
411 
412  int index[3];
413  this->worldToImageSliceIndex(pickedPoint, index);
414  m_sigSlicingStarted->asyncEmit();
415 
416  int i;
417  for (i = 0; i < 3; i++)
418  {
419  if (index[i] == sliceIndex[i]->value())
420  {
421  this->setOrientation((Orientation)i);
422  break;
423  }
424  }
425  SLM_INFO_IF( "unknown orientation", i == 3 );
426  if(i != 3)
427  {
428  this->updateSlicing(pickedPoint);
429  }
430 }
431 
432 //-----------------------------------------------------------------------------
433 
434 void SNegatoSlicingInteractor::stopSlicing()
435 {
436  ::fwData::Image::sptr image = this->getInOut< ::fwData::Image >(s_IMAGE_INOUT);
437  SLM_ASSERT("Missing image", image);
438  m_sigSlicingStopped->asyncEmit();
439 
440  auto sig = image->signal< ::fwData::Image::SliceIndexModifiedSignalType >(
442  {
443  ::fwCom::Connection::Blocker block(sig->getConnection(this->slot(s_UPDATE_SLICE_INDEX_SLOT)));
444  sig->asyncEmit(m_axialIndex->value(), m_frontalIndex->value(), m_sagittalIndex->value());
445  }
446 }
447 
448 //-----------------------------------------------------------------------------
449 
450 void SNegatoSlicingInteractor::updateSlicing( double pickedPoint[3] )
451 {
452  ::fwData::Image::sptr image = this->getInOut< ::fwData::Image >(s_IMAGE_INOUT);
453  SLM_ASSERT("Missing image", image);
454 
455  ::fwData::Integer::sptr sliceIndex[3];
456  this->getSliceIndex(sliceIndex);
457 
458  int index[3];
459  int originalIndex = sliceIndex[m_orientation]->value();
460 
461  this->worldToImageSliceIndex(pickedPoint, index);
462  index[m_orientation] = originalIndex;
463 
464  OSLM_TRACE("sliceIndex[0] "<< sliceIndex[0]->value()<< " index[0] " << index[0] << " pickedPt "<<pickedPoint[0]);
465  OSLM_TRACE("sliceIndex[1] "<< sliceIndex[1]->value()<< " index[1] " << index[1] << " pickedPt "<<pickedPoint[1]);
466  OSLM_TRACE("sliceIndex[2] "<< sliceIndex[2]->value()<< " index[2] " << index[2] << " pickedPt "<<pickedPoint[2]);
467 
468 #ifdef DEBUG
469  for ( int i = 0; i < image->getNumberOfDimensions(); i++ )
470  {
471  OSLM_ASSERT("index["<< i <<"] = " << index[i]
472  << " and image->getSize()[" << i << "] = " << image->getSize()[i],
473  index[i] >= 0 && index[i] < image->getSize()[i]);
474  }
475 #endif
476 
477  if(setSliceIndex(index))
478  {
479  auto sig = image->signal< ::fwData::Image::SliceIndexModifiedSignalType >(
481  {
482  ::fwCom::Connection::Blocker block(sig->getConnection(this->slot(s_UPDATE_SLICE_INDEX_SLOT)));
483  sig->asyncEmit(m_axialIndex->value(), m_frontalIndex->value(), m_sagittalIndex->value());
484  }
485  }
486 }
487 
488 //-----------------------------------------------------------------------------
489 
490 void SNegatoSlicingInteractor::pushSlice( int factor, Orientation axis)
491 {
492  ::fwData::Integer::sptr sliceIndex[3];
493  this->getSliceIndex(sliceIndex);
494 
495  int index[3];
496  index[0] = sliceIndex[0]->value();
497  index[1] = sliceIndex[1]->value();
498  index[2] = sliceIndex[2]->value();
499  index[axis] += factor;
500 
501  int size[3];
502  this->getImageDataSize(size);
503 
504  if (index[axis] < 0)
505  {
506  index[axis] = 0;
507  }
508  else if (index[axis] >= size[axis])
509  {
510  index[axis] = size[axis]-1;
511  }
512 
513  if(setSliceIndex(index))
514  {
515  ::fwData::Image::sptr image = this->getInOut< ::fwData::Image >(s_IMAGE_INOUT);
516  SLM_ASSERT("Missing image", image);
517 
518  m_sigSlicingStopped->asyncEmit();
519 
520  auto sig = image->signal< ::fwData::Image::SliceIndexModifiedSignalType >(
522  {
523  ::fwCom::Connection::Blocker block(sig->getConnection(this->slot(s_UPDATE_SLICE_INDEX_SLOT)));
524  sig->asyncEmit(m_axialIndex->value(), m_frontalIndex->value(), m_sagittalIndex->value());
525  }
526  }
527 }
528 
529 //------------------------------------------------------------------------------
530 
532 {
533  KeyConnectionsMap connections;
534  connections.push(s_IMAGE_INOUT, ::fwData::Image::s_MODIFIED_SIG, s_UPDATE_SLOT);
535  connections.push(s_IMAGE_INOUT, ::fwData::Image::s_SLICE_INDEX_MODIFIED_SIG, s_UPDATE_SLICE_INDEX_SLOT );
536  connections.push(s_IMAGE_INOUT, ::fwData::Image::s_SLICE_TYPE_MODIFIED_SIG, s_UPDATE_SLICE_TYPE_SLOT );
537  connections.push(s_IMAGE_INOUT, ::fwData::Image::s_BUFFER_MODIFIED_SIG, s_UPDATE_SLOT );
538 
539  return connections;
540 }
541 
542 //------------------------------------------------------------------------------
543 
544 } //namespace visuVTKAdaptor
static FWDATA_APIconst::fwCom::Signals::SignalKeyType s_SLICE_INDEX_MODIFIED_SIG
Type of signal when image&#39;s buffer is added.
This class is a helper to define the connections of a service and its data.
Definition: IService.hpp:454
#define OSLM_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:310
Class allowing to block a Connection.
Definition: Connection.hpp:20
The namespace visuVTKAdaptor contains the list of adaptors available for the generic scene...
static VISUVTKADAPTOR_APIconst::fwCom::Signals::SignalKeyType s_SLICING_STARTED_SIG
Type of signal when slicing is updated.
void updateSliceIndex(int axial, int frontal, int sagittal)
Slot: update image slice index.
static VISUVTKADAPTOR_APIconst::fwCom::Signals::SignalKeyType s_SLICING_STOPPED_SIG
Type of signal when slicing is updated.
#define OSLM_TRACE(message)
Definition: spyLog.hpp:230
VISUVTKADAPTOR_API void stopping() override
Uninitialize the service activity. The stop() method is always invoked before destroying a service...
VISUVTKADAPTOR_API void starting() override
Initialize the service activity.
#define SLM_INFO_IF(message, cond)
Definition: spyLog.hpp:254
#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
VISUVTKADAPTOR_API void updating() override
Perform some computations according to object (this service is attached to) attribute values and its ...
VISUVTKADAPTOR_API void configuring() override
Configure the service before starting. Apply the configuration to service.
static FWDATA_APIconst::fwCom::Signals::SignalKeyType s_BUFFER_MODIFIED_SIG
Type of signal when image&#39;s buffer is added.
static FWDATA_APIconst::fwCom::Signals::SignalKeyType s_MODIFIED_SIG
Key in m_signals map of signal m_sigModified.
#define SLM_TRACE(message)
Definition: spyLog.hpp:228
virtual VISUVTKADAPTOR_API KeyConnectionsMap getAutoConnections() const override
Returns proposals to connect service slots to associated object signals, this method is used for obj/...
void updateSliceType(int from, int to)
Slot: update image slice type.
static FWDATA_APIconst::fwCom::Signals::SignalKeyType s_SLICE_TYPE_MODIFIED_SIG
Type of signal when image&#39;s buffer is added.