Medical Imaging Interaction Toolkit  2016.11.0
Medical Imaging Interaction Toolkit
mitkSlicedGeometry3D.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 <itkSpatialOrientationAdapter.h>
18 
19 #include "mitkSlicedGeometry3D.h"
22 #include "mitkInteractionConst.h"
23 #include "mitkPlaneGeometry.h"
24 #include "mitkPlaneOperation.h"
26 #include "mitkRotationOperation.h"
28 
29 const mitk::ScalarType PI = 3.14159265359;
30 
32  : m_EvenlySpaced(true), m_Slices(0), m_ReferenceGeometry(nullptr), m_SliceNavigationController(nullptr)
33 {
34  m_DirectionVector.Fill(0);
36 }
37 
39  : Superclass(other),
40  m_EvenlySpaced(other.m_EvenlySpaced),
41  m_Slices(other.m_Slices),
42  m_ReferenceGeometry(other.m_ReferenceGeometry),
43  m_SliceNavigationController(other.m_SliceNavigationController)
44 {
45  m_DirectionVector.Fill(0);
46  SetSpacing(other.GetSpacing());
48 
49  if (m_EvenlySpaced)
50  {
51  assert(!other.m_PlaneGeometries.empty() && "This may happen when you use one of the old Initialize methods, which had a bool parameter that is implicitly casted to the number of slices now.");
52  PlaneGeometry::Pointer geometry = other.m_PlaneGeometries[0]->Clone();
53  assert(geometry.IsNotNull());
54  SetPlaneGeometry(geometry, 0);
55  }
56  else
57  {
58  unsigned int s;
59  for (s = 0; s < other.m_Slices; ++s)
60  {
61  if (other.m_PlaneGeometries[s].IsNull())
62  {
63  assert(other.m_EvenlySpaced);
64  m_PlaneGeometries[s] = nullptr;
65  }
66  else
67  {
68  PlaneGeometry *geometry2D = other.m_PlaneGeometries[s]->Clone();
69  assert(geometry2D != nullptr);
70  SetPlaneGeometry(geometry2D, s);
71  }
72  }
73  }
74 }
75 
77 {
78 }
79 
81 {
82  mitk::PlaneGeometry::Pointer geometry2D = nullptr;
83 
84  if (this->IsValidSlice(s))
85  {
86  geometry2D = m_PlaneGeometries[s];
87 
88  // If (a) m_EvenlySpaced==true, (b) we don't have a PlaneGeometry stored
89  // for the requested slice, and (c) the first slice (s=0)
90  // is a PlaneGeometry instance, then we calculate the geometry of the
91  // requested as the plane of the first slice shifted by m_Spacing[2]*s
92  // in the direction of m_DirectionVector.
93  if ((m_EvenlySpaced) && (geometry2D.IsNull()))
94  {
95  PlaneGeometry *firstSlice = m_PlaneGeometries[0];
96 
97  if (firstSlice != nullptr &&
98  dynamic_cast<AbstractTransformGeometry *>(m_PlaneGeometries[0].GetPointer()) == nullptr)
99  {
100  if ((m_DirectionVector[0] == 0.0) && (m_DirectionVector[1] == 0.0) && (m_DirectionVector[2] == 0.0))
101  {
102  m_DirectionVector = firstSlice->GetNormal();
103  m_DirectionVector.Normalize();
104  }
105 
106  Vector3D direction;
107  direction = m_DirectionVector * this->GetSpacing()[2];
108 
109  mitk::PlaneGeometry::Pointer requestedslice;
110  requestedslice = static_cast<mitk::PlaneGeometry *>(firstSlice->Clone().GetPointer());
111 
112  requestedslice->SetOrigin(requestedslice->GetOrigin() + direction * s);
113 
114  geometry2D = requestedslice;
115  m_PlaneGeometries[s] = geometry2D;
116  }
117  }
118  return geometry2D;
119  }
120  else
121  {
122  return nullptr;
123  }
124 }
125 
127 {
128  assert(this->IsBoundingBoxNull() == false);
129  return Superclass::GetBoundingBox();
130 }
131 
133 {
134  if (this->IsValidSlice(s))
135  {
136  m_PlaneGeometries[s] = geometry2D;
137  m_PlaneGeometries[s]->SetReferenceGeometry(m_ReferenceGeometry);
138  return true;
139  }
140  return false;
141 }
142 
144 {
145  Superclass::Initialize();
146  m_Slices = slices;
147 
148  PlaneGeometry::Pointer gnull = nullptr;
149  m_PlaneGeometries.assign(m_Slices, gnull);
150 
151  Vector3D spacing;
152  spacing.Fill(1.0);
153  this->SetSpacing(spacing);
154 
155  m_DirectionVector.Fill(0);
156 }
157 
159 {
160  assert(geometry2D != nullptr);
161  this->InitializeEvenlySpaced(geometry2D, geometry2D->GetExtentInMM(2) / geometry2D->GetExtent(2), slices);
162 }
163 
165  mitk::ScalarType zSpacing,
166  unsigned int slices)
167 {
168  assert(geometry2D != nullptr);
169  assert(geometry2D->GetExtent(0) > 0);
170  assert(geometry2D->GetExtent(1) > 0);
171 
172  geometry2D->Register();
173 
174  Superclass::Initialize();
175  m_Slices = slices;
176 
177  BoundingBox::BoundsArrayType bounds = geometry2D->GetBounds();
178  bounds[4] = 0;
179  bounds[5] = slices;
180 
181  // clear and reserve
182  PlaneGeometry::Pointer gnull = nullptr;
183  m_PlaneGeometries.assign(m_Slices, gnull);
184 
185  Vector3D directionVector = geometry2D->GetAxisVector(2);
186  directionVector.Normalize();
187  directionVector *= zSpacing;
188 
189  // Normally we should use the following four lines to create a copy of
190  // the transform contrained in geometry2D, because it may not be changed
191  // by us. But we know that SetSpacing creates a new transform without
192  // changing the old (coming from geometry2D), so we can use the fifth
193  // line instead. We check this at (**).
194  //
195  // AffineTransform3D::Pointer transform = AffineTransform3D::New();
196  // transform->SetMatrix(geometry2D->GetIndexToWorldTransform()->GetMatrix());
197  // transform->SetOffset(geometry2D->GetIndexToWorldTransform()->GetOffset());
198  // SetIndexToWorldTransform(transform);
199 
200  this->SetIndexToWorldTransform(const_cast<AffineTransform3D *>(geometry2D->GetIndexToWorldTransform()));
201 
202  mitk::Vector3D spacing;
203  FillVector3D(spacing, geometry2D->GetExtentInMM(0) / bounds[1], geometry2D->GetExtentInMM(1) / bounds[3], zSpacing);
204 
205  this->SetDirectionVector(directionVector);
206  this->SetBounds(bounds);
207  this->SetPlaneGeometry(geometry2D, 0);
208  this->SetSpacing(spacing, true);
209  this->SetEvenlySpaced();
210 
211  // this->SetTimeBounds( geometry2D->GetTimeBounds() );
212 
213  assert(this->GetIndexToWorldTransform() != geometry2D->GetIndexToWorldTransform()); // (**) see above.
214 
215  this->SetFrameOfReferenceID(geometry2D->GetFrameOfReferenceID());
216  this->SetImageGeometry(geometry2D->GetImageGeometry());
217 
218  geometry2D->UnRegister();
219 }
220 
222  mitk::PlaneGeometry::PlaneOrientation planeorientation,
223  bool top,
224  bool frontside,
225  bool rotated)
226 {
227  m_ReferenceGeometry = geometry3D;
228 
230  planeGeometry->InitializeStandardPlane(geometry3D, top, planeorientation, frontside, rotated);
231 
232  int worldAxis =
233  planeorientation == PlaneGeometry::Sagittal ? 0 :
234  planeorientation == PlaneGeometry::Frontal ? 1 : 2;
235 
236  // Inspired by:
237  // http://www.na-mic.org/Wiki/index.php/Coordinate_System_Conversion_Between_ITK_and_Slicer3
238 
239  mitk::AffineTransform3D::MatrixType matrix = geometry3D->GetIndexToWorldTransform()->GetMatrix();
240  matrix.GetVnlMatrix().normalize_columns();
241  mitk::AffineTransform3D::MatrixType::InternalMatrixType inverseMatrix = matrix.GetInverse();
242 
243  int dominantAxis = itk::Function::Max3(
244  inverseMatrix[0][worldAxis],
245  inverseMatrix[1][worldAxis],
246  inverseMatrix[2][worldAxis]);
247 
248  ScalarType viewSpacing = geometry3D->GetSpacing()[dominantAxis];
249 
253  unsigned int slices = static_cast<unsigned int>(geometry3D->GetExtent(dominantAxis) + 0.5);
254 
255 #ifndef NDEBUG
256  int upDirection = itk::Function::Sign(inverseMatrix[dominantAxis][worldAxis]);
257 
261 
262  Vector3D worldPlaneNormal = inverseMatrix.get_row(dominantAxis) * (upDirection * viewSpacing);
263 
265  Vector3D standardPlaneNormal = planeGeometry->GetNormal();
266 
270  assert((standardPlaneNormal - (top ? 1.0 : -1.0) * worldPlaneNormal).GetSquaredNorm() < 0.000001);
271 #endif
272 
273  this->InitializeEvenlySpaced(planeGeometry, viewSpacing, slices);
274 
275 #ifndef NDEBUG
276  Vector3D zAxisVector = this->GetAxisVector(2);
279  Vector3D upscaledStandardPlaneNormal = standardPlaneNormal;
280  upscaledStandardPlaneNormal *= slices;
281  assert((zAxisVector - upscaledStandardPlaneNormal).GetSquaredNorm() < 0.000001);
282 
287 // ScalarType det = vnl_det(this->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix());
288 // MITK_DEBUG << "world axis: " << worldAxis << (det > 0 ? " ; right-handed" : " ; left-handed");
289 #endif
290 }
291 
292 void mitk::SlicedGeometry3D::ReinitializePlanes(const Point3D &center, const Point3D &referencePoint)
293 {
294  // Need a reference frame to align the rotated planes
295  if (!m_ReferenceGeometry)
296  {
297  return;
298  }
299 
300  // Get first plane of plane stack
301  PlaneGeometry *firstPlane = m_PlaneGeometries[0];
302 
303  // If plane stack is empty, exit
304  if (!firstPlane || dynamic_cast<AbstractTransformGeometry *>(firstPlane))
305  {
306  return;
307  }
308 
309  // Calculate the "directed" spacing when taking the plane (defined by its axes
310  // vectors and normal) as the reference coordinate frame.
311  //
312  // This is done by calculating the radius of the ellipsoid defined by the
313  // original volume spacing axes, in the direction of the respective axis of the
314  // reference frame.
315  mitk::Vector3D axis0 = firstPlane->GetAxisVector(0);
316  mitk::Vector3D axis1 = firstPlane->GetAxisVector(1);
317  mitk::Vector3D normal = firstPlane->GetNormal();
318  normal.Normalize();
319 
320  Vector3D spacing;
321  spacing[0] = this->CalculateSpacing(axis0);
322  spacing[1] = this->CalculateSpacing(axis1);
323  spacing[2] = this->CalculateSpacing(normal);
324 
325  Superclass::SetSpacing(spacing);
326 
327  // Now we need to calculate the number of slices in the plane's normal
328  // direction, so that the entire volume is covered. This is done by first
329  // calculating the dot product between the volume diagonal (the maximum
330  // distance inside the volume) and the normal, and dividing this value by
331  // the directed spacing calculated above.
332  ScalarType directedExtent = std::abs(m_ReferenceGeometry->GetExtentInMM(0) * normal[0]) +
333  std::abs(m_ReferenceGeometry->GetExtentInMM(1) * normal[1]) +
334  std::abs(m_ReferenceGeometry->GetExtentInMM(2) * normal[2]);
335 
336  if (directedExtent >= spacing[2])
337  {
338  m_Slices = static_cast<unsigned int>(directedExtent / spacing[2] + 0.5);
339  }
340  else
341  {
342  m_Slices = 1;
343  }
344 
345  // The origin of our "first plane" needs to be adapted to this new extent.
346  // To achieve this, we first calculate the current distance to the volume's
347  // center, and then shift the origin in the direction of the normal by the
348  // difference between this distance and half of the new extent.
349  double centerOfRotationDistance = firstPlane->SignedDistanceFromPlane(center);
350 
351  if (centerOfRotationDistance > 0)
352  {
353  firstPlane->SetOrigin(firstPlane->GetOrigin() + normal * (centerOfRotationDistance - directedExtent / 2.0));
354  m_DirectionVector = normal;
355  }
356  else
357  {
358  firstPlane->SetOrigin(firstPlane->GetOrigin() + normal * (directedExtent / 2.0 + centerOfRotationDistance));
359  m_DirectionVector = -normal;
360  }
361 
362  // Now we adjust this distance according with respect to the given reference
363  // point: we need to make sure that the point is touched by one slice of the
364  // new slice stack.
365  double referencePointDistance = firstPlane->SignedDistanceFromPlane(referencePoint);
366 
367  int referencePointSlice = static_cast<int>(referencePointDistance / spacing[2]);
368 
369  double alignmentValue = referencePointDistance / spacing[2] - referencePointSlice;
370 
371  firstPlane->SetOrigin(firstPlane->GetOrigin() + normal * alignmentValue * spacing[2]);
372 
373  // Finally, we can clear the previous geometry stack and initialize it with
374  // our re-initialized "first plane".
375  m_PlaneGeometries.assign(m_Slices, PlaneGeometry::Pointer(nullptr));
376 
377  if (m_Slices > 0)
378  {
379  m_PlaneGeometries[0] = firstPlane;
380  }
381 
382  // Reinitialize SNC with new number of slices
383  m_SliceNavigationController->GetSlice()->SetSteps(m_Slices);
384 
385  this->Modified();
386 }
387 
389 {
390  // Need the spacing of the underlying dataset / geometry
391  if (!m_ReferenceGeometry)
392  {
393  return 1.0;
394  }
395 
396  const mitk::Vector3D &spacing = m_ReferenceGeometry->GetSpacing();
397  return SlicedGeometry3D::CalculateSpacing(spacing, d);
398 }
399 
401 {
402  // The following can be derived from the ellipsoid equation
403  //
404  // 1 = x^2/a^2 + y^2/b^2 + z^2/c^2
405  //
406  // where (a,b,c) = spacing of original volume (ellipsoid radii)
407  // and (x,y,z) = scaled coordinates of vector d (according to ellipsoid)
408  //
409  double scaling = d[0] * d[0] / (spacing[0] * spacing[0]) + d[1] * d[1] / (spacing[1] * spacing[1]) +
410  d[2] * d[2] / (spacing[2] * spacing[2]);
411 
412  scaling = sqrt(scaling);
413 
414  return (sqrt(d[0] * d[0] + d[1] * d[1] + d[2] * d[2]) / scaling);
415 }
416 
418 {
420  m_ReferenceGeometry->GetIndexToWorldTransform()->GetInverse(inverse);
421 
422  Vector3D transformedNormal = inverse->TransformVector(normal);
423 
424  transformedNormal.Normalize();
425  return transformedNormal;
426 }
427 
428 void mitk::SlicedGeometry3D::SetImageGeometry(const bool isAnImageGeometry)
429 {
430  Superclass::SetImageGeometry(isAnImageGeometry);
431 
432  unsigned int s;
433  for (s = 0; s < m_Slices; ++s)
434  {
435  mitk::BaseGeometry *geometry = m_PlaneGeometries[s];
436  if (geometry != nullptr)
437  {
438  geometry->SetImageGeometry(isAnImageGeometry);
439  }
440  }
441 }
442 
444 {
445  unsigned int s;
446  for (s = 0; s < m_Slices; ++s)
447  {
448  mitk::BaseGeometry *geometry = m_PlaneGeometries[s];
449  if (geometry != nullptr)
450  {
451  geometry->ChangeImageGeometryConsideringOriginOffset(isAnImageGeometry);
452  }
453  }
454 
455  Superclass::ChangeImageGeometryConsideringOriginOffset(isAnImageGeometry);
456 }
457 
459 {
460  return ((s >= 0) && (s < (int)m_Slices));
461 }
462 
464 {
465  return m_ReferenceGeometry;
466 }
467 
469 {
470  m_ReferenceGeometry = referenceGeometry;
471 
472  std::vector<PlaneGeometry::Pointer>::iterator it;
473 
474  for (it = m_PlaneGeometries.begin(); it != m_PlaneGeometries.end(); ++it)
475  {
476  (*it)->SetReferenceGeometry(referenceGeometry);
477  }
478 }
479 
481 {
482  return ( m_ReferenceGeometry != nullptr );
483 }
484 
486 {
487  bool hasEvenlySpacedPlaneGeometry = false;
488  mitk::Point3D origin;
489  mitk::Vector3D rightDV, bottomDV;
491 
492  // Check for valid spacing
493  if (!(aSpacing[0] > 0 && aSpacing[1] > 0 && aSpacing[2] > 0))
494  {
495  mitkThrow() << "You try to set a spacing with at least one element equal or "
496  "smaller to \"0\". This might lead to a crash during rendering. Please double"
497  " check your data!";
498  }
499 
500  // In case of evenly-spaced data: re-initialize instances of PlaneGeometry,
501  // since the spacing influences them
502  if ((m_EvenlySpaced) && (m_PlaneGeometries.size() > 0))
503  {
504  const PlaneGeometry *planeGeometry = m_PlaneGeometries[0];
505 
506  if (planeGeometry && !dynamic_cast<const AbstractTransformGeometry *>(planeGeometry))
507  {
508  this->WorldToIndex(planeGeometry->GetOrigin(), origin);
509  this->WorldToIndex(planeGeometry->GetAxisVector(0), rightDV);
510  this->WorldToIndex(planeGeometry->GetAxisVector(1), bottomDV);
511 
512  bounds = planeGeometry->GetBounds();
513  hasEvenlySpacedPlaneGeometry = true;
514  }
515  }
516 
517  BaseGeometry::_SetSpacing(aSpacing);
518 
519  mitk::PlaneGeometry::Pointer firstGeometry;
520 
521  // In case of evenly-spaced data: re-initialize instances of PlaneGeometry,
522  // since the spacing influences them
523  if (hasEvenlySpacedPlaneGeometry)
524  {
525  // create planeGeometry according to new spacing
526  this->IndexToWorld(origin, origin);
527  this->IndexToWorld(rightDV, rightDV);
528  this->IndexToWorld(bottomDV, bottomDV);
529 
531  planeGeometry->SetImageGeometry(this->GetImageGeometry());
532 
533  planeGeometry->SetReferenceGeometry(m_ReferenceGeometry);
534 
535  // Store spacing, as Initialize... needs a pointer
536  mitk::Vector3D lokalSpacing = this->GetSpacing();
537  planeGeometry->InitializeStandardPlane(rightDV.GetVnlVector(), bottomDV.GetVnlVector(), &lokalSpacing);
538  planeGeometry->SetOrigin(origin);
539  planeGeometry->SetBounds(bounds);
540 
541  firstGeometry = planeGeometry;
542  }
543  else if ((m_EvenlySpaced) && (m_PlaneGeometries.size() > 0))
544  {
545  firstGeometry = m_PlaneGeometries[0].GetPointer();
546  }
547 
548  // clear and reserve
549  PlaneGeometry::Pointer gnull = nullptr;
550  m_PlaneGeometries.assign(m_Slices, gnull);
551 
552  if (m_Slices > 0)
553  {
554  m_PlaneGeometries[0] = firstGeometry;
555  }
556 
557  this->Modified();
558 }
559 
561 {
562  m_SliceNavigationController = snc;
563 }
564 
566 {
567  return m_SliceNavigationController;
568 }
569 
571 {
572  if (m_EvenlySpaced != on)
573  {
574  m_EvenlySpaced = on;
575  this->Modified();
576  }
577 }
578 
580 {
581  Vector3D newDir = directionVector;
582  newDir.Normalize();
583  if (newDir != m_DirectionVector)
584  {
585  m_DirectionVector = newDir;
586  this->Modified();
587  }
588 }
589 
590 // void
591 // mitk::SlicedGeometry3D::SetTimeBounds( const mitk::TimeBounds& timebounds )
592 //{
593 // Superclass::SetTimeBounds( timebounds );
594 //
595 // unsigned int s;
596 // for ( s = 0; s < m_Slices; ++s )
597 // {
598 // if(m_Geometry2Ds[s].IsNotNull())
599 // {
600 // m_Geometry2Ds[s]->SetTimeBounds( timebounds );
601 // }
602 // }
603 // m_TimeBounds = timebounds;
604 //}
605 
607 {
608  Self::Pointer newGeometry = new SlicedGeometry3D(*this);
609  newGeometry->UnRegister();
610  return newGeometry.GetPointer();
611 }
612 
613 void mitk::SlicedGeometry3D::PrintSelf(std::ostream &os, itk::Indent indent) const
614 {
615  Superclass::PrintSelf(os, indent);
616  os << indent << " EvenlySpaced: " << m_EvenlySpaced << std::endl;
617  if (m_EvenlySpaced)
618  {
619  os << indent << " DirectionVector: " << m_DirectionVector << std::endl;
620  }
621  os << indent << " Slices: " << m_Slices << std::endl;
622 
623  os << std::endl;
624  os << indent << " GetPlaneGeometry(0): ";
625  if (this->GetPlaneGeometry(0) == nullptr)
626  {
627  os << "NULL" << std::endl;
628  }
629  else
630  {
631  this->GetPlaneGeometry(0)->Print(os, indent);
632  }
633 }
634 
636 {
637  PlaneGeometry::Pointer geometry2D;
638  ApplyTransformMatrixOperation *applyMatrixOp;
639  Point3D center;
640 
641  switch (operation->GetOperationType())
642  {
643  case OpNOTHING:
644  break;
645 
646  case OpROTATE:
647  if (m_EvenlySpaced)
648  {
649  // Need a reference frame to align the rotation
650  if (m_ReferenceGeometry)
651  {
652  // Clear all generated geometries and then rotate only the first slice.
653  // The other slices will be re-generated on demand
654 
655  // Save first slice
656  PlaneGeometry::Pointer geometry2D = m_PlaneGeometries[0];
657 
658  RotationOperation *rotOp = dynamic_cast<RotationOperation *>(operation);
659 
660  // Generate a RotationOperation using the dataset center instead of
661  // the supplied rotation center. This is necessary so that the rotated
662  // zero-plane does not shift away. The supplied center is instead used
663  // to adjust the slice stack afterwards.
664  Point3D center = m_ReferenceGeometry->GetCenter();
665 
666  RotationOperation centeredRotation(
667  rotOp->GetOperationType(), center, rotOp->GetVectorOfRotation(), rotOp->GetAngleOfRotation());
668 
669  // Rotate first slice
670  geometry2D->ExecuteOperation(&centeredRotation);
671 
672  // Clear the slice stack and adjust it according to the center of
673  // the dataset and the supplied rotation center (see documentation of
674  // ReinitializePlanes)
675  this->ReinitializePlanes(center, rotOp->GetCenterOfRotation());
676 
677  geometry2D->SetSpacing(this->GetSpacing());
678 
679  if (m_SliceNavigationController)
680  {
681  m_SliceNavigationController->SelectSliceByPoint(rotOp->GetCenterOfRotation());
682  m_SliceNavigationController->AdjustSliceStepperRange();
683  }
684 
685  BaseGeometry::ExecuteOperation(&centeredRotation);
686  }
687  else
688  {
689  // we also have to consider the case, that there is no reference geometry available.
690  if (m_PlaneGeometries.size() > 0)
691  {
692  // Reach through to all slices in my container
693  for (auto iter = m_PlaneGeometries.begin(); iter != m_PlaneGeometries.end(); ++iter)
694  {
695  // Test for empty slices, which can happen if evenly spaced geometry
696  if ((*iter).IsNotNull())
697  {
698  (*iter)->ExecuteOperation(operation);
699  }
700  }
701 
702  // rotate overall geometry
703  RotationOperation *rotOp = dynamic_cast<RotationOperation *>(operation);
705  }
706  }
707  }
708  else
709  {
710  // Reach through to all slices
711  for (auto iter = m_PlaneGeometries.begin(); iter != m_PlaneGeometries.end(); ++iter)
712  {
713  (*iter)->ExecuteOperation(operation);
714  }
715  }
716  break;
717 
718  case OpORIENT:
719  if (m_EvenlySpaced)
720  {
721  // get operation data
722  PlaneOperation *planeOp = dynamic_cast<PlaneOperation *>(operation);
723 
724  // Get first slice
725  PlaneGeometry::Pointer planeGeometry = m_PlaneGeometries[0];
726 
727  // Need a PlaneGeometry, a PlaneOperation and a reference frame to
728  // carry out the re-orientation. If not all avaialble, stop here
729  if (!m_ReferenceGeometry ||
730  (!planeGeometry || dynamic_cast<AbstractTransformGeometry *>(planeGeometry.GetPointer())) || !planeOp)
731  {
732  break;
733  }
734 
735  // General Behavior:
736  // Clear all generated geometries and then rotate only the first slice.
737  // The other slices will be re-generated on demand
738 
739  //
740  // 1st Step: Reorient Normal Vector of first plane
741  //
742  Point3D center = planeOp->GetPoint(); // m_ReferenceGeometry->GetCenter();
743  mitk::Vector3D currentNormal = planeGeometry->GetNormal();
744  mitk::Vector3D newNormal;
745  if (planeOp->AreAxisDefined())
746  {
747  // If planeOp was defined by one centerpoint and two axis vectors
748  newNormal = CrossProduct(planeOp->GetAxisVec0(), planeOp->GetAxisVec1());
749  }
750  else
751  {
752  // If planeOp was defined by one centerpoint and one normal vector
753  newNormal = planeOp->GetNormal();
754  }
755 
756  // Get Rotation axis und angle
757  currentNormal.Normalize();
758  newNormal.Normalize();
759  ScalarType rotationAngle = angle(currentNormal.GetVnlVector(), newNormal.GetVnlVector());
760 
761  rotationAngle *= 180.0 / vnl_math::pi; // from rad to deg
762  Vector3D rotationAxis = itk::CrossProduct(currentNormal, newNormal);
763  if (std::abs(rotationAngle - 180) < mitk::eps)
764  {
765  // current Normal and desired normal are not linear independent!!(e.g 1,0,0 and -1,0,0).
766  // Rotation Axis should be ANY vector that is 90� to current Normal
767  mitk::Vector3D helpNormal;
768  helpNormal = currentNormal;
769  helpNormal[0] += 1;
770  helpNormal[1] -= 1;
771  helpNormal[2] += 1;
772  helpNormal.Normalize();
773  rotationAxis = itk::CrossProduct(helpNormal, currentNormal);
774  }
775 
776  RotationOperation centeredRotation(mitk::OpROTATE, center, rotationAxis, rotationAngle);
777 
778  // Rotate first slice
779  planeGeometry->ExecuteOperation(&centeredRotation);
780 
781  // Reinitialize planes and select slice, if my rotations are all done.
782  if (!planeOp->AreAxisDefined())
783  {
784  // Clear the slice stack and adjust it according to the center of
785  // rotation and plane position (see documentation of ReinitializePlanes)
786  this->ReinitializePlanes(center, planeOp->GetPoint());
787  planeGeometry->SetSpacing(this->GetSpacing());
788 
789  if (m_SliceNavigationController)
790  {
791  m_SliceNavigationController->SelectSliceByPoint(planeOp->GetPoint());
792  m_SliceNavigationController->AdjustSliceStepperRange();
793  }
794  }
795 
796  // Also apply rotation on the slicedGeometry - Geometry3D (Bounding geometry)
797  BaseGeometry::ExecuteOperation(&centeredRotation);
798 
799  //
800  // 2nd step. If axis vectors were defined, rotate the plane around its normal to fit these
801  //
802 
803  if (planeOp->AreAxisDefined())
804  {
805  mitk::Vector3D vecAxixNew = planeOp->GetAxisVec0();
806  vecAxixNew.Normalize();
807  mitk::Vector3D VecAxisCurr = planeGeometry->GetAxisVector(0);
808  VecAxisCurr.Normalize();
809 
810  ScalarType rotationAngle = angle(VecAxisCurr.GetVnlVector(), vecAxixNew.GetVnlVector());
811  rotationAngle = rotationAngle * 180 / PI; // Rad to Deg
812 
813  // we rotate around the normal of the plane, but we do not know, if we need to rotate clockwise
814  // or anti-clockwise. So we rotate around the crossproduct of old and new Axisvector.
815  // Since both axis vectors lie in the plane, the crossproduct is the planes normal or the negative planes
816  // normal
817 
818  rotationAxis = itk::CrossProduct(VecAxisCurr, vecAxixNew);
819  if (std::abs(rotationAngle - 180) < mitk::eps)
820  {
821  // current axisVec and desired axisVec are not linear independent!!(e.g 1,0,0 and -1,0,0).
822  // Rotation Axis can be just plane Normal. (have to rotate by 180�)
823  rotationAxis = newNormal;
824  }
825 
826  // Perfom Rotation
827  mitk::RotationOperation op(mitk::OpROTATE, center, rotationAxis, rotationAngle);
828  planeGeometry->ExecuteOperation(&op);
829 
830  // Apply changes on first slice to whole slice stack
831  this->ReinitializePlanes(center, planeOp->GetPoint());
832  planeGeometry->SetSpacing(this->GetSpacing());
833 
834  if (m_SliceNavigationController)
835  {
836  m_SliceNavigationController->SelectSliceByPoint(planeOp->GetPoint());
837  m_SliceNavigationController->AdjustSliceStepperRange();
838  }
839 
840  // Also apply rotation on the slicedGeometry - Geometry3D (Bounding geometry)
842  }
843  }
844  else
845  {
846  // Reach through to all slices
847  for (auto iter = m_PlaneGeometries.begin(); iter != m_PlaneGeometries.end(); ++iter)
848  {
849  (*iter)->ExecuteOperation(operation);
850  }
851  }
852  break;
853 
855  if (m_EvenlySpaced)
856  {
857  // Save first slice
858  PlaneGeometry::Pointer planeGeometry = m_PlaneGeometries[0];
859 
860  RestorePlanePositionOperation *restorePlaneOp = dynamic_cast<RestorePlanePositionOperation *>(operation);
861 
862  // Need a PlaneGeometry, a PlaneOperation and a reference frame to
863  // carry out the re-orientation
864  if (m_ReferenceGeometry &&
865  (planeGeometry && dynamic_cast<AbstractTransformGeometry *>(planeGeometry.GetPointer()) == nullptr) &&
866  restorePlaneOp)
867  {
868  // Clear all generated geometries and then rotate only the first slice.
869  // The other slices will be re-generated on demand
870 
871  // Rotate first slice
872  planeGeometry->ExecuteOperation(restorePlaneOp);
873 
874  m_DirectionVector = restorePlaneOp->GetDirectionVector();
875 
876  double centerOfRotationDistance = planeGeometry->SignedDistanceFromPlane(m_ReferenceGeometry->GetCenter());
877 
878  if (centerOfRotationDistance <= 0)
879  {
880  m_DirectionVector = -m_DirectionVector;
881  }
882 
883  Vector3D spacing = restorePlaneOp->GetSpacing();
884 
885  Superclass::SetSpacing(spacing);
886 
887  // /*Now we need to calculate the number of slices in the plane's normal
888  // direction, so that the entire volume is covered. This is done by first
889  // calculating the dot product between the volume diagonal (the maximum
890  // distance inside the volume) and the normal, and dividing this value by
891  // the directed spacing calculated above.*/
892  ScalarType directedExtent = std::abs(m_ReferenceGeometry->GetExtentInMM(0) * m_DirectionVector[0]) +
893  std::abs(m_ReferenceGeometry->GetExtentInMM(1) * m_DirectionVector[1]) +
894  std::abs(m_ReferenceGeometry->GetExtentInMM(2) * m_DirectionVector[2]);
895 
896  if (directedExtent >= spacing[2])
897  {
898  m_Slices = static_cast<unsigned int>(directedExtent / spacing[2] + 0.5);
899  }
900  else
901  {
902  m_Slices = 1;
903  }
904 
905  m_PlaneGeometries.assign(m_Slices, PlaneGeometry::Pointer(nullptr));
906 
907  if (m_Slices > 0)
908  {
909  m_PlaneGeometries[0] = planeGeometry;
910  }
911 
912  m_SliceNavigationController->GetSlice()->SetSteps(m_Slices);
913 
914  this->Modified();
915 
916  // End Reinitialization
917 
918  if (m_SliceNavigationController)
919  {
920  m_SliceNavigationController->GetSlice()->SetPos(restorePlaneOp->GetPos());
921  m_SliceNavigationController->AdjustSliceStepperRange();
922  }
923  BaseGeometry::ExecuteOperation(restorePlaneOp);
924  }
925  }
926  else
927  {
928  // Reach through to all slices
929  for (auto iter = m_PlaneGeometries.begin(); iter != m_PlaneGeometries.end(); ++iter)
930  {
931  (*iter)->ExecuteOperation(operation);
932  }
933  }
934  break;
935 
937 
938  // Clear all generated geometries and then transform only the first slice.
939  // The other slices will be re-generated on demand
940 
941  // Save first slice
942  geometry2D = m_PlaneGeometries[0];
943 
944  applyMatrixOp = dynamic_cast<ApplyTransformMatrixOperation *>(operation);
945 
946  // Apply transformation to first plane
947  geometry2D->ExecuteOperation(applyMatrixOp);
948 
949  // Generate a ApplyTransformMatrixOperation using the dataset center instead of
950  // the supplied rotation center. The supplied center is instead used to adjust the
951  // slice stack afterwards (see OpROTATE).
952  center = m_ReferenceGeometry->GetCenter();
953 
954  // Clear the slice stack and adjust it according to the center of
955  // the dataset and the supplied rotation center (see documentation of
956  // ReinitializePlanes)
957  this->ReinitializePlanes(center, applyMatrixOp->GetReferencePoint());
958 
959  BaseGeometry::ExecuteOperation(applyMatrixOp);
960  break;
961 
962  default: // let handle by base class if we don't do anything
964  }
965 
966  this->Modified();
967 }
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.
virtual void InitializePlanes(const mitk::BaseGeometry *geometry3D, mitk::PlaneGeometry::PlaneOrientation planeorientation, bool top=true, bool frontside=true, bool rotated=false)
Completely initialize this instance as evenly-spaced plane slices parallel to a side of the provided ...
itk::BoundingBox< unsigned long, 3, ScalarType > BoundingBox
Standard 3D-BoundingBox typedef.
itk::SmartPointer< Self > Pointer
void SetSpacing(const mitk::Vector3D &aSpacing, bool enforceSetSpacing=false)
Set the spacing (m_Spacing).
virtual void InitializeSlicedGeometry(unsigned int slices)
Tell this instance how many PlaneGeometries it shall manage. Bounding box and the PlaneGeometries mus...
ScalarType SignedDistanceFromPlane(const Point3D &pt3d_mm) const
Signed distance of the point from the plane (bounding-box not considered)
virtual const Vector3D GetVectorOfRotation()
GetVectorOfRotation getter for the rotation axis.
Base class of all Operation-classes.
Definition: mitkOperation.h:33
virtual const Point3D GetCenterOfRotation()
GetCenterOfRotation getter for the anchor point of rotation.
double ScalarType
void _SetSpacing(const mitk::Vector3D &aSpacing, bool enforceSetSpacing=false)
virtual void ReinitializePlanes(const Point3D &center, const Point3D &referencePoint)
virtual void SetEvenlySpaced(bool on=true)
const mitk::Vector3D GetSpacing() const
Get the spacing (size of a pixel).
mitk::SliceNavigationController * GetSliceNavigationController()
virtual bool SetPlaneGeometry(mitk::PlaneGeometry *geometry2D, int s)
Set PlaneGeometry of slice s.
virtual const BaseGeometry * GetReferenceGeometry() const
Constants for most interaction classes, due to the generic StateMachines.
virtual itk::LightObject::Pointer InternalClone() const override
clones the geometry
const BoundsArrayType GetBounds() const
Pointer Clone() const
virtual void PreSetSpacing(const mitk::Vector3D &aSpacing) override
PreSetSpacing.
virtual void InitializeEvenlySpaced(mitk::PlaneGeometry *geometry2D, unsigned int slices)
Completely initialize this instance as evenly-spaced with slices parallel to the provided PlaneGeomet...
virtual void SetSliceNavigationController(mitk::SliceNavigationController *snc)
Set the SliceNavigationController corresponding to this sliced geometry.
std::vector< PlaneGeometry::Pointer > m_PlaneGeometries
void FillVector3D(Tout &out, mitk::ScalarType x, mitk::ScalarType y, mitk::ScalarType z)
Definition: mitkArray.h:110
Controls the selection of the slice the associated BaseRenderer will display.
virtual const mitk::BoundingBox * GetBoundingBox() const override
Vector3D GetNormal() const
Normal of the plane.
virtual unsigned int GetFrameOfReferenceID() const
Get the DICOM FrameOfReferenceID referring to the used world coordinate system.
const mitk::ScalarType PI
virtual void SetImageGeometry(const bool isAnImageGeometry) override
Define that this BaseGeometry is refering to an Image.
void PrintSelf(std::ostream &os, itk::Indent indent) const override
virtual ScalarType GetAngleOfRotation()
GetAngleOfRotation getter for rotation angle.
Vector3D GetAxisVector(unsigned int direction) const
Get vector along bounding-box in the specified direction in mm.
virtual void ChangeImageGeometryConsideringOriginOffset(const bool isAnImageGeometry) override
When switching from an Image Geometry to a normal Geometry (and the other way around), you have to.
#define mitkThrow()
void SetOrigin(const Point3D &origin)
Set the origin, i.e. the upper-left corner of the plane.
virtual mitk::PlaneGeometry * GetPlaneGeometry(int s) const
Returns the PlaneGeometry of the slice (s).
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.
Describes the geometry of a data object consisting of slices.
virtual void ChangeImageGeometryConsideringOriginOffset(const bool isAnImageGeometry)
When switching from an Image Geometry to a normal Geometry (and the other way around), you have to.
static double CalculateSpacing(const mitk::Vector3D &spacing, const mitk::Vector3D &d)
virtual const mitk::Vector3D & GetDirectionVector() const
static Pointer New()
MITKCORE_EXPORT const ScalarType eps
Describes a two-dimensional, rectangular plane.
mitk::Vector3D AdjustNormal(const mitk::Vector3D &normal) const
OperationType GetOperationType()
Operation, that holds everything necessary for an rotation operation on mitk::BaseData.
virtual void SetDirectionVector(const mitk::Vector3D &directionVector)
Set/Get the vector between slices for the evenly-spaced case (m_EvenlySpaced==true).
ScalarType GetExtentInMM(int direction) const
Get the extent of the bounding-box in the specified direction in mm.
virtual void SetReferenceGeometry(const BaseGeometry *referenceGeometry)
virtual void ExecuteOperation(Operation *operation) override
executes affine operations (translate, rotate, scale)
virtual bool GetImageGeometry() const
Is this an ImageGeometry?
virtual void SetImageGeometry(bool _arg)
Define that this BaseGeometry is refering to an Image.
BaseGeometry Describes the geometry of a data object.
virtual void ExecuteOperation(Operation *operation) override
executes affine operations (translate, rotate, scale)
virtual bool IsValidSlice(int s=0) const
Check whether a slice exists.
mitk::AffineTransform3D * GetIndexToWorldTransform()
Get the transformation used to convert from index to world coordinates.
BoundingBoxType::BoundsArrayType BoundsArrayType
static itkEventMacro(BoundingShapeInteractionEvent, itk::AnyEvent) class MITKBOUNDINGSHAPE_EXPORT BoundingShapeInteractor Pointer New()
Basic interaction methods for mitk::GeometryData.