Medical Imaging Interaction Toolkit  2016.11.0
Medical Imaging Interaction Toolkit
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.