Medical Imaging Interaction Toolkit  2016.11.0
Medical Imaging Interaction Toolkit
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
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.