InterpolatePy 3.0.1__tar.gz → 3.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (220) hide show
  1. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/.github/workflows/docs.yml +10 -10
  2. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/.github/workflows/pre-commit.yml +3 -3
  3. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/.github/workflows/publish.yml +2 -2
  4. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/.github/workflows/test.yml +3 -3
  5. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/InterpolatePy.egg-info/PKG-INFO +1 -1
  6. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/InterpolatePy.egg-info/SOURCES.txt +2 -1
  7. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/PKG-INFO +1 -1
  8. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/docs/tutorials/quaternion-interpolation.md +1 -1
  9. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/examples/log_quat_new_ex.py +0 -4
  10. interpolatepy-3.1.0/examples/lqi_spiral_frenet_ex.py +342 -0
  11. interpolatepy-3.1.0/examples/mlqi_spiral_frenet_ex.py +210 -0
  12. interpolatepy-3.1.0/interpolatepy/log_quat.py +470 -0
  13. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/interpolatepy/quat_core.py +13 -4
  14. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/interpolatepy/version.py +1 -1
  15. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/pyproject.toml +4 -1
  16. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/tests/test_log_quat.py +2 -219
  17. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/uv.lock +62 -0
  18. interpolatepy-3.0.1/examples/log_quat_ex.py +0 -207
  19. interpolatepy-3.0.1/interpolatepy/log_quat.py +0 -950
  20. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/.editorconfig +0 -0
  21. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/.gitattributes +0 -0
  22. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/.github/FUNDING.yml +0 -0
  23. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/.gitignore +0 -0
  24. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/.pre-commit-config.yaml +0 -0
  25. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/.python-version +0 -0
  26. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/ALGORITHMS.md +0 -0
  27. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/InterpolatePy.egg-info/dependency_links.txt +0 -0
  28. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/InterpolatePy.egg-info/requires.txt +0 -0
  29. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/InterpolatePy.egg-info/top_level.txt +0 -0
  30. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/LICENSE +0 -0
  31. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/README.md +0 -0
  32. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/codecov.yml +0 -0
  33. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/CMakeLists.txt +0 -0
  34. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/bindings/CMakeLists.txt +0 -0
  35. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/bindings/bind_acc_splines.cpp +0 -0
  36. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/bindings/bind_bspline.cpp +0 -0
  37. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/bindings/bind_cubic_spline.cpp +0 -0
  38. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/bindings/bind_motion.cpp +0 -0
  39. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/bindings/bind_paths.cpp +0 -0
  40. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/bindings/bind_quaternion.cpp +0 -0
  41. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/bindings/bind_smoothing_search.cpp +0 -0
  42. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/bindings/bind_smoothing_spline.cpp +0 -0
  43. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/bindings/bind_tridiagonal.cpp +0 -0
  44. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/bindings/module.cpp +0 -0
  45. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/cmake/InterpolateCppConfig.cmake.in +0 -0
  46. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/cmake/version.hpp.in +0 -0
  47. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/examples/CMakeLists.txt +0 -0
  48. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/examples/bspline_approx_smooth_example.cpp +0 -0
  49. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/examples/bspline_cubic_example.cpp +0 -0
  50. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/examples/bspline_example.cpp +0 -0
  51. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/examples/bspline_interpolator_example.cpp +0 -0
  52. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/examples/concepts_example.cpp +0 -0
  53. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/examples/cubic_smoothing_example.cpp +0 -0
  54. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/examples/cubic_spline_acc1_example.cpp +0 -0
  55. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/examples/cubic_spline_acc2_example.cpp +0 -0
  56. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/examples/cubic_spline_example.cpp +0 -0
  57. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/examples/double_s_example.cpp +0 -0
  58. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/examples/parabolic_linear_example.cpp +0 -0
  59. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/examples/paths_example.cpp +0 -0
  60. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/examples/polynomial_example.cpp +0 -0
  61. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/examples/quaternion_example.cpp +0 -0
  62. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/examples/shared/example_utils.hpp +0 -0
  63. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/examples/trapezoidal_example.cpp +0 -0
  64. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/include/interpolatecpp/bspline/approximation_bspline.hpp +0 -0
  65. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/include/interpolatecpp/bspline/bspline.hpp +0 -0
  66. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/include/interpolatecpp/bspline/bspline_interpolator.hpp +0 -0
  67. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/include/interpolatecpp/bspline/bspline_parameters.hpp +0 -0
  68. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/include/interpolatecpp/bspline/cubic_bspline_interpolation.hpp +0 -0
  69. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/include/interpolatecpp/bspline/smoothing_cubic_bspline.hpp +0 -0
  70. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/include/interpolatecpp/concepts.hpp +0 -0
  71. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/include/interpolatecpp/config.hpp +0 -0
  72. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/include/interpolatecpp/motion/double_s_trajectory.hpp +0 -0
  73. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/include/interpolatecpp/motion/motion_types.hpp +0 -0
  74. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/include/interpolatecpp/motion/parabolic_blend_trajectory.hpp +0 -0
  75. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/include/interpolatecpp/motion/polynomial_trajectory.hpp +0 -0
  76. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/include/interpolatecpp/motion/trapezoidal_trajectory.hpp +0 -0
  77. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/include/interpolatecpp/path/circular_path.hpp +0 -0
  78. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/include/interpolatecpp/path/frenet_frame.hpp +0 -0
  79. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/include/interpolatecpp/path/linear_path.hpp +0 -0
  80. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/include/interpolatecpp/path/linear_traj.hpp +0 -0
  81. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/include/interpolatecpp/quat/log_quaternion_interpolation.hpp +0 -0
  82. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/include/interpolatecpp/quat/modified_log_quaternion_interpolation.hpp +0 -0
  83. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/include/interpolatecpp/quat/quaternion.hpp +0 -0
  84. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/include/interpolatecpp/quat/quaternion_spline.hpp +0 -0
  85. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/include/interpolatecpp/quat/squad_c2.hpp +0 -0
  86. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/include/interpolatecpp/spline/cubic_smoothing_spline.hpp +0 -0
  87. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/include/interpolatecpp/spline/cubic_spline.hpp +0 -0
  88. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/include/interpolatecpp/spline/cubic_spline_with_acc1.hpp +0 -0
  89. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/include/interpolatecpp/spline/cubic_spline_with_acc2.hpp +0 -0
  90. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/include/interpolatecpp/spline/smoothing_search.hpp +0 -0
  91. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/include/interpolatecpp/spline/spline_parameters.hpp +0 -0
  92. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/include/interpolatecpp/tridiagonal.hpp +0 -0
  93. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/src/approximation_bspline.cpp +0 -0
  94. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/src/bspline.cpp +0 -0
  95. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/src/bspline_interpolator.cpp +0 -0
  96. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/src/circular_path.cpp +0 -0
  97. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/src/cubic_bspline_interpolation.cpp +0 -0
  98. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/src/cubic_smoothing_spline.cpp +0 -0
  99. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/src/cubic_spline.cpp +0 -0
  100. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/src/cubic_spline_with_acc1.cpp +0 -0
  101. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/src/cubic_spline_with_acc2.cpp +0 -0
  102. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/src/double_s_trajectory.cpp +0 -0
  103. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/src/frenet_frame.cpp +0 -0
  104. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/src/linear_path.cpp +0 -0
  105. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/src/linear_traj.cpp +0 -0
  106. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/src/log_quaternion_interpolation.cpp +0 -0
  107. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/src/modified_log_quaternion_interpolation.cpp +0 -0
  108. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/src/parabolic_blend_trajectory.cpp +0 -0
  109. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/src/polynomial_trajectory.cpp +0 -0
  110. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/src/quaternion.cpp +0 -0
  111. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/src/quaternion_spline.cpp +0 -0
  112. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/src/smoothing_cubic_bspline.cpp +0 -0
  113. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/src/smoothing_search.cpp +0 -0
  114. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/src/squad_c2.cpp +0 -0
  115. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/src/trapezoidal_trajectory.cpp +0 -0
  116. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/tests/CMakeLists.txt +0 -0
  117. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/tests/shared/test_data.hpp +0 -0
  118. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/tests/test_bspline.cpp +0 -0
  119. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/tests/test_bspline_variants.cpp +0 -0
  120. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/tests/test_concepts.cpp +0 -0
  121. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/tests/test_cubic_smoothing_spline.cpp +0 -0
  122. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/tests/test_cubic_spline.cpp +0 -0
  123. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/tests/test_cubic_spline_with_acc1.cpp +0 -0
  124. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/tests/test_cubic_spline_with_acc2.cpp +0 -0
  125. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/tests/test_double_s_trajectory.cpp +0 -0
  126. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/tests/test_parabolic_blend_trajectory.cpp +0 -0
  127. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/tests/test_paths.cpp +0 -0
  128. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/tests/test_polynomial_trajectory.cpp +0 -0
  129. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/tests/test_quaternion.cpp +0 -0
  130. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/tests/test_quaternion_spline.cpp +0 -0
  131. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/tests/test_smoothing_search.cpp +0 -0
  132. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/tests/test_trapezoidal_trajectory.cpp +0 -0
  133. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/tests/test_tridiagonal.cpp +0 -0
  134. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/docs/algorithms.md +0 -0
  135. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/docs/api-reference.md +0 -0
  136. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/docs/architecture.md +0 -0
  137. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/docs/assets/extra.css +0 -0
  138. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/docs/assets/extra.js +0 -0
  139. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/docs/changelog.md +0 -0
  140. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/docs/contributing.md +0 -0
  141. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/docs/examples.md +0 -0
  142. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/docs/index.md +0 -0
  143. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/docs/installation.md +0 -0
  144. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/docs/quickstart.md +0 -0
  145. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/docs/troubleshooting.md +0 -0
  146. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/docs/tutorials/motion-profiles.md +0 -0
  147. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/docs/tutorials/path-planning.md +0 -0
  148. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/docs/tutorials/spline-interpolation.md +0 -0
  149. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/docs/user-guide.md +0 -0
  150. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/examples/b_spline_approx_ex.py +0 -0
  151. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/examples/b_spline_cubic_ex.py +0 -0
  152. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/examples/b_spline_ex.py +0 -0
  153. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/examples/b_spline_interpolate_ex.py +0 -0
  154. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/examples/b_spline_smooth_ex.py +0 -0
  155. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/examples/c_s_smoot_search_ex.py +0 -0
  156. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/examples/c_s_smoothing_ex.py +0 -0
  157. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/examples/c_s_with_acc1_ex.py +0 -0
  158. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/examples/c_s_with_acc2_ex.py +0 -0
  159. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/examples/cubic_spline_ex.py +0 -0
  160. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/examples/double_s_ex.py +0 -0
  161. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/examples/frenet_frame_ex.py +0 -0
  162. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/examples/lin_poly_parabolic_ex.py +0 -0
  163. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/examples/linear_ex.py +0 -0
  164. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/examples/main.py +0 -0
  165. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/examples/polynomials_ex.py +0 -0
  166. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/examples/protocols_ex.py +0 -0
  167. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/examples/quat_visualization_ex.py +0 -0
  168. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/examples/simple_paths_ex.py +0 -0
  169. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/examples/squad_c2_ex.py +0 -0
  170. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/examples/trapezoidal_ex.py +0 -0
  171. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/interpolatepy/__init__.py +0 -0
  172. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/interpolatepy/_adapters/__init__.py +0 -0
  173. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/interpolatepy/_adapters/_bspline.py +0 -0
  174. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/interpolatepy/_adapters/_direct.py +0 -0
  175. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/interpolatepy/_adapters/_motion.py +0 -0
  176. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/interpolatepy/_adapters/_paths.py +0 -0
  177. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/interpolatepy/_adapters/_quaternion.py +0 -0
  178. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/interpolatepy/_adapters/_spline.py +0 -0
  179. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/interpolatepy/_api.py +0 -0
  180. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/interpolatepy/_backend.py +0 -0
  181. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/interpolatepy/b_spline.py +0 -0
  182. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/interpolatepy/b_spline_approx.py +0 -0
  183. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/interpolatepy/b_spline_cubic.py +0 -0
  184. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/interpolatepy/b_spline_interpolate.py +0 -0
  185. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/interpolatepy/b_spline_smooth.py +0 -0
  186. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/interpolatepy/c_s_smoot_search.py +0 -0
  187. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/interpolatepy/c_s_smoothing.py +0 -0
  188. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/interpolatepy/c_s_with_acc1.py +0 -0
  189. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/interpolatepy/c_s_with_acc2.py +0 -0
  190. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/interpolatepy/cubic_spline.py +0 -0
  191. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/interpolatepy/double_s.py +0 -0
  192. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/interpolatepy/frenet_frame.py +0 -0
  193. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/interpolatepy/lin_poly_parabolic.py +0 -0
  194. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/interpolatepy/linear.py +0 -0
  195. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/interpolatepy/polynomials.py +0 -0
  196. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/interpolatepy/protocols.py +0 -0
  197. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/interpolatepy/quat_spline.py +0 -0
  198. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/interpolatepy/quat_visualization.py +0 -0
  199. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/interpolatepy/simple_paths.py +0 -0
  200. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/interpolatepy/squad_c2.py +0 -0
  201. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/interpolatepy/trapezoidal.py +0 -0
  202. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/interpolatepy/tridiagonal_inv.py +0 -0
  203. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/mkdocs.yml +0 -0
  204. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/setup.cfg +0 -0
  205. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/tests/__init__.py +0 -0
  206. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/tests/inv_test.py +0 -0
  207. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/tests/test_b_spline.py +0 -0
  208. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/tests/test_b_spline_variants.py +0 -0
  209. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/tests/test_backend.py +0 -0
  210. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/tests/test_cubic_spline.py +0 -0
  211. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/tests/test_lin_poly_parabolic.py +0 -0
  212. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/tests/test_linear.py +0 -0
  213. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/tests/test_motion_profiles.py +0 -0
  214. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/tests/test_path_planning.py +0 -0
  215. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/tests/test_polynomials.py +0 -0
  216. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/tests/test_protocols.py +0 -0
  217. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/tests/test_quat_interp.py +0 -0
  218. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/tests/test_quat_visualization.py +0 -0
  219. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/tests/test_smoothing.py +0 -0
  220. {interpolatepy-3.0.1 → interpolatepy-3.1.0}/tests/test_squad_c2.py +0 -0
