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