openscvx 0.5.3.dev42__tar.gz → 0.5.3.dev43__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 (467) hide show
  1. {openscvx-0.5.3.dev42/openscvx.egg-info → openscvx-0.5.3.dev43}/PKG-INFO +1 -1
  2. openscvx-0.5.3.dev43/examples/drone/boresight_trace_mpcc.py +584 -0
  3. openscvx-0.5.3.dev43/examples/drone/logo_utils/circle.svg +3 -0
  4. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/drone/logo_utils/svg_path_utils.py +66 -1
  5. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/_version.py +3 -3
  6. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/plotting/viser/animated.py +4 -1
  7. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43/openscvx.egg-info}/PKG-INFO +1 -1
  8. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx.egg-info/SOURCES.txt +2 -0
  9. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx.egg-info/scm_file_list.json +2 -0
  10. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx.egg-info/scm_version.json +2 -2
  11. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/.github/assets/logo.svg +0 -0
  12. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/.github/release-drafter.yml +0 -0
  13. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/.github/workflows/_docs.yml +0 -0
  14. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/.github/workflows/_extra.yml +0 -0
  15. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/.github/workflows/branch-name.yml +0 -0
  16. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/.github/workflows/docs.yml +0 -0
  17. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/.github/workflows/lint.yml +0 -0
  18. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/.github/workflows/nightly.yml +0 -0
  19. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/.github/workflows/release-drafter.yml +0 -0
  20. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/.github/workflows/release.yml +0 -0
  21. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/.github/workflows/tests-cvxpygen.yml +0 -0
  22. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/.github/workflows/tests-examples.yml +0 -0
  23. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/.github/workflows/tests-lie.yml +0 -0
  24. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/.github/workflows/tests-mjx.yml +0 -0
  25. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/.github/workflows/tests-qpax.yml +0 -0
  26. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/.github/workflows/tests.yml +0 -0
  27. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/.gitignore +0 -0
  28. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/.gitmodules +0 -0
  29. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/CITATION.cff +0 -0
  30. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/CONTRIBUTING.md +0 -0
  31. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/LICENSE +0 -0
  32. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/README.md +0 -0
  33. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/docs/Foundations/constraint_reformulation.md +0 -0
  34. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/docs/Foundations/control_parameterization.md +0 -0
  35. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/docs/Foundations/discretization.md +0 -0
  36. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/docs/Foundations/ocp.md +0 -0
  37. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/docs/Foundations/scvx.md +0 -0
  38. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/docs/Foundations/time_dilation.md +0 -0
  39. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/docs/UnderTheHood/batching_jit_grad.md +0 -0
  40. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/docs/UnderTheHood/custom_algorithms_autotuners.md +0 -0
  41. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/docs/UnderTheHood/lowering_architecture.md +0 -0
  42. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/docs/UnderTheHood/vectorization_and_vmapping.md +0 -0
  43. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/docs/UsersGuide/00_introduction.md +0 -0
  44. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/docs/UsersGuide/01_hello_world_brachistochrone.md +0 -0
  45. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/docs/UsersGuide/02_drone_racing_constraints.md +0 -0
  46. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/docs/UsersGuide/03_obstacle_avoidance_vmap.md +0 -0
  47. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/docs/UsersGuide/04_viewpoint_constraints.md +0 -0
  48. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/docs/UsersGuide/05_visualization.md +0 -0
  49. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/docs/UsersGuide/06_logic.md +0 -0
  50. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/docs/UsersGuide/07_lie.md +0 -0
  51. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/docs/UsersGuide/08_mpcc.md +0 -0
  52. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/docs/UsersGuide/09_mjx_dynamics.md +0 -0
  53. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/docs/assets/favicon.png +0 -0
  54. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/docs/assets/images/ct-scvx_dark.png +0 -0
  55. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/docs/assets/images/ct-scvx_light.png +0 -0
  56. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/docs/assets/images/ctcs_dark.png +0 -0
  57. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/docs/assets/images/ctcs_light.png +0 -0
  58. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/docs/assets/images/problem_class_dark.png +0 -0
  59. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/docs/assets/images/problem_class_light.png +0 -0
  60. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/docs/assets/logo.svg +0 -0
  61. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/docs/assets/openscvx_logo_square.png +0 -0
  62. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/docs/assets/viser-client/index.html +0 -0
  63. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/docs/assets/viser-recordings/drone_racing.viser +0 -0
  64. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/docs/assets/viser-recordings/franka_fr3v2_pick_place.viser +0 -0
  65. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/docs/changelog.md +0 -0
  66. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/docs/citation.md +0 -0
  67. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/docs/examples.md +0 -0
  68. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/docs/index.md +0 -0
  69. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/docs/installation.md +0 -0
  70. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/docs/javascripts/mathjax.js +0 -0
  71. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/docs/llms.txt +0 -0
  72. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/docs/versions.json +0 -0
  73. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/_viser_embed_export.py +0 -0
  74. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/abstract/brachistochrone.py +0 -0
  75. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/abstract/brachistochrone_batched.py +0 -0
  76. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/abstract/flappy_bird.py +0 -0
  77. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/abstract/hypersensitive.py +0 -0
  78. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/abstract/impulsive.py +0 -0
  79. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/abstract/stl_integer_variable.py +0 -0
  80. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/abstract/stl_or.py +0 -0
  81. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/aircraft/dynamic_soaring.py +0 -0
  82. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/aircraft/supersonic_time_to_climb.py +0 -0
  83. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/animations/7_dof_arm.py +0 -0
  84. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/animations/_camera.py +0 -0
  85. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/animations/_render.py +0 -0
  86. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/animations/_sensor_view.py +0 -0
  87. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/animations/dr_vp_polytope.py +0 -0
  88. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/animations/franka_fr3v2_pick_place.py +0 -0
  89. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/animations/logo.py +0 -0
  90. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/animations/obstacle_avoidance_vmap.py +0 -0
  91. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/arm/3_dof_arm.py +0 -0
  92. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/arm/7_dof_arm.py +0 -0
  93. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/arm/7_dof_arm_collision.py +0 -0
  94. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/arm/7_dof_arm_vp.py +0 -0
  95. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/arm/franka_fr3v2_pick_place.py +0 -0
  96. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/arm/franka_fr3v2_viewplanning.py +0 -0
  97. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/arm/franka_fr3v2_viewplanning_nodal.py +0 -0
  98. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/car/dubins_car.py +0 -0
  99. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/car/dubins_car_disjoint.py +0 -0
  100. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/car/dubins_car_obstacle_conditional.py +0 -0
  101. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/car/dubins_car_obstacle_stl.py +0 -0
  102. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/car/dubins_car_stl_or.py +0 -0
  103. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/car/dubins_car_waypoint_stl.py +0 -0
  104. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/double_integrator/_plotting.py +0 -0
  105. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/double_integrator/moving_safe_zones.py +0 -0
  106. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/drone/2d_obstacle_avoidance_batched_ic.py +0 -0
  107. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/drone/cinema_vp.py +0 -0
  108. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/drone/cinema_vp_nodal.py +0 -0
  109. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/drone/dr_double_integrator.py +0 -0
  110. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/drone/dr_vp.py +0 -0
  111. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/drone/dr_vp_nodal.py +0 -0
  112. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/drone/dr_vp_polytope.py +0 -0
  113. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/drone/drone_racing.py +0 -0
  114. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/drone/drone_racing_batched_gates.py +0 -0
  115. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/drone/logo.py +0 -0
  116. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/drone/logo_utils/acl_logo.svg +0 -0
  117. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/drone/logo_utils/openscvx_logo_single.svg +0 -0
  118. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/drone/logo_utils/quadrotor_mesh.py +0 -0
  119. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/drone/multi_agent_circle_swap.py +0 -0
  120. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/drone/obstacle_avoidance.py +0 -0
  121. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/drone/obstacle_avoidance_nodal.py +0 -0
  122. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/drone/obstacle_avoidance_vmap.py +0 -0
  123. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/drone/obstacle_avoidance_vmap_2d.py +0 -0
  124. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/drone/openscvx_logo.py +0 -0
  125. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/frax/panda_frax.py +0 -0
  126. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/frax/panda_frax_pick_place.py +0 -0
  127. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/frax/panda_frax_viewplanning.py +0 -0
  128. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/frax/panda_frax_waypoint.py +0 -0
  129. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/frax/ur5e_assets/ur5e.urdf +0 -0
  130. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/frax/ur5e_pen_trace.py +0 -0
  131. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/mjx/cartpole_mjx.py +0 -0
  132. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/mjx/double_cartpole_mjx.py +0 -0
  133. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/mjx/skydio_x2_mjx.py +0 -0
  134. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/mjx/triple_cartpole_3d_mjx.py +0 -0
  135. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/mjx/triple_cartpole_game.py +0 -0
  136. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/mjx/triple_cartpole_mjx.py +0 -0
  137. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/mpc/double_integrator_discrete.py +0 -0
  138. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/mpc/double_integrator_drone_racing.py +0 -0
  139. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/mpc/dubins_car_circle_analytical.py +0 -0
  140. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/mpc/dubins_car_circle_discrete.py +0 -0
  141. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/mpc/realtime_double_integrator_drone_racing.py +0 -0
  142. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/plotting.py +0 -0
  143. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/plotting_viser.py +0 -0
  144. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/realtime/3DoF_pdg_realtime.py +0 -0
  145. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/realtime/6DoF_pdg_realtime.py +0 -0
  146. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/realtime/base_problems/3DoF_pdg_realtime_base.py +0 -0
  147. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/realtime/base_problems/6DoF_pdg_realtime_base.py +0 -0
  148. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/realtime/base_problems/cinema_vp_realtime_base.py +0 -0
  149. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/realtime/base_problems/drone_racing_realtime_base.py +0 -0
  150. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/realtime/base_problems/dubins_car_realtime_base.py +0 -0
  151. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/realtime/base_problems/obstacle_avoidance_realtime_base.py +0 -0
  152. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/realtime/cinema_vp_realtime.py +0 -0
  153. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/realtime/drone_racing_realtime.py +0 -0
  154. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/realtime/dubins_car_realtime.py +0 -0
  155. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/realtime/obstacle_avoidance_realtime.py +0 -0
  156. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/rocket/3DoF_pdg.py +0 -0
  157. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/rocket/6DoF_pdg.py +0 -0
  158. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/rocket/6DoF_pdg_batched_ic.py +0 -0
  159. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/rocket/ascent_launch_vehicle.py +0 -0
  160. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/spacecraft/halo_orbit.py +0 -0
  161. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/spacecraft/hohmann_transfer.py +0 -0
  162. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/spacecraft/let_transfer.py +0 -0
  163. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/spacecraft/proxops_cw.py +0 -0
  164. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/spacecraft/relative_loitering.py +0 -0
  165. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/figures/ctlos_cine.gif +0 -0
  166. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/figures/ctlos_dr.gif +0 -0
  167. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/figures/dtlos_cine.gif +0 -0
  168. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/figures/dtlos_dr.gif +0 -0
  169. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/figures/openscvx_logo.svg +0 -0
  170. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/figures/openscvx_logo_square.png +0 -0
  171. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/figures/oscvx_structure_full_dark.svg +0 -0
  172. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/figures/video_preview.png +0 -0
  173. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/material/__init__.py +0 -0
  174. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/material/overrides/assets/stylesheets/custom.css +0 -0
  175. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/material/overrides/assets/stylesheets/home-dropin.css +0 -0
  176. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/material/overrides/assets/stylesheets/home-hero.css +0 -0
  177. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/material/overrides/assets/stylesheets/home-viser.css +0 -0
  178. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/material/overrides/home.html +0 -0
  179. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/material/overrides/main.html +0 -0
  180. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/material/overrides/partials/home-diagram.html +0 -0
  181. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/material/overrides/partials/home-dropin-banner.html +0 -0
  182. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/material/overrides/partials/home-hero.html +0 -0
  183. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/material/overrides/partials/home-pipeline.html +0 -0
  184. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/material/overrides/partials/home-viser-strip.html +0 -0
  185. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/material/overrides/sitemap.xml +0 -0
  186. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/mkdocs.yml +0 -0
  187. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/__init__.py +0 -0
  188. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/__main__.py +0 -0
  189. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/algorithms/__init__.py +0 -0
  190. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/algorithms/autotuner/__init__.py +0 -0
  191. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/algorithms/autotuner/acceptance_ratio.py +0 -0
  192. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/algorithms/autotuner/adaptive_proximal_weight.py +0 -0
  193. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/algorithms/autotuner/augmented_lagrangian.py +0 -0
  194. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/algorithms/autotuner/base.py +0 -0
  195. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/algorithms/autotuner/constant_proximal_weight.py +0 -0
  196. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/algorithms/autotuner/ramp_proximal_weight.py +0 -0
  197. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/algorithms/base.py +0 -0
  198. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/algorithms/history.py +0 -0
  199. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/algorithms/hyperparams.py +0 -0
  200. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/algorithms/loop.py +0 -0
  201. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/algorithms/optimization_results.py +0 -0
  202. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/algorithms/penalty.py +0 -0
  203. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/algorithms/scvx/__init__.py +0 -0
  204. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/algorithms/scvx/iteration.py +0 -0
  205. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/algorithms/scvx/penalized_trust_region.py +0 -0
  206. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/algorithms/state.py +0 -0
  207. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/algorithms/weights.py +0 -0
  208. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/config.py +0 -0
  209. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/discretization/__init__.py +0 -0
  210. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/discretization/base.py +0 -0
  211. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/discretization/discretize_linearize.py +0 -0
  212. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/discretization/linearize_discretize.py +0 -0
  213. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/discretization/linearize_discretize_sparse.py +0 -0
  214. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/discretization/sparse_utils/__init__.py +0 -0
  215. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/discretization/sparse_utils/bcoo_helpers.py +0 -0
  216. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/discretization/sparse_utils/sparse_jacobian.py +0 -0
  217. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/expert/__init__.py +0 -0
  218. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/expert/byof.py +0 -0
  219. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/expert/lowering.py +0 -0
  220. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/expert/validation.py +0 -0
  221. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/init/__init__.py +0 -0
  222. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/init/interpolation.py +0 -0
  223. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/init/inverse_kinematics.py +0 -0
  224. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/integrations/__init__.py +0 -0
  225. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/integrations/_utils.py +0 -0
  226. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/integrations/base.py +0 -0
  227. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/integrations/frax.py +0 -0
  228. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/integrations/menagerie.py +0 -0
  229. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/integrations/mjx.py +0 -0
  230. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/integrators/__init__.py +0 -0
  231. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/integrators/diffrax.py +0 -0
  232. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/integrators/runge_kutta.py +0 -0
  233. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/loader.py +0 -0
  234. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/lowered/__init__.py +0 -0
  235. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/lowered/cvxpy_constraints.py +0 -0
  236. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/lowered/cvxpy_variables.py +0 -0
  237. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/lowered/dynamics.py +0 -0
  238. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/lowered/jax_constraints.py +0 -0
  239. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/lowered/parameters.py +0 -0
  240. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/lowered/problem.py +0 -0
  241. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/lowered/unified.py +0 -0
  242. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/plotting/__init__.py +0 -0
  243. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/plotting/plotting.py +0 -0
  244. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/plotting/publication.py +0 -0
  245. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/plotting/scp_iteration.py +0 -0
  246. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/plotting/viser/__init__.py +0 -0
  247. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/plotting/viser/coordinates.py +0 -0
  248. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/plotting/viser/orbits.py +0 -0
  249. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/plotting/viser/plotly_integration.py +0 -0
  250. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/plotting/viser/primitives.py +0 -0
  251. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/plotting/viser/scp.py +0 -0
  252. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/plotting/viser/server.py +0 -0
  253. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/problem.py +0 -0
  254. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/propagation/__init__.py +0 -0
  255. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/propagation/post_processing.py +0 -0
  256. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/propagation/propagation.py +0 -0
  257. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/solvers/__init__.py +0 -0
  258. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/solvers/base.py +0 -0
  259. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/solvers/cones.py +0 -0
  260. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/solvers/cvxpy_ptr_solver.py +0 -0
  261. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/solvers/moreau_ptr_solver.py +0 -0
  262. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/solvers/ptr_solver.py +0 -0
  263. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/solvers/qpax_ptr_solver.py +0 -0
  264. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/__init__.py +0 -0
  265. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/affine.py +0 -0
  266. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/augmentation.py +0 -0
  267. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/builder.py +0 -0
  268. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/canonicalize.py +0 -0
  269. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/constraint_set.py +0 -0
  270. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/expr/__init__.py +0 -0
  271. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/expr/arithmetic.py +0 -0
  272. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/expr/array.py +0 -0
  273. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/expr/constraint.py +0 -0
  274. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/expr/control.py +0 -0
  275. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/expr/expr.py +0 -0
  276. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/expr/lie/__init__.py +0 -0
  277. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/expr/lie/adjoint.py +0 -0
  278. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/expr/lie/se3.py +0 -0
  279. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/expr/lie/so3.py +0 -0
  280. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/expr/linalg.py +0 -0
  281. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/expr/logic.py +0 -0
  282. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/expr/math.py +0 -0
  283. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/expr/parameter.py +0 -0
  284. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/expr/spatial.py +0 -0
  285. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/expr/state.py +0 -0
  286. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/expr/stl.py +0 -0
  287. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/expr/stljax.py +0 -0
  288. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/expr/time.py +0 -0
  289. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/expr/variable.py +0 -0
  290. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/expr/vmap.py +0 -0
  291. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/hashing.py +0 -0
  292. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/lower.py +0 -0
  293. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/lowerers/__init__.py +0 -0
  294. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/lowerers/cvxpy/__init__.py +0 -0
  295. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/lowerers/cvxpy/_lowerer.py +0 -0
  296. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/lowerers/cvxpy/_registry.py +0 -0
  297. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/lowerers/cvxpy/arithmetic.py +0 -0
  298. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/lowerers/cvxpy/array.py +0 -0
  299. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/lowerers/cvxpy/constraint.py +0 -0
  300. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/lowerers/cvxpy/control.py +0 -0
  301. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/lowerers/cvxpy/expr.py +0 -0
  302. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/lowerers/cvxpy/linalg.py +0 -0
  303. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/lowerers/cvxpy/logic.py +0 -0
  304. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/lowerers/cvxpy/math.py +0 -0
  305. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/lowerers/cvxpy/state.py +0 -0
  306. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/lowerers/jax/__init__.py +0 -0
  307. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/lowerers/jax/_lowerer.py +0 -0
  308. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/lowerers/jax/_registry.py +0 -0
  309. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/lowerers/jax/arithmetic.py +0 -0
  310. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/lowerers/jax/array.py +0 -0
  311. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/lowerers/jax/constraint.py +0 -0
  312. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/lowerers/jax/control.py +0 -0
  313. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/lowerers/jax/expr.py +0 -0
  314. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/lowerers/jax/lie.py +0 -0
  315. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/lowerers/jax/linalg.py +0 -0
  316. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/lowerers/jax/logic.py +0 -0
  317. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/lowerers/jax/math.py +0 -0
  318. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/lowerers/jax/spatial.py +0 -0
  319. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/lowerers/jax/state.py +0 -0
  320. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/lowerers/jax/stl.py +0 -0
  321. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/lowerers/jax/stljax.py +0 -0
  322. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/lowerers/jax/vmap.py +0 -0
  323. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/lowerers/latex/__init__.py +0 -0
  324. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/lowerers/latex/_lowerer.py +0 -0
  325. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/lowerers/latex/_registry.py +0 -0
  326. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/lowerers/latex/arithmetic.py +0 -0
  327. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/lowerers/latex/array.py +0 -0
  328. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/lowerers/latex/constraint.py +0 -0
  329. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/lowerers/latex/control.py +0 -0
  330. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/lowerers/latex/expr.py +0 -0
  331. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/lowerers/latex/formulation.py +0 -0
  332. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/lowerers/latex/lie.py +0 -0
  333. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/lowerers/latex/linalg.py +0 -0
  334. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/lowerers/latex/logic.py +0 -0
  335. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/lowerers/latex/math.py +0 -0
  336. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/lowerers/latex/spatial.py +0 -0
  337. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/lowerers/latex/state.py +0 -0
  338. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/lowerers/latex/stl.py +0 -0
  339. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/lowerers/latex/vmap.py +0 -0
  340. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/parser/__init__.py +0 -0
  341. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/parser/_registry.py +0 -0
  342. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/parser/array.py +0 -0
  343. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/parser/constraint.py +0 -0
  344. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/parser/lie.py +0 -0
  345. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/parser/linalg.py +0 -0
  346. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/parser/logic.py +0 -0
  347. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/parser/math.py +0 -0
  348. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/parser/parser.py +0 -0
  349. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/parser/spatial.py +0 -0
  350. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/parser/stl.py +0 -0
  351. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/parser/stljax.py +0 -0
  352. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/parser/tokenizer.py +0 -0
  353. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/preprocessing.py +0 -0
  354. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/problem.py +0 -0
  355. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/sparsity.py +0 -0
  356. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/unified.py +0 -0
  357. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/utils/__init__.py +0 -0
  358. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/utils/cache.py +0 -0
  359. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/utils/caching.py +0 -0
  360. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/utils/printing.py +0 -0
  361. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/utils/profiling.py +0 -0
  362. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/utils/utils.py +0 -0
  363. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx.egg-info/dependency_links.txt +0 -0
  364. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx.egg-info/entry_points.txt +0 -0
  365. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx.egg-info/requires.txt +0 -0
  366. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx.egg-info/top_level.txt +0 -0
  367. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/pyproject.toml +0 -0
  368. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/scripts/gen_example_pages.py +0 -0
  369. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/scripts/gen_ref_pages.py +0 -0
  370. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/scripts/mkdocs_copy_viser_client_hook.py +0 -0
  371. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/scripts/mkdocs_seo_hook.py +0 -0
  372. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/setup.cfg +0 -0
  373. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/__init__.py +0 -0
  374. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/algorithms/__init__.py +0 -0
  375. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/algorithms/_iteration_helpers.py +0 -0
  376. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/algorithms/autotuner/__init__.py +0 -0
  377. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/algorithms/autotuner/test_defaults_agreement.py +0 -0
  378. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/algorithms/autotuner/test_hyper_overrides.py +0 -0
  379. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/algorithms/autotuner/test_update_weights_jit.py +0 -0
  380. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/algorithms/test_algorithm_state_pytree.py +0 -0
  381. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/algorithms/test_iteration_fn_jit.py +0 -0
  382. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/algorithms/test_iteration_fn_parity.py +0 -0
  383. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/algorithms/test_make_solve_loop.py +0 -0
  384. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/brachistochrone_analytical.py +0 -0
  385. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/conftest.py +0 -0
  386. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/e2e/__init__.py +0 -0
  387. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/e2e/test_brachistochrone.py +0 -0
  388. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/e2e/test_impulsive.py +0 -0
  389. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/e2e/test_multishot_propagation.py +0 -0
  390. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/e2e/test_solve_batched_brachistochrone.py +0 -0
  391. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/e2e/test_solve_batched_cvxpy_export_errors.py +0 -0
  392. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/e2e/test_solve_batched_export_roundtrip.py +0 -0
  393. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/e2e/test_solve_jax_bare_brachistochrone.py +0 -0
  394. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/e2e/test_solve_jax_jit_brachistochrone.py +0 -0
  395. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/e2e/test_solve_jax_vmap_brachistochrone.py +0 -0
  396. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/e2e/test_solve_jax_vmap_converged_no_drift.py +0 -0
  397. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/fixtures/brachistochrone.json +0 -0
  398. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/fixtures/brachistochrone.yaml +0 -0
  399. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/hohmann_analytical.py +0 -0
  400. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/integrations/__init__.py +0 -0
  401. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/integrations/test_mjx.py +0 -0
  402. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/integrations/test_mjx_dynamics.py +0 -0
  403. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/solvers/__init__.py +0 -0
  404. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/solvers/_iteration_callback_helpers.py +0 -0
  405. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/solvers/test_cvxpy_callback_jit_spike.py +0 -0
  406. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/solvers/test_iteration_callback_cvxpy.py +0 -0
  407. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/solvers/test_iteration_callback_moreau.py +0 -0
  408. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/solvers/test_iteration_callback_qpax.py +0 -0
  409. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/solvers/test_iteration_callback_vmap.py +0 -0
  410. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/solvers/test_moreau_ptr_solver.py +0 -0
  411. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/solvers/test_qpax_ptr_solver.py +0 -0
  412. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/solvers/test_subproblem_pytree.py +0 -0
  413. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/symbolic/__init__.py +0 -0
  414. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/symbolic/expr/__init__.py +0 -0
  415. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/symbolic/expr/test_arithmetic.py +0 -0
  416. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/symbolic/expr/test_array.py +0 -0
  417. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/symbolic/expr/test_constraint.py +0 -0
  418. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/symbolic/expr/test_expr.py +0 -0
  419. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/symbolic/expr/test_lie.py +0 -0
  420. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/symbolic/expr/test_linalg.py +0 -0
  421. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/symbolic/expr/test_logic.py +0 -0
  422. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/symbolic/expr/test_math.py +0 -0
  423. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/symbolic/expr/test_node_reference.py +0 -0
  424. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/symbolic/expr/test_parameters.py +0 -0
  425. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/symbolic/expr/test_scaling.py +0 -0
  426. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/symbolic/expr/test_spatial.py +0 -0
  427. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/symbolic/expr/test_stl.py +0 -0
  428. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/symbolic/expr/test_variable.py +0 -0
  429. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/symbolic/expr/test_vmap.py +0 -0
  430. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/symbolic/parser/__init__.py +0 -0
  431. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/symbolic/parser/test_array.py +0 -0
  432. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/symbolic/parser/test_constraint.py +0 -0
  433. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/symbolic/parser/test_lie.py +0 -0
  434. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/symbolic/parser/test_linalg.py +0 -0
  435. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/symbolic/parser/test_load.py +0 -0
  436. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/symbolic/parser/test_logic.py +0 -0
  437. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/symbolic/parser/test_math.py +0 -0
  438. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/symbolic/parser/test_parser.py +0 -0
  439. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/symbolic/parser/test_spatial.py +0 -0
  440. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/symbolic/parser/test_stl.py +0 -0
  441. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/symbolic/parser/test_tokenizer.py +0 -0
  442. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/symbolic/parser/test_vmap.py +0 -0
  443. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/symbolic/test_augmentation.py +0 -0
  444. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/symbolic/test_gmsr.py +0 -0
  445. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/symbolic/test_hashing.py +0 -0
  446. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/symbolic/test_latex_formulation.py +0 -0
  447. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/symbolic/test_lower_cvxpy.py +0 -0
  448. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/symbolic/test_lower_jax.py +0 -0
  449. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/symbolic/test_lower_latex.py +0 -0
  450. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/symbolic/test_preprocessing.py +0 -0
  451. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/symbolic/test_sparsity.py +0 -0
  452. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/symbolic/test_unified.py +0 -0
  453. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/test_autotuning.py +0 -0
  454. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/test_cvxpygen_optional.py +0 -0
  455. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/test_discretization.py +0 -0
  456. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/test_examples.py +0 -0
  457. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/test_expert.py +0 -0
  458. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/test_init.py +0 -0
  459. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/test_initialize_syncs_guesses.py +0 -0
  460. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/test_integrators.py +0 -0
  461. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/test_loader.py +0 -0
  462. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/test_optimization_results.py +0 -0
  463. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/test_plotting.py +0 -0
  464. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/test_post_process_batched_tau_clamp.py +0 -0
  465. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/test_propagation.py +0 -0
  466. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/test_results_parameters.py +0 -0
  467. {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/test_solve_batched_inference.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: openscvx
3
- Version: 0.5.3.dev42
3
+ Version: 0.5.3.dev43
4
4
  Summary: A general Python-based successive convexification implementation which uses a JAX backend.
5
5
  Author-email: Chris Hayner and Griffin Norris <haynec@uw.edu>
6
6
  License: Apache Software License
@@ -0,0 +1,584 @@
1
+ """MPCC SVG tracing: a quadrotor draws an SVG path with its camera boresight.
2
+
3
+ This example unifies two ideas from the examples suite:
4
+
5
+ - ``drone/openscvx_logo.py`` — a 6-DOF quadrotor whose boresight traces the
6
+ OpenSCvx logo, with the target point parametrized by *time*
7
+ - ``mpc/double_integrator_drone_racing.py`` — model-predictive contouring
8
+ control (MPCC, [Romero et al. 2022](https://arxiv.org/abs/2108.13205)),
9
+ where progress along a reference path is a state the optimizer advances
10
+
11
+ Parametrizing the logo target by time couples the node count to the path
12
+ complexity (the one-shot version needs 500 nodes). Here the target is
13
+ parametrized by *arc length* instead: a cubic spline (``ox.Cinterp``) maps a
14
+ progress state to a point on the SVG path, and a progress-rate control
15
+ advances it as fast as drawing accuracy allows while a short receding horizon
16
+ retraces the path chunk by chunk. The "pen" is the point where the camera
17
+ boresight pierces the drawing plane, and the contouring cost — the pen's
18
+ squared distance to the path point at the current progress — is fully
19
+ symbolic, spline lookup included: no bring-your-own-function dynamics. Any
20
+ single-path SVG works unchanged (the OpenSCvx wordmark by default):
21
+
22
+ python boresight_trace_mpcc.py [path/to/drawing.svg]
23
+
24
+ Requires:
25
+ pip install svgpathtools
26
+ """
27
+
28
+ import os
29
+ import sys
30
+
31
+ import numpy as np
32
+
33
+ current_dir = os.path.dirname(os.path.abspath(__file__))
34
+ grandparent_dir = os.path.dirname(os.path.dirname(current_dir))
35
+ sys.path.append(grandparent_dir)
36
+
37
+ import openscvx as ox
38
+ from examples.drone.logo_utils.svg_path_utils import extract_svg_arc_length_path
39
+ from openscvx import Problem
40
+
41
+ ###############################################################################
42
+ # Reference path: SVG -> arc-length-parametrized curve in a vertical plane
43
+ ###############################################################################
44
+
45
+ _DEFAULT_SVG = os.path.join(current_dir, "logo_utils", "openscvx_logo_single.svg")
46
+ svg_file = sys.argv[1] if len(sys.argv) > 1 else _DEFAULT_SVG
47
+
48
+ path_width = 10.0 # Larger dimension of the drawing [m]
49
+ path_center = np.array([1.0, 0.0, 5.0]) # Center of the drawing plane
50
+ hover_pos = np.array([-5.0, 0.0, 5.0]) # Drone hovers here, looking +x at the plane
51
+
52
+ # Path 0 is the drawing in both bundled SVGs (later paths are background rects)
53
+ s_path, planar_points = extract_svg_arc_length_path(svg_file, path_indices=[0], width=path_width)
54
+ total_arc_length = s_path[-1]
55
+
56
+ # Lift the planar curve into the world: the drawing spans the y-z plane at
57
+ # x = path_center[0], with the SVG's y-axis pointing up (world z).
58
+ path_points = path_center + np.column_stack(
59
+ [np.zeros(len(s_path)), planar_points[:, 0], planar_points[:, 1]]
60
+ )
61
+
62
+ # Spline tables extend the reference past the true end by more than one horizon
63
+ # of pen travel, holding it parked at the final path point. The progress reward
64
+ # keeps pulling the progress state forward — without the extension it saturates
65
+ # as the horizon runs out of table and the endgame dissolves into a hovering
66
+ # scribble — but the reference *point* stays put, so the pen decelerates into
67
+ # the endpoint and settles there instead of chasing a fictitious continuation
68
+ # off the wordmark. The loop stops as the pen crosses the true finish, so the
69
+ # parked tail collapses onto the endpoint rather than trailing ink past it.
70
+ pad_length = 10.0 # [m]
71
+ _ds = s_path[1] - s_path[0]
72
+ _s_pad = np.arange(1, int(pad_length / _ds)) * _ds
73
+ s_ref = np.concatenate([s_path, total_arc_length + _s_pad])
74
+ ref_points = np.concatenate([path_points, np.tile(path_points[-1], (len(_s_pad), 1))])
75
+ padded_arc_length = s_ref[-1]
76
+
77
+
78
+ def ref_point(s):
79
+ """Numeric reference lookup used for guesses and analysis (matches Cinterp data)."""
80
+ return np.stack([np.interp(s, s_ref, ref_points[:, i]) for i in range(3)], axis=-1)
81
+
82
+
83
+ boresight_body = np.array([1.0, 0.0, 0.0]) # Boresight is the body x-axis
84
+
85
+
86
+ def look_at(from_pos, target):
87
+ """Quaternion [w, x, y, z] rotating the body boresight onto ``target - from_pos``."""
88
+ a = target - from_pos
89
+ a = a / np.linalg.norm(a)
90
+ q = np.concatenate([[1.0 + boresight_body @ a], np.cross(boresight_body, a)])
91
+ return q / np.linalg.norm(q)
92
+
93
+
94
+ ###############################################################################
95
+ # MPCC problem: 6-DOF quadrotor + progress along the path
96
+ ###############################################################################
97
+
98
+ n_mpc = 13 # Horizon nodes
99
+ horizon_duration = 2.4 # Horizon length [s]
100
+ dt_mpc = horizon_duration / (n_mpc - 1) # Executed step [s]
101
+
102
+ Q_TRACE = 4e1 # Contouring error weight (squared trace-vs-path distance)
103
+ Q_PROGRESS = 1e-1 # Progress reward
104
+ Q_SMOOTH = 2e-1 # Acceleration (thrust tilt) penalty: calms the airframe
105
+
106
+ trace_rate_max = 1.5 # Max speed of the traced point along the path [m/s]
107
+ trace_rate_guess = 1.0
108
+
109
+ m = 1.0 # Mass of the drone
110
+ g_const = -9.81
111
+ J_b = np.array([0.01, 0.01, 0.02]) # Body inertia of a 1 kg racing quad [kg m^2]
112
+
113
+ # --- States ---
114
+ # The x band holds a healthy standoff from the canvas: too close and the
115
+ # pointing geometry demands wild attitude swings, too far and every milliradian
116
+ # of attitude wobble smears centimeters of ink. The y-z room generously covers
117
+ # the drawing so the drone can track the pen's height and never gets cornered.
118
+ position = ox.State("position", shape=(3,))
119
+ position.max = np.array([-4.0, 7.0, 11.0])
120
+ position.min = np.array([-7.5, -7.0, 0.5])
121
+ position.initial = hover_pos
122
+ position.final = [ox.Free(0.0), ox.Free(0.0), ox.Free(0.0)]
123
+
124
+ velocity = ox.State("velocity", shape=(3,))
125
+ velocity.max = np.array([10.0, 10.0, 10.0])
126
+ velocity.min = np.array([-10.0, -10.0, -10.0])
127
+ velocity.initial = np.zeros(3)
128
+ velocity.final = [ox.Free(0.0), ox.Free(0.0), ox.Free(0.0)]
129
+
130
+ attitude = ox.State("attitude", shape=(4,)) # Quaternion [w, x, y, z]
131
+ attitude.max = np.ones(4)
132
+ attitude.min = -np.ones(4)
133
+ attitude.initial = look_at(hover_pos, ref_point(0.0))
134
+ attitude.final = [ox.Free(0.0)] * 4
135
+
136
+ angular_velocity = ox.State("angular_velocity", shape=(3,))
137
+ angular_velocity.max = np.array([6.0, 6.0, 6.0])
138
+ angular_velocity.min = np.array([-6.0, -6.0, -6.0])
139
+ angular_velocity.initial = np.zeros(3)
140
+ angular_velocity.final = [ox.Free(0.0)] * 3
141
+
142
+ progress = ox.State("progress", shape=(1,)) # Arc length along the SVG path
143
+ progress.min = np.array([0.0])
144
+ progress.max = np.array([padded_arc_length])
145
+ progress.initial = np.array([0.0])
146
+ progress.final = [ox.Maximize(0.0)]
147
+
148
+ trace_error = ox.State("trace_error", shape=(1,)) # Integrated squared trace error
149
+ trace_error.min = np.array([0.0])
150
+ trace_error.max = np.array([2.0])
151
+ trace_error.initial = np.array([0.0])
152
+ trace_error.final = [ox.Minimize(0.0)]
153
+
154
+ smoothness = ox.State("smoothness", shape=(1,)) # Integrated squared acceleration
155
+ smoothness.min = np.array([0.0])
156
+ smoothness.max = np.array([100.0])
157
+ smoothness.initial = np.array([0.0])
158
+ smoothness.final = [ox.Minimize(0.0)]
159
+
160
+ # --- Controls ---
161
+ thrust_force = ox.Control("thrust_force", shape=(3,)) # Body-frame thrust (z only)
162
+ thrust_force.max = np.array([0.0, 0.0, 4.179446268 * 9.81])
163
+ thrust_force.min = np.array([0.0, 0.0, 0.0])
164
+
165
+ torque = ox.Control("torque", shape=(3,))
166
+ torque.max = np.array([1.0, 1.0, 0.3])
167
+ torque.min = np.array([-1.0, -1.0, -0.3])
168
+
169
+ trace_rate = ox.Control("trace_rate", shape=(1,)) # d(progress)/dt
170
+ trace_rate.min = np.array([0.0]) # The pen only moves forward
171
+ trace_rate.max = np.array([trace_rate_max])
172
+
173
+ # --- Contouring error (fully symbolic via spline lookup of the path) ---
174
+ # The "pen" is where the boresight ray pierces the drawing plane; the error is
175
+ # its distance to the path point at the current progress. Penalizing the pen
176
+ # (rather than a pointing angle) makes the optimizer orchestrate position and
177
+ # attitude together — a pitched quadrotor necessarily accelerates sideways, so
178
+ # drawing accurately while hovering is a genuinely coupled 6-DOF problem.
179
+ p_ref = ox.Concat(
180
+ ox.Cinterp(progress[0], s_ref, ref_points[:, 0]),
181
+ ox.Cinterp(progress[0], s_ref, ref_points[:, 1]),
182
+ ox.Cinterp(progress[0], s_ref, ref_points[:, 2]),
183
+ )
184
+
185
+ # Guarded denominators: SCP iterates can wander through states the constraints
186
+ # only push back on softly (q near 0, boresight grazing the plane), and the
187
+ # linearization must stay finite there. Both guards are inactive at any
188
+ # solution the constraints accept.
189
+ attitude_normalized = attitude / ox.Sqrt(ox.Sum(attitude * attitude) + 1e-8)
190
+ boresight = ox.spatial.QDCM(attitude_normalized) @ ox.Constant(boresight_body)
191
+
192
+ pen = position + boresight * ((path_center[0] - position[0]) / ox.Max(boresight[0], 0.1))
193
+ pen_error = pen - p_ref
194
+ trace_cost = ox.Sum(pen_error * pen_error)
195
+
196
+ # --- Dynamics ---
197
+ # The smoothness integrand prices acceleration (tilt-induced lurching that
198
+ # drags the pen off the path) and velocity (damping that keeps the
199
+ # receding-horizon loop from building oscillations step over step). Velocity is
200
+ # weighted 3x: a becalmed airframe holds the pen on the line far more than it
201
+ # slows the pen's pace, so the heavier damping cuts trace error without
202
+ # stretching the flight. Together with the progress reward this sets the cruise
203
+ # pace of the pen.
204
+ J_b_diag = ox.linalg.Diag(J_b)
205
+ acceleration = (1.0 / m) * ox.spatial.QDCM(attitude_normalized) @ thrust_force + np.array(
206
+ [0.0, 0.0, g_const]
207
+ )
208
+ dynamics = {
209
+ "position": velocity,
210
+ "velocity": acceleration,
211
+ "attitude": 0.5 * ox.spatial.SSMP(angular_velocity) @ attitude_normalized,
212
+ "angular_velocity": ox.linalg.Diag(1.0 / J_b)
213
+ @ (torque - ox.spatial.SSM(angular_velocity) @ J_b_diag @ angular_velocity),
214
+ "progress": trace_rate,
215
+ "trace_error": trace_cost,
216
+ "smoothness": ox.Sum(acceleration * acceleration) + 3.0 * ox.Sum(velocity * velocity),
217
+ }
218
+
219
+ # --- Constraints ---
220
+ states = [position, velocity, attitude, angular_velocity, progress, trace_error, smoothness]
221
+ controls = [thrust_force, torque, trace_rate]
222
+
223
+ constraints = []
224
+ for state in [position, velocity, angular_velocity]:
225
+ constraints.extend([ox.ctcs(state <= state.max), ox.ctcs(state.min <= state)])
226
+
227
+ # Keep the camera facing the canvas (also keeps the pen intersection well-posed)
228
+ constraints.append(ox.ctcs(0.2 <= boresight[0]))
229
+
230
+ # --- Time: fixed horizon with a pinned first step (the executed segment) ---
231
+ # Dilation bounds keep the node spacing near-uniform: freely stretched
232
+ # segments integrate saturated dynamics over long intervals and blow up
233
+ t = ox.Time(
234
+ initial=0.0,
235
+ final=horizon_duration,
236
+ min=0.0,
237
+ max=horizon_duration,
238
+ time_dilation_min=0.5 * horizon_duration,
239
+ time_dilation_max=2.0 * horizon_duration,
240
+ )
241
+ constraints.append((t == dt_mpc).convex().at(1))
242
+
243
+
244
+ ###############################################################################
245
+ # Initial guess and warm-starting
246
+ ###############################################################################
247
+
248
+
249
+ def set_initial_guess():
250
+ """Hover at the start location, boresight swept along the first path chunk."""
251
+ arc_guess = np.linspace(0.0, trace_rate_guess * horizon_duration, n_mpc)
252
+ position.guess = np.tile(hover_pos, (n_mpc, 1))
253
+ velocity.guess = np.zeros((n_mpc, 3))
254
+ attitude.guess = np.array([look_at(hover_pos, ref_point(s)) for s in arc_guess])
255
+ attitude.initial = attitude.guess[0]
256
+ angular_velocity.guess = np.zeros((n_mpc, 3))
257
+ progress.guess = arc_guess.reshape(-1, 1)
258
+ trace_error.guess = np.zeros((n_mpc, 1))
259
+ smoothness.guess = np.zeros((n_mpc, 1))
260
+
261
+ thrust_force.guess = np.tile([0.0, 0.0, -m * g_const], (n_mpc, 1))
262
+ torque.guess = np.zeros((n_mpc, 3))
263
+ trace_rate.guess = np.full((n_mpc, 1), trace_rate_guess)
264
+
265
+
266
+ def update_initial_conditions(nodes: dict):
267
+ """Set initial conditions from node 1 of the previous solution (simulate one step).
268
+
269
+ Solutions can sit exactly on a state bound, so clip away the solver's
270
+ float roundoff before handing the values back as fixed initial conditions.
271
+ """
272
+ for state in [position, velocity, attitude, angular_velocity, progress]:
273
+ state.initial = np.clip(nodes[state.name][1], state.min, state.max)
274
+ # Cost integrators restart from zero each horizon
275
+ trace_error.initial = np.array([0.0])
276
+ smoothness.initial = np.array([0.0])
277
+
278
+
279
+ def shift_guess(nodes: dict):
280
+ """Shift the previous solution by one node; extend the tip with a hover prior.
281
+
282
+ The freshly revealed tip node points its attitude along the path at the
283
+ extended progress but is otherwise seeded as a calm hover — zero velocity,
284
+ hover thrust, zero torque, position held from the last node. Repeating the
285
+ last node's momentum instead extrapolates any drift into the new node,
286
+ which the receding-horizon loop can amplify step over step; a hover prior
287
+ lets the optimizer discover the motion the path actually needs.
288
+ """
289
+ ext_progress = min(
290
+ nodes["progress"][-1, 0] + nodes["trace_rate"][-1, 0] * dt_mpc, padded_arc_length
291
+ )
292
+ ext_attitude = look_at(nodes["position"][-1], ref_point(ext_progress))
293
+
294
+ position.guess = np.vstack([nodes["position"][1:], nodes["position"][-1:]])
295
+ velocity.guess = np.vstack([nodes["velocity"][1:], np.zeros((1, 3))])
296
+ attitude.guess = np.vstack([nodes["attitude"][1:], [ext_attitude]])
297
+ angular_velocity.guess = np.vstack([nodes["angular_velocity"][1:], np.zeros((1, 3))])
298
+ progress.guess = np.vstack([nodes["progress"][1:], [[ext_progress]]])
299
+
300
+ for integrator, key in [(trace_error, "trace_error"), (smoothness, "smoothness")]:
301
+ offset = nodes[key][1]
302
+ integrator.guess = np.maximum(
303
+ np.vstack([nodes[key][1:] - offset, nodes[key][-1:] - offset]), 0.0
304
+ )
305
+
306
+ thrust_force.guess = np.vstack([nodes["thrust_force"][1:], [[0.0, 0.0, -m * g_const]]])
307
+ torque.guess = np.vstack([nodes["torque"][1:], np.zeros((1, 3))])
308
+ trace_rate.guess = np.vstack([nodes["trace_rate"][1:], nodes["trace_rate"][-1:]])
309
+
310
+ # Time: shift and renormalize so the horizon starts at t = 0
311
+ ext_time = nodes["time"][-1, 0] + nodes["_time_dilation"][-1, 0] * (1.0 / (n_mpc - 1))
312
+ shifted_time = np.vstack([nodes["time"][1:], [[ext_time]]])
313
+ t.guess = shifted_time - shifted_time[0]
314
+ t._time_dilation_control.guess = np.vstack(
315
+ [nodes["_time_dilation"][1:], nodes["_time_dilation"][-1:]]
316
+ )
317
+
318
+
319
+ set_initial_guess()
320
+
321
+ problem = Problem(
322
+ dynamics=dynamics,
323
+ states=states,
324
+ controls=controls,
325
+ time=t,
326
+ constraints=constraints,
327
+ N=n_mpc,
328
+ algorithm={
329
+ "autotuner": ox.ConstantProximalWeight(),
330
+ "lam_cost": {"trace_error": Q_TRACE, "progress": Q_PROGRESS, "smoothness": Q_SMOOTH},
331
+ "lam_prox": 3e1, # Strong anchoring: weak prox never converges here
332
+ "lam_vc": 1e4, # Keep dynamics honest: far above the largest cost weight
333
+ "k_max": 30, # Warm-started horizons converge in a handful of iterations
334
+ },
335
+ float_dtype="float64",
336
+ # Integrator tolerance must stay well below the O(1e-4) trace-error
337
+ # integrand or it silently swallows the cost signal; 1e-6 is the fastest
338
+ # setting that preserves tracking accuracy
339
+ discretizer=ox.DiscretizeLinearizeVectorize(diffrax_kwargs={"atol": 1e-6, "rtol": 1e-6}),
340
+ # Relaxed subproblem tolerances: corner subproblems otherwise grind toward
341
+ # QOCO's defaults and dominate the worst-case step time
342
+ solver={"solver_args": {"abstol": 3e-5, "reltol": 3e-7, "enforce_dpp": True}},
343
+ )
344
+ problem.settings.dev.printing = False
345
+
346
+
347
+ ###############################################################################
348
+ # Analysis: where does the boresight actually draw on the plane?
349
+ ###############################################################################
350
+
351
+
352
+ def qdcm(q):
353
+ """Quaternion [w, x, y, z] to DCM (numeric twin of ox.spatial.QDCM)."""
354
+ w, x, y, z = q / np.linalg.norm(q)
355
+ return np.array(
356
+ [
357
+ [1 - 2 * (y**2 + z**2), 2 * (x * y - z * w), 2 * (x * z + y * w)],
358
+ [2 * (x * y + z * w), 1 - 2 * (x**2 + z**2), 2 * (y * z - x * w)],
359
+ [2 * (x * z - y * w), 2 * (y * z + x * w), 1 - 2 * (x**2 + y**2)],
360
+ ]
361
+ )
362
+
363
+
364
+ def boresight_trace(positions, attitudes):
365
+ """Intersect the boresight ray with the drawing plane x = path_center[0]."""
366
+ points = []
367
+ for pos, att in zip(positions, attitudes):
368
+ direction = qdcm(att) @ boresight_body
369
+ points.append(pos + direction * (path_center[0] - pos[0]) / direction[0])
370
+ return np.array(points)
371
+
372
+
373
+ def trace_figure(traced, times):
374
+ """Plotly figure: the SVG reference path against the drawn trace, colored by pen speed."""
375
+ import plotly.graph_objects as go
376
+
377
+ speed = np.linalg.norm(np.gradient(traced, times, axis=0), axis=1)
378
+
379
+ fig = go.Figure()
380
+ fig.add_trace(
381
+ go.Scatter(
382
+ x=path_points[:, 1],
383
+ y=path_points[:, 2],
384
+ mode="lines",
385
+ name="reference path",
386
+ line=dict(color="black", dash="dash", width=1),
387
+ )
388
+ )
389
+ fig.add_trace(
390
+ go.Scatter(
391
+ x=traced[:, 1],
392
+ y=traced[:, 2],
393
+ mode="markers",
394
+ name="pen trace",
395
+ marker=dict(
396
+ color=speed,
397
+ colorscale="Rainbow",
398
+ size=4,
399
+ colorbar=dict(title="pen speed [m/s]"),
400
+ showscale=True,
401
+ ),
402
+ )
403
+ )
404
+ fig.update_layout(
405
+ title=f"Boresight pen trace — {os.path.basename(svg_file)}",
406
+ xaxis=dict(title="y [m]", scaleanchor="y"),
407
+ yaxis=dict(title="z [m]"),
408
+ legend=dict(x=0.01, y=0.99),
409
+ height=600,
410
+ )
411
+ return fig
412
+
413
+
414
+ def deviation_figure(progress_dense, trace_err):
415
+ """Plotly figure: trace deviation against arc length along the path."""
416
+ import plotly.graph_objects as go
417
+
418
+ fig = go.Figure()
419
+ fig.add_trace(
420
+ go.Scatter(
421
+ x=progress_dense,
422
+ y=trace_err * 100,
423
+ mode="lines",
424
+ name="trace error",
425
+ line=dict(color="black", width=1),
426
+ )
427
+ )
428
+ fig.update_layout(
429
+ title=f"Trace deviation — {os.path.basename(svg_file)}",
430
+ xaxis=dict(title="path length [m]"),
431
+ yaxis=dict(title="trace error [cm]"),
432
+ height=400,
433
+ )
434
+ return fig
435
+
436
+
437
+ ###############################################################################
438
+ # Main: receding-horizon loop
439
+ ###############################################################################
440
+
441
+ if __name__ == "__main__":
442
+ print("Quadrotor boresight tracing via MPCC")
443
+ print("=" * 60)
444
+ print(
445
+ f"Path: {os.path.basename(svg_file)}, {total_arc_length:.1f} m of stroke "
446
+ f"at {path_width:.0f} m wide | Horizon: {n_mpc} nodes / {horizon_duration} s"
447
+ )
448
+ print()
449
+
450
+ problem.initialize()
451
+
452
+ max_steps = 600
453
+
454
+ seg_positions = []
455
+ seg_attitudes = []
456
+ seg_progress = []
457
+ seg_times = []
458
+ solve_times = []
459
+
460
+ for step in range(max_steps):
461
+ problem.reset()
462
+ results = problem.solve()
463
+ results = problem.post_process()
464
+ nodes = results.nodes
465
+ solve_times.append(problem.timing_solve)
466
+
467
+ # The executed segment is the dense trajectory from node 0 to node 1
468
+ traj_time = results.trajectory["time"].flatten()
469
+ seg_end = np.searchsorted(traj_time, dt_mpc, side="right")
470
+ seg_positions.append(results.trajectory["position"][:seg_end].copy())
471
+ seg_attitudes.append(results.trajectory["attitude"][:seg_end].copy())
472
+ seg_progress.append(results.trajectory["progress"][:seg_end, 0].copy())
473
+ seg_times.append(traj_time[:seg_end] + step * dt_mpc)
474
+
475
+ cur_progress = nodes["progress"][0, 0]
476
+ print(
477
+ f"step {step:3d}: progress={cur_progress:7.2f} m "
478
+ f"({100 * cur_progress / total_arc_length:5.1f}%), "
479
+ f"trace_cost={nodes['trace_error'][-1, 0]:.5f}"
480
+ )
481
+
482
+ # The pen has crossed the true end of the drawing (the horizon may
483
+ # already be planning into the reference extension, which is fine)
484
+ if nodes["progress"][1, 0] >= total_arc_length:
485
+ break
486
+
487
+ update_initial_conditions(nodes)
488
+ shift_guess(nodes)
489
+
490
+ # --- Trace accuracy report ---
491
+ positions = np.concatenate(seg_positions)
492
+ attitudes = np.concatenate(seg_attitudes)
493
+ progress_dense = np.concatenate(seg_progress)
494
+ times = np.concatenate(seg_times)
495
+
496
+ traced = boresight_trace(positions, attitudes)
497
+ trace_err = np.linalg.norm(traced - ref_point(progress_dense), axis=1)
498
+ print(
499
+ f"\nTraced {progress_dense[-1]:.1f} / {total_arc_length:.1f} m "
500
+ f"in {times[-1]:.1f} s with {n_mpc}-node horizons"
501
+ )
502
+ print(
503
+ f"Trace error vs reference: mean {trace_err.mean() * 100:.2f} cm, "
504
+ f"max {trace_err.max() * 100:.2f} cm"
505
+ )
506
+
507
+ # First step pays the JIT warmup, so report it separately
508
+ solve_ms = 1000 * np.array(solve_times[1:])
509
+ print(
510
+ f"Solve time per MPC step: mean {solve_ms.mean():.0f} ms, "
511
+ f"p50 {np.percentile(solve_ms, 50):.0f} ms, "
512
+ f"p99 {np.percentile(solve_ms, 99):.0f} ms, max {solve_ms.max():.0f} ms "
513
+ f"(first step {1000 * solve_times[0]:.0f} ms; replanning budget {1000 * dt_mpc:.0f} ms)"
514
+ )
515
+
516
+ trace_figure(traced, times).show()
517
+ deviation_figure(progress_dense, trace_err).show()
518
+
519
+ # --- Visualization ---
520
+ from examples.drone.logo_utils.quadrotor_mesh import make_quadrotor_mesh
521
+ from openscvx.plotting.viser import add_animation_controls, create_server
522
+
523
+ server = create_server(positions)
524
+
525
+ # The canvas: reference path as a continuous grey line, gold ink stroked on
526
+ # top of it as the animation plays (all trails are line segments — point
527
+ # clouds fail to render on some browser/GPU combinations)
528
+ server.scene.add_line_segments(
529
+ "/reference_path",
530
+ points=np.stack([path_points[:-1], path_points[1:]], axis=1).astype(np.float32),
531
+ colors=(140, 140, 145),
532
+ line_width=2.0,
533
+ )
534
+
535
+ def add_line_trail(name, trail, color, line_width):
536
+ """Growing line-strip trail; returns an update callback for the animation.
537
+
538
+ The segment buffer keeps a constant size — not-yet-drawn segments are
539
+ collapsed to zero length instead of sliced away, because some clients
540
+ drop buffer updates that change size.
541
+ """
542
+ segments = np.stack([trail[:-1], trail[1:]], axis=1).astype(np.float32)
543
+ handle = server.scene.add_line_segments(
544
+ name, points=segments, colors=color, line_width=line_width
545
+ )
546
+
547
+ def update(frame_idx: int) -> None:
548
+ shown = segments.copy()
549
+ shown[frame_idx:] = trail[min(frame_idx, len(trail) - 1)]
550
+ handle.points = shown
551
+
552
+ return update
553
+
554
+ # Trails load fully drawn; pressing Play rewinds and animates the drawing
555
+ update_ink = add_line_trail("/ink", traced, (255, 200, 60), line_width=5.0)
556
+ update_flight = add_line_trail("/flight_path", positions, (120, 170, 255), line_width=2.0)
557
+
558
+ # The artist: posed quadrotor mesh with its boresight beam to the pen
559
+ mesh_vertices, mesh_faces = make_quadrotor_mesh()
560
+ drone_handle = server.scene.add_mesh_simple(
561
+ "/drone",
562
+ vertices=np.asarray(mesh_vertices, dtype=np.float32),
563
+ faces=np.asarray(mesh_faces, dtype=np.uint32),
564
+ color=(150, 155, 165),
565
+ position=tuple(positions[0]),
566
+ wxyz=tuple(attitudes[0]),
567
+ )
568
+ beam_handle = server.scene.add_line_segments(
569
+ "/boresight_beam",
570
+ points=np.array([[positions[0], traced[0]]]),
571
+ colors=(255, 90, 90),
572
+ line_width=2.0,
573
+ )
574
+
575
+ def update_drone(frame_idx: int) -> None:
576
+ q = attitudes[frame_idx] / np.linalg.norm(attitudes[frame_idx])
577
+ drone_handle.position = tuple(float(x) for x in positions[frame_idx])
578
+ drone_handle.wxyz = tuple(float(x) for x in q)
579
+
580
+ def update_beam(frame_idx: int) -> None:
581
+ beam_handle.points = np.array([[positions[frame_idx], traced[frame_idx]]])
582
+
583
+ add_animation_controls(server, times, [update_ink, update_flight, update_drone, update_beam])
584
+ server.sleep_forever()
@@ -0,0 +1,3 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
2
+ <circle cx="50" cy="50" r="40" fill="none" stroke="black" stroke-width="1"/>
3
+ </svg>
@@ -2,7 +2,14 @@
2
2
 
3
3
  import jax.numpy as jnp
4
4
  import numpy as np
5
- from svgpathtools import svg2paths2
5
+
6
+ try:
7
+ from svgpathtools import svg2paths2
8
+ except ImportError:
9
+ raise ImportError(
10
+ "svgpathtools is required for the SVG-tracing examples. "
11
+ "Install it with: pip install svgpathtools"
12
+ ) from None
6
13
 
7
14
 
8
15
  def print_svg_path_attributes(svg_file_path):
@@ -79,3 +86,61 @@ def get_svg_path_function(svg_file_path, path_indices=None, preserve_aspect_rati
79
86
  path_indices=path_indices,
80
87
  preserve_aspect_ratio=preserve_aspect_ratio,
81
88
  )
89
+
90
+
91
+ def extract_svg_arc_length_path(svg_file_path, n_samples=1000, path_indices=None, width=20.0):
92
+ """Sample an SVG path uniformly in arc length for progress-parametrized tracing.
93
+
94
+ Unlike :func:`extract_svg_path`, which parametrizes the path by a normalized
95
+ time in ``[0, 1]``, this returns the cumulative arc length ``s`` alongside
96
+ the sampled points, so a contouring (MPCC-style) problem can use ``s``
97
+ directly as its progress coordinate. Expects a single continuous path — a
98
+ pen lift between subpaths would be traced as a straight-line jump.
99
+
100
+ Args:
101
+ svg_file_path: Path to the SVG file.
102
+ n_samples: Number of arc-length-uniform samples to return.
103
+ path_indices: Optional indices selecting which SVG paths to use.
104
+ width: Uniform scale target — the larger bounding-box dimension of the
105
+ returned points spans this many meters (aspect ratio preserved).
106
+
107
+ Returns:
108
+ Tuple ``(s, points)`` where ``s`` is a strictly increasing
109
+ ``(n_samples,)`` array of arc length starting at 0, and ``points`` is
110
+ an ``(n_samples, 2)`` array of planar coordinates centered at the
111
+ origin with y up.
112
+ """
113
+ paths, _, _ = svg2paths2(svg_file_path)
114
+ if path_indices is not None:
115
+ paths = [paths[i] for i in path_indices]
116
+
117
+ # Dense parameter-space sampling, allocated per segment by segment length
118
+ dense = []
119
+ total_length = sum(path.length() for path in paths)
120
+ for path in paths:
121
+ for seg in path:
122
+ n_seg = max(2, int(4 * n_samples * seg.length() / total_length))
123
+ for t in np.linspace(0, 1, n_seg, endpoint=False):
124
+ pt = seg.point(t)
125
+ dense.append([pt.real, pt.imag])
126
+ end = paths[-1][-1].point(1.0)
127
+ dense.append([end.real, end.imag])
128
+ dense = np.array(dense)
129
+
130
+ # Center at the origin with y up, then scale uniformly to the target width
131
+ dense[:, 1] = -dense[:, 1]
132
+ dense -= 0.5 * (dense.min(axis=0) + dense.max(axis=0))
133
+ dense *= width / (dense.max(axis=0) - dense.min(axis=0)).max()
134
+
135
+ # Cumulative arc length; drop stationary points so s is strictly increasing
136
+ step = np.linalg.norm(np.diff(dense, axis=0), axis=1)
137
+ keep = np.concatenate([[True], step > 1e-9])
138
+ dense = dense[keep]
139
+ s_dense = np.concatenate([[0.0], np.cumsum(step[step > 1e-9])])
140
+
141
+ # Resample uniformly in arc length
142
+ s = np.linspace(0.0, s_dense[-1], n_samples)
143
+ points = np.column_stack(
144
+ [np.interp(s, s_dense, dense[:, 0]), np.interp(s, s_dense, dense[:, 1])]
145
+ )
146
+ return s, points
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
18
18
  commit_id: str | None
19
19
  __commit_id__: str | None
20
20
 
21
- __version__ = version = '0.5.3.dev42'
22
- __version_tuple__ = version_tuple = (0, 5, 3, 'dev42')
21
+ __version__ = version = '0.5.3.dev43'
22
+ __version_tuple__ = version_tuple = (0, 5, 3, 'dev43')
23
23
 
24
- __commit_id__ = commit_id = 'g2dfd86d6e'
24
+ __commit_id__ = commit_id = 'g3b8696f53'