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