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
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.