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
mitkPlaneGeometry.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 
17 #include "mitkPlaneGeometry.h"
18 #include "mitkInteractionConst.h"
19 #include "mitkLine.h"
20 #include "mitkPlaneOperation.h"
21 
22 #include <itkSpatialOrientationAdapter.h>
23 
24 #include <vtkTransform.h>
25 
26 #include <vnl/vnl_cross.h>
27 
28 namespace mitk
29 {
30  PlaneGeometry::PlaneGeometry() : Superclass(), m_ReferenceGeometry(nullptr) { Initialize(); }
33  : Superclass(other), m_ReferenceGeometry(other.m_ReferenceGeometry)
34  {
35  }
36 
38  {
43  VnlVector normal = vnl_cross_3d(transform->GetMatrix().GetVnlMatrix().get_column(0),
44  transform->GetMatrix().GetVnlMatrix().get_column(1));
45  normal.normalize(); // Now normal is a righthand normal unit vector, perpendicular to the plane.
46 
47  ScalarType len = transform->GetMatrix().GetVnlMatrix().get_column(2).two_norm();
48  if (len == 0)
49  {
50  len = 1;
51  }
52  normal *= len;
53 
54  // Get the existing normal vector zed:
55  vnl_vector_fixed<double, 3> zed = transform->GetMatrix().GetVnlMatrix().get_column(2);
56 
58  if (vnl_determinant(transform->GetMatrix().GetVnlMatrix()) < 0)
59  {
60  MITK_DEBUG << "EnsurePerpendicularNormal(): Lefthanded geometry preserved, rh-normal: [ " << normal << " ],";
61  normal *= (-1.0);
62  MITK_DEBUG << "lh-normal: [ " << normal << " ], original vector zed is: [ " << zed << " ]";
63  }
64 
65  // Now lets compare and only replace if necessary and only then warn the user:
66 
67  // float epsilon is precise enough here, but we need to respect numerical condition:
68  // Higham, N., 2002, Accuracy and Stability of Numerical Algorithms,
69  // SIAM, page 37, 2nd edition:
70  double feps = std::numeric_limits<float>::epsilon();
71  double zedsMagnitude = zed.two_norm();
72  feps = feps * zedsMagnitude * 2;
73 
75  if (normal != zed)
76  {
77  vnl_vector_fixed<double, 3> parallel;
78  for (unsigned int i = 0; i < 3; ++i)
79  {
80  parallel[i] = normal[i] / zed[i]; // Remember linear algebra: checking for parallelity.
81  }
82  // Checking if really not paralell i.e. non-colinear vectors by comparing these floating point numbers:
83  if ((parallel[0] + feps < parallel[1] || feps + parallel[1] < parallel[0]) &&
84  (parallel[0] + feps < parallel[2] || feps + parallel[2] < parallel[0]))
85  {
86  MITK_WARN
87  << "EnsurePerpendicularNormal(): Plane geometry was _/askew/_, so here it gets rectified by substituting"
88  << " the 3rd column of the indexToWorldMatrix with an appropriate normal vector: [ " << normal
89  << " ], original vector zed was: [ " << zed << " ].";
90 
91  Matrix3D matrix = transform->GetMatrix();
92  matrix.GetVnlMatrix().set_column(2, normal);
93  transform->SetMatrix(matrix);
94  }
95  }
96  else
97  {
98  // Nothing to do, 3rd column of indexToWorldTransformMatrix already was perfectly perpendicular.
99  }
100  }
101 
103  {
104  EnsurePerpendicularNormal(transform);
105  }
106 
108  {
109  // error: unused parameter 'bounds'
110  // this happens in release mode, where the assert macro is defined empty
111  // hence we "use" the parameter:
112  (void)bounds;
113 
114  // currently the unit rectangle must be starting at the origin [0,0]
115  assert(bounds[0] == 0);
116  assert(bounds[2] == 0);
117  // the unit rectangle must be two-dimensional
118  assert(bounds[1] > 0);
119  assert(bounds[3] > 0);
120  }
121 
122  void PlaneGeometry::IndexToWorld(const Point2D &pt_units, Point2D &pt_mm) const
123  {
124  pt_mm[0] = GetExtentInMM(0) / GetExtent(0) * pt_units[0];
125  pt_mm[1] = GetExtentInMM(1) / GetExtent(1) * pt_units[1];
126  }
127 
128  void PlaneGeometry::WorldToIndex(const Point2D &pt_mm, Point2D &pt_units) const
129  {
130  pt_units[0] = pt_mm[0] * (1.0 / (GetExtentInMM(0) / GetExtent(0)));
131  pt_units[1] = pt_mm[1] * (1.0 / (GetExtentInMM(1) / GetExtent(1)));
132  }
133 
134  void PlaneGeometry::IndexToWorld(const Point2D & /*atPt2d_units*/, const Vector2D &vec_units, Vector2D &vec_mm) const
135  {
136  MITK_WARN << "Warning! Call of the deprecated function PlaneGeometry::IndexToWorld(point, vec, vec). Use "
137  "PlaneGeometry::IndexToWorld(vec, vec) instead!";
138  this->IndexToWorld(vec_units, vec_mm);
139  }
140 
141  void PlaneGeometry::IndexToWorld(const Vector2D &vec_units, Vector2D &vec_mm) const
142  {
143  vec_mm[0] = (GetExtentInMM(0) / GetExtent(0)) * vec_units[0];
144  vec_mm[1] = (GetExtentInMM(1) / GetExtent(1)) * vec_units[1];
145  }
146 
147  void PlaneGeometry::WorldToIndex(const Point2D & /*atPt2d_mm*/, const Vector2D &vec_mm, Vector2D &vec_units) const
148  {
149  MITK_WARN << "Warning! Call of the deprecated function PlaneGeometry::WorldToIndex(point, vec, vec). Use "
150  "PlaneGeometry::WorldToIndex(vec, vec) instead!";
151  this->WorldToIndex(vec_mm, vec_units);
152  }
153 
154  void PlaneGeometry::WorldToIndex(const Vector2D &vec_mm, Vector2D &vec_units) const
155  {
156  vec_units[0] = vec_mm[0] * (1.0 / (GetExtentInMM(0) / GetExtent(0)));
157  vec_units[1] = vec_mm[1] * (1.0 / (GetExtentInMM(1) / GetExtent(1)));
158  }
159 
161  ScalarType height,
162  const Vector3D &spacing,
163  PlaneGeometry::PlaneOrientation planeorientation,
164  ScalarType zPosition,
165  bool frontside,
166  bool rotated,
167  bool top)
168  {
169  AffineTransform3D::Pointer transform;
170 
171  transform = AffineTransform3D::New();
172  AffineTransform3D::MatrixType matrix;
173  AffineTransform3D::MatrixType::InternalMatrixType &vnlmatrix = matrix.GetVnlMatrix();
174 
175  vnlmatrix.set_identity();
176  vnlmatrix(0, 0) = spacing[0];
177  vnlmatrix(1, 1) = spacing[1];
178  vnlmatrix(2, 2) = spacing[2];
179  transform->SetIdentity();
180  transform->SetMatrix(matrix);
181 
182  InitializeStandardPlane(width, height, transform.GetPointer(), planeorientation, zPosition, frontside, rotated, top);
183  }
184 
186  mitk::ScalarType height,
187  const AffineTransform3D *transform /* = nullptr */,
188  PlaneGeometry::PlaneOrientation planeorientation /* = Axial */,
189  mitk::ScalarType zPosition /* = 0 */,
190  bool frontside /* = true */,
191  bool rotated /* = false */,
192  bool top /* = true */)
193  {
195 
197 
198  // We define at the moment "frontside" as: axial from above,
199  // coronal from front (nose), saggital from right.
200  // TODO: Double check with medicals doctors or radiologists [ ].
201 
202  // We define the orientation in patient's view, e.g. LAI is in a axial cut
203  // (parallel to the triangle ear-ear-nose):
204  // first axis: To the left ear of the patient
205  // seecond axis: To the nose of the patient
206  // third axis: To the legs of the patient.
207 
208  // Options are: L/R left/right; A/P anterior/posterior; I/S inferior/superior
209  // (AKA caudal/cranial).
210  // We note on all cases in the following switch block r.h. for right handed
211  // or l.h. for left handed to describe the different cases.
212  // However, which system is chosen is defined at the end of the switch block.
213 
214  // CAVE / be careful: the vectors right and bottom are relative to the plane
215  // and do NOT describe e.g. the right side of the patient.
216 
217  Point3D origin;
219  VnlVector rightDV(3), bottomDV(3);
221  origin.Fill(0);
228  int normalDirection;
229 
230  switch (planeorientation) // Switch through our limited choice of standard planes:
231  {
232  case None:
235  case Axial:
236  if (frontside) // Radiologist's view from below. A cut along the triangle ear-ear-nose.
237  {
238  if (rotated == false)
250  {
251  FillVector3D(origin, 0, 0, zPosition);
252  FillVector3D(rightDV, 1, 0, 0);
253  FillVector3D(bottomDV, 0, 1, 0);
254  }
255  else // Origin rotated to the bottom-right corner, x=[-1; 0; 0], y=[0; -1; 0], z=[0; 0; 1],
256  // origin=[w,h,zpos]: RPI (r.h.)
257  { // Caveat emptor: Still using top-left as origin of index coordinate system!
258  FillVector3D(origin, width, height, zPosition);
259  FillVector3D(rightDV, -1, 0, 0);
260  FillVector3D(bottomDV, 0, -1, 0);
261  }
262  }
263  else // 'Backside, not frontside.' Neuro-Surgeons's view from above patient.
264  {
265  if (rotated == false) // x=[-1; 0; 0], y=[0; 1; 0], z=[0; 0; 1], origin=[w,0,zpos]: RAS (r.h.)
266  {
267  FillVector3D(origin, width, 0, zPosition);
268  FillVector3D(rightDV, -1, 0, 0);
269  FillVector3D(bottomDV, 0, 1, 0);
270  }
271  else // Origin in the bottom-left corner, x=[1; 0; 0], y=[0; -1; 0], z=[0; 0; 1],
272  // origin=[0,h,zpos]: LPS (r.h.)
273  {
274  FillVector3D(origin, 0, height, zPosition);
275  FillVector3D(rightDV, 1, 0, 0);
276  FillVector3D(bottomDV, 0, -1, 0);
277  }
278  }
279  normalDirection = 2; // That is S=Superior=z=third_axis=middlefinger in righthanded LPS-system.
280  break;
281 
282  // Frontal is known as Coronal in mitk. Plane cuts through patient's ear-ear-heel-heel:
283  case Frontal:
284  if (frontside)
285  {
286  if (rotated == false) // x=[1; 0; 0], y=[0; 0; 1], z=[0; 1; 0], origin=[0,zpos,0]: LAI (r.h.)
287  {
288  FillVector3D(origin, 0, zPosition, 0);
289  FillVector3D(rightDV, 1, 0, 0);
290  FillVector3D(bottomDV, 0, 0, 1);
291  }
292  else // x=[-1;0;0], y=[0;0;-1], z=[0;1;0], origin=[w,zpos,h]: RAS (r.h.)
293  {
294  FillVector3D(origin, width, zPosition, height);
295  FillVector3D(rightDV, -1, 0, 0);
296  FillVector3D(bottomDV, 0, 0, -1);
297  }
298  }
299  else
300  {
301  if (rotated == false) // x=[-1;0;0], y=[0;0;1], z=[0;1;0], origin=[w,zpos,0]: RPI (r.h.)
302  {
303  FillVector3D(origin, width, zPosition, 0);
304  FillVector3D(rightDV, -1, 0, 0);
305  FillVector3D(bottomDV, 0, 0, 1);
306  }
307  else // x=[1;0;0], y=[0;1;0], z=[0;0;-1], origin=[0,zpos,h]: LPS (r.h.)
308  {
309  FillVector3D(origin, 0, zPosition, height);
310  FillVector3D(rightDV, 1, 0, 0);
311  FillVector3D(bottomDV, 0, 0, -1);
312  }
313  }
314  normalDirection = 1; // Normal vector = posterior direction.
315  break;
316 
317  case Sagittal: // Sagittal=Medial plane, the symmetry-plane mirroring your face.
318  if (frontside)
319  {
320  if (rotated == false) // x=[0;1;0], y=[0;0;1], z=[1;0;0], origin=[zpos,0,0]: LAI (r.h.)
321  {
322  FillVector3D(origin, zPosition, 0, 0);
323  FillVector3D(rightDV, 0, 1, 0);
324  FillVector3D(bottomDV, 0, 0, 1);
325  }
326  else // x=[0;-1;0], y=[0;0;-1], z=[1;0;0], origin=[zpos,w,h]: LPS (r.h.)
327  {
328  FillVector3D(origin, zPosition, width, height);
329  FillVector3D(rightDV, 0, -1, 0);
330  FillVector3D(bottomDV, 0, 0, -1);
331  }
332  }
333  else
334  {
335  if (rotated == false) // x=[0;-1;0], y=[0;0;1], z=[1;0;0], origin=[zpos,w,0]: RPI (r.h.)
336  {
337  FillVector3D(origin, zPosition, width, 0);
338  FillVector3D(rightDV, 0, -1, 0);
339  FillVector3D(bottomDV, 0, 0, 1);
340  }
341  else // x=[0;1;0], y=[0;0;-1], z=[1;0;0], origin=[zpos,0,h]: RAS (r.h.)
342  {
343  FillVector3D(origin, zPosition, 0, height);
344  FillVector3D(rightDV, 0, 1, 0);
345  FillVector3D(bottomDV, 0, 0, -1);
346  }
347  }
348  normalDirection = 0; // Normal vector = Lateral direction: Left in a LPS-system.
349  break;
350 
351  default:
352  itkExceptionMacro("unknown PlaneOrientation");
353  }
354 
355  VnlVector normal(3);
356  FillVector3D(normal, 0, 0, 0);
357  normal[normalDirection] = top ? 1 : -1;
358 
359  if ( transform != nullptr )
360  {
361  origin = transform->TransformPoint( origin );
362  rightDV = transform->TransformVector( rightDV );
363  bottomDV = transform->TransformVector( bottomDV );
364  normal = transform->TransformVector( normal );
365  }
366 
367  ScalarType bounds[6] = {0, width, 0, height, 0, 1};
368  this->SetBounds(bounds);
369 
371  Matrix3D matrix;
372  matrix.GetVnlMatrix().set_column(0, rightDV);
373  matrix.GetVnlMatrix().set_column(1, bottomDV);
374  matrix.GetVnlMatrix().set_column(2, normal);
375  planeTransform->SetMatrix(matrix);
376  planeTransform->SetOffset(this->GetIndexToWorldTransform()->GetOffset());
377  this->SetIndexToWorldTransform(planeTransform);
378 
379  this->SetOrigin(origin);
380  }
381 
383  PlaneOrientation planeorientation,
384  ScalarType zPosition,
385  bool frontside,
386  bool rotated,
387  bool top)
388  {
389  this->SetReferenceGeometry(geometry3D);
390 
391  ScalarType width, height;
392 
393  // Inspired by:
394  // http://www.na-mic.org/Wiki/index.php/Coordinate_System_Conversion_Between_ITK_and_Slicer3
395 
396  mitk::AffineTransform3D::MatrixType matrix = geometry3D->GetIndexToWorldTransform()->GetMatrix();
397 
398  matrix.GetVnlMatrix().normalize_columns();
399  mitk::AffineTransform3D::MatrixType::InternalMatrixType inverseMatrix = matrix.GetInverse();
400 
402  int axes[3];
406  int directions[3];
407  ScalarType extents[3];
408  ScalarType spacings[3];
409  for (int i = 0; i < 3; ++i)
410  {
411  int dominantAxis = itk::Function::Max3(
412  inverseMatrix[0][i],
413  inverseMatrix[1][i],
414  inverseMatrix[2][i]
415  );
416  axes[i] = dominantAxis;
417  directions[i] = itk::Function::Sign(inverseMatrix[dominantAxis][i]);
418  extents[i] = geometry3D->GetExtent(dominantAxis);
419  spacings[i] = geometry3D->GetSpacing()[dominantAxis];
420  }
421 
422  // matrix(column) = inverseTransformMatrix(row) * flippedAxes * spacing
423  matrix[0][0] = inverseMatrix[axes[0]][0] * directions[0] * spacings[0];
424  matrix[1][0] = inverseMatrix[axes[0]][1] * directions[0] * spacings[0];
425  matrix[2][0] = inverseMatrix[axes[0]][2] * directions[0] * spacings[0];
426  matrix[0][1] = inverseMatrix[axes[1]][0] * directions[1] * spacings[1];
427  matrix[1][1] = inverseMatrix[axes[1]][1] * directions[1] * spacings[1];
428  matrix[2][1] = inverseMatrix[axes[1]][2] * directions[1] * spacings[1];
429  matrix[0][2] = inverseMatrix[axes[2]][0] * directions[2] * spacings[2];
430  matrix[1][2] = inverseMatrix[axes[2]][1] * directions[2] * spacings[2];
431  matrix[2][2] = inverseMatrix[axes[2]][2] * directions[2] * spacings[2];
432 
436  Point3D worldOrigin = geometry3D->GetOrigin();
437  for (int i = 0; i < 3; ++i)
438  {
440  double offset = directions[i] > 0 ? 0.0 : extents[i];
441 
442  if (geometry3D->GetImageGeometry())
443  {
444  offset += directions[i] * 0.5;
445  }
446 
447  for (int j = 0; j < 3; ++j)
448  {
449  worldOrigin[j] -= offset * matrix[j][i];
450  }
451  }
452 
453  switch(planeorientation)
454  {
455  case None:
458  case Axial:
459  width = extents[0];
460  height = extents[1];
461  break;
462  case Frontal:
463  width = extents[0];
464  height = extents[2];
465  break;
466  case Sagittal:
467  width = extents[1];
468  height = extents[2];
469  break;
470  default:
471  itkExceptionMacro("unknown PlaneOrientation");
472  }
473 
474  ScalarType bounds[6]= { 0, width, 0, height, 0, 1 };
475  this->SetBounds( bounds );
476 
478  transform->SetMatrix(matrix);
479  transform->SetOffset(worldOrigin.GetVectorFromOrigin());
480 
482  width, height, transform, planeorientation, zPosition, frontside, rotated, top);
483  }
484 
486  const BaseGeometry *geometry3D, bool top, PlaneOrientation planeorientation, bool frontside, bool rotated)
487  {
489  int worldAxis;
490  switch(planeorientation)
491  {
492  case None:
495  case Axial:
496  worldAxis = 2;
497  break;
498  case Frontal:
499  worldAxis = 1;
500  break;
501  case Sagittal:
502  worldAxis = 0;
503  break;
504  default:
505  itkExceptionMacro("unknown PlaneOrientation");
506  }
507 
508  // Inspired by:
509  // http://www.na-mic.org/Wiki/index.php/Coordinate_System_Conversion_Between_ITK_and_Slicer3
510 
511  mitk::AffineTransform3D::ConstPointer affineTransform = geometry3D->GetIndexToWorldTransform();
512  mitk::AffineTransform3D::MatrixType matrix = affineTransform->GetMatrix();
513  matrix.GetVnlMatrix().normalize_columns();
514  mitk::AffineTransform3D::MatrixType::InternalMatrixType inverseMatrix = matrix.GetInverse();
515 
517  int dominantAxis = itk::Function::Max3(
518  inverseMatrix[0][worldAxis],
519  inverseMatrix[1][worldAxis],
520  inverseMatrix[2][worldAxis]);
521 
522  ScalarType zPosition = top ? 0.5 : geometry3D->GetExtent(dominantAxis) - 0.5;
523 
524  InitializeStandardPlane(geometry3D, planeorientation, zPosition, frontside, rotated, top);
525  }
526 
528  const Vector3D &downVector,
529  const Vector3D *spacing)
530  {
531  InitializeStandardPlane(rightVector.GetVnlVector(), downVector.GetVnlVector(), spacing);
532  }
533 
535  const VnlVector &downVector,
536  const Vector3D *spacing)
537  {
538  ScalarType width = rightVector.two_norm();
539  ScalarType height = downVector.two_norm();
540 
541  InitializeStandardPlane(width, height, rightVector, downVector, spacing);
542  }
543 
545  ScalarType height,
546  const Vector3D &rightVector,
547  const Vector3D &downVector,
548  const Vector3D *spacing)
549  {
550  InitializeStandardPlane(width, height, rightVector.GetVnlVector(), downVector.GetVnlVector(), spacing);
551  }
552 
554  ScalarType height,
555  const VnlVector &rightVector,
556  const VnlVector &downVector,
557  const Vector3D *spacing)
558  {
559  assert(width > 0);
560  assert(height > 0);
561 
562  VnlVector rightDV = rightVector;
563  rightDV.normalize();
564  VnlVector downDV = downVector;
565  downDV.normalize();
566  VnlVector normal = vnl_cross_3d(rightVector, downVector);
567  normal.normalize();
568  // Crossproduct vnl_cross_3d is always righthanded, but that is okay here
569  // because in this method we create a new IndexToWorldTransform and
570  // spacing with 1 or 3 negative components could still make it lefthanded.
571 
572  if (spacing != nullptr)
573  {
574  rightDV *= (*spacing)[0];
575  downDV *= (*spacing)[1];
576  normal *= (*spacing)[2];
577  }
578 
580  Matrix3D matrix;
581  matrix.GetVnlMatrix().set_column(0, rightDV);
582  matrix.GetVnlMatrix().set_column(1, downDV);
583  matrix.GetVnlMatrix().set_column(2, normal);
584  transform->SetMatrix(matrix);
585  transform->SetOffset(this->GetIndexToWorldTransform()->GetOffset());
586 
587  ScalarType bounds[6] = {0, width, 0, height, 0, 1};
588  this->SetBounds(bounds);
589 
590  this->SetIndexToWorldTransform(transform);
591  }
592 
593  void PlaneGeometry::InitializePlane(const Point3D &origin, const Vector3D &normal)
594  {
595  VnlVector rightVectorVnl(3), downVectorVnl;
596 
597  if (Equal(normal[1], 0.0f) == false)
598  {
599  FillVector3D(rightVectorVnl, 1.0f, -normal[0] / normal[1], 0.0f);
600  rightVectorVnl.normalize();
601  }
602  else
603  {
604  FillVector3D(rightVectorVnl, 0.0f, 1.0f, 0.0f);
605  }
606  downVectorVnl = vnl_cross_3d(normal.GetVnlVector(), rightVectorVnl);
607  downVectorVnl.normalize();
608  // Crossproduct vnl_cross_3d is always righthanded.
609 
610  InitializeStandardPlane(rightVectorVnl, downVectorVnl);
611 
612  SetOrigin(origin);
613  }
614 
616  const VnlVector &downVector,
617  ScalarType thickness /* = 1.0 */)
618  {
619  VnlVector normal = vnl_cross_3d(rightVector, downVector);
620  normal.normalize();
621  normal *= thickness;
622  // Crossproduct vnl_cross_3d is always righthanded, but that is okay here
623  // because in this method we create a new IndexToWorldTransform and
624  // a negative thickness could still make it lefthanded.
625 
627  Matrix3D matrix;
628  matrix.GetVnlMatrix().set_column(0, rightVector);
629  matrix.GetVnlMatrix().set_column(1, downVector);
630  matrix.GetVnlMatrix().set_column(2, normal);
631  transform->SetMatrix(matrix);
632  transform->SetOffset(this->GetIndexToWorldTransform()->GetOffset());
633  SetIndexToWorldTransform(transform);
634  }
635 
637  {
638  Vector3D frontToBack;
639  frontToBack.SetVnlVector(this->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(2));
640 
641  return frontToBack;
642  }
643 
645  {
646  return this->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(2);
647  }
648 
649  ScalarType PlaneGeometry::DistanceFromPlane(const Point3D &pt3d_mm) const { return fabs(SignedDistance(pt3d_mm)); }
650  ScalarType PlaneGeometry::SignedDistance(const Point3D &pt3d_mm) const { return SignedDistanceFromPlane(pt3d_mm); }
651  bool PlaneGeometry::IsAbove(const Point3D &pt3d_mm, bool considerBoundingBox) const
652  {
653  if (considerBoundingBox)
654  {
655  Point3D pt3d_units;
656  BaseGeometry::WorldToIndex(pt3d_mm, pt3d_units);
657  return (pt3d_units[2] > this->GetBoundingBox()->GetBounds()[4]);
658  }
659  else
660  return SignedDistanceFromPlane(pt3d_mm) > 0;
661  }
662 
663  bool PlaneGeometry::IntersectionLine(const PlaneGeometry *plane, Line3D &crossline) const
664  {
665  Vector3D normal = this->GetNormal();
666  normal.Normalize();
667 
668  Vector3D planeNormal = plane->GetNormal();
669  planeNormal.Normalize();
670 
671  Vector3D direction = itk::CrossProduct(normal, planeNormal);
672 
673  if (direction.GetSquaredNorm() < eps)
674  return false;
675 
676  crossline.SetDirection(direction);
677 
678  double N1dN2 = normal * planeNormal;
679  double determinant = 1.0 - N1dN2 * N1dN2;
680 
681  Vector3D origin = this->GetOrigin().GetVectorFromOrigin();
682  Vector3D planeOrigin = plane->GetOrigin().GetVectorFromOrigin();
683 
684  double d1 = normal * origin;
685  double d2 = planeNormal * planeOrigin;
686 
687  double c1 = (d1 - d2 * N1dN2) / determinant;
688  double c2 = (d2 - d1 * N1dN2) / determinant;
689 
690  Vector3D p = normal * c1 + planeNormal * c2;
691  crossline.GetPoint().GetVnlVector() = p.GetVnlVector();
692 
693  return true;
694  }
695 
696  unsigned int PlaneGeometry::IntersectWithPlane2D(const PlaneGeometry *plane, Point2D &lineFrom, Point2D &lineTo) const
697  {
698  Line3D crossline;
699  if (this->IntersectionLine(plane, crossline) == false)
700  return 0;
701 
702  Point2D point2;
703  Vector2D direction2;
704 
705  this->Map(crossline.GetPoint(), point2);
706  this->Map(crossline.GetPoint(), crossline.GetDirection(), direction2);
707 
709  0, 0, GetExtentInMM(0), GetExtentInMM(1), point2, direction2, lineFrom, lineTo);
710  }
711 
712  double PlaneGeometry::Angle(const PlaneGeometry *plane) const
713  {
714  return angle(plane->GetMatrixColumn(2), GetMatrixColumn(2));
715  }
716 
717  double PlaneGeometry::Angle(const Line3D &line) const
718  {
719  return vnl_math::pi_over_2 - angle(line.GetDirection().GetVnlVector(), GetMatrixColumn(2));
720  }
721 
722  bool PlaneGeometry::IntersectionPoint(const Line3D &line, Point3D &intersectionPoint) const
723  {
724  Vector3D planeNormal = this->GetNormal();
725  planeNormal.Normalize();
726 
727  Vector3D lineDirection = line.GetDirection();
728  lineDirection.Normalize();
729 
730  double t = planeNormal * lineDirection;
731  if (fabs(t) < eps)
732  {
733  return false;
734  }
735 
736  Vector3D diff;
737  diff = this->GetOrigin() - line.GetPoint();
738  t = (planeNormal * diff) / t;
739 
740  intersectionPoint = line.GetPoint() + lineDirection * t;
741  return true;
742  }
743 
744  bool PlaneGeometry::IntersectionPointParam(const Line3D &line, double &t) const
745  {
746  Vector3D planeNormal = this->GetNormal();
747 
748  Vector3D lineDirection = line.GetDirection();
749 
750  t = planeNormal * lineDirection;
751 
752  if (fabs(t) < eps)
753  {
754  return false;
755  }
756 
757  Vector3D diff;
758  diff = this->GetOrigin() - line.GetPoint();
759  t = (planeNormal * diff) / t;
760  return true;
761  }
762 
763  bool PlaneGeometry::IsParallel(const PlaneGeometry *plane) const
764  {
765  return ((Angle(plane) < 10.0 * mitk::sqrteps) || (Angle(plane) > (vnl_math::pi - 10.0 * sqrteps)));
766  }
767 
768  bool PlaneGeometry::IsOnPlane(const Point3D &point) const { return Distance(point) < eps; }
770  {
771  return ((Distance(line.GetPoint()) < eps) && (Distance(line.GetPoint2()) < eps));
772  }
773 
774  bool PlaneGeometry::IsOnPlane(const PlaneGeometry *plane) const
775  {
776  return (IsParallel(plane) && (Distance(plane->GetOrigin()) < eps));
777  }
778 
780  {
781  ScalarType len = this->GetNormalVnl().two_norm();
782  return pt - this->GetNormal() * this->SignedDistanceFromPlane(pt) / len;
783  }
784 
786  {
787  Self::Pointer newGeometry = new PlaneGeometry(*this);
788  newGeometry->UnRegister();
789  return newGeometry.GetPointer();
790  }
791 
793  {
794  vtkTransform *transform = vtkTransform::New();
795  transform->SetMatrix(this->GetVtkMatrix());
796 
797  switch (operation->GetOperationType())
798  {
799  case OpORIENT:
800  {
801  mitk::PlaneOperation *planeOp = dynamic_cast<mitk::PlaneOperation *>(operation);
802  if (planeOp == nullptr)
803  {
804  return;
805  }
806 
807  Point3D center = planeOp->GetPoint();
808 
809  Vector3D orientationVector = planeOp->GetNormal();
810  Vector3D defaultVector;
811  FillVector3D(defaultVector, 0.0, 0.0, 1.0);
812 
813  Vector3D rotationAxis = itk::CrossProduct(orientationVector, defaultVector);
814  // double rotationAngle = acos( orientationVector[2] / orientationVector.GetNorm() );
815 
816  double rotationAngle = atan2((double)rotationAxis.GetNorm(), (double)(orientationVector * defaultVector));
817  rotationAngle *= 180.0 / vnl_math::pi;
818 
819  transform->PostMultiply();
820  transform->Identity();
821  transform->Translate(center[0], center[1], center[2]);
822  transform->RotateWXYZ(rotationAngle, rotationAxis[0], rotationAxis[1], rotationAxis[2]);
823  transform->Translate(-center[0], -center[1], -center[2]);
824  break;
825  }
827  {
829  if (op == nullptr)
830  {
831  return;
832  }
833 
835  Matrix3D matrix;
836  matrix.GetVnlMatrix().set_column(0, op->GetTransform()->GetMatrix().GetVnlMatrix().get_column(0));
837  matrix.GetVnlMatrix().set_column(1, op->GetTransform()->GetMatrix().GetVnlMatrix().get_column(1));
838  matrix.GetVnlMatrix().set_column(2, op->GetTransform()->GetMatrix().GetVnlMatrix().get_column(2));
839  transform2->SetMatrix(matrix);
840  Vector3D offset = op->GetTransform()->GetOffset();
841  transform2->SetOffset(offset);
842 
843  this->SetIndexToWorldTransform(transform2);
844  ScalarType bounds[6] = {0, op->GetWidth(), 0, op->GetHeight(), 0, 1};
845  this->SetBounds(bounds);
846  this->Modified();
847  transform->Delete();
848  return;
849  }
850  default:
851  Superclass::ExecuteOperation(operation);
852  transform->Delete();
853  return;
854  }
855 
856  this->SetVtkMatrixDeepCopy(transform);
857  this->Modified();
858  transform->Delete();
859  }
860 
861  void PlaneGeometry::PrintSelf(std::ostream &os, itk::Indent indent) const
862  {
863  Superclass::PrintSelf(os, indent);
864  os << indent << " ScaleFactorMMPerUnitX: " << GetExtentInMM(0) / GetExtent(0) << std::endl;
865  os << indent << " ScaleFactorMMPerUnitY: " << GetExtentInMM(1) / GetExtent(1) << std::endl;
866  os << indent << " Normal: " << GetNormal() << std::endl;
867  }
868 
869  bool PlaneGeometry::Map(const mitk::Point3D &pt3d_mm, mitk::Point2D &pt2d_mm) const
870  {
871  assert(this->IsBoundingBoxNull() == false);
872 
873  Point3D pt3d_units;
874  Superclass::WorldToIndex(pt3d_mm, pt3d_units);
875  pt2d_mm[0] = pt3d_units[0] * GetExtentInMM(0) / GetExtent(0);
876  pt2d_mm[1] = pt3d_units[1] * GetExtentInMM(1) / GetExtent(1);
877  pt3d_units[2] = 0;
878  return const_cast<BoundingBox *>(this->GetBoundingBox())->IsInside(pt3d_units);
879  }
880 
881  void PlaneGeometry::Map(const mitk::Point2D &pt2d_mm, mitk::Point3D &pt3d_mm) const
882  {
883  // pt2d_mm is measured from the origin of the world geometry (at leats it called form BaseRendere::Mouse...Event)
884  Point3D pt3d_units;
885  pt3d_units[0] = pt2d_mm[0] / (GetExtentInMM(0) / GetExtent(0));
886  pt3d_units[1] = pt2d_mm[1] / (GetExtentInMM(1) / GetExtent(1));
887  pt3d_units[2] = 0;
888  // pt3d_units is a continuos index. We divided it with the Scale Factor (= spacing in x and y) to convert it from mm
889  // to index units.
890  //
891  pt3d_mm = GetIndexToWorldTransform()->TransformPoint(pt3d_units);
892  // now we convert the 3d index to a 3D world point in mm. We could have used IndexToWorld as well as
893  // GetITW->Transform...
894  }
895 
897  {
898  ScalarType bounds[6] = {0, width, 0, height, 0, 1};
899  ScalarType extent, newextentInMM;
900  if (GetExtent(0) > 0)
901  {
902  extent = GetExtent(0);
903  if (width > extent)
904  newextentInMM = GetExtentInMM(0) / width * extent;
905  else
906  newextentInMM = GetExtentInMM(0) * extent / width;
907  SetExtentInMM(0, newextentInMM);
908  }
909  if (GetExtent(1) > 0)
910  {
911  extent = GetExtent(1);
912  if (width > extent)
913  newextentInMM = GetExtentInMM(1) / height * extent;
914  else
915  newextentInMM = GetExtentInMM(1) * extent / height;
916  SetExtentInMM(1, newextentInMM);
917  }
918  SetBounds(bounds);
919  }
920 
921  bool PlaneGeometry::Project(const mitk::Point3D &pt3d_mm, mitk::Point3D &projectedPt3d_mm) const
922  {
923  assert(this->IsBoundingBoxNull() == false);
924 
925  Point3D pt3d_units;
926  Superclass::WorldToIndex(pt3d_mm, pt3d_units);
927  pt3d_units[2] = 0;
928  projectedPt3d_mm = GetIndexToWorldTransform()->TransformPoint(pt3d_units);
929  return const_cast<BoundingBox *>(this->GetBoundingBox())->IsInside(pt3d_units);
930  }
931 
932  bool PlaneGeometry::Project(const mitk::Vector3D &vec3d_mm, mitk::Vector3D &projectedVec3d_mm) const
933  {
934  assert(this->IsBoundingBoxNull() == false);
935 
936  Vector3D vec3d_units;
937  Superclass::WorldToIndex(vec3d_mm, vec3d_units);
938  vec3d_units[2] = 0;
939  projectedVec3d_mm = GetIndexToWorldTransform()->TransformVector(vec3d_units);
940  return true;
941  }
942 
943  bool PlaneGeometry::Project(const mitk::Point3D &atPt3d_mm,
944  const mitk::Vector3D &vec3d_mm,
945  mitk::Vector3D &projectedVec3d_mm) const
946  {
947  MITK_WARN << "Deprecated function! Call Project(vec3D,vec3D) instead.";
948  assert(this->IsBoundingBoxNull() == false);
949 
950  Vector3D vec3d_units;
951  Superclass::WorldToIndex(atPt3d_mm, vec3d_mm, vec3d_units);
952  vec3d_units[2] = 0;
953  projectedVec3d_mm = GetIndexToWorldTransform()->TransformVector(vec3d_units);
954 
955  Point3D pt3d_units;
956  Superclass::WorldToIndex(atPt3d_mm, pt3d_units);
957  return const_cast<BoundingBox *>(this->GetBoundingBox())->IsInside(pt3d_units);
958  }
959 
960  bool PlaneGeometry::Map(const mitk::Point3D &atPt3d_mm,
961  const mitk::Vector3D &vec3d_mm,
962  mitk::Vector2D &vec2d_mm) const
963  {
964  Point2D pt2d_mm_start, pt2d_mm_end;
965  Point3D pt3d_mm_end;
966  bool inside = Map(atPt3d_mm, pt2d_mm_start);
967  pt3d_mm_end = atPt3d_mm + vec3d_mm;
968  inside &= Map(pt3d_mm_end, pt2d_mm_end);
969  vec2d_mm = pt2d_mm_end - pt2d_mm_start;
970  return inside;
971  }
972 
973  void PlaneGeometry::Map(const mitk::Point2D & /*atPt2d_mm*/,
974  const mitk::Vector2D & /*vec2d_mm*/,
975  mitk::Vector3D & /*vec3d_mm*/) const
976  {
977  //@todo implement parallel to the other Map method!
978  assert(false);
979  }
980 
983  bool PlaneGeometry::HasReferenceGeometry() const { return (m_ReferenceGeometry != nullptr); }
984 } // namespace
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.
static void EnsurePerpendicularNormal(AffineTransform3D *transform)
Change transform so that the third column of the transform-martix is perpendicular to the first two c...
bool IntersectionPointParam(const Line3D &line, double &t) const
Calculate line parameter of intersection point between the plane and a line.
itk::BoundingBox< unsigned long, 3, ScalarType > BoundingBox
Standard 3D-BoundingBox typedef.
Descibes a line.
Definition: mitkLine.h:32
static char * line
Definition: svm.cpp:2884
itk::SmartPointer< Self > Pointer
double Angle(const PlaneGeometry *plane) const
Calculate the angle between two planes.
vtkMatrix4x4 * GetVtkMatrix()
virtual void ExecuteOperation(Operation *operation) override
void SetDirection(const itk::Vector< TCoordRep, NPointDimension > &direction)
Set the direction vector of the line.
Definition: mitkLine.h:80
unsigned int IntersectWithPlane2D(const PlaneGeometry *plane, Point2D &lineFrom, Point2D &lineTo) const
Calculate two points where another plane intersects the border of this plane.
void SetIndexToWorldTransform(mitk::AffineTransform3D *transform)
bool IntersectionPoint(const Line3D &line, Point3D &intersectionPoint) const
Calculate intersection point between the plane and a line.
ScalarType SignedDistanceFromPlane(const Point3D &pt3d_mm) const
Signed distance of the point from the plane (bounding-box not considered)
virtual void PrintSelf(std::ostream &os, itk::Indent indent) const override
Base class of all Operation-classes.
Definition: mitkOperation.h:33
virtual void PrintSelf(std::ostream &os, itk::Indent indent) const override
virtual bool Map(const mitk::Point3D &pt3d_mm, mitk::Point2D &pt2d_mm) const
Project a 3D point given in mm (pt3d_mm) onto the 2D geometry. The result is a 2D point in mm (pt2d_m...
vnl_vector< ScalarType > VnlVector
Definition: mitkVector.h:138
double ScalarType
bool IsOnPlane(const Point3D &point) const
Returns whether the point is on the plane (bounding-box not considered)
virtual ScalarType SignedDistance(const Point3D &pt3d_mm) const
#define MITK_DEBUG
Definition: mitkLogMacros.h:26
DataCollection - Class to facilitate loading/accessing structured data.
ScalarType Distance(const Point3D &pt3d_mm) const
Distance of the point from the geometry (bounding-box not considered)
const mitk::Vector3D GetSpacing() const
Get the spacing (size of a pixel).
void Initialize()
Initialize the BaseGeometry.
Constants for most interaction classes, due to the generic StateMachines.
VnlVector GetNormalVnl() const
Normal of the plane as VnlVector.
VnlVector GetMatrixColumn(unsigned int direction) const
Get a VnlVector along bounding-box in the specified direction, length is spacing. ...
const itk::Vector< TCoordRep, NPointDimension > & GetDirection() const
Get the direction vector of the line.
Definition: mitkLine.h:74
virtual void WorldToIndex(const Point2D &pt_mm, Point2D &pt_units) const
const BoundsArrayType GetBounds() const
virtual void IndexToWorld(const Point2D &pt_units, Point2D &pt_mm) const
virtual void CheckIndexToWorldTransform(mitk::AffineTransform3D *transform) override
CheckIndexToWorldTransform.
void FillVector3D(Tout &out, mitk::ScalarType x, mitk::ScalarType y, mitk::ScalarType z)
Definition: mitkArray.h:110
Vector3D GetNormal() const
Normal of the plane.
itk::SmartPointer< const Self > ConstPointer
ScalarType DistanceFromPlane(const Point3D &pt3d_mm) const
Distance of the point from the plane (bounding-box not considered)
const mitk::BaseGeometry * m_ReferenceGeometry
static Vector3D offset
virtual bool IsAbove(const Point3D &pt3d_mm, bool considerBoundingBox=false) const
Calculates, whether a point is below or above the plane. There are two different calculation methods...
void SetVtkMatrixDeepCopy(vtkTransform *vtktransform)
#define MITK_WARN
Definition: mitkLogMacros.h:23
virtual void SetSizeInUnits(mitk::ScalarType width, mitk::ScalarType height)
Set the width and height of this 2D-geometry in units by calling SetBounds. This does not change the ...
bool IsParallel(const PlaneGeometry *plane) const
Returns whether the plane is parallel to another plane.
const itk::Point< TCoordRep, NPointDimension > & GetPoint() const
Get start point of the line.
Definition: mitkLine.h:53
void SetOrigin(const Point3D &origin)
Set the origin, i.e. the upper-left corner of the plane.
void Modified() const override
Overload of function Modified() to prohibit several calls of Modified() using the ModifiedLock class...
Operation for setting a plane (defined by its origin and normal)
void SetReferenceGeometry(const mitk::BaseGeometry *geometry)
Set the geometrical frame of reference in which this PlaneGeometry is placed.
itk::AffineGeometryFrame< ScalarType, 3 >::TransformType AffineTransform3D
virtual bool Project(const mitk::Point3D &pt3d_mm, mitk::Point3D &projectedPt3d_mm) const
Project a 3D point given in mm (pt3d_mm) onto the 2D geometry. The result is a 3D point in mm (projec...
bool HasReferenceGeometry() const
MITKCORE_EXPORT const ScalarType sqrteps
Point3D ProjectPointOntoPlane(const Point3D &pt) const
Returns the lot from the point to the plane.
const BaseGeometry * GetReferenceGeometry() const
Get the geometrical frame of reference for this PlaneGeometry.
MITKNEWMODULE_EXPORT bool Equal(mitk::ExampleDataStructure *leftHandSide, mitk::ExampleDataStructure *rightHandSide, mitk::ScalarType eps, bool verbose)
Returns true if the example data structures are considered equal.
void SetBounds(const BoundsArrayType &bounds)
Set the bounding box (in index/unit coordinates)
itk::Point< TCoordRep, NPointDimension > GetPoint2() const
Get end point of the line.
Definition: mitkLine.h:121
bool IntersectionLine(const PlaneGeometry *plane, Line3D &crossline) const
Calculate the intersecting line of two planes.
virtual itk::LightObject::Pointer InternalClone() const override
clones the geometry
void SetExtentInMM(int direction, ScalarType extentInMM)
Set the extent of the bounding-box in the specified direction in mm.
static int RectangleLineIntersection(TCoordRep x1, TCoordRep y1, TCoordRep x2, TCoordRep y2, itk::Point< TCoordRep, 2 > p, itk::Vector< TCoordRep, 2 > d, itk::Point< TCoordRep, 2 > &s1, itk::Point< TCoordRep, 2 > &s2)
Calculates the intersection points of a straight line in 2D with a rectangle.
Definition: mitkLine.h:264
void SetMatrixByVectors(const VnlVector &rightVector, const VnlVector &downVector, ScalarType thickness=1.0)
Initialize plane by right-/down-vector.
virtual void CheckBounds(const BoundsArrayType &bounds) override
CheckBounds.
virtual void InitializePlane(const Point3D &origin, const Vector3D &normal)
Initialize plane by origin and normal (size is 1.0 mm in all directions, direction of right-/down-vec...
MITKCORE_EXPORT const ScalarType eps
virtual void InitializeStandardPlane(const BaseGeometry *geometry3D, PlaneOrientation planeorientation=Axial, ScalarType zPosition=0, bool frontside=true, bool rotated=false, bool top=true)
Initialize a plane with orientation planeorientation (default: axial) with respect to BaseGeometry (d...
bool IsBoundingBoxNull() const
Describes a two-dimensional, rectangular plane.
bool IsInside(const mitk::Point3D &p) const
Test whether the point p (world coordinates in mm) is inside the bounding box.
OperationType GetOperationType()
ScalarType GetExtentInMM(int direction) const
Get the extent of the bounding-box in the specified direction in mm.
virtual bool GetImageGeometry() const
Is this an ImageGeometry?
BaseGeometry Describes the geometry of a data object.
virtual void ExecuteOperation(Operation *operation) override
executes affine operations (translate, rotate, scale)
mitk::AffineTransform3D * GetIndexToWorldTransform()
Get the transformation used to convert from index to world coordinates.
BoundingBoxType::BoundsArrayType BoundsArrayType
void WorldToIndex(const mitk::Point3D &pt_mm, mitk::Point3D &pt_units) const
Convert world coordinates (in mm) of a point to (continuous!) index coordinates.
virtual const BoundingBoxType * GetBoundingBox()
static itkEventMacro(BoundingShapeInteractionEvent, itk::AnyEvent) class MITKBOUNDINGSHAPE_EXPORT BoundingShapeInteractor Pointer New()
Basic interaction methods for mitk::GeometryData.