Medical Imaging Interaction Toolkit  2016.11.0
Medical Imaging Interaction Toolkit
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
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.