hexagon.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313
  1. #define Matrix rlMatrix
  2. #define Vector3 rlVector3
  3. #define Quaternion rlQuaternion
  4. #include <raylib.h>
  5. #include <rlgl.h>
  6. #include <external/glad.h>
  7. #define RAYGUI_IMPLEMENTATION
  8. #include <raygui_dl.h>
  9. #undef Quaternion
  10. #undef Vector3
  11. #undef Matrix
  12. #include "jet.hpp"
  13. #include <array>
  14. #include <Eigen/Dense>
  15. #include <Eigen/Geometry>
  16. #include <iostream>
  17. #include <cmath>
  18. #ifdef __EMSCRIPTEN__
  19. #include <emscripten.h>
  20. #endif
  21. using namespace Eigen;
  22. template<typename T>
  23. using Vector6 = Eigen::Vector<T, 6>;
  24. template<typename T>
  25. using Matrix6 = Eigen::Matrix<T, 6, 6>;
  26. template<typename scalar>
  27. requires(scalar::n_vars >= 6)
  28. struct transform{
  29. Vector3<scalar> translation;
  30. Vector3<scalar> euler_rotation_angles;
  31. transform() : translation(0,0,0), euler_rotation_angles(0,0,0){
  32. for(size_t i = 0;i < 3;i++){
  33. translation[i].deriv(i) = 1;
  34. euler_rotation_angles[i].deriv(i + 3) = 1;
  35. }
  36. }
  37. Matrix3<scalar> matrix()const noexcept{
  38. return quat().toRotationMatrix();
  39. }
  40. Quaternion<scalar> quat()const noexcept{
  41. Eigen::AngleAxis<scalar> rollAngle(euler_rotation_angles.x(), Eigen::Vector3<scalar>::UnitX());
  42. Eigen::AngleAxis<scalar> yawAngle(euler_rotation_angles.y(), Eigen::Vector3<scalar>::UnitY());
  43. Eigen::AngleAxis<scalar> pitchAngle(euler_rotation_angles.z(), Eigen::Vector3<scalar>::UnitZ());
  44. return rollAngle * yawAngle * pitchAngle;
  45. }
  46. template<int N>
  47. Matrix<scalar, 3, N> apply(const Matrix<scalar, 3, N>& input){
  48. Matrix<scalar, 3, N> trfed = matrix() * input;
  49. trfed.colwise() += translation;
  50. return trfed;
  51. }
  52. };
  53. template<typename T>
  54. Matrix<T, 3, 6> generate_hexagon(T scale, Vector3<T> translation = Vector3<T>(0, 0, 0)){
  55. T scale3 = scale * std::sqrt(T(3));
  56. T scale32 = scale3 / T(2);
  57. translation.x() -= scale * 0.5;
  58. translation.y() -= scale32;
  59. Matrix<T, 3, 6> vertices;
  60. vertices.col(0) = translation + Vector3<T>(0, 0, 0);
  61. vertices.col(1) = translation + Vector3<T>(scale, 0, 0);
  62. vertices.col(2) = translation + Vector3<T>(scale * 1.5, scale32, 0);
  63. vertices.col(3) = translation + Vector3<T>(scale, scale3, 0);
  64. vertices.col(4) = translation + Vector3<T>(0, scale3, 0);
  65. vertices.col(5) = translation + Vector3<T>(-scale * 0.5, scale32, 0);
  66. return vertices;
  67. }
  68. template<typename T>
  69. Matrix<T, 3, 6> generate_scronched_hexagon(T scale, Vector3<T> translation = Vector3<T>(0, 0, 0)){
  70. T scale3 = scale * std::sqrt(T(3));
  71. T scale32 = scale3 / T(2);
  72. translation.x() -= scale * 0.5;
  73. translation.y() -= scale32;
  74. Matrix<T, 3, 6> vertices;
  75. vertices.col(0) = translation + Vector3<T>(0, 0, 0);
  76. vertices.col(1) = translation + Vector3<T>(0, 0, 0);
  77. vertices.col(2) = translation + Vector3<T>(scale * 1.5, scale32, 0);
  78. vertices.col(3) = translation + Vector3<T>(scale * 1.5, scale32, 0);
  79. vertices.col(4) = translation + Vector3<T>(0, scale3, 0);
  80. vertices.col(5) = translation + Vector3<T>(0, scale3, 0);
  81. return vertices;
  82. }
  83. int GuiSlider(Rectangle bounds, const char *textLeft, const char *textRight, double *value, float minValue, float maxValue){
  84. float v = *value;
  85. int res = GuiSlider(bounds, textLeft, textRight, &v, minValue, maxValue);
  86. *value = v;
  87. return res;
  88. }
  89. template<typename scalar>
  90. using jet_type = jet<scalar, 12>;
  91. template<typename scalar>
  92. using hexagon = Matrix<jet_type<scalar>, 3, 6>;
  93. constexpr double distance = 1.5;
  94. template<typename scalar>
  95. std::pair<Vector6<scalar>, Matrix6<scalar>> evaluate_with_jacobian(const transform<jet_type<scalar>>& trf, const hexagon<scalar>& points, const hexagon<scalar>& anchors){
  96. hexagon<scalar> trfed = trf.matrix() * points;
  97. trfed.colwise() += trf.translation;
  98. Vector6<jet_type<scalar>> distances;
  99. for(size_t i = 0;i < 6;i++){
  100. distances(i) = (trfed.col(i) - anchors.col(i)).norm();
  101. }
  102. std::pair<Vector6<scalar>, Matrix6<scalar>> ret;
  103. for(size_t i = 0;i < 6;i++){
  104. ret.first(i) = distances(i).value - distance;
  105. ret.second.array().row(i) = distances(i).deriv.template head<6>();
  106. }
  107. return ret;
  108. }
  109. template<typename scalar>
  110. std::pair<Vector6<scalar>, Matrix6<scalar>> evaluate_with_jacobian_wrt_actuators(const transform<jet_type<scalar>>& trf, const hexagon<scalar>& points, const hexagon<scalar>& anchors){
  111. hexagon<scalar> trfed = trf.matrix() * points;
  112. trfed.colwise() += trf.translation;
  113. Vector6<jet_type<scalar>> distances;
  114. for(size_t i = 0;i < 6;i++){
  115. distances(i) = (trfed.col(i) - anchors.col(i)).norm();
  116. }
  117. std::pair<Vector6<scalar>, Matrix6<scalar>> ret;
  118. for(size_t i = 0;i < 6;i++){
  119. ret.first(i) = distances(i).value - distance;
  120. ret.second.array().row(i) = distances(i).deriv.template tail<6>();
  121. }
  122. return ret;
  123. }
  124. template<typename T, int N>
  125. std::array<rlVector3, N> convert_to_raylib(const Matrix<T, 3, N>& mat){
  126. std::array<rlVector3, N> ret;
  127. for(int i = 0;i < N;i++){
  128. ret[i] = rlVector3(mat.col(i)[0], mat.col(i)[1], mat.col(i)[2]);
  129. }
  130. return ret;
  131. }
  132. rlVector3 erl(const auto& v){
  133. rlVector3 ret(v(0), v(1), v(2));
  134. return ret;
  135. }
  136. RenderTexture vp;
  137. float camangle = 0.0f;
  138. float campitch = 1.0f;
  139. bool camdragging = false;
  140. Vector6<float> actuator_angles = Vector6<float>::Zero();
  141. transform<jet_type<double>> current_transform; // Identity
  142. void gameLoop(){
  143. using scalar = double;
  144. rlVector3 targ{0.5f,std::sqrt(3.0f) / 2.0f,0};
  145. campitch = std::clamp(campitch, -1.2f, 1.2f);
  146. Camera3D cam{.position = rlVector3{std::sin(camangle) * 4.0f + targ.x,std::cos(camangle) * 4.0f + targ.y, std::tan(campitch)}, .target = targ, .up = {0,0,1},.fovy = 60.0f, .projection = CAMERA_PERSPECTIVE};
  147. BeginDrawing();
  148. if(!IsMouseButtonDown(MOUSE_LEFT_BUTTON)){
  149. camdragging = false;
  150. }
  151. if(IsMouseButtonPressed(MOUSE_LEFT_BUTTON)){
  152. if(GetMouseX() <= 1000){
  153. //std::cout << "oof\n";
  154. camdragging = true;
  155. }
  156. }else if(IsMouseButtonDown(MOUSE_BUTTON_LEFT) && camdragging){
  157. camangle += GetMouseDelta().x * 0.01f;
  158. campitch += GetMouseDelta().y * 0.01f;
  159. }
  160. ClearBackground(Color(30,30,10,255));
  161. BeginTextureMode(vp);
  162. ClearBackground(Color(30,30,10,255));
  163. BeginMode3D(cam);
  164. auto unit_hexagon = generate_scronched_hexagon(jet_type<scalar>(1.0));
  165. auto anchors = generate_hexagon(jet_type<scalar>(1.6), Vector3<jet_type<scalar>>(0, 0, -1.0));
  166. std::array<std::pair<Vector3<double>, Vector3<double>>, 6> rotary_planes;
  167. double rad = 0.4;
  168. for(int i = 0;i < 6;i++){
  169. Vector3<double> diag = anchors.col((i + 3) % 6).cast<double>() - anchors.col(i).cast<double>();
  170. rotary_planes[i].first = Vector3<double>(-diag.y(), diag.x(), 0).normalized();
  171. rotary_planes[i].second = Vector3<double>(0,0,1);
  172. for(int j = 0;j < 128;j++){
  173. double angle = 2.0 * M_PI * double(j) / 127;
  174. double anglen = 2.0 * M_PI * double(j + 1) / 127;
  175. Vector3<double> from = anchors.col(i).cast<double>() + rotary_planes[i].first * std::cos(angle) * rad + rotary_planes[i].second * std::sin(angle) * rad;
  176. Vector3<double> to = anchors.col(i).cast<double>() + rotary_planes[i].first * std::cos(anglen) * rad + rotary_planes[i].second * std::sin(anglen) * rad;
  177. rlVector3 rlf = erl(from);
  178. rlVector3 rlt = erl(to);
  179. DrawLine3D(rlf, rlt, ORANGE);
  180. }
  181. }
  182. for(int i = 0;i < 6;i++){
  183. jet_type<double> actuator_angle_jet(actuator_angles[i]);
  184. actuator_angle_jet.deriv(i + 6) = 1;
  185. anchors.col(i) += (jet_type<double>(rad) * cos(actuator_angle_jet) * rotary_planes[i].first .cast<jet_type<double>>())
  186. + (jet_type<double>(rad) * sin(actuator_angle_jet) * rotary_planes[i].second.cast<jet_type<double>>());
  187. }
  188. //anchors.col(0)(2) += 0.1 * std::sin(5.0 * GetTime());
  189. auto trf_backup = current_transform;
  190. current_transform = transform<jet_type<double>>();
  191. for(int i = 0;i < 20;i++){
  192. auto[v, J] = evaluate_with_jacobian<double>(current_transform, unit_hexagon, anchors);
  193. FullPivHouseholderQR<Matrix6<double>> qr(J);
  194. Vector6<double> subtract = qr.solve(v);
  195. current_transform.translation -= subtract.head<3>().cast<jet_type<double>>();
  196. current_transform.euler_rotation_angles -= subtract.segment(3, 3).cast<jet_type<double>>();
  197. //std::cout << v << "\n";
  198. //std::cout << "Rank: " << J.fullPivHouseholderQr().rank() << "\n";
  199. //std::cout << "Rank: " << J.fullPivHouseholderQr().solve(v) << "\n";
  200. auto[vi, Ji] = evaluate_with_jacobian_wrt_actuators<double>(current_transform, unit_hexagon, anchors);
  201. if(vi.norm() > 0.1){
  202. current_transform = trf_backup;
  203. }
  204. }
  205. Matrix<double, 3, 6> hexagon_vertices = current_transform.apply(unit_hexagon).cast<double>();
  206. std::array<rlVector3, 6> hvrl = convert_to_raylib(hexagon_vertices);
  207. std::array<rlVector3, 6> arl = convert_to_raylib(anchors);
  208. for(int i = 0;i < 6;i++){
  209. DrawLine3D(hvrl[i], arl[i], WHITE);
  210. }
  211. DrawTriangle3D(hvrl[0], hvrl[2], hvrl[4], Color{255, 255, 0, 120});
  212. EndMode3D();
  213. EndTextureMode();
  214. SetTextureFilter(vp.texture, TEXTURE_FILTER_BILINEAR);
  215. GenTextureMipmaps(&vp.texture);
  216. DrawTexturePro(vp.texture, Rectangle(0,0,vp.texture.width, -float(vp.texture.height)), Rectangle(0,0,1000,1000), ::Vector2(0,0), 0.0f, WHITE);
  217. DrawText(TextFormat("FPS: %d", GetFPS()), 0,0 , 40, GREEN);
  218. for(int i = 0;i < 6;i++){
  219. GuiSlider(Rectangle(1100, i * 50 + 100, 200, 40), TextFormat("Actuator %d", i + 1), "", actuator_angles.data() + i, 0.0f, 4.0f * M_PI);
  220. }
  221. constexpr const char* names[] = {"X", "Y", "Z", "Roll", "Pitch", "Yaw"};
  222. bool changed = false;
  223. auto aa_backup = actuator_angles;
  224. for(int i = 0;i < 3;i++){
  225. //float x = 0;
  226. //std::cout << current_transform.translation(i).value << ", ";
  227. double pp = current_transform.translation(i).value;
  228. double pv = current_transform.euler_rotation_angles(i).value;
  229. GuiSlider(Rectangle(1100, i * 50 + 500, 200, 40), names[i + 0], "", &current_transform.translation(i).value, -1.0f, 1.0f);
  230. GuiSlider(Rectangle(1100, i * 50 + 700, 200, 40), names[i + 3], "", &current_transform.euler_rotation_angles(i).value, -0.5f, 0.5f);
  231. if(std::abs(current_transform.translation(i).value - pp) > 1e-5){
  232. changed = true;
  233. }
  234. if(std::abs(current_transform.euler_rotation_angles(i).value - pv) > 1e-5){
  235. changed = true;
  236. }
  237. }
  238. if (GuiButton(Rectangle(1100, 55, 200, 40), "Reset")){
  239. changed = false;
  240. actuator_angles.fill(0);
  241. }
  242. if(changed){
  243. //std::cout << "Changed\n";
  244. for(int i = 0;i < 20;i++){
  245. auto[v, J] = evaluate_with_jacobian_wrt_actuators<double>(current_transform, unit_hexagon, anchors);
  246. FullPivHouseholderQR<Matrix6<double>> qr(J);
  247. Vector6<double> subtract = qr.solve(v);
  248. actuator_angles -= subtract.cast<float>();
  249. anchors = generate_hexagon(jet_type<scalar>(1.6), Vector3<jet_type<scalar>>(0, 0, -1.0));
  250. for(int j = 0;j < 6;j++){
  251. jet_type<double> actuator_angle_jet(actuator_angles[j]);
  252. actuator_angle_jet.deriv(j + 6) = 1;
  253. anchors.col(j) += (jet_type<double>(rad) * cos(actuator_angle_jet) * rotary_planes[j].first .cast<jet_type<double>>())
  254. + (jet_type<double>(rad) * sin(actuator_angle_jet) * rotary_planes[j].second.cast<jet_type<double>>());
  255. }
  256. //std::cout << v << "\n";
  257. if(i == 19){
  258. auto[vi, Ji] = evaluate_with_jacobian_wrt_actuators<double>(current_transform, unit_hexagon, anchors);
  259. if(vi.norm() > 0.1){
  260. actuator_angles = aa_backup;
  261. }
  262. }
  263. //std::cout << anchors << "\n";
  264. }
  265. for(int j = 0;j < 6;j++){
  266. actuator_angles(j) = std::fmod(actuator_angles(j) + 2.0 * M_PI, 2.0 * M_PI);
  267. }
  268. }
  269. EndDrawing();
  270. }
  271. int main(){
  272. InitWindow(1500, 1000, "Simulator");
  273. rlSetLineWidth(5.0f);
  274. vp = LoadRenderTexture(4000, 4000);
  275. GuiSetStyle(DEFAULT, TEXT_SIZE, 30);
  276. GuiSetStyle(DEFAULT, TEXT_COLOR_NORMAL, ColorToInt(Color{100,255,100,255}));
  277. GuiSetStyle(DEFAULT, TEXT_SPACING, 4.0f);
  278. SetTargetFPS(60);
  279. rlDisableBackfaceCulling();
  280. #ifdef __EMSCRIPTEN__
  281. emscripten_set_main_loop(gameLoop, 0, 0);
  282. #else
  283. while(!WindowShouldClose()){
  284. gameLoop();
  285. }
  286. #endif
  287. //std::cout << current_guess.translation << "\n";
  288. //std::cout << current_guess.euler_rotation_angles << "\n\n";
  289. //for(int i = 0;i < 6;i++)
  290. // std::cout << (current_guess.apply(unit_hexagon).col(i).cast<double>() - anchors.col(i).cast<double>()).norm() << "\n";
  291. //auto[v, J] = evaluate_with_jacobian<double>(current_guess, unit_hexagon, anchors);
  292. }