Medical Imaging Interaction Toolkit  2018.4.99-389bf124
Medical Imaging Interaction Toolkit
mitkPlaneGeometryDataToSurfaceFilter.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 
15 #include "mitkGeometry3D.h"
16 #include "mitkPlaneGeometry.h"
17 #include "mitkPlaneGeometryData.h"
18 #include "mitkSurface.h"
19 
20 #include <vtkPlaneSource.h>
21 #include <vtkPolyData.h>
22 #include <vtkTransformPolyDataFilter.h>
23 
24 #include <vtkBox.h>
25 #include <vtkClipPolyData.h>
26 #include <vtkCubeSource.h>
27 #include <vtkCutter.h>
28 #include <vtkGeneralTransform.h>
29 #include <vtkPPolyDataNormals.h>
30 #include <vtkPlane.h>
31 #include <vtkStripper.h>
32 #include <vtkTextureMapToPlane.h>
33 #include <vtkTransform.h>
34 #include <vtkTransformPolyDataFilter.h>
35 #include <vtkTriangleFilter.h>
36 
38  : m_UseGeometryParametricBounds(true),
39  m_XResolution(10),
40  m_YResolution(10),
41  m_PlaceByGeometry(false),
42  m_UseBoundingBox(false)
43 {
44  m_PlaneSource = vtkPlaneSource::New();
45  m_Transform = vtkTransform::New();
46 
47  m_CubeSource = vtkCubeSource::New();
48  m_PolyDataTransformer = vtkTransformPolyDataFilter::New();
49 
50  m_Plane = vtkPlane::New();
51  m_PlaneCutter = vtkCutter::New();
52  m_PlaneStripper = vtkStripper::New();
53  m_PlanePolyData = vtkPolyData::New();
54  m_NormalsUpdater = vtkPPolyDataNormals::New();
55  m_PlaneTriangler = vtkTriangleFilter::New();
56  m_TextureMapToPlane = vtkTextureMapToPlane::New();
57 
58  m_Box = vtkBox::New();
59  m_PlaneClipper = vtkClipPolyData::New();
60 
61  m_VtkTransformPlaneFilter = vtkTransformPolyDataFilter::New();
62  m_VtkTransformPlaneFilter->SetInputConnection(m_PlaneSource->GetOutputPort());
63 }
64 
66 {
67  m_PlaneSource->Delete();
68  m_Transform->Delete();
69 
70  m_CubeSource->Delete();
71  m_PolyDataTransformer->Delete();
72 
73  m_Plane->Delete();
74  m_PlaneCutter->Delete();
75  m_PlaneStripper->Delete();
76  m_PlanePolyData->Delete();
77  m_NormalsUpdater->Delete();
78  m_PlaneTriangler->Delete();
79  m_TextureMapToPlane->Delete();
80 
81  m_Box->Delete();
82  m_PlaneClipper->Delete();
83 
84  m_VtkTransformPlaneFilter->Delete();
85 }
86 
88 {
90  mitk::Surface::Pointer output = this->GetOutput();
91 
92  if (input.IsNull() || (input->GetPlaneGeometry() == nullptr) || (input->GetPlaneGeometry()->IsValid() == false) ||
93  (m_UseBoundingBox && (m_BoundingBox.IsNull() || (m_BoundingBox->GetDiagonalLength2() < mitk::eps))))
94  {
95  return;
96  }
97 
98  Point3D origin;
99  Point3D right, bottom;
100 
101  vtkPolyData *planeSurface = nullptr;
102 
103  // Does the PlaneGeometryData contain an AbstractTransformGeometry?
104  if (auto *abstractGeometry =
105  dynamic_cast<AbstractTransformGeometry *>(input->GetPlaneGeometry()))
106  {
107  // In the case of an AbstractTransformGeometry (which holds a possibly
108  // non-rigid transform), we proceed slightly differently: since the
109  // plane can be arbitrarily deformed, we need to transform it by the
110  // abstract transform before clipping it. The setup for this is partially
111  // done in the constructor.
112  origin = abstractGeometry->GetPlane()->GetOrigin();
113  right = origin + abstractGeometry->GetPlane()->GetAxisVector(0);
114  bottom = origin + abstractGeometry->GetPlane()->GetAxisVector(1);
115 
116  // Define the plane
117  m_PlaneSource->SetOrigin(origin[0], origin[1], origin[2]);
118  m_PlaneSource->SetPoint1(right[0], right[1], right[2]);
119  m_PlaneSource->SetPoint2(bottom[0], bottom[1], bottom[2]);
120 
121  // Set the plane's resolution (unlike for non-deformable planes, the plane
122  // grid needs to have a certain resolution so that the deformation has the
123  // desired effect).
125  {
126  m_PlaneSource->SetXResolution((int)abstractGeometry->GetParametricExtent(0));
127  m_PlaneSource->SetYResolution((int)abstractGeometry->GetParametricExtent(1));
128  }
129  else
130  {
131  m_PlaneSource->SetXResolution(m_XResolution);
132  m_PlaneSource->SetYResolution(m_YResolution);
133  }
134  if (m_PlaceByGeometry)
135  {
136  // Let the output use the input geometry to appropriately transform the
137  // coordinate system.
138  mitk::Geometry3D::TransformType *affineTransform = abstractGeometry->GetIndexToWorldTransform();
139 
140  TimeGeometry *timeGeometry = output->GetTimeGeometry();
141  BaseGeometry *g3d = timeGeometry->GetGeometryForTimeStep(0);
142  g3d->SetIndexToWorldTransform(affineTransform);
143 
144  vtkGeneralTransform *composedResliceTransform = vtkGeneralTransform::New();
145  composedResliceTransform->Identity();
146  composedResliceTransform->Concatenate(abstractGeometry->GetVtkTransform()->GetLinearInverse());
147  composedResliceTransform->Concatenate(abstractGeometry->GetVtkAbstractTransform());
148  // Use the non-rigid transform for transforming the plane.
149  m_VtkTransformPlaneFilter->SetTransform(composedResliceTransform);
150  }
151  else
152  {
153  // Use the non-rigid transform for transforming the plane.
154  m_VtkTransformPlaneFilter->SetTransform(abstractGeometry->GetVtkAbstractTransform());
155  }
156 
157  if (m_UseBoundingBox)
158  {
159  mitk::BoundingBox::PointType boundingBoxMin = m_BoundingBox->GetMinimum();
160  mitk::BoundingBox::PointType boundingBoxMax = m_BoundingBox->GetMaximum();
161  // mitk::BoundingBox::PointType boundingBoxCenter = m_BoundingBox->GetCenter();
162 
163  m_Box->SetXMin(boundingBoxMin[0], boundingBoxMin[1], boundingBoxMin[2]);
164  m_Box->SetXMax(boundingBoxMax[0], boundingBoxMax[1], boundingBoxMax[2]);
165  }
166  else
167  {
168  // Plane will not be clipped
169  m_Box->SetXMin(-10000.0, -10000.0, -10000.0);
170  m_Box->SetXMax(10000.0, 10000.0, 10000.0);
171  }
172 
173  m_Transform->Identity();
174  m_Transform->Concatenate(input->GetPlaneGeometry()->GetVtkTransform());
175  m_Transform->PreMultiply();
176 
177  m_Box->SetTransform(m_Transform);
178 
179  m_PlaneClipper->SetInputConnection(m_VtkTransformPlaneFilter->GetOutputPort());
180  m_PlaneClipper->SetClipFunction(m_Box);
181  m_PlaneClipper->GenerateClippedOutputOff(); // important to NOT generate normals data for clipped part
182  m_PlaneClipper->InsideOutOn();
183  m_PlaneClipper->SetValue(0.0);
184  m_PlaneClipper->Update();
185 
186  planeSurface = m_PlaneClipper->GetOutput();
187  }
188  // Does the PlaneGeometryData contain a PlaneGeometry?
189  else if (dynamic_cast<PlaneGeometry *>(input->GetPlaneGeometry()) != nullptr)
190  {
191  auto *planeGeometry = dynamic_cast<PlaneGeometry *>(input->GetPlaneGeometry());
192 
193  if (m_PlaceByGeometry)
194  {
195  // Let the output use the input geometry to appropriately transform the
196  // coordinate system.
197  mitk::Geometry3D::TransformType *affineTransform = planeGeometry->GetIndexToWorldTransform();
198 
199  TimeGeometry *timeGeometry = output->GetTimeGeometry();
200  BaseGeometry *geometrie3d = timeGeometry->GetGeometryForTimeStep(0);
201  geometrie3d->SetIndexToWorldTransform(affineTransform);
202  }
203 
204  if (!m_UseBoundingBox)
205  {
206  // We do not have a bounding box, so no clipping is required.
207 
208  if (m_PlaceByGeometry)
209  {
210  // Derive coordinate axes and origin from input geometry extent
211  origin.Fill(0.0);
212  FillVector3D(right, planeGeometry->GetExtent(0), 0.0, 0.0);
213  FillVector3D(bottom, 0.0, planeGeometry->GetExtent(1), 0.0);
214  }
215  else
216  {
217  // Take the coordinate axes and origin directly from the input geometry.
218  origin = planeGeometry->GetOrigin();
219  right = planeGeometry->GetCornerPoint(false, true);
220  bottom = planeGeometry->GetCornerPoint(true, false);
221  }
222 
223  // Since the plane is planar, there is no need to subdivide the grid
224  // (cf. AbstractTransformGeometry case)
225  m_PlaneSource->SetXResolution(1);
226  m_PlaneSource->SetYResolution(1);
227 
228  m_PlaneSource->SetOrigin(origin[0], origin[1], origin[2]);
229  m_PlaneSource->SetPoint1(right[0], right[1], right[2]);
230  m_PlaneSource->SetPoint2(bottom[0], bottom[1], bottom[2]);
231 
232  m_PlaneSource->Update();
233  planeSurface = m_PlaneSource->GetOutput();
234  }
235  else
236  {
237  // Set up a cube with the extent and origin of the bounding box. This
238  // cube will be clipped by a plane later on. The intersection of the
239  // cube and the plane will be the surface we are interested in. Note
240  // that the bounding box needs to be explicitly specified by the user
241  // of this class, since it is not necessarily clear from the data
242  // available herein which bounding box to use. In most cases, this
243  // would be the bounding box of the input geometry's reference
244  // geometry, but this is not an inevitable requirement.
245  mitk::BoundingBox::PointType boundingBoxMin = m_BoundingBox->GetMinimum();
246  mitk::BoundingBox::PointType boundingBoxMax = m_BoundingBox->GetMaximum();
247  mitk::BoundingBox::PointType boundingBoxCenter = m_BoundingBox->GetCenter();
248 
249  m_CubeSource->SetXLength(boundingBoxMax[0] - boundingBoxMin[0]);
250  m_CubeSource->SetYLength(boundingBoxMax[1] - boundingBoxMin[1]);
251  m_CubeSource->SetZLength(boundingBoxMax[2] - boundingBoxMin[2]);
252  m_CubeSource->SetCenter(boundingBoxCenter[0], boundingBoxCenter[1], boundingBoxCenter[2]);
253 
254  // Now we have to transform the cube, so that it will cut our plane
255  // appropriately. (As can be seen below, the plane corresponds to the
256  // z-plane in the coordinate system and is *not* transformed.) Therefore,
257  // we get the inverse of the plane geometry's transform and concatenate
258  // it with the transform of the reference geometry, if available.
259  m_Transform->Identity();
260  m_Transform->Concatenate(planeGeometry->GetVtkTransform()->GetLinearInverse());
261 
262  const BaseGeometry *referenceGeometry = planeGeometry->GetReferenceGeometry();
263  if (referenceGeometry)
264  {
265  m_Transform->Concatenate(referenceGeometry->GetVtkTransform());
266  }
267 
268  // Transform the cube accordingly (s.a.)
269  m_PolyDataTransformer->SetInputConnection(m_CubeSource->GetOutputPort());
270  m_PolyDataTransformer->SetTransform(m_Transform);
271 
272  // Initialize the plane to clip the cube with, as lying on the z-plane
273  m_Plane->SetOrigin(0.0, 0.0, 0.0);
274  m_Plane->SetNormal(0.0, 0.0, 1.0);
275 
276  // Cut the plane with the cube.
277  m_PlaneCutter->SetInputConnection(m_PolyDataTransformer->GetOutputPort());
278  m_PlaneCutter->SetCutFunction(m_Plane);
279 
280  // The output of the cutter must be converted into appropriate poly data.
281  m_PlaneStripper->SetInputConnection(m_PlaneCutter->GetOutputPort());
282  m_PlaneStripper->Update();
283 
284  if (m_PlaneStripper->GetOutput()->GetNumberOfPoints() < 3)
285  {
286  return;
287  }
288 
289  m_PlanePolyData->SetPoints(m_PlaneStripper->GetOutput()->GetPoints());
290  m_PlanePolyData->SetPolys(m_PlaneStripper->GetOutput()->GetLines());
291 
292  m_PlaneTriangler->SetInputData(m_PlanePolyData);
293 
294  // Get bounds of the resulting surface and use it to generate the texture
295  // mapping information
296  m_PlaneTriangler->Update();
297  m_PlaneTriangler->GetOutput()->ComputeBounds();
298  double *surfaceBounds = m_PlaneTriangler->GetOutput()->GetBounds();
299 
300  origin[0] = surfaceBounds[0];
301  origin[1] = surfaceBounds[2];
302  origin[2] = surfaceBounds[4];
303 
304  right[0] = surfaceBounds[1];
305  right[1] = surfaceBounds[2];
306  right[2] = surfaceBounds[4];
307 
308  bottom[0] = surfaceBounds[0];
309  bottom[1] = surfaceBounds[3];
310  bottom[2] = surfaceBounds[4];
311 
312  // Now we tell the data how it shall be textured afterwards;
313  // description see above.
314  m_TextureMapToPlane->SetInputConnection(m_PlaneTriangler->GetOutputPort());
315  m_TextureMapToPlane->AutomaticPlaneGenerationOn();
316  m_TextureMapToPlane->SetOrigin(origin[0], origin[1], origin[2]);
317  m_TextureMapToPlane->SetPoint1(right[0], right[1], right[2]);
318  m_TextureMapToPlane->SetPoint2(bottom[0], bottom[1], bottom[2]);
319 
320  // Need to call update so that output data and bounds are immediately
321  // available
322  m_TextureMapToPlane->Update();
323 
324  // Return the output of this generation process
325  planeSurface = dynamic_cast<vtkPolyData *>(m_TextureMapToPlane->GetOutput());
326  }
327  }
328 
329  m_NormalsUpdater->SetInputData(planeSurface);
330  m_NormalsUpdater->AutoOrientNormalsOn(); // that's the trick! Brings consistency between
331  // normals direction and front/back faces direction (see bug 1440)
332  m_NormalsUpdater->ComputePointNormalsOn();
333  m_NormalsUpdater->Update();
334 
335  output->SetVtkPolyData(m_NormalsUpdater->GetOutput());
336  output->CalculateBoundingBox();
337 }
338 
340 {
341  mitk::Surface::Pointer output = this->GetOutput();
342 
343  if (output.IsNull())
344  return;
345  if (output->GetVtkPolyData() == nullptr)
346  return;
347 
348  // output->GetVtkPolyData()->Update(); //VTK6_TODO vtk pipeline
349 }
350 
352 {
353  if (this->GetNumberOfInputs() < 1)
354  {
355  return nullptr;
356  }
357 
358  return static_cast<const mitk::PlaneGeometryData *>(this->ProcessObject::GetInput(0));
359 }
360 
362 {
363  return static_cast<const mitk::PlaneGeometryData *>(this->ProcessObject::GetInput(idx));
364 }
365 
367 {
368  // Process object is not const-correct so the const_cast is required here
369  this->ProcessObject::SetNthInput(0, const_cast<mitk::PlaneGeometryData *>(input));
370 }
371 
373 {
374  if (index + 1 > this->GetNumberOfInputs())
375  {
376  this->SetNumberOfRequiredInputs(index + 1);
377  }
378  // Process object is not const-correct so the const_cast is required here
379  this->ProcessObject::SetNthInput(index, const_cast<mitk::PlaneGeometryData *>(input));
380 }
381 
383 {
384  m_BoundingBox = boundingBox;
385  this->UseBoundingBoxOn();
386 }
387 
389 {
390  return m_BoundingBox.GetPointer();
391 }
itk::BoundingBox< unsigned long, 3, ScalarType > BoundingBox
Standard 3D-BoundingBox typedef.
void SetIndexToWorldTransform(mitk::AffineTransform3D *transform)
virtual void SetInput(const PlaneGeometryData *image)
vtkLinearTransform * GetVtkTransform() const
Get the m_IndexToWorldTransform as a vtkLinearTransform.
GeometryTransformHolder::TransformType TransformType
OutputType * GetOutput()
bool m_PlaceByGeometry
Define whether the Surface is at the origin and placed using the Geometry.
void FillVector3D(Tout &out, mitk::ScalarType x, mitk::ScalarType y, mitk::ScalarType z)
Definition: mitkArray.h:106
Data class containing PlaneGeometry objects.
vtkPlaneSource * m_PlaneSource
Source to create the vtk-representation of the parameter space rectangle of the PlaneGeometry.
virtual BaseGeometry::Pointer GetGeometryForTimeStep(TimeStepType timeStep) const =0
Returns the geometry which corresponds to the given time step.
vtkTransformPolyDataFilter * m_VtkTransformPlaneFilter
Filter to create the vtk-representation of the PlaneGeometry, which is a transformation of the m_Plan...
MITKCORE_EXPORT const ScalarType eps
Describes a two-dimensional, rectangular plane.
BaseGeometry Describes the geometry of a data object.
bool m_UseGeometryParametricBounds
If true, use Geometry3D::GetParametricBounds() to define the resolution in parameter space...
mitk::AffineTransform3D * GetIndexToWorldTransform()
Get the transformation used to convert from index to world coordinates.