Medical Imaging Interaction Toolkit  2016.11.0
Medical Imaging Interaction Toolkit
mitkBoundingShapeVtkMapper2D.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 detailocalStorage.
14 
15 ===================================================================*/
16 
17 #include "../DataManagement/mitkBoundingShapeUtil.h"
18 #include <mitkBaseProperty.h>
20 
21 #include <vtkActor2D.h>
22 #include <vtkAppendPolyData.h>
23 #include <vtkCoordinate.h>
24 #include <vtkCubeSource.h>
25 #include <vtkMath.h>
26 #include <vtkPointData.h>
27 #include <vtkPolyData.h>
28 #include <vtkPolyDataMapper2D.h>
29 #include <vtkProperty2D.h>
30 #include <vtkSphereSource.h>
31 #include <vtkStripper.h>
32 #include <vtkTransformFilter.h>
33 #include <vtkTransformPolyDataFilter.h>
34 
35 static vtkSmartPointer<vtkSphereSource> CreateHandle()
36 {
38 
39  handle->SetPhiResolution(8);
40  handle->SetThetaResolution(16);
41 
42  return handle;
43 }
44 
45 namespace mitk
46 {
47  class BoundingShapeVtkMapper2D::Impl
48  {
49  public:
50  Impl()
51  {
52  Point3D initialPoint;
53  initialPoint.Fill(0);
54 
55  for (int i = 0; i < 6; ++i)
56  HandlePropertyList.push_back(Handle(initialPoint, i, GetHandleIndices(i)));
57  }
58 
59  std::vector<Handle> HandlePropertyList;
60  mitk::LocalStorageHandler<LocalStorage> LocalStorageHandler;
61  };
62 }
63 
64 mitk::BoundingShapeVtkMapper2D::LocalStorage::LocalStorage()
65  : m_Actor(vtkSmartPointer<vtkActor>::New()),
66  m_HandleActor(vtkSmartPointer<vtkActor2D>::New()),
67  m_SelectedHandleActor(vtkSmartPointer<vtkActor2D>::New()),
68  m_Mapper(vtkSmartPointer<vtkPolyDataMapper>::New()),
69  m_HandleMapper(vtkSmartPointer<vtkPolyDataMapper2D>::New()),
70  m_SelectedHandleMapper(vtkSmartPointer<vtkPolyDataMapper2D>::New()),
71  m_Cutter(vtkSmartPointer<vtkCutter>::New()),
72  m_CuttingPlane(vtkSmartPointer<vtkPlane>::New()),
73  m_LastSliceNumber(0),
74  m_PropAssembly(vtkSmartPointer<vtkPropAssembly>::New()),
75  m_ZoomFactor(1.0)
76 {
77  m_Actor->SetMapper(m_Mapper);
78  m_Actor->GetProperty()->SetOpacity(0.3);
79  m_Actor->VisibilityOn();
80 
81  m_HandleActor->SetMapper(m_HandleMapper);
82  m_HandleActor->VisibilityOn();
83 
84  m_SelectedHandleActor->VisibilityOn();
85  m_SelectedHandleActor->GetProperty()->SetColor(0, 1.0, 0);
86  m_SelectedHandleActor->SetMapper(m_SelectedHandleMapper);
87 
88  vtkCoordinate *tcoord = vtkCoordinate::New();
89  tcoord->SetCoordinateSystemToWorld();
90  m_SelectedHandleMapper->SetTransformCoordinate(tcoord);
91  tcoord->Delete();
92 
93  m_Cutter->SetCutFunction(m_CuttingPlane);
94 
95  for (int i = 0; i < 6; ++i)
96  m_Handles.push_back(CreateHandle());
97 
98  m_PropAssembly->AddPart(m_Actor);
99  m_PropAssembly->AddPart(m_HandleActor);
100  m_PropAssembly->VisibilityOn();
101 }
102 
103 bool mitk::BoundingShapeVtkMapper2D::LocalStorage::IsUpdateRequired(mitk::BaseRenderer *renderer,
104  mitk::Mapper *mapper,
105  mitk::DataNode *dataNode)
106 {
107  const mitk::PlaneGeometry *worldGeometry = renderer->GetCurrentWorldPlaneGeometry();
108 
109  if (m_LastGenerateDataTime < worldGeometry->GetMTime())
110  return true;
111 
112  unsigned int sliceNumber = renderer->GetSlice();
113 
114  if (m_LastSliceNumber != sliceNumber)
115  return true;
116 
117  if (mapper && m_LastGenerateDataTime < mapper->GetMTime())
118  return true;
119 
120  if (dataNode)
121  {
122  if (m_LastGenerateDataTime < dataNode->GetMTime())
123  return true;
124 
125  mitk::BaseData *data = dataNode->GetData();
126 
127  if (data && m_LastGenerateDataTime < data->GetMTime())
128  return true;
129  }
130 
131  return false;
132 }
133 
134 mitk::BoundingShapeVtkMapper2D::LocalStorage::~LocalStorage()
135 {
136 }
137 
138 void mitk::BoundingShapeVtkMapper2D::Update(mitk::BaseRenderer *renderer)
139 {
140  this->GenerateDataForRenderer(renderer);
141 }
142 
144 {
145  Superclass::SetDefaultProperties(node, renderer, overwrite);
146 }
147 
148 mitk::BoundingShapeVtkMapper2D::BoundingShapeVtkMapper2D() : m_Impl(new Impl)
149 {
150 }
151 
152 mitk::BoundingShapeVtkMapper2D::~BoundingShapeVtkMapper2D()
153 {
154  delete m_Impl;
155 }
156 
157 void mitk::BoundingShapeVtkMapper2D::GenerateDataForRenderer(BaseRenderer *renderer)
158 {
159  const DataNode::Pointer node = GetDataNode();
160  if (node == nullptr)
161  return;
162 
163  LocalStorage *localStorage = m_Impl->LocalStorageHandler.GetLocalStorage(renderer);
164 
165  // either update if GeometryData was modified or if the zooming was performed
166  bool needGenerateData = localStorage->IsUpdateRequired(
167  renderer, this, GetDataNode()); // true; // localStorage->GetLastGenerateDataTime() < node->GetMTime() ||
168  // localStorage->GetLastGenerateDataTime() < node->GetData()->GetMTime();
169  // //localStorage->IsGenerateDataRequired(renderer, this, GetDataNode());
170 
171  double scale = renderer->GetScaleFactorMMPerDisplayUnit();
172 
173  if (std::abs(scale - localStorage->m_ZoomFactor) > 0.001)
174  {
175  localStorage->m_ZoomFactor = scale;
176  needGenerateData = true;
177  }
178 
179  if (needGenerateData)
180  {
181  bool visible = true;
182  GetDataNode()->GetVisibility(visible, renderer, "visible");
183 
184  if (!visible)
185  {
186  localStorage->m_Actor->VisibilityOff();
187  return;
188  }
189  GeometryData::Pointer shape = static_cast<GeometryData *>(node->GetData());
190  if (shape == nullptr)
191  return;
192 
193  mitk::BaseGeometry::Pointer geometry = shape->GetGeometry();
194  mitk::Vector3D spacing = geometry->GetSpacing();
195 
196  // calculate cornerpoints and extent from geometry with visualization offset
197  std::vector<Point3D> cornerPoints = GetCornerPoints(geometry, true);
198  Point3D p0 = cornerPoints[0];
199  Point3D p1 = cornerPoints[1];
200  Point3D p2 = cornerPoints[2];
201  Point3D p4 = cornerPoints[4];
202  Point3D extent;
203  extent[0] =
204  sqrt((p0[0] - p4[0]) * (p0[0] - p4[0]) + (p0[1] - p4[1]) * (p0[1] - p4[1]) + (p0[2] - p4[2]) * (p0[2] - p4[2]));
205  extent[1] =
206  sqrt((p0[0] - p2[0]) * (p0[0] - p2[0]) + (p0[1] - p2[1]) * (p0[1] - p2[1]) + (p0[2] - p2[2]) * (p0[2] - p2[2]));
207  extent[2] =
208  sqrt((p0[0] - p1[0]) * (p0[0] - p1[0]) + (p0[1] - p1[1]) * (p0[1] - p1[1]) + (p0[2] - p1[2]) * (p0[2] - p1[2]));
209 
210  // calculate center based on half way of the distance between two opposing cornerpoints
211  mitk::Point3D center = CalcAvgPoint(cornerPoints[7], cornerPoints[0]);
212 
213  if (m_Impl->HandlePropertyList.size() == 6)
214  {
215  // set handle positions
216  Point3D pointLeft = CalcAvgPoint(cornerPoints[5], cornerPoints[6]);
217  Point3D pointRight = CalcAvgPoint(cornerPoints[1], cornerPoints[2]);
218  Point3D pointTop = CalcAvgPoint(cornerPoints[0], cornerPoints[6]);
219  Point3D pointBottom = CalcAvgPoint(cornerPoints[7], cornerPoints[1]);
220  Point3D pointFront = CalcAvgPoint(cornerPoints[2], cornerPoints[7]);
221  Point3D pointBack = CalcAvgPoint(cornerPoints[4], cornerPoints[1]);
222 
223  m_Impl->HandlePropertyList[0].SetPosition(pointLeft);
224  m_Impl->HandlePropertyList[1].SetPosition(pointRight);
225  m_Impl->HandlePropertyList[2].SetPosition(pointTop);
226  m_Impl->HandlePropertyList[3].SetPosition(pointBottom);
227  m_Impl->HandlePropertyList[4].SetPosition(pointFront);
228  m_Impl->HandlePropertyList[5].SetPosition(pointBack);
229  }
230 
231  // caculate face normals
232  double result0[3], result1[3], result2[3];
233  double a[3], b[3];
234  a[0] = (cornerPoints[5][0] - cornerPoints[6][0]);
235  a[1] = (cornerPoints[5][1] - cornerPoints[6][1]);
236  a[2] = (cornerPoints[5][2] - cornerPoints[6][2]);
237 
238  b[0] = (cornerPoints[5][0] - cornerPoints[4][0]);
239  b[1] = (cornerPoints[5][1] - cornerPoints[4][1]);
240  b[2] = (cornerPoints[5][2] - cornerPoints[4][2]);
241 
242  vtkMath::Cross(a, b, result0);
243 
244  a[0] = (cornerPoints[0][0] - cornerPoints[6][0]);
245  a[1] = (cornerPoints[0][1] - cornerPoints[6][1]);
246  a[2] = (cornerPoints[0][2] - cornerPoints[6][2]);
247 
248  b[0] = (cornerPoints[0][0] - cornerPoints[2][0]);
249  b[1] = (cornerPoints[0][1] - cornerPoints[2][1]);
250  b[2] = (cornerPoints[0][2] - cornerPoints[2][2]);
251 
252  vtkMath::Cross(a, b, result1);
253 
254  a[0] = (cornerPoints[2][0] - cornerPoints[7][0]);
255  a[1] = (cornerPoints[2][1] - cornerPoints[7][1]);
256  a[2] = (cornerPoints[2][2] - cornerPoints[7][2]);
257 
258  b[0] = (cornerPoints[2][0] - cornerPoints[6][0]);
259  b[1] = (cornerPoints[2][1] - cornerPoints[6][1]);
260  b[2] = (cornerPoints[2][2] - cornerPoints[6][2]);
261 
262  vtkMath::Cross(a, b, result2);
263 
264  vtkMath::Normalize(result0);
265  vtkMath::Normalize(result1);
266  vtkMath::Normalize(result2);
267 
268  // create cube for rendering bounding box
269  auto cube = vtkCubeSource::New();
270  cube->SetXLength(extent[0] / spacing[0]);
271  cube->SetYLength(extent[1] / spacing[1]);
272  cube->SetZLength(extent[2] / spacing[2]);
273 
274  // calculates translation based on offset+extent not on the transformation matrix
275  vtkSmartPointer<vtkMatrix4x4> imageTransform = geometry->GetVtkTransform()->GetMatrix();
276  auto translation = vtkSmartPointer<vtkTransform>::New();
277  translation->Translate(center[0] - imageTransform->GetElement(0, 3),
278  center[1] - imageTransform->GetElement(1, 3),
279  center[2] - imageTransform->GetElement(2, 3));
280 
281  auto transform = vtkSmartPointer<vtkTransform>::New();
282  transform->SetMatrix(imageTransform);
283  transform->PostMultiply();
284  transform->Concatenate(translation);
285  transform->Update();
286  cube->Update();
287 
288  auto transformFilter = vtkSmartPointer<vtkTransformFilter>::New();
289  transformFilter->SetInputData(cube->GetOutput());
290  transformFilter->SetTransform(transform);
291  transformFilter->Update();
292  cube->Delete();
293 
294  vtkSmartPointer<vtkPolyData> polydata = transformFilter->GetPolyDataOutput();
295  if (polydata == nullptr || (polydata->GetNumberOfPoints() < 1))
296  {
297  localStorage->m_Actor->VisibilityOff();
298  localStorage->m_HandleActor->VisibilityOff();
299  localStorage->m_SelectedHandleActor->VisibilityOff();
300  return;
301  }
302 
303  // estimate current image plane to decide whether the cube is visible or not
304  const PlaneGeometry *planeGeometry = renderer->GetCurrentWorldPlaneGeometry();
305  if ((planeGeometry == nullptr) || (!planeGeometry->IsValid()) || (!planeGeometry->HasReferenceGeometry()))
306  return;
307 
308  double origin[3];
309  origin[0] = planeGeometry->GetOrigin()[0];
310  origin[1] = planeGeometry->GetOrigin()[1];
311  origin[2] = planeGeometry->GetOrigin()[2];
312 
313  double normal[3];
314  normal[0] = planeGeometry->GetNormal()[0];
315  normal[1] = planeGeometry->GetNormal()[1];
316  normal[2] = planeGeometry->GetNormal()[2];
317 
318  // MITK_INFO << "normal1 " << normal[0] << " " << normal[1] << " " << normal[2];
319  localStorage->m_CuttingPlane->SetOrigin(origin);
320  localStorage->m_CuttingPlane->SetNormal(normal);
321 
322  // add cube polydata to local storage
323  localStorage->m_Cutter->SetInputData(polydata);
324  localStorage->m_Cutter->SetGenerateCutScalars(1);
325  localStorage->m_Cutter->Update();
326 
327  if (localStorage->m_PropAssembly->GetParts()->IsItemPresent(localStorage->m_HandleActor))
328  localStorage->m_PropAssembly->RemovePart(localStorage->m_HandleActor);
329  if (localStorage->m_PropAssembly->GetParts()->IsItemPresent(localStorage->m_Actor))
330  localStorage->m_PropAssembly->RemovePart(localStorage->m_Actor);
331 
332  vtkCoordinate *tcoord = vtkCoordinate::New();
333  tcoord->SetCoordinateSystemToWorld();
334  localStorage->m_HandleMapper->SetTransformCoordinate(tcoord);
335  tcoord->Delete();
336 
337  if (localStorage->m_Cutter->GetOutput()->GetNumberOfPoints() > 0) // if plane is visible in the renderwindow
338  {
339  mitk::DoubleProperty::Pointer handleSizeProperty =
340  dynamic_cast<mitk::DoubleProperty *>(this->GetDataNode()->GetProperty("Bounding Shape.Handle Size Factor"));
341 
342  ScalarType initialHandleSize;
343  if (handleSizeProperty != nullptr)
344  initialHandleSize = handleSizeProperty->GetValue();
345  else
346  initialHandleSize = 1.0 / 40.0;
347 
348  mitk::Point2D displaySize = renderer->GetDisplaySizeInMM();
349  double handleSize = ((displaySize[0] + displaySize[1]) / 2.0) * initialHandleSize;
350 
351  auto appendPoly = vtkSmartPointer<vtkAppendPolyData>::New();
352  unsigned int i = 0;
353 
354  // add handles and their assigned properties to the local storage
355  mitk::IntProperty::Pointer activeHandleId =
356  dynamic_cast<mitk::IntProperty *>(node->GetProperty("Bounding Shape.Active Handle ID"));
357 
358  bool visible = false;
359  bool selected = false;
360  for (auto handle : localStorage->m_Handles)
361  {
362  Point3D handleCenter = m_Impl->HandlePropertyList[i].GetPosition();
363 
364  handle->SetRadius(handleSize);
365  handle->SetCenter(handleCenter[0], handleCenter[1], handleCenter[2]);
366 
367  vtkMath::Normalize(normal);
368  double angle = vtkMath::DegreesFromRadians(acos(vtkMath::Dot(normal, result0)));
369  double angle1 = vtkMath::DegreesFromRadians(acos(vtkMath::Dot(normal, result1)));
370  double angle2 = vtkMath::DegreesFromRadians(acos(vtkMath::Dot(normal, result2)));
371 
372  // show handles only if the corresponding face is aligned to the render window
373  if ((((std::abs(angle - 0) < 0.001) || (std::abs(angle - 180) < 0.001)) && i != 0 && i != 1) ||
374  (((std::abs(angle1 - 0) < 0.001) || (std::abs(angle1 - 180) < 0.001)) && i != 2 && i != 3) ||
375  (((std::abs(angle2 - 0) < 0.001) || (std::abs(angle2 - 180) < 0.001)) && i != 4 && i != 5))
376  {
377  if (activeHandleId == nullptr)
378  {
379  appendPoly->AddInputConnection(handle->GetOutputPort());
380  }
381  else
382  {
383  if ((activeHandleId->GetValue() != m_Impl->HandlePropertyList[i].GetIndex()))
384  {
385  appendPoly->AddInputConnection(handle->GetOutputPort());
386  }
387  else
388  {
389  handle->Update();
390  localStorage->m_SelectedHandleMapper->SetInputData(handle->GetOutput());
391  localStorage->m_SelectedHandleActor->VisibilityOn();
392  selected = true;
393  }
394  }
395  visible = true;
396  }
397 
398  i++;
399  }
400 
401  if (visible)
402  {
403  appendPoly->Update();
404  }
405  else
406  {
407  localStorage->m_HandleActor->VisibilityOff();
408  localStorage->m_SelectedHandleActor->VisibilityOff();
409  }
410 
411  auto stripper = vtkSmartPointer<vtkStripper>::New();
412  stripper->SetInputData(localStorage->m_Cutter->GetOutput());
413  stripper->Update();
414 
415  auto cutPolyData = vtkSmartPointer<vtkPolyData>::New();
416  cutPolyData->SetPoints(stripper->GetOutput()->GetPoints());
417  cutPolyData->SetPolys(stripper->GetOutput()->GetLines());
418 
419  localStorage->m_Actor->GetMapper()->SetInputDataObject(cutPolyData);
420  mitk::ColorProperty::Pointer selectedColor = dynamic_cast<mitk::ColorProperty *>(node->GetProperty("color"));
421  if (selectedColor != nullptr)
422  {
423  mitk::Color color = selectedColor->GetColor();
424  localStorage->m_Actor->GetProperty()->SetColor(color[0], color[1], color[2]);
425  }
426 
427  if (activeHandleId != nullptr)
428  {
429  localStorage->m_HandleActor->GetProperty()->SetColor(1, 0, 0);
430  }
431  else
432  {
433  localStorage->m_HandleActor->GetProperty()->SetColor(1, 1, 1);
434  }
435  localStorage->m_HandleActor->GetMapper()->SetInputDataObject(appendPoly->GetOutput());
436 
437  // add parts to the overall storage
438  localStorage->m_PropAssembly->AddPart(localStorage->m_Actor);
439  localStorage->m_PropAssembly->AddPart(localStorage->m_HandleActor);
440  if (selected)
441  {
442  localStorage->m_PropAssembly->AddPart(localStorage->m_SelectedHandleActor);
443  }
444 
445  localStorage->m_PropAssembly->VisibilityOn();
446  localStorage->m_Actor->VisibilityOn();
447  localStorage->m_HandleActor->VisibilityOn();
448  }
449  else
450  {
451  localStorage->m_PropAssembly->VisibilityOff();
452  localStorage->m_Actor->VisibilityOff();
453  localStorage->m_HandleActor->VisibilityOff();
454  localStorage->m_SelectedHandleActor->VisibilityOff();
455  localStorage->UpdateGenerateDataTime();
456  }
457  localStorage->UpdateGenerateDataTime();
458  }
459 }
460 
462 {
463  return m_Impl->LocalStorageHandler.GetLocalStorage(renderer)->m_PropAssembly;
464 }
465 
467 {
468 }
vtkProp * GetVtkProp(BaseRenderer *renderer) override
virtual unsigned int GetSlice() const
Base of all data objects.
Definition: mitkBaseData.h:39
void ApplyColorAndOpacityProperties(BaseRenderer *, vtkActor *) override
Apply color and opacity properties read from the PropertyList. Called by mapper subclasses.
double ScalarType
itk::SmartPointer< Self > Pointer
static void SetDefaultProperties(DataNode *node, BaseRenderer *renderer=nullptr, bool overwrite=false)
Organizes the rendering process.
DataCollection - Class to facilitate loading/accessing structured data.
virtual const PlaneGeometry * GetCurrentWorldPlaneGeometry()
Get the current 2D-worldgeometry (m_CurrentWorldPlaneGeometry) used for 2D-rendering.
BaseData * GetData() const
Get the data object (instance of BaseData, e.g., an Image) managed by this DataNode.
std::vector< mitk::Point3D > GetCornerPoints(mitk::BaseGeometry::Pointer geometry, bool visualizationOffset)
helper function for calculating corner points of the bounding object from a given geometry ...
Base class of all mappers, Vtk as well as OpenGL mappers.
Definition: mitkMapper.h:54
std::vector< int > GetHandleIndices(int index)
itk::SmartPointer< Self > Pointer
Definition: mitkDataNode.h:81
The ColorProperty class RGB color property.
Point< ScalarType, 3 > Point3D
Definition: mitkPoint.h:99
itk::RGBPixel< float > Color
Color Standard RGB color typedef (float)
void Normalize(itk::Image< TPixel, VImageDimension > *itkImage, mitk::Image::Pointer im2, mitk::Image::Pointer mask1, std::string output)
Definition: CLBrainMask.cpp:40
const float selectedColor[]
Describes a two-dimensional, rectangular plane.
static vtkSmartPointer< vtkSphereSource > CreateHandle()
mitk::Point3D CalcAvgPoint(mitk::Point3D a, mitk::Point3D b)
helper function for calculating the average of two points
Class for nodes of the DataTree.
Definition: mitkDataNode.h:66
static itkEventMacro(BoundingShapeInteractionEvent, itk::AnyEvent) class MITKBOUNDINGSHAPE_EXPORT BoundingShapeInteractor Pointer New()
Basic interaction methods for mitk::GeometryData.