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