@@ -28,15 +28,15 @@ concurrency:
28
28
  jobs:
29
29
  build:
30
30
  runs-on: ubuntu-latest
31
-
31
+
32
32
  steps:
33
33
  - name: Checkout repository
34
- uses: actions/checkout@v4
34
+ uses: actions/checkout@v6
35
35
  with:
36
36
  fetch-depth: 0
37
-
37
+
38
38
  - name: Install uv
39
- uses: astral-sh/setup-uv@v6
39
+ uses: astral-sh/setup-uv@v8.1.0
40
40
  with:
41
41
  enable-cache: true
42
42
  cache-dependency-glob: uv.lock
@@ -46,14 +46,14 @@ jobs:
46
46
 
47
47
  - name: Build documentation
48
48
  run: uv run mkdocs build --clean --strict
49
-
49
+
50
50
  - name: Setup Pages
51
51
  if: github.ref == 'refs/heads/main' && github.event_name == 'push'
52
- uses: actions/configure-pages@v4
53
-
52
+ uses: actions/configure-pages@v6
53
+
54
54
  - name: Upload artifact
55
55
  if: github.ref == 'refs/heads/main' && github.event_name == 'push'
56
- uses: actions/upload-pages-artifact@v3
56
+ uses: actions/upload-pages-artifact@v5
57
57
  with:
