ApprEllipseByArcs.h 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  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/ContScribeCircle2.h>
  9. #include <vector>
  10. // The ellipse is (x/a)^2 + (y/b)^2 = 1, but only the portion in the first
  11. // quadrant (x >= 0 and y >= 0) is approximated. Generate numArcs >= 2 arcs
  12. // by constructing points corresponding to the weighted averages of the
  13. // curvatures at the ellipse points (a,0) and (0,b). The returned input point
  14. // array has numArcs+1 elements and the returned input center and radius
  15. // arrays each have numArc elements. The arc associated with points[i] and
  16. // points[i+1] has center centers[i] and radius radii[i]. The algorithm
  17. // is described in
  18. // https://www.geometrictools.com/Documentation/ApproximateEllipse.pdf
  19. namespace WwiseGTE
  20. {
  21. // The function returns 'true' when the approximation succeeded, in which
  22. // case the output arrays are nonempty. If the 'numArcs' is smaller than
  23. // 2 or a == b or one of the calls to Circumscribe fails, the function
  24. // returns 'false'.
  25. template <typename Real>
  26. bool ApproximateEllipseByArcs(Real a, Real b, int numArcs,
  27. std::vector<Vector2<Real>>& points, std::vector<Vector2<Real>>& centers,
  28. std::vector<Real>& radii)
  29. {
  30. if (numArcs < 2 || a == b)
  31. {
  32. // At least 2 arcs are required. The ellipse cannot already be a
  33. // circle.
  34. points.clear();
  35. centers.clear();
  36. radii.clear();
  37. return false;
  38. }
  39. points.resize(numArcs + 1);
  40. centers.resize(numArcs);
  41. radii.resize(numArcs);
  42. // Compute intermediate ellipse quantities.
  43. Real a2 = a * a, b2 = b * b, ab = a * b;
  44. Real invB2mA2 = (Real)1 / (b2 - a2);
  45. // Compute the endpoints of the ellipse in the first quadrant. The
  46. // points are generated in counterclockwise order.
  47. points[0] = { a, (Real)0 };
  48. points[numArcs] = { (Real)0, b };
  49. // Compute the curvature at the endpoints. These are used when
  50. // computing the arcs.
  51. Real curv0 = a / b2;
  52. Real curv1 = b / a2;
  53. // Select the ellipse points based on curvature properties.
  54. Real invNumArcs = (Real)1 / numArcs;
  55. for (int i = 1; i < numArcs; ++i)
  56. {
  57. // The curvature at a new point is a weighted average of curvature
  58. // at the endpoints.
  59. Real weight1 = static_cast<Real>(i) * invNumArcs;
  60. Real weight0 = (Real)1 - weight1;
  61. Real curv = weight0 * curv0 + weight1 * curv1;
  62. // Compute point having this curvature.
  63. Real tmp = std::pow(ab / curv, (Real)2 / (Real)3);
  64. points[i][0] = a * std::sqrt(std::fabs((tmp - a2) * invB2mA2));
  65. points[i][1] = b * std::sqrt(std::fabs((tmp - b2) * invB2mA2));
  66. }
  67. // Compute the arc at (a,0).
  68. Circle2<Real> circle;
  69. Vector2<Real> const& p0 = points[0];
  70. Vector2<Real> const& p1 = points[1];
  71. if (!Circumscribe(Vector2<Real>{ p1[0], -p1[1] }, p0, p1, circle))
  72. {
  73. // This should not happen for the arc-fitting algorithm.
  74. points.clear();
  75. centers.clear();
  76. radii.clear();
  77. return false;
  78. }
  79. centers[0] = circle.center;
  80. radii[0] = circle.radius;
  81. // Compute arc at (0,b).
  82. int last = numArcs - 1;
  83. Vector2<Real> const& pNm1 = points[last];
  84. Vector2<Real> const& pN = points[numArcs];
  85. if (!Circumscribe(Vector2<Real>{ -pNm1[0], pNm1[1] }, pN, pNm1, circle))
  86. {
  87. // This should not happen for the arc-fitting algorithm.
  88. points.clear();
  89. centers.clear();
  90. radii.clear();
  91. return false;
  92. }
  93. centers[last] = circle.center;
  94. radii[last] = circle.radius;
  95. // Compute arcs at intermediate points between (a,0) and (0,b).
  96. for (int iM = 0, i = 1, iP = 2; i < last; ++iM, ++i, ++iP)
  97. {
  98. Circumscribe(points[iM], points[i], points[iP], circle);
  99. centers[i] = circle.center;
  100. radii[i] = circle.radius;
  101. }
  102. return true;
  103. }
  104. }