fw4spl
SSlicesCursor.cpp
1 /* ***** BEGIN LICENSE BLOCK *****
2  * FW4SPL - Copyright (C) IRCAD, 2009-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 "visuVTKAdaptor/SSlicesCursor.hpp"
8 
9 #include <fwCom/Slot.hpp>
10 #include <fwCom/Slot.hxx>
11 #include <fwCom/Slots.hpp>
12 #include <fwCom/Slots.hxx>
13 
14 #include <fwCore/base.hpp>
15 
16 #include <fwData/Float.hpp>
17 #include <fwData/Image.hpp>
18 #include <fwData/String.hpp>
19 
20 #include <fwDataTools/fieldHelper/Image.hpp>
21 #include <fwDataTools/fieldHelper/MedicalImageHelpers.hpp>
22 
23 #include <fwServices/macros.hpp>
24 
25 #include <boost/assign/list_of.hpp>
26 
27 #include <vtkActor.h>
28 #include <vtkCellArray.h>
29 #include <vtkCellData.h>
30 #include <vtkLine.h> // CELL
31 #include <vtkPolyData.h>
32 #include <vtkPolyDataMapper.h>
33 #include <vtkProperty.h>
34 #include <vtkRenderer.h>
35 #include <vtkTransform.h>
36 
37 fwServicesRegisterMacro( ::fwRenderVTK::IAdaptor, ::visuVTKAdaptor::SSlicesCursor);
38 
39 namespace visuVTKAdaptor
40 {
41 
42 const ::fwCom::Slots::SlotKeyType SSlicesCursor::s_UPDATE_SLICE_INDEX_SLOT = "updateSliceIndex";
43 const ::fwCom::Slots::SlotKeyType SSlicesCursor::s_UPDATE_SLICE_TYPE_SLOT = "updateSliceType";
44 const ::fwCom::Slots::SlotKeyType SSlicesCursor::s_UPDATE_IMAGE_SLOT = "updateImage";
45 
46 const ::fwCom::Slots::SlotKeyType SSlicesCursor::s_SHOW_FULL_CROSS_SLOT = "showFullCross";
47 const ::fwCom::Slots::SlotKeyType SSlicesCursor::s_SHOW_NORMAL_CROSS_SLOT = "showNormalCross";
48 const ::fwCom::Slots::SlotKeyType SSlicesCursor::s_SET_CROSS_SCALE_SLOT = "setCrossScale";
49 
50 static const ::fwServices::IService::KeyType s_IMAGE_INOUT = "image";
51 
52 //-----------------------------------------------------------------------------
53 
54 SSlicesCursor::SSlicesCursor() noexcept :
55  m_cursorPolyData( vtkPolyData::New() ),
56  m_cursorMapper( vtkPolyDataMapper::New() ),
57  m_cursorActor( vtkActor::New() ),
58  m_scale(0.5f),
59  m_isSelected(false)
60 {
61  newSlot(s_UPDATE_SLICE_INDEX_SLOT, &SSlicesCursor::updateSliceIndex, this);
62  newSlot(s_UPDATE_SLICE_TYPE_SLOT, &SSlicesCursor::updateSliceType, this);
63  newSlot(s_UPDATE_IMAGE_SLOT, &SSlicesCursor::updateImage, this);
64  newSlot(s_SHOW_FULL_CROSS_SLOT, &SSlicesCursor::showFullCross, this);
65  newSlot(s_SHOW_NORMAL_CROSS_SLOT, &SSlicesCursor::showNormalCross, this);
67 }
68 
69 //-----------------------------------------------------------------------------
70 
71 SSlicesCursor::~SSlicesCursor() noexcept
72 {
73  m_cursorActor->Delete();
74  m_cursorActor = NULL;
75  m_cursorMapper->Delete();
76  m_cursorMapper = NULL;
77  m_cursorPolyData->Delete();
78 }
79 
80 //-----------------------------------------------------------------------------
81 
82 void SSlicesCursor::setCrossScale(double scale)
83 {
84  m_scale = static_cast<float>(scale);
85  this->updating();
86 }
87 
88 //-----------------------------------------------------------------------------
89 
91 {
92  this->configureParams();
93 
94  const ConfigType config = this->getConfigTree().get_child("config.<xmlattr>");
95 
96  m_scale = config.get<float>("scale", 0.5f);
97 }
98 
99 //-----------------------------------------------------------------------------
100 
102 {
103  this->initialize();
104 
105  ::fwData::Image::sptr image = this->getInOut< ::fwData::Image >(s_IMAGE_INOUT);
106  SLM_ASSERT("Missing image", image);
107 
108  this->buildPolyData();
109  this->buildColorAttribute();
110  this->updateImageInfos(image);
111  this->updateColors();
112  m_cursorMapper->SetInputData( m_cursorPolyData );
113  m_cursorActor->SetMapper(m_cursorMapper);
114  m_cursorActor->GetProperty()->SetOpacity(0.9);
115 
116  if(!this->getTransformId().empty())
117  {
118  m_cursorActor->SetUserTransform(this->getTransform());
119  }
120  this->addToRenderer(m_cursorActor);
121  this->setVtkPipelineModified();
122  this->requestRender();
123 }
124 
125 //-----------------------------------------------------------------------------
126 
128 {
129  m_isSelected = false;
131  this->requestRender();
132 }
133 
135 // SHAPE
136 // B---------BC----------C
137 // | | |
138 // | | |
139 // | BCM |
140 // | |
141 // | |
142 // AB----ABM CDM-----CD
143 // | |
144 // | ADM |
145 // | | |
146 // | | |
147 // A---------AD----------D
148 //
149 // no for id point A,B,C,D
150 // id point AB, BC, CD, AD = 0,1,...,3
151 // id point ABM, BCM, CDM, ADM = 4,..,7
152 
153 void SSlicesCursor::buildPolyData()
154 {
155  int nbPoints = 8;
156  vtkPoints* points = vtkPoints::New(VTK_DOUBLE);
157  points->SetNumberOfPoints(nbPoints);
158  int i;
159  for (i = 0; i < nbPoints; i++)
160  {
161  points->SetPoint(i, 0.0, 0.0, 0.0);
162  }
163 
164  vtkCellArray* cells = vtkCellArray::New();
165  cells->Allocate(cells->EstimateSize(nbPoints, 2));
166 
167  for ( int line = 0; line < 4; ++line)
168  {
169  vtkLine* lineCell = vtkLine::New();
170  lineCell->GetPointIds()->SetId(0, line );
171  lineCell->GetPointIds()->SetId(1, line+ 4 );
172  cells->InsertNextCell(lineCell);
173  lineCell->Delete();
174  }
175 
176  m_cursorPolyData->SetPoints(points);
177  points->Delete();
178  m_cursorPolyData->SetLines(cells);
179  cells->Delete();
180  this->setVtkPipelineModified();
181 }
182 
183 //-----------------------------------------------------------------------------
184 
185 void SSlicesCursor::barycenter( double ptA[3], double ptB[3], float scale, double result[3] )
186 {
187  for (int i = 0; i < 3; ++i )
188  {
189  result[i] = scale*ptA[i] + (1-scale)*ptB[i];
190  }
191 }
192 
193 //-----------------------------------------------------------------------------
194 
195 void SSlicesCursor::computeCrossPoints( double _ptA[3], double _ptB[3], double _ptP[3], double _scale,
196  double _ptAprime[3], double _ptBprime[3] )
197 {
198  double ptPBprime[3];
199  double norm2PBprime = 0.0;
200  double norm2BP = 0;
201  double norm2AP = 0;
202 
203  // Compute AB x scale / 2 = |A'P| x AB = |PB'| x AB
204  for (int i = 0; i < 3; ++i )
205  {
206  ptPBprime[i] = ( _ptB[i] - _ptA[i] ) * (1-_scale)/2.0;
207  norm2PBprime += ptPBprime[i]*ptPBprime[i];
208  norm2AP += (_ptP[i] - _ptA[i])*(_ptP[i] - _ptA[i]);
209  norm2BP += (_ptP[i] - _ptB[i])*(_ptP[i] - _ptB[i]);
210  }
211 
212  // Compute APrime = P + PA' = P - |A'P| x AB
213  // BPrime = P + pB' = P + |PB'| x AB
214  if ( norm2PBprime > norm2BP )
215  {
216  for (int i = 0; i < 3; ++i )
217  {
218  _ptAprime[i] = _ptP[i] - ptPBprime[i];
219  _ptBprime[i] = _ptB[i];
220  }
221  }
222  else if ( norm2PBprime > norm2AP )
223  {
224  for (int i = 0; i < 3; ++i )
225  {
226  _ptAprime[i] = _ptA[i];
227  _ptBprime[i] = _ptP[i] + ptPBprime[i];
228  }
229  }
230  else
231  {
232  for (int i = 0; i < 3; ++i )
233  {
234  _ptAprime[i] = _ptP[i] - ptPBprime[i];
235  _ptBprime[i] = _ptP[i] + ptPBprime[i];
236  }
237  }
238 }
239 
240 //-----------------------------------------------------------------------------
241 
242 void SSlicesCursor::buildColorAttribute()
243 {
244  unsigned char red[3] = {255, 0, 0};
245  unsigned char green[3] = {0, 255, 0};
246  unsigned char blue[3] = {0, 0, 255};
247 
248  typedef unsigned char* RGBColor;
249  typedef std::map< std::string, std::pair< RGBColor, RGBColor> > DicoType;
250  DicoType dict;
251  dict["colorXAxis"] = std::make_pair(green, red);
252  dict["colorYAxis"] = std::make_pair(red, blue);
253  dict["colorZAxis"] = std::make_pair(blue, green );
254 
255  for ( DicoType::iterator i = dict.begin(); i != dict.end(); ++i )
256  {
257  vtkUnsignedCharArray* colors = vtkUnsignedCharArray::New();
258  colors->SetNumberOfComponents(3);
259  colors->SetName( i->first.c_str() );
260 
261  // Since VTK 7.1, InsertNextTupleValue is deprecated in favor of InsertNextTypedTuple.
262  #if (VTK_MAJOR_VERSION == 7 && VTK_MINOR_VERSION >= 1) || VTK_MAJOR_VERSION > 7
263  colors->InsertNextTypedTuple( i->second.first );
264  colors->InsertNextTypedTuple( i->second.second );
265  colors->InsertNextTypedTuple( i->second.first );
266  colors->InsertNextTypedTuple( i->second.second );
267 #else
268  colors->InsertNextTupleValue( i->second.first );
269  colors->InsertNextTupleValue( i->second.second );
270  colors->InsertNextTupleValue( i->second.first );
271  colors->InsertNextTupleValue( i->second.second );
272 #endif
273  m_cursorPolyData->GetCellData()->AddArray(colors);
274  colors->Delete();
275  }
276  m_cursorMapper->SetScalarModeToUseCellFieldData();
277  this->setVtkPipelineModified();
278 }
279 
280 //-----------------------------------------------------------------------------
281 
282 void SSlicesCursor::updateColors()
283 {
284  switch (m_orientation )
285  {
286  case 0: m_cursorMapper->SelectColorArray("colorXAxis"); break;
287  case 1: m_cursorMapper->SelectColorArray("colorYAxis"); break;
288  case 2: m_cursorMapper->SelectColorArray("colorZAxis"); break;
289 
290  }
291  this->setVtkPipelineModified();
292 }
293 
294 //-----------------------------------------------------------------------------
295 
297 {
298  ::fwData::Image::sptr image = this->getInOut< ::fwData::Image >(s_IMAGE_INOUT);
299  SLM_ASSERT("Missing image", image);
300 
302 
303  if ( imageIsValid)
304  {
305  this->updateImageSliceIndex(image);
306  this->updateColors();
307  this->requestRender();
308  }
309 }
310 
311 //-----------------------------------------------------------------------------
312 
313 void SSlicesCursor::updateImageSliceIndex( ::fwData::Image::sptr image )
314 {
315  float scale = m_isSelected ? 1.0 : m_scale;
316  if (scale <= 0)
317  {
318  m_cursorActor->VisibilityOff();
319  }
320  else
321  {
322  m_cursorActor->VisibilityOn();
323 
324  int pos[3];
325 
326  pos[2] = m_axialIndex->value();
327  pos[1] = m_frontalIndex->value();
328  pos[0] = m_sagittalIndex->value();
329 
330  const ::fwData::Image::SpacingType spacing = image->getSpacing();
331  const ::fwData::Image::OriginType origin = image->getOrigin();
332  const ::fwData::Image::SizeType size = image->getSize();
333  double sliceWorld[3];
334  for (unsigned int dim = 0; dim < 3; ++dim )
335  {
336  sliceWorld[dim] = pos[dim] * spacing[dim] + origin.at(dim);
337  }
338 
339  double cursorPoints[8][3]; // point AB,BC,CD,AD,ABM,BCM,CDM,ADM
340  for (unsigned int p = 0; p < 2; ++p )
341  {
342  for (unsigned int dim = 0; dim < 3; ++dim )
343  {
344  if ( (dim + p + 1)%3 == m_orientation )
345  {
346  cursorPoints[p][dim] = origin.at(dim);
347  cursorPoints[p+2][dim] = (size[dim]-1) * spacing[dim] + origin.at(dim);
348  }
349  else
350  {
351  cursorPoints[p][dim] = sliceWorld[dim];
352  cursorPoints[p+2][dim] = sliceWorld[dim];
353  }
354  }
355  }
356 
357  // Compute ABM & CDM
358  computeCrossPoints( cursorPoints[0], cursorPoints[2], sliceWorld, scale, cursorPoints[4], cursorPoints[6] );
359  // Compute BCM & ADM
360  computeCrossPoints( cursorPoints[1], cursorPoints[3], sliceWorld, scale, cursorPoints[5], cursorPoints[7] );
361 
362  vtkPoints* points = m_cursorPolyData->GetPoints();
363 
364  for ( int i = 0; i < 8; ++i)
365  {
366  points->SetPoint(i, cursorPoints[i]);
367  }
368  }
369  m_cursorPolyData->Modified();
370  this->setVtkPipelineModified();
371 }
372 
373 //-----------------------------------------------------------------------------
374 
375 void SSlicesCursor::updateSliceIndex(int axial, int frontal, int sagittal)
376 {
377  m_axialIndex->value() = axial;
378  m_frontalIndex->value() = frontal;
379  m_sagittalIndex->value() = sagittal;
380 
381  ::fwData::Image::sptr image = this->getInOut< ::fwData::Image >(s_IMAGE_INOUT);
382 
383  this->updateImageSliceIndex(image);
384  this->updating();
385 }
386 //-----------------------------------------------------------------------------
387 
388 void SSlicesCursor::showFullCross()
389 {
390  m_isSelected = true;
391 }
392 
393 //-----------------------------------------------------------------------------
394 
395 void SSlicesCursor::showNormalCross()
396 {
397  m_isSelected = false;
398 }
399 
400 //-----------------------------------------------------------------------------
401 
402 void SSlicesCursor::updateSliceType(int from, int to)
403 {
404  if( to == static_cast<int>(m_orientation) )
405  {
406  setOrientation( static_cast< Orientation >( from ));
407  }
408  else if( from == static_cast<int>(m_orientation) )
409  {
410  setOrientation( static_cast< Orientation >( to ));
411  }
412  this->updating();
413 }
414 
415 //-----------------------------------------------------------------------------
416 
417 void SSlicesCursor::updateImage()
418 {
419  ::fwData::Image::sptr image = this->getInOut< ::fwData::Image >(s_IMAGE_INOUT);
420  SLM_ASSERT("Missing image", image);
421 
422  this->updateImageInfos(image);
423  this->updating();
424 }
425 
426 //------------------------------------------------------------------------------
427 
429 {
430  KeyConnectionsMap connections;
431  connections.push(s_IMAGE_INOUT, ::fwData::Image::s_MODIFIED_SIG, s_UPDATE_IMAGE_SLOT);
432  connections.push(s_IMAGE_INOUT, ::fwData::Image::s_SLICE_INDEX_MODIFIED_SIG, s_UPDATE_SLICE_INDEX_SLOT);
433  connections.push(s_IMAGE_INOUT, ::fwData::Image::s_SLICE_TYPE_MODIFIED_SIG, s_UPDATE_SLICE_TYPE_SLOT);
434  connections.push(s_IMAGE_INOUT, ::fwData::Image::s_BUFFER_MODIFIED_SIG, s_UPDATE_IMAGE_SLOT);
435 
436  return connections;
437 }
438 
439 //-----------------------------------------------------------------------------
440 
441 } //namespace visuVTKAdaptor
void setCrossScale(double scale)
Slot: set the scale for the cross : 1. means full cross, 0.5 half cross, 0. no cross.
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
FWRENDERVTK_API void addToRenderer(vtkProp *prop)
Adds the vtkProp to the renderer.
The namespace visuVTKAdaptor contains the list of adaptors available for the generic scene...
::fwData::Integer::sptr m_axialIndex
Axial slice index.
static const ::fwCom::Slots::SlotKeyType s_SHOW_FULL_CROSS_SLOT
Slot: update image slice index.
FWRENDERVTK_API void configureParams()
Parse the xml configuration for renderer, picker and transform.
virtual FWDATATOOLS_API void setOrientation(Orientation orientation)
Set the image orientation.
FWRENDERVTK_API void requestRender()
notify a render request iff vtkPipeline is modified
FWRENDERVTK_API void setVtkPipelineModified()
End-user have to call this method when a vtk structure has been modified, thus a render request will ...
FWRENDERVTK_API void removeAllPropFromRenderer()
Removes all the vtkProp from the renderer.
::fwData::Integer::sptr m_sagittalIndex
Sagittal slice index.
FWRENDERVTK_API vtkTransform * getTransform()
Returns the transform used by this adaptor.
static const ::fwCom::Slots::SlotKeyType s_UPDATE_SLICE_INDEX_SLOT
Slot: update image slice index.
Orientation m_orientation
Image orientation.
FWDATATOOLS_API void updateImageInfos(::fwData::Image::sptr image)
Update the image information (slice index, min/max,...)
VISUVTKADAPTOR_API void configuring() override
Configure the service before starting. Apply the configuration to service.
#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
FWRENDERVTK_API SRender::VtkObjectIdType getTransformId() const
Returns the identifier of the transform used by this adaptor.
VISUVTKADAPTOR_API void updating() override
Perform some computations according to object (this service is attached to) attribute values and its ...
static FWDATA_APIconst::fwCom::Signals::SignalKeyType s_BUFFER_MODIFIED_SIG
Type of signal when image&#39;s buffer is added.
virtual VISUVTKADAPTOR_API KeyConnectionsMap getAutoConnections() const override
Returns proposals to connect service slots to associated object signals, this method is used for obj/...
static FWDATA_APIconst::fwCom::Signals::SignalKeyType s_MODIFIED_SIG
Key in m_signals map of signal m_sigModified.
::fwData::Integer::sptr m_frontalIndex
Frontal slice index.
static FWDATATOOLS_API bool checkImageValidity(::fwData::Image::csptr _pImg)
Check if the image is valid.
VISUVTKADAPTOR_API void starting() override
Initialize the service activity.
FWRENDERVTK_API void initialize()
Initialize the adaptor with the associated render service. (must be call in starting).
static const ::fwCom::Slots::SlotKeyType s_SET_CROSS_SCALE_SLOT
Slot: update image slice index.
VISUVTKADAPTOR_API void stopping() override
Uninitialize the service activity. The stop() method is always invoked before destroying a service...
static const ::fwCom::Slots::SlotKeyType s_SHOW_NORMAL_CROSS_SLOT
Slot: update image slice index.
static FWDATA_APIconst::fwCom::Signals::SignalKeyType s_SLICE_TYPE_MODIFIED_SIG
Type of signal when image&#39;s buffer is added.
static const ::fwCom::Slots::SlotKeyType s_UPDATE_SLICE_TYPE_SLOT
Slot: update image slice index.
static const ::fwCom::Slots::SlotKeyType s_UPDATE_IMAGE_SLOT
Slot: update image slice index.
FWSERVICES_API ConfigType getConfigTree() const
Return the configuration, in an boost property tree.
Definition: IService.cpp:247