58
58
  path: ./site
59
59
 
@@ -64,9 +64,9 @@ jobs:
64
64
  url: ${{ steps.deployment.outputs.page_url }}
65
65
  runs-on: ubuntu-latest
66
66
  needs: build
67
-
67
+
68
68
  steps:
69
69
  - name: Deploy to GitHub Pages
70
70
  id: deployment
71
- uses: actions/deploy-pages@v4
71
+ uses: actions/deploy-pages@v5
72
72
 
@@ -9,16 +9,16 @@ jobs:
9
9
  pre-commit:
10
10
  runs-on: ubuntu-latest
11
11
  steps:
12
- - uses: actions/checkout@v4
12
+ - uses: actions/checkout@v6
13
13
  - name: Install uv
14
- uses: astral-sh/setup-uv@v6
14
+ uses: astral-sh/setup-uv@v8.1.0
15
15
  with:
16
16
  enable-cache: true
17
17
  cache-dependency-glob: uv.lock
18
18
  - name: Sync dependencies
19
19
  run: uv sync --locked --no-default-groups --group dev
20
20
  - name: Cache pre-commit hook environments
21
- uses: actions/cache@v4
21
+ uses: actions/cache@v5
22
22
  with:
23
23
  path: ~/.cache/pre-commit
24
24
  key: pre-commit-${{ runner.os }}-${{ hashFiles('.pre-commit-config.yaml') }}
