ContEllipsoid3.h 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. // David Eberly, Geometric Tools, Redmond WA 98052
  2. // Copyright (c) 1998-2020
  3. // Distributed under the Boost Software License, Version 1.0.
  4. // https://www.boost.org/LICENSE_1_0.txt
  5. // https://www.geometrictools.com/License/Boost/LICENSE_1_0.txt
  6. // Version: 4.0.2019.08.13
  7. #pragma once
  8. #include <Mathematics/ApprGaussian3.h>
  9. #include <Mathematics/Hyperellipsoid.h>
  10. #include <Mathematics/Matrix3x3.h>
  11. #include <Mathematics/Projection.h>
  12. #include <Mathematics/Rotation.h>
  13. namespace WwiseGTE
  14. {
  15. // The input points are fit with a Gaussian distribution. The center C of
  16. // the ellipsoid is chosen to be the mean of the distribution. The axes
  17. // of the ellipsoid are chosen to be the eigenvectors of the covariance
  18. // matrix M. The shape of the ellipsoid is determined by the absolute
  19. // values of the eigenvalues. NOTE: The construction is ill-conditioned
  20. // if the points are (nearly) collinear or (nearly) planar. In this case
  21. // M has a (nearly) zero eigenvalue, so inverting M is problematic.
  22. template <typename Real>
  23. bool GetContainer(int numPoints, Vector3<Real> const* points, Ellipsoid3<Real>& ellipsoid)
  24. {
  25. // Fit the points with a Gaussian distribution. The covariance
  26. // matrix is M = sum_j D[j]*U[j]*U[j]^T, where D[j] are the
  27. // eigenvalues and U[j] are corresponding unit-length eigenvectors.
  28. ApprGaussian3<Real> fitter;
  29. if (fitter.Fit(numPoints, points))
  30. {
  31. OrientedBox3<Real> box = fitter.GetParameters();
  32. // If either eigenvalue is nonpositive, adjust the D[] values so
  33. // that we actually build an ellipsoid.
  34. for (int j = 0; j < 3; ++j)
  35. {
  36. if (box.extent[j] < (Real)0)
  37. {
  38. box.extent[j] = -box.extent[j];
  39. }
  40. }
  41. // Grow the ellipsoid, while retaining its shape determined by the
  42. // covariance matrix, to enclose all the input points. The
  43. // quadratic/ form that is used for the ellipsoid construction is
  44. // Q(X) = (X-C)^T*M*(X-C)
  45. // = (X-C)^T*(sum_j D[j]*U[j]*U[j]^T)*(X-C)
  46. // = sum_j D[j]*Dot(U[j],X-C)^2
  47. // If the maximum value of Q(X[i]) for all input points is V^2,
  48. // then a bounding ellipsoid is Q(X) = V^2 since Q(X[i]) <= V^2
  49. // for all i.
  50. Real maxValue = (Real)0;
  51. for (int i = 0; i < numPoints; ++i)
  52. {
  53. Vector3<Real> diff = points[i] - box.center;
  54. Real dot[3] =
  55. {
  56. Dot(box.axis[0], diff),
  57. Dot(box.axis[1], diff),
  58. Dot(box.axis[2], diff)
  59. };
  60. Real value =
  61. box.extent[0] * dot[0] * dot[0] +
  62. box.extent[1] * dot[1] * dot[1] +
  63. box.extent[2] * dot[2] * dot[2];
  64. if (value > maxValue)
  65. {
  66. maxValue = value;
  67. }
  68. }
  69. // Arrange for the quadratic to satisfy Q(X) <= 1.
  70. ellipsoid.center = box.center;
  71. for (int j = 0; j < 3; ++j)
  72. {
  73. ellipsoid.axis[j] = box.axis[j];
  74. ellipsoid.extent[j] = std::sqrt(maxValue / box.extent[j]);
  75. }
  76. return true;
  77. }
  78. return false;
  79. }
  80. // Test for containment of a point inside an ellipsoid.
  81. template <typename Real>
  82. bool InContainer(Vector3<Real> const& point, Ellipsoid3<Real> const& ellipsoid)
  83. {
  84. Vector3<Real> diff = point - ellipsoid.center;
  85. Vector3<Real> standardized{
  86. Dot(diff, ellipsoid.axis[0]) / ellipsoid.extent[0],
  87. Dot(diff, ellipsoid.axis[1]) / ellipsoid.extent[1],
  88. Dot(diff, ellipsoid.axis[2]) / ellipsoid.extent[2] };
  89. return Length(standardized) <= (Real)1;
  90. }
  91. // Construct a bounding ellipsoid for the two input ellipsoids. The result is
  92. // not necessarily the minimum-volume ellipsoid containing the two ellipsoids.
  93. template <typename Real>
  94. bool MergeContainers(Ellipsoid3<Real> const& ellipsoid0,
  95. Ellipsoid3<Real> const& ellipsoid1, Ellipsoid3<Real>& merge)
  96. {
  97. // Compute the average of the input centers
  98. merge.center = (Real)0.5 * (ellipsoid0.center + ellipsoid1.center);
  99. // The bounding ellipsoid orientation is the average of the input
  100. // orientations.
  101. Matrix3x3<Real> rot0, rot1;
  102. rot0.SetCol(0, ellipsoid0.axis[0]);
  103. rot0.SetCol(1, ellipsoid0.axis[1]);
  104. rot0.SetCol(2, ellipsoid0.axis[2]);
  105. rot1.SetCol(0, ellipsoid1.axis[0]);
  106. rot1.SetCol(1, ellipsoid1.axis[1]);
  107. rot1.SetCol(2, ellipsoid1.axis[2]);
  108. Quaternion<Real> q0 = Rotation<3, Real>(rot0);
  109. Quaternion<Real> q1 = Rotation<3, Real>(rot1);
  110. if (Dot(q0, q1) < (Real)0)
  111. {
  112. q1 = -q1;
  113. }
  114. Quaternion<Real> q = q0 + q1;
  115. Normalize(q);
  116. Matrix3x3<Real> rot = Rotation<3, Real>(q);
  117. for (int j = 0; j < 3; ++j)
  118. {
  119. merge.axis[j] = rot.GetCol(j);
  120. }
  121. // Project the input ellipsoids onto the axes obtained by the average
  122. // of the orientations and that go through the center obtained by the
  123. // average of the centers.
  124. for (int i = 0; i < 3; ++i)
  125. {
  126. // Projection axis.
  127. Line3<Real> line(merge.center, merge.axis[i]);
  128. // Project ellipsoids onto the axis.
  129. Real min0, max0, min1, max1;
  130. Project(ellipsoid0, line, min0, max0);
  131. Project(ellipsoid1, line, min1, max1);
  132. // Determine the smallest interval containing the projected
  133. // intervals.
  134. Real maxIntr = (max0 >= max1 ? max0 : max1);
  135. Real minIntr = (min0 <= min1 ? min0 : min1);
  136. // Update the average center to be the center of the bounding box
  137. // defined by the projected intervals.
  138. merge.center += line.direction * ((Real)0.5 * (minIntr + maxIntr));
  139. // Compute the extents of the box based on the new center.
  140. merge.extent[i] = (Real)0.5 * (maxIntr - minIntr);
  141. }
  142. return true;
  143. }
  144. }