Medical Imaging Interaction Toolkit  2016.11.0
Medical Imaging Interaction Toolkit
mitkContourModelLiveWireInteractor.cpp
Go to the documentation of this file.
1 /*===================================================================
2 
3 The Medical Imaging Interaction Toolkit (MITK)
4 
5 Copyright (c) German Cancer Research Center,
6 Division of Medical and Biological Informatics.
7 All rights reserved.
8 
9 This software is distributed WITHOUT ANY WARRANTY; without
10 even the implied warranty of MERCHANTABILITY or FITNESS FOR
11 A PARTICULAR PURPOSE.
12 
13 See LICENSE.txt or http://www.mitk.org for details.
14 
15 ===================================================================*/
16 
18 
20 #include "mitkToolManager.h"
21 
22 #include "mitkBaseRenderer.h"
23 #include "mitkRenderingManager.h"
24 
25 #include <mitkInteractionConst.h>
26 
27 #include "mitkIOUtil.h"
28 
30 {
32 
33  m_NextActiveVertexDown.Fill(0);
34  m_NextActiveVertexUp.Fill(0);
35 }
36 
38 {
39 }
40 
42 {
43  CONNECT_CONDITION("checkisOverPoint", OnCheckPointClick);
44  CONNECT_CONDITION("mouseMove", IsHovering);
45 
46  CONNECT_FUNCTION("movePoint", OnMovePoint);
47  CONNECT_FUNCTION("deletePoint", OnDeletePoint);
48  CONNECT_FUNCTION("finish", OnFinishEditing);
49 }
50 
52 {
53  const InteractionPositionEvent *positionEvent = dynamic_cast<const InteractionPositionEvent *>(interactionEvent);
54 
55  if (!positionEvent)
56  return false;
57 
58  int timestep = positionEvent->GetSender()->GetTimeStep();
59 
60  mitk::ContourModel *contour = dynamic_cast<mitk::ContourModel *>(this->GetDataNode()->GetData());
61  if (contour == nullptr)
62  {
63  MITK_ERROR << "Invalid Contour";
64  return false;
65  }
66 
67  contour->Deselect();
68 
69  // Check distance to any vertex.
70  // Transition YES if click close to a vertex
71  mitk::Point3D click = positionEvent->GetPositionInWorld();
72 
73  if (contour->SelectVertexAt(click, 1.5, timestep))
74  {
75  contour->SetSelectedVertexAsControlPoint(false);
76  m_ContourLeft = mitk::ContourModel::New();
77  // get coordinates of next active vertex downwards from selected vertex
78  int downIndex = this->SplitContourFromSelectedVertex(contour, m_ContourLeft, false, timestep);
79 
80  m_NextActiveVertexDownIter = contour->IteratorBegin() + downIndex;
81  m_NextActiveVertexDown = (*m_NextActiveVertexDownIter)->Coordinates;
82 
83  m_ContourRight = mitk::ContourModel::New();
84 
85  // get coordinates of next active vertex upwards from selected vertex
86  int upIndex = this->SplitContourFromSelectedVertex(contour, m_ContourRight, true, timestep);
87 
88  m_NextActiveVertexUpIter = contour->IteratorBegin() + upIndex;
89  m_NextActiveVertexUp = (*m_NextActiveVertexUpIter)->Coordinates;
90 
91  // clear previous void positions
92  this->m_LiveWireFilter->ClearRepulsivePoints();
93 
94  // set the current contour as void positions in the cost map
95  // start with down side
96  mitk::ContourModel::VertexIterator iter = contour->IteratorBegin(timestep);
97  for (; iter != m_NextActiveVertexDownIter; iter++)
98  {
99  itk::Index<2> idx;
100  this->m_WorkingSlice->GetGeometry()->WorldToIndex((*iter)->Coordinates, idx);
101  this->m_LiveWireFilter->AddRepulsivePoint(idx);
102  }
103 
104  // continue with upper side
105  iter = m_NextActiveVertexUpIter + 1;
106  for (; iter != contour->IteratorEnd(timestep); iter++)
107  {
108  itk::Index<2> idx;
109  this->m_WorkingSlice->GetGeometry()->WorldToIndex((*iter)->Coordinates, idx);
110  this->m_LiveWireFilter->AddRepulsivePoint(idx);
111  }
112 
113  // clear container with void points between neighboring control points
114  m_ContourBeingModified.clear();
115 
116  // let us have the selected point as a control point
117  contour->SetSelectedVertexAsControlPoint(true);
118 
119  // finally, return true to pass this condition
120  return true;
121  }
122  else
123  {
124  // do not pass condition
125  return false;
126  }
127 
129  return true;
130 }
131 
133 {
134  if (this->m_EditingContourNode != _arg)
135  {
136  this->m_EditingContourNode = _arg;
137  }
138 }
139 
141 {
142  if (this->m_WorkingSlice != _arg)
143  {
144  this->m_WorkingSlice = _arg;
145  this->m_LiveWireFilter->SetInput(this->m_WorkingSlice);
146  }
147 }
148 
150 {
151  int timestep = interactionEvent->GetSender()->GetTimeStep();
152 
153  mitk::ContourModel *contour = dynamic_cast<mitk::ContourModel *>(this->GetDataNode()->GetData());
154  if (contour == nullptr)
155  {
156  MITK_ERROR << "Invalid Contour!";
157  return;
158  }
159 
160  if (contour->GetSelectedVertex())
161  {
163  newContour->Expand(contour->GetTimeSteps());
164 
165  newContour->Concatenate(m_ContourLeft, timestep);
166 
167  // recompute contour between neighbored two active control points
168  this->m_LiveWireFilter->SetStartPoint(this->m_NextActiveVertexDown);
169  this->m_LiveWireFilter->SetEndPoint(this->m_NextActiveVertexUp);
170  // this->m_LiveWireFilter->ClearRepulsivePoints();
171  this->m_LiveWireFilter->Update();
172 
173  mitk::ContourModel *liveWireContour = this->m_LiveWireFilter->GetOutput();
174  assert(liveWireContour);
175 
176  if (liveWireContour->IsEmpty(timestep))
177  return;
178 
179  liveWireContour->RemoveVertexAt(0, timestep);
180  liveWireContour->RemoveVertexAt(liveWireContour->GetNumberOfVertices(timestep) - 1, timestep);
181 
182  // insert new live wire computed points
183  newContour->Concatenate(liveWireContour, timestep);
184 
185  // insert right side of original contour
186  newContour->Concatenate(this->m_ContourRight, timestep);
187 
188  newContour->SetClosed(contour->IsClosed(timestep), timestep);
189 
190  // instead of leaving a single point, delete all points
191  if (newContour->GetNumberOfVertices(timestep) <= 2)
192  {
193  newContour->Clear(timestep);
194  }
195 
196  this->GetDataNode()->SetData(newContour);
197 
199  }
200 }
201 
203 {
204  const InteractionPositionEvent *positionEvent = dynamic_cast<const InteractionPositionEvent *>(interactionEvent);
205  if (!positionEvent)
206  return;
207 
208  int timestep = positionEvent->GetSender()->GetTimeStep();
209  mitk::Point3D currentPosition = positionEvent->GetPositionInWorld();
210 
211  mitk::ContourModel *contour = dynamic_cast<mitk::ContourModel *>(this->GetDataNode()->GetData());
212  if (contour == nullptr)
213  {
214  MITK_ERROR << "invalid contour";
215  return;
216  }
217 
219  editingContour->Expand(contour->GetTimeSteps());
220 
221  // recompute left live wire, i.e. the contour between previous active vertex and selected vertex
222  this->m_LiveWireFilter->SetStartPoint(this->m_NextActiveVertexDown);
223  this->m_LiveWireFilter->SetEndPoint(currentPosition);
224 
225  // remove void positions between previous active vertex and next active vertex.
226  if (!m_ContourBeingModified.empty())
227  {
228  std::vector<itk::Index<2>>::const_iterator iter = m_ContourBeingModified.begin();
229  for (; iter != m_ContourBeingModified.end(); iter++)
230  {
231  this->m_LiveWireFilter->RemoveRepulsivePoint((*iter));
232  }
233  }
234 
235  // update to get the left livewire. Remember that the points in the rest of the contour are already
236  // set as void positions in the filter
237  this->m_LiveWireFilter->Update();
238 
239  mitk::ContourModel::Pointer leftLiveWire = this->m_LiveWireFilter->GetOutput();
240  assert(leftLiveWire);
241 
242  if (!leftLiveWire->IsEmpty(timestep))
243  leftLiveWire->RemoveVertexAt(0, timestep);
244 
245  editingContour->Concatenate(leftLiveWire, timestep);
246 
247  // the new index of the selected vertex
248  unsigned int selectedVertexIndex =
249  this->m_ContourLeft->GetNumberOfVertices(timestep) + leftLiveWire->GetNumberOfVertices(timestep) - 1;
250 
251  // at this point the container has to be empty
252  m_ContourBeingModified.clear();
253 
254  // add points from left live wire contour
255  mitk::ContourModel::VertexIterator iter = leftLiveWire->IteratorBegin(timestep);
256  for (; iter != leftLiveWire->IteratorEnd(timestep); iter++)
257  {
258  itk::Index<2> idx;
259  this->m_WorkingSlice->GetGeometry()->WorldToIndex((*iter)->Coordinates, idx);
260  this->m_LiveWireFilter->AddRepulsivePoint(idx);
261 
262  // add indices
263  m_ContourBeingModified.push_back(idx);
264  }
265 
266  // recompute right live wire, i.e. the contour between selected vertex and next active vertex
267  this->m_LiveWireFilter->SetStartPoint(currentPosition);
268  this->m_LiveWireFilter->SetEndPoint(m_NextActiveVertexUp);
269 
270  // update filter with all contour points set as void but the right live wire portion to be calculated now
271  this->m_LiveWireFilter->Update();
272 
273  mitk::ContourModel::Pointer rightLiveWire = this->m_LiveWireFilter->GetOutput();
274  assert(rightLiveWire);
275 
276  // reject strange paths
277  if (abs(rightLiveWire->GetNumberOfVertices(timestep) - leftLiveWire->GetNumberOfVertices(timestep)) > 50)
278  {
279  return;
280  }
281 
282  if (!leftLiveWire->IsEmpty(timestep))
283  leftLiveWire->SetControlVertexAt(leftLiveWire->GetNumberOfVertices() - 1, timestep);
284 
285  if (!rightLiveWire->IsEmpty(timestep))
286  rightLiveWire->RemoveVertexAt(0, timestep);
287 
288  editingContour->Concatenate(rightLiveWire, timestep);
289 
290  m_EditingContourNode->SetData(editingContour);
291 
293  newContour->Expand(contour->GetTimeSteps());
294 
295  // concatenate left original contour
296  newContour->Concatenate(this->m_ContourLeft, timestep);
297 
298  newContour->Concatenate(editingContour, timestep, true);
299 
300  // set last inserted vertex as selected
301  newContour->SelectVertexAt(selectedVertexIndex, timestep);
302 
303  // set as control point
304  newContour->SetSelectedVertexAsControlPoint(true);
305 
306  // concatenate right original contour
307  newContour->Concatenate(this->m_ContourRight, timestep);
308 
309  newContour->SetClosed(contour->IsClosed(timestep), timestep);
310  this->GetDataNode()->SetData(newContour);
311 
313 }
314 
316 {
317  const InteractionPositionEvent *positionEvent = dynamic_cast<const InteractionPositionEvent *>(interactionEvent);
318  if (!positionEvent)
319  return false;
320 
321  int timestep = positionEvent->GetSender()->GetTimeStep();
322 
323  mitk::ContourModel *contour = dynamic_cast<mitk::ContourModel *>(this->GetDataNode()->GetData());
324 
325  mitk::Point3D currentPosition = positionEvent->GetPositionInWorld();
326 
327  bool isHover = false;
328  this->GetDataNode()->GetBoolProperty("contour.hovering", isHover, positionEvent->GetSender());
329  if (contour->IsNearContour(currentPosition, 1.5, timestep))
330  {
331  if (isHover == false)
332  {
333  this->GetDataNode()->SetBoolProperty("contour.hovering", true);
335  }
336  }
337  else
338  {
339  if (isHover == true)
340  {
341  this->GetDataNode()->SetBoolProperty("contour.hovering", false);
343  }
344  }
345  return false;
346 }
347 
349  mitk::ContourModel *destContour,
350  bool fromSelectedUpwards,
351  int timestep)
352 {
355 
356  // search next active control point to left and rigth and set as start and end point for filter
357  mitk::ContourModel::VertexIterator itSelected = begin;
358 
359  // move iterator to position
360  while ((*itSelected) != srcContour->GetSelectedVertex())
361  {
362  itSelected++;
363  }
364 
365  // CASE search upwards for next control point
366  if (fromSelectedUpwards)
367  {
368  mitk::ContourModel::VertexIterator itUp = itSelected;
369 
370  if (itUp != end)
371  {
372  itUp++; // step once up otherwise the loop breaks immediately
373  }
374 
375  while (itUp != end && !((*itUp)->IsControlPoint))
376  {
377  itUp++;
378  }
379 
381 
382  if (itSelected != begin)
383  {
384  // copy the rest of the original contour
385  while (it != end)
386  {
387  destContour->AddVertex((*it)->Coordinates, (*it)->IsControlPoint, timestep);
388  it++;
389  }
390  }
391  // else do not copy the contour
392 
393  // return the offset of iterator at one before next-vertex-upwards
394  if (itUp != begin)
395  {
396  return std::distance(begin, itUp) - 1;
397  }
398  else
399  {
400  return std::distance(begin, itUp);
401  }
402  }
403  else // CASE search downwards for next control point
404  {
405  mitk::ContourModel::VertexIterator itDown = itSelected;
407 
408  if (itSelected != begin)
409  {
410  if (itDown != begin)
411  {
412  itDown--; // step once down otherwise the the loop breaks immediately
413  }
414 
415  while (itDown != begin && !((*itDown)->IsControlPoint))
416  {
417  itDown--;
418  }
419 
420  if (it != end) // if not empty
421  {
422  // always add the first vertex
423  destContour->AddVertex((*it)->Coordinates, (*it)->IsControlPoint, timestep);
424  it++;
425  }
426  // copy from begin to itDown
427  while (it <= itDown)
428  {
429  destContour->AddVertex((*it)->Coordinates, (*it)->IsControlPoint, timestep);
430  it++;
431  }
432  }
433  else
434  {
435  // if selected vertex is the first element search from end of contour downwards
436  itDown = end;
437  itDown--;
438  while (!((*itDown)->IsControlPoint) && itDown != begin)
439  {
440  itDown--;
441  }
442 
443  // move one forward as we don't want the first control point
444  it++;
445  // move iterator to second control point
446  while ((it != end) && !((*it)->IsControlPoint))
447  {
448  it++;
449  }
450  // copy from begin to itDown
451  while (it <= itDown)
452  {
453  // copy the contour from second control point to itDown
454  destContour->AddVertex((*it)->Coordinates, (*it)->IsControlPoint, timestep);
455  it++;
456  }
457  }
458  /*
459  //add vertex at itDown - it's not considered during while loop
460  if( it != begin && it != end)
461  {
462  //destContour->AddVertex( (*it)->Coordinates, (*it)->IsControlPoint, timestep);
463  }
464  */
465  // return the offset of iterator at one after next-vertex-downwards
466  if (itDown != end)
467  {
468  return std::distance(begin, itDown); // + 1;//index of next vertex
469  }
470  else
471  {
472  return std::distance(begin, itDown) - 1;
473  }
474  }
475 }
476 
478 {
479  int timestep = interactionEvent->GetSender()->GetTimeStep();
480 
481  mitk::ContourModel *editingContour = dynamic_cast<mitk::ContourModel *>(this->m_EditingContourNode->GetData());
482 
483  editingContour->Clear(timestep);
484 
486 }
virtual bool IsHovering(const InteractionEvent *interactionEvent) override
virtual void OnMovePoint(StateMachineAction *, InteractionEvent *interactionEvent) override
bool RemoveVertexAt(int index, int timestep=0)
Remove a vertex at given index within the container.
virtual void Clear(int timestep)
Clear the storage container at given timestep.
ContourModel is a structure of linked vertices defining a contour in 3D space. The vertices are store...
Super class for all position events.
BaseRenderer * GetSender() const
void Deselect()
Deselect vertex.
#define MITK_ERROR
Definition: mitkLogMacros.h:24
virtual void OnFinishEditing(StateMachineAction *, InteractionEvent *interactionEvent) override
virtual bool IsEmpty(int timestep) const
Returns whether the contour model is empty at a given timestep. timestep - default = 0...
mitk::ContourElement::VertexIterator VertexIterator
virtual bool OnCheckPointClick(const InteractionEvent *interactionEvent) override
Constants for most interaction classes, due to the generic StateMachines.
unsigned int GetTimeSteps() const
Get the number of time steps from the TimeGeometry As the base data has not a data vector given by it...
Definition: mitkBaseData.h:346
int SplitContourFromSelectedVertex(mitk::ContourModel *srcContour, mitk::ContourModel *destContour, bool fromSelectedUpwards, int timestep)
VertexIterator IteratorBegin(int timestep=0) const
Returns a const VertexIterator at the start element of the contour.
mitk::ImageLiveWireContourModelFilter::Pointer m_LiveWireFilter
bool SelectVertexAt(int index, int timestep=0)
Mark a vertex at an index in the container as selected.
void AddVertex(mitk::Point3D &vertex, int timestep=0)
Add a vertex to the contour at given timestep. The vertex is added at the end of contour.
virtual bool IsNearContour(mitk::Point3D &point, float eps, int timestep)
Check if mouse cursor is near the contour.
VertexType * GetSelectedVertex()
Get the current selected vertex.
static RenderingManager * GetInstance()
Represents an action, that is executed after a certain event (in statemachine-mechanism) TODO: implem...
Image class for storing images.
Definition: mitkImage.h:76
void SetSelectedVertexAsControlPoint(bool isControlPoint=true)
Set selected vertex as control point.
virtual unsigned int GetTimeStep() const
void RequestUpdate(vtkRenderWindow *renderWindow)
#define CONNECT_CONDITION(a, f)
bool IsClosed(int timestep=0) const
Return if the contour is closed or not.
static Pointer New()
#define CONNECT_FUNCTION(a, f)
VertexIterator IteratorEnd(int timestep=0) const
Returns a const VertexIterator at the end element of the contour.
itk::SmartPointer< Self > Pointer
Definition: mitkBaseData.h:42
virtual void OnDeletePoint(StateMachineAction *, InteractionEvent *interactionEvent) override
vtkRenderWindow * GetRenderWindow() const
Access the RenderWindow into which this renderer renders.
virtual void SetEditingContourModelNode(mitk::DataNode *_arg)
Class for nodes of the DataTree.
Definition: mitkDataNode.h:66
int GetNumberOfVertices(int timestep=0) const
Returns the number of vertices at a given timestep.