@@ -12,11 +12,11 @@ jobs:
12
12
  contents: read
13
13
  environment: pypi
14
14
  steps:
15
- - uses: actions/checkout@v4
15
+ - uses: actions/checkout@v6
16
16
  with:
17
17
  fetch-depth: 0
18
18
  - name: Install uv
19
- uses: astral-sh/setup-uv@v6
19
+ uses: astral-sh/setup-uv@v8.1.0
20
20
  with:
21
21
  enable-cache: true
22
22
  cache-dependency-glob: uv.lock
@@ -16,9 +16,9 @@ jobs:
16
16
  env:
17
17
  UV_PYTHON: ${{ matrix.python-version }}
18
18
  steps:
19
- - uses: actions/checkout@v4
19
+ - uses: actions/checkout@v6
20
20
  - name: Install uv
21
- uses: astral-sh/setup-uv@v6
21
+ uses: astral-sh/setup-uv@v8.1.0
22
22
  with:
23
23
  enable-cache: true
24
24
  cache-dependency-glob: uv.lock
@@ -27,7 +27,7 @@ jobs:
27
27
  - name: Testing with coverage
28
28
  run: uv run pytest tests --cov=interpolatepy --cov-report=xml --cov-report=term-missing
29
29
  - name: Upload coverage to Codecov
30
- uses: codecov/codecov-action@v5
30
+ uses: codecov/codecov-action@v6
31
31
  with:
32
32
  token: ${{ secrets.CODECOV_TOKEN }}
33
33
  files: ./coverage.xml
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: InterpolatePy
3
- Version: 3.0.1
3
+ Version: 3.1.0
4
4
  Summary: A comprehensive Python library for generating smooth trajectories and curves with precise control over position, velocity, acceleration, and jerk profiles
5
5
  Author-email: Giorgio Medico <giorgio.medico11@gmail.com>
6
6
  Maintainer-email: Giorgio Medico <giorgio.medico11@gmail.com>
@@ -152,9 +152,10 @@ examples/double_s_ex.py
152
152
  examples/frenet_frame_ex.py
153
153
  examples/lin_poly_parabolic_ex.py
154
154
  examples/linear_ex.py
155
- examples/log_quat_ex.py
156
155
  examples/log_quat_new_ex.py
156
+ examples/lqi_spiral_frenet_ex.py
157
157
  examples/main.py
158
+ examples/mlqi_spiral_frenet_ex.py
158
159
  examples/polynomials_ex.py
159
160
  examples/protocols_ex.py
160
161
  examples/quat_visualization_ex.py
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: InterpolatePy
3
- Version: 3.0.1
3
+ Version: 3.1.0
4
4
  Summary: A comprehensive Python library for generating smooth trajectories and curves with precise control over position, velocity, acceleration, and jerk profiles
5
5
  Author-email: Giorgio Medico <giorgio.medico11@gmail.com>
6
6
  Maintainer-email: Giorgio Medico <giorgio.medico11@gmail.com>
