bezier_polynomial_detail.hpp 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. // Copyright Nick Thompson, 2021
  2. // Use, modification and distribution are subject to the
  3. // Boost Software License, Version 1.0.
  4. // (See accompanying file LICENSE_1_0.txt
  5. // or copy at http://www.boost.org/LICENSE_1_0.txt)
  6. #ifndef BOOST_MATH_INTERPOLATORS_BEZIER_POLYNOMIAL_DETAIL_HPP
  7. #define BOOST_MATH_INTERPOLATORS_BEZIER_POLYNOMIAL_DETAIL_HPP
  8. #include <stdexcept>
  9. #include <iostream>
  10. #include <string>
  11. #include <limits>
  12. namespace boost::math::interpolators::detail {
  13. template <class RandomAccessContainer>
  14. static inline RandomAccessContainer& get_bezier_storage()
  15. {
  16. static thread_local RandomAccessContainer the_storage;
  17. return the_storage;
  18. }
  19. template <class RandomAccessContainer>
  20. class bezier_polynomial_imp
  21. {
  22. public:
  23. using Point = typename RandomAccessContainer::value_type;
  24. using Real = typename Point::value_type;
  25. using Z = typename RandomAccessContainer::size_type;
  26. bezier_polynomial_imp(RandomAccessContainer && control_points)
  27. {
  28. using std::to_string;
  29. if (control_points.size() < 2) {
  30. std::string err = std::string(__FILE__) + ":" + to_string(__LINE__)
  31. + " At least two points are required to form a Bezier curve. Only " + to_string(control_points.size()) + " points have been provided.";
  32. throw std::logic_error(err);
  33. }
  34. Z dimension = control_points[0].size();
  35. for (Z i = 0; i < control_points.size(); ++i) {
  36. if (control_points[i].size() != dimension) {
  37. std::string err = std::string(__FILE__) + ":" + to_string(__LINE__)
  38. + " All points passed to the Bezier polynomial must have the same dimension.";
  39. throw std::logic_error(err);
  40. }
  41. }
  42. control_points_ = std::move(control_points);
  43. auto & storage = get_bezier_storage<RandomAccessContainer>();
  44. if (storage.size() < control_points_.size() -1) {
  45. storage.resize(control_points_.size() -1);
  46. }
  47. }
  48. inline Point operator()(Real t) const
  49. {
  50. if (t < 0 || t > 1) {
  51. std::cerr << __FILE__ << ":" << __LINE__ << ":" << __func__ << "\n";
  52. std::cerr << "Querying the Bezier curve interpolator at t = " << t << " is not allowed; t in [0,1] is required.\n";
  53. Point p;
  54. for (Z i = 0; i < p.size(); ++i) {
  55. p[i] = std::numeric_limits<Real>::quiet_NaN();
  56. }
  57. return p;
  58. }
  59. auto & scratch_space = get_bezier_storage<RandomAccessContainer>();
  60. for (Z i = 0; i < control_points_.size() - 1; ++i) {
  61. for (Z j = 0; j < control_points_[0].size(); ++j) {
  62. scratch_space[i][j] = (1-t)*control_points_[i][j] + t*control_points_[i+1][j];
  63. }
  64. }
  65. decasteljau_recursion(scratch_space, control_points_.size() - 1, t);
  66. return scratch_space[0];
  67. }
  68. Point prime(Real t) {
  69. auto & scratch_space = get_bezier_storage<RandomAccessContainer>();
  70. for (Z i = 0; i < control_points_.size() - 1; ++i) {
  71. for (Z j = 0; j < control_points_[0].size(); ++j) {
  72. scratch_space[i][j] = control_points_[i+1][j] - control_points_[i][j];
  73. }
  74. }
  75. decasteljau_recursion(scratch_space, control_points_.size() - 1, t);
  76. for (Z j = 0; j < control_points_[0].size(); ++j) {
  77. scratch_space[0][j] *= (control_points_.size()-1);
  78. }
  79. return scratch_space[0];
  80. }
  81. void edit_control_point(Point const & p, Z index)
  82. {
  83. if (index >= control_points_.size()) {
  84. std::cerr << __FILE__ << ":" << __LINE__ << ":" << __func__ << "\n";
  85. std::cerr << "Attempting to edit a control point outside the bounds of the container; requested edit of index " << index << ", but there are only " << control_points_.size() << " control points.\n";
  86. return;
  87. }
  88. control_points_[index] = p;
  89. }
  90. RandomAccessContainer const & control_points() const {
  91. return control_points_;
  92. }
  93. // See "Bezier and B-spline techniques", section 2.7:
  94. // I cannot figure out why this doesn't work.
  95. /*RandomAccessContainer indefinite_integral() const {
  96. using std::fma;
  97. // control_points_.size() == n + 1
  98. RandomAccessContainer c(control_points_.size() + 1);
  99. // This is the constant of integration, chosen arbitrarily to be zero:
  100. for (Z j = 0; j < control_points_[0].size(); ++j) {
  101. c[0][j] = Real(0);
  102. }
  103. // Make the reciprocal approximation to unroll the iteration into a pile of fma's:
  104. Real rnp1 = Real(1)/control_points_.size();
  105. for (Z i = 1; i < c.size(); ++i) {
  106. for (Z j = 0; j < control_points_[0].size(); ++j) {
  107. //c[i][j] = c[i-1][j] + control_points_[i-1][j]*rnp1;
  108. c[i][j] = fma(rnp1, control_points_[i-1][j], c[i-1][j]);
  109. }
  110. }
  111. return c;
  112. }*/
  113. friend std::ostream& operator<<(std::ostream& out, bezier_polynomial_imp<RandomAccessContainer> const & bp) {
  114. out << "{";
  115. for (Z i = 0; i < bp.control_points_.size() - 1; ++i) {
  116. out << "(";
  117. for (Z j = 0; j < bp.control_points_[0].size() - 1; ++j) {
  118. out << bp.control_points_[i][j] << ", ";
  119. }
  120. out << bp.control_points_[i][bp.control_points_[0].size() - 1] << "), ";
  121. }
  122. out << "(";
  123. for (Z j = 0; j < bp.control_points_[0].size() - 1; ++j) {
  124. out << bp.control_points_.back()[j] << ", ";
  125. }
  126. out << bp.control_points_.back()[bp.control_points_[0].size() - 1] << ")}";
  127. return out;
  128. }
  129. private:
  130. void decasteljau_recursion(RandomAccessContainer & points, Z n, Real t) const {
  131. if (n <= 1) {
  132. return;
  133. }
  134. for (Z i = 0; i < n - 1; ++i) {
  135. for (Z j = 0; j < points[0].size(); ++j) {
  136. points[i][j] = (1-t)*points[i][j] + t*points[i+1][j];
  137. }
  138. }
  139. decasteljau_recursion(points, n - 1, t);
  140. }
  141. RandomAccessContainer control_points_;
  142. };
  143. }
  144. #endif