math.hpp 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  1. #ifndef COMMON_MATH_HPP
  2. #define COMMON_MATH_HPP
  3. #include "simple/support.hpp"
  4. #include "simple/geom.hpp"
  5. namespace common
  6. {
  7. using namespace simple;
  8. constexpr auto half = geom::vector<float,2>::one(.5f);
  9. const float tau = 2*std::acos(-1);
  10. // https://www.youtube.com/watch?v=tk58sBLWzHk
  11. using proportion3 = geom::vector<float,3>;
  12. proportion3 meet(proportion3 line1, proportion3 line2)
  13. {
  14. const auto [x1, y1, z1] = line1;
  15. const auto [x2, y2, z2] = line2;
  16. // this be the mysterious cross product they say
  17. // the source above has the sing of z flipped, but that does not seem to
  18. // actually matter as long as you're consistent
  19. return {y1*z2 - y2*z1, z1*x2 - z2*x1, x1*y2 - x2*y1};
  20. }
  21. proportion3 join(proportion3 point1, proportion3 point2)
  22. {
  23. return meet(point1, point2);
  24. }
  25. proportion3 join(geom::vector<float,2> point1, geom::vector<float,2> point2)
  26. {
  27. constexpr auto denominator = geom::vector(1.f);
  28. return join(point1.concat(denominator), point2.concat(denominator));
  29. }
  30. template <typename Vector>
  31. [[nodiscard]] constexpr
  32. Vector project_on_unit(Vector x, Vector surface)
  33. {
  34. return surface * surface(x);
  35. }
  36. template <typename Vector>
  37. [[nodiscard]] constexpr
  38. Vector project(Vector x, Vector surface)
  39. {
  40. return project_on_unit(x,surface) / surface.magnitude();
  41. }
  42. template <typename Vector>
  43. [[nodiscard]] constexpr
  44. Vector reject_from_unit(Vector x, Vector surface)
  45. {
  46. return x - project_on_unit(x,surface);
  47. }
  48. template <typename Vector>
  49. [[nodiscard]] constexpr
  50. Vector reject(Vector x, Vector surface)
  51. {
  52. return x - project(x,surface);
  53. }
  54. template <typename Vector>
  55. [[nodiscard]] constexpr
  56. Vector reflect_in_unit(Vector x, Vector surface)
  57. {
  58. return 2*project_on_unit(x,surface) - x;
  59. // return project_on_unit(x,surface) - reject_from_unit(x,surface);
  60. }
  61. template <typename Vector>
  62. [[nodiscard]] constexpr
  63. Vector reflect(Vector x, Vector surface)
  64. {
  65. return project(x,surface) - reject(x,surface);
  66. }
  67. template <typename Vector>
  68. [[nodiscard]] constexpr
  69. Vector rotate_scale(Vector x, Vector half_angle)
  70. {
  71. x.y() = -x.y();
  72. return reflect_in_unit(x, half_angle);
  73. // return reflect_in_unit(
  74. // reflect_in_unit(x, Vector::i()),
  75. // half_angle );
  76. }
  77. template <typename Vector>
  78. [[nodiscard]] constexpr
  79. Vector rotate(Vector x, support::range<Vector> half_angle)
  80. {
  81. return reflect( reflect(x, half_angle.lower()), half_angle.upper() );
  82. }
  83. template <typename Vector>
  84. [[nodiscard]] constexpr
  85. Vector rotate(Vector x, Vector half_angle)
  86. {
  87. return rotate( x, support::range{Vector::i(), half_angle} );
  88. }
  89. template <typename Vector>
  90. [[nodiscard]] constexpr
  91. Vector normalize(Vector x)
  92. {
  93. assert(x.quadrance() != 0);
  94. return x / support::root2(x.quadrance());
  95. }
  96. // see angle_from_cord_length.cpp
  97. template <typename Number>
  98. [[nodiscard]] constexpr
  99. geom::vector<Number,2> cord_quadrance_angle(Number radius, Number cord_quadrance)
  100. {
  101. auto mystery_ratio = cord_quadrance/(2*radius);
  102. return
  103. {
  104. radius - mystery_ratio,
  105. support::root2(cord_quadrance - mystery_ratio*mystery_ratio)
  106. };
  107. }
  108. template <typename Number>
  109. [[nodiscard]] constexpr
  110. geom::vector<Number,2> cord_length_angle(Number radius, Number cord_length)
  111. {
  112. return cord_quadrance_angle(radius, cord_length * cord_length);
  113. }
  114. template <size_t Exponent = 3, typename Value = float>
  115. class protractor
  116. {
  117. public:
  118. using vector = geom::vector<Value,2>;
  119. using array = std::array<vector, (size_t(1)<<Exponent) + 1>;
  120. static constexpr array circle = []()
  121. {
  122. using support::halfway;
  123. array points{};
  124. points.front() = vector::i();
  125. points.back() = -vector::i();
  126. auto bisect = [](auto begin, auto end,
  127. auto& self)
  128. {
  129. if(end - begin <= 2)
  130. return;
  131. auto middle = halfway(begin, end);
  132. *(middle) = normalize(halfway(*begin, *(end - 1)));
  133. self(begin, middle + 1,
  134. self);
  135. self(middle, end,
  136. self);
  137. };
  138. if(Exponent > 0)
  139. {
  140. auto middle = halfway(points.begin(), points.end());
  141. *middle = vector::j();
  142. bisect(points.begin(), middle + 1,
  143. bisect);
  144. bisect(middle, points.end(),
  145. bisect);
  146. }
  147. return points;
  148. }();
  149. static constexpr vector tau(Value factor)
  150. {
  151. assert(Value{0} <= factor && factor <= Value{1});
  152. Value index = factor * (protractor::circle.size() - 1);
  153. int floor = index;
  154. Value fraction = index - floor;
  155. int ceil = floor + (fraction > 0);
  156. using support::way;
  157. return way(circle[floor], circle[ceil], fraction);
  158. }
  159. static constexpr Value tau(vector half_angle)
  160. {
  161. assert(half_angle.y() > 0);
  162. // too clever? I mean I could have done lower_bound upper_bound to be
  163. // oh so clever... this is just mildly clever... still fear that
  164. // find_if would optimize better
  165. auto lower = std::partition_point(circle.begin(), circle.end(),
  166. [half_angle](auto x)
  167. {
  168. auto side = rotate(x, tau(1/4.f));
  169. return side(half_angle) > 0;
  170. }
  171. );
  172. assert(lower != circle.end());
  173. auto upper = std::next(lower);
  174. auto index = lower - circle.begin();
  175. // NOTE: could just use half_angle itself if it was close enough to the circle, not sure if worth checking though
  176. auto on_circle = meet(join(vector::zero(),half_angle), join(lower,upper));
  177. auto sector_ratio = (on_circle - lower) / (upper - lower);
  178. return (index + sector_ratio.x()) / circle.size();
  179. }
  180. static constexpr vector tau()
  181. {
  182. return -vector::i();
  183. }
  184. static constexpr vector small_radian(Value value)
  185. {
  186. return {support::root2(Value{1} - value*value), value};
  187. }
  188. static vector radian(Value angle)
  189. {
  190. return {std::cos(angle), std::sin(angle)};
  191. }
  192. static constexpr vector angle(Value tau_factor)
  193. {
  194. if(tau_factor < (Value{1}/(circle.size()-1))/16 )
  195. return small_radian(tau_factor * common::tau);
  196. else
  197. return protractor::tau(tau_factor);
  198. }
  199. };
  200. float sinus(float angle)
  201. { return common::rotate(geom::vector<float,2>::i(), common::protractor<>::tau(support::wrap(angle,1.f))).y(); }
  202. float cosinus(float angle)
  203. { return common::rotate(geom::vector<float,2>::i(), common::protractor<>::tau(support::wrap(angle,1.f))).x(); }
  204. } // namespace common
  205. #endif /* end of include guard */