@@ -209,4 +209,4 @@ Interpolation near 180-degree rotations can be problematic because the interpola
209
209
 
210
210
  - **[API Reference](../api-reference.md#quaternion)** for complete method documentation
211
211
  - **[Algorithms Guide](../algorithms.md)** for mathematical foundations
212
- - **Example scripts**: `examples/squad_c2_ex.py`, `examples/log_quat_ex.py`, `examples/quat_visualization_ex.py`
212
+ - **Example scripts**: `examples/squad_c2_ex.py`, `examples/log_quat_new_ex.py`, `examples/quat_visualization_ex.py`
@@ -5,9 +5,6 @@ This example demonstrates the LogQuaternionInterpolation class (LQI method)
5
5
  for smooth quaternion trajectory generation using logarithmic quaternion
6
6
  representation with B-spline interpolation.
7
7
 
8
- Note: This example has been updated to use LogQuaternionInterpolation instead
9
- of the deprecated LogQuaternionBSpline class.
10
-
11
8
  Key Features Demonstrated:
12
9
  - Logarithmic quaternion space interpolation for smooth trajectories
13
10
  - Algorithm 1 from Parker et al. (2023) for continuous axis-angle recovery
@@ -27,7 +24,6 @@ Mathematical Background:
27
24
  import matplotlib.pyplot as plt
28
25
  import numpy as np
29
26
 
30
- # Using LogQuaternionInterpolation instead of deprecated LogQuaternionBSpline
31
27
  from interpolatepy.log_quat import LogQuaternionInterpolation, ModifiedLogQuaternionInterpolation
32
28
  from interpolatepy.quat_core import Quaternion
33
29
  from interpolatepy.quat_spline import QuaternionSpline
@@ -0,0 +1,342 @@
1
+ """
2
+ Logarithmic Quaternion Interpolation (LQI) along a cylindrical helix oriented
3
+ with the Frenet frame.
4
+
5
+ Pipeline:
6
+ 1. Define a cylindrical helix p(u) = (r*cos(u), r*sin(u), b*u) with analytical
7
+ derivatives so the Frenet frame can be evaluated in closed form. The radius
8
+ is constant, so curvature and torsion are constant along the curve.
9
+ 2. Sample a sparse set of waypoints along the helix and compute the Frenet
10
+ frame [tangent, normal, binormal] at each waypoint.
11
+ 3. Convert each Frenet rotation matrix to a unit quaternion.
12
+ 4. Feed the (time, quaternion) waypoints to LogQuaternionInterpolation to
13
+ obtain a smooth C^2 orientation trajectory. LQI splines the rotation
14
+ vector r(u) = theta(u) * n_hat(u) as a single 3D quantity (in contrast
15
+ with mLQI, which splines theta and (X, Y, Z) on separate channels).
16
+ 5. Evaluate the position helix and the interpolated orientation at a dense
17
+ set of times and visualize the resulting tool frames side by side with the
18
+ ground-truth Frenet frames.
19
+ """
20
+
21
+ from __future__ import annotations
22
+
23
+ import matplotlib.pyplot as plt
24
+ import numpy as np
25
+ from mpl_toolkits.mplot3d import Axes3D # noqa: F401 (registers 3D projection)
26
+
27
+ from interpolatepy.frenet_frame import compute_trajectory_frames
28
+ from interpolatepy.frenet_frame import plot_frames
29
+ from interpolatepy.log_quat import LogQuaternionInterpolation
30
+ from interpolatepy.quat_core import Quaternion
31
+ from interpolatepy.quat_visualization import QuaternionTrajectoryVisualizer
32
+
33
+
34
+ def cylindrical_helix_with_derivatives(
35
+ u: float, r: float = 1.0, b: float = 0.3
36
+ ) -> tuple[np.ndarray, np.ndarray, np.ndarray]:
37
+ """
38
+ Cylindrical helix (constant radius) position and first two derivatives.
39
+
40
+ p(u) = ( r*cos(u), r*sin(u), b*u )
41
+ dp/du = (-r*sin(u), r*cos(u), b )
42
+ d2p/du2 = (-r*cos(u), -r*sin(u), 0 )
43
+ """
44
+ cos_u = np.cos(u)
45
+ sin_u = np.sin(u)
46
+
47
+ p = np.array([r * cos_u, r * sin_u, b * u])
48
+ dp_du = np.array([-r * sin_u, r * cos_u, b])
49
+ d2p_du2 = np.array([-r * cos_u, -r * sin_u, 0.0])
50
+
51
+ return p, dp_du, d2p_du2
52
+
53
+
54
+ def frame_to_quaternion(frame: np.ndarray) -> Quaternion:
55
+ """
56
+ Convert a 3x3 frame whose columns are [tangent, normal, binormal] into a
57
+ unit quaternion. The frame matrix is itself the rotation matrix from the
58
+ local Frenet basis to the world basis, so we can hand it directly to
59
+ Quaternion.from_rotation_matrix.
60
+ """
61
+ return Quaternion.from_rotation_matrix(frame).unit()
62
+
63
+
64
+ def build_waypoints(
65
+ n_waypoints: int = 10,
66
+ u_min: float = 0.5,
67
+ u_max: float = 6.0 * np.pi,
68
+ r: float = 1.0,
69
+ b: float = 0.3,
70
+ ) -> tuple[np.ndarray, list[Quaternion], np.ndarray, np.ndarray]:
71
+ """
72
+ Sample the helix at n_waypoints values of u, compute Frenet frames there,
73
+ and return (times, quaternions, waypoint_positions, waypoint_frames).
74
+
75
+ Times are taken equal to the parameter u so the orientation evolution
76
+ matches the curve parameterization.
77
+ """
78
+ u_waypoints = np.linspace(u_min, u_max, n_waypoints)
79
+
80
+ def helix_func(u: float) -> tuple[np.ndarray, np.ndarray, np.ndarray]:
81
+ return cylindrical_helix_with_derivatives(u, r=r, b=b)
82
+
83
+ positions, frames = compute_trajectory_frames(helix_func, u_waypoints)
84
+
85
+ quaternions = [frame_to_quaternion(frames[i]) for i in range(len(u_waypoints))]
86
+
87
+ return u_waypoints, quaternions, positions, frames
88
+
89
+
90
+ def evaluate_interpolated_trajectory(
91
+ lqi: LogQuaternionInterpolation,
92
+ u_dense: np.ndarray,
93
+ r: float,
94
+ b: float,
95
+ ) -> tuple[np.ndarray, np.ndarray, list[Quaternion]]:
96
+ """
97
+ Evaluate the helix positions, the LQI-interpolated orientation frames,
98
+ and the underlying interpolated quaternions at the dense parameter values.
99
+ """
100
+ dense_positions = np.zeros((len(u_dense), 3))
101
+ dense_frames = np.zeros((len(u_dense), 3, 3))
102
+ dense_quaternions: list[Quaternion] = []
103
+
104
+ for i, u in enumerate(u_dense):
105
+ dense_positions[i], _, _ = cylindrical_helix_with_derivatives(u, r=r, b=b)
106
+ q = lqi.evaluate(u)
107
+ dense_frames[i] = q.to_rotation_matrix()
108
+ dense_quaternions.append(q)
109
+
110
+ return dense_positions, dense_frames, dense_quaternions
111
+
112
+
113
+ def angular_error_deg(frame_truth: np.ndarray, frame_est: np.ndarray) -> float:
114
+ """
115
+ Geodesic angle (in degrees) between two rotation matrices.
116
+ """
117
+ r = frame_truth.T @ frame_est
118
+ cos_angle = np.clip(0.5 * (np.trace(r) - 1.0), -1.0, 1.0)
119
+ return np.degrees(np.arccos(cos_angle))
120
+
121
+
122
+ def main() -> None:
123
+ print("LQI on a Cylindrical Helix with Frenet Frame Waypoints")
124
+ print("=" * 60)
125
+
126
+ # Helix parameters
127
+ r = 1.0 # constant radius
128
+ b = 0.3 # vertical rise per radian
129
+ u_min = 0.5
130
+ u_max = 6.0 * np.pi
131
+ n_waypoints = 1000
132
+
133
+ # Sample waypoints + their Frenet frames.
134
+ times, quaternions, wp_positions, _wp_frames = build_waypoints(
135
+ n_waypoints=n_waypoints,
136
+ u_min=u_min,
137
+ u_max=u_max,
138
+ r=r,
139
+ b=b,
140
+ )
141
+
142
+ print(f"Sampled {n_waypoints} Frenet waypoints in u ∈ [{u_min:.2f}, {u_max:.2f}]")
143
+ print(f"Time/parameter range used for LQI: [{times[0]:.3f}, {times[-1]:.3f}]")
144
+
145
+ # Build the LQI interpolator over the waypoint orientations.
146
+ lqi = LogQuaternionInterpolation(
147
+ time_points=times,
148
+ quaternions=quaternions,
149
+ degree=3,
150
+ )
151
+
152
+ # Dense evaluation of position + interpolated orientation.
153
+ n_dense = 240
154
+ u_dense = np.linspace(u_min, u_max, n_dense)
155
+ dense_positions, dense_frames_lqi, dense_quaternions_lqi = (
156
+ evaluate_interpolated_trajectory(lqi, u_dense, r=r, b=b)
157
+ )
158
+
159
+ # Ground-truth Frenet frames on the same dense grid, for comparison.
160
+ def helix_func(u: float) -> tuple[np.ndarray, np.ndarray, np.ndarray]:
161
+ return cylindrical_helix_with_derivatives(u, r=r, b=b)
162
+
163
+ _, dense_frames_truth = compute_trajectory_frames(helix_func, u_dense)
164
+
165
+ # Report worst-case angular deviation between interpolated orientation and
166
+ # the analytical Frenet frame.
167
+ errors = np.array([
168
+ angular_error_deg(dense_frames_truth[i], dense_frames_lqi[i])
169
+ for i in range(n_dense)
170
+ ])
171
+ print(f"Mean angular error vs Frenet truth: {errors.mean():.3f} deg")
172
+ print(f"Max angular error vs Frenet truth: {errors.max():.3f} deg")
173
+
174
+ # ------------------------------------------------------------------
175
+ # Visualization
176
+ # ------------------------------------------------------------------
177
+ # Cap the number of waypoint markers actually drawn so the plots stay
178
+ # readable when n_waypoints is large (e.g. 1000). The interpolator still
179
+ # uses all of them — this only affects the visual overlay.
180
+ max_shown_waypoints = 30
181
+ if n_waypoints > max_shown_waypoints:
182
+ show_idx = np.linspace(0, n_waypoints - 1, max_shown_waypoints, dtype=int)
183
+ else:
184
+ show_idx = np.arange(n_waypoints)
185
+ wp_positions_shown = wp_positions[show_idx]
186
+ times_shown = times[show_idx]
187
+ quaternions_shown = [quaternions[i] for i in show_idx]
188
+
189
+ fig = plt.figure(figsize=(16, 7))
190
+
191
+ # Left panel: waypoints + interpolated LQI tool frames.
192
+ ax_left = fig.add_subplot(121, projection="3d")
193
+ plot_frames(ax_left, dense_positions, dense_frames_lqi, scale=0.6, skip=12)
194
+ ax_left.scatter(
195
+ wp_positions_shown[:, 0],
196
+ wp_positions_shown[:, 1],
197
+ wp_positions_shown[:, 2],
198
+ color="magenta",
199
+ s=40,
200
+ depthshade=False,
201
+ label=f"LQI waypoints ({len(show_idx)} of {n_waypoints} shown)",
202
+ )
203
+ ax_left.set_title("Helix + LQI-interpolated frames")
204
+ ax_left.set_xlabel("x")
205
+ ax_left.set_ylabel("y")
206
+ ax_left.set_zlabel("z")
207
+ ax_left.legend(loc="upper left")
208
+
209
+ # Right panel: ground-truth analytical Frenet frames on the dense grid.
210
+ ax_right = fig.add_subplot(122, projection="3d")
211
+ plot_frames(ax_right, dense_positions, dense_frames_truth, scale=0.6, skip=12)
212
+ ax_right.set_title("Analytical Frenet frames (reference)")
213
+ ax_right.set_xlabel("x")
214
+ ax_right.set_ylabel("y")
215
+ ax_right.set_zlabel("z")
216
+
217
+ for ax in (ax_left, ax_right):
218
+ ax.set_box_aspect([1, 1, 1])
219
+
220
+ plt.tight_layout()
221
+
222
+ # Quaternion trajectory in stereographic (MRP) projection space.
223
+ # This shows the orientation evolution in the *rotation* space rather than
224
+ # in the cartesian position space, with the input waypoints highlighted.
225
+ visualizer = QuaternionTrajectoryVisualizer()
226
+ visualizer.plot_3d_trajectory(
227
+ dense_quaternions_lqi,
228
+ waypoints=quaternions_shown,
229
+ waypoint_times=list(times_shown),
230
+ title="LQI quaternion trajectory (stereographic MRP projection)",
231
+ color="purple",
232
+ line_width=2.5,
233
+ point_size=15,
234
+ waypoint_color="magenta",
235
+ show_waypoint_labels=False,
236
+ figsize=(10, 8),
237
+ )
238
+
239
+ # Bottom: angular error along the curve.
240
+ _fig2, ax_err = plt.subplots(figsize=(10, 3.5))
241
+ ax_err.plot(u_dense, errors, color="purple", linewidth=2.0)
242
+ ax_err.fill_between(u_dense, errors, alpha=0.25, color="purple")
243
+ ax_err.set_xlabel("Parameter u")
244
+ ax_err.set_ylabel("Angular error [deg]")
245
+ ax_err.set_title("Geodesic deviation: LQI orientation vs Frenet truth")
246
+ ax_err.grid(True, alpha=0.3)
247
+ plt.tight_layout()
248
+
249
+ # ------------------------------------------------------------------
250
+ # Rotation-vector decomposition produced internally by LQI.
251
+ # LQI splines a single 3D vector r(u) = theta(u) * n_hat(u). We plot
252
+ # both the raw components r_x, r_y, r_z and the (theta, n_hat)
253
+ # decomposition recovered from |r| and r/|r|.
254
+ # ------------------------------------------------------------------
255
+ r_dense = np.array([lqi.bspline_interpolator.evaluate(u) for u in u_dense])
256
+ r_wp = np.array([lqi.bspline_interpolator.evaluate(u) for u in times_shown])
257
+
258
+ theta_dense = np.linalg.norm(r_dense, axis=1)
259
+ theta_wp = np.linalg.norm(r_wp, axis=1)
260
+
261
+ # Unit axis from r / |r|, with a safe fallback near theta ~ 0.
262
+ eps_axis = 1e-12
263
+ safe_theta_dense = np.where(theta_dense > eps_axis, theta_dense, 1.0)
264
+ nhat_dense = r_dense / safe_theta_dense[:, None]
265
+ nhat_dense[theta_dense <= eps_axis] = np.array([1.0, 0.0, 0.0])
266
+
267
+ safe_theta_wp = np.where(theta_wp > eps_axis, theta_wp, 1.0)
268
+ nhat_wp = r_wp / safe_theta_wp[:, None]
269
+ nhat_wp[theta_wp <= eps_axis] = np.array([1.0, 0.0, 0.0])
270
+
271
+ _fig3, axes3 = plt.subplots(4, 1, figsize=(11, 9), sharex=True)
272
+ component_labels = [r"$\theta = \|r\|$ [rad]", r"$\hat n_x$", r"$\hat n_y$", r"$\hat n_z$"]
273
+ component_data = [theta_dense, nhat_dense[:, 0], nhat_dense[:, 1], nhat_dense[:, 2]]
274
+ waypoint_data = [theta_wp, nhat_wp[:, 0], nhat_wp[:, 1], nhat_wp[:, 2]]
275
+ component_colors = ["tab:orange", "tab:red", "tab:green", "tab:blue"]
276
+
277
+ for ax, label, curve, wp_vals, c in zip(
278
+ axes3, component_labels, component_data, waypoint_data, component_colors
279
+ ):
280
+ ax.plot(u_dense, curve, color=c, linewidth=2.0, label="LQI")
281
+ ax.scatter(times_shown, wp_vals, color="magenta", s=25, zorder=5, label="waypoints")
282
+ ax.set_ylabel(label)
283
+ ax.grid(True, alpha=0.3)
284
+ ax.legend(loc="upper right", fontsize=8)
285
+
286
+ axes3[-1].set_xlabel("Parameter u")
287
+ axes3[0].set_title(
288
+ r"LQI internal state recovered from $r(u) = \theta(u)\,\hat n(u)$"
289
+ )
290
+ plt.tight_layout()
291
+
292
+ # Raw rotation-vector components r_x, r_y, r_z (what LQI actually splines).
293
+ _fig3b, axes3b = plt.subplots(3, 1, figsize=(11, 7), sharex=True)
294
+ raw_labels = [r"$r_x$", r"$r_y$", r"$r_z$"]
295
+ raw_colors = ["tab:red", "tab:green", "tab:blue"]
296
+ for ax, label, k, c in zip(axes3b, raw_labels, range(3), raw_colors):
297
+ ax.plot(u_dense, r_dense[:, k], color=c, linewidth=2.0, label="LQI")
298
+ ax.scatter(times_shown, r_wp[:, k], color="magenta", s=25, zorder=5, label="waypoints")
299
+ ax.set_ylabel(label)
300
+ ax.grid(True, alpha=0.3)
301
+ ax.legend(loc="upper right", fontsize=8)
302
+ axes3b[-1].set_xlabel("Parameter u")
303
+ axes3b[0].set_title(r"LQI spline state: rotation-vector components $r(u)$")
304
+ plt.tight_layout()
305
+
306
+ # ------------------------------------------------------------------
307
+ # Physical angular velocity (omega) and acceleration (alpha) in 3D.
308
+ # ------------------------------------------------------------------
309
+ omega = np.zeros((n_dense, 3))
310
+ alpha = np.zeros((n_dense, 3))
311
+ for i, u in enumerate(u_dense):
312
+ omega[i], alpha[i] = lqi.get_physical_kinematics(u)
313
+
314
+ omega_norm = np.linalg.norm(omega, axis=1)
315
+ alpha_norm = np.linalg.norm(alpha, axis=1)
316
+
317
+ _fig4, (ax_w, ax_a) = plt.subplots(2, 1, figsize=(11, 7), sharex=True)
318
+
319
+ for k, axis_name, c in zip(range(3), ("x", "y", "z"), ("tab:red", "tab:green", "tab:blue")):
320
+ ax_w.plot(u_dense, omega[:, k], color=c, linewidth=1.8, label=rf"$\omega_{axis_name}$")
321
+ ax_a.plot(u_dense, alpha[:, k], color=c, linewidth=1.8, label=rf"$\alpha_{axis_name}$")
322
+
323
+ ax_w.plot(u_dense, omega_norm, color="black", linewidth=1.2, linestyle="--", label=r"$\|\omega\|$")
324
+ ax_a.plot(u_dense, alpha_norm, color="black", linewidth=1.2, linestyle="--", label=r"$\|\alpha\|$")
325
+
326
+ ax_w.set_ylabel(r"Angular velocity $\omega$ [rad/u]")
327
+ ax_w.set_title("Physical angular velocity and acceleration from LQI")
328
+ ax_w.legend(loc="upper right", ncol=4, fontsize=9)
329
+ ax_w.grid(True, alpha=0.3)
330
+
331
+ ax_a.set_ylabel(r"Angular acceleration $\alpha$ [rad/u$^2$]")
332
+ ax_a.set_xlabel("Parameter u")
333
+ ax_a.legend(loc="upper right", ncol=4, fontsize=9)
334
+ ax_a.grid(True, alpha=0.3)
335
+
336
+ plt.tight_layout()
337
+
338
+ plt.show()
339
+
340
+
341
+ if __name__ == "__main__":
342
+ main()