openscvx 0.4.1.dev171__tar.gz → 0.4.1.dev172__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.
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/.gitignore +3 -0
- {openscvx-0.4.1.dev171/openscvx.egg-info → openscvx-0.4.1.dev172}/PKG-INFO +1 -1
- openscvx-0.4.1.dev172/examples/animations/7_dof_arm.py +225 -0
- openscvx-0.4.1.dev172/examples/animations/_camera.py +142 -0
- openscvx-0.4.1.dev172/examples/animations/_render.py +261 -0
- openscvx-0.4.1.dev172/examples/animations/_sensor_view.py +306 -0
- openscvx-0.4.1.dev172/examples/animations/dr_vp_polytope.py +180 -0
- openscvx-0.4.1.dev172/examples/animations/logo.py +154 -0
- openscvx-0.4.1.dev172/examples/animations/obstacle_avoidance_vmap.py +151 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/examples/plotting_viser.py +138 -68
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/_version.py +3 -3
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/plotting/viser/animated.py +37 -12
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172/openscvx.egg-info}/PKG-INFO +1 -1
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx.egg-info/SOURCES.txt +7 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/tests/test_examples.py +4 -2
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/.github/assets/logo.svg +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/.github/release-drafter.yml +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/.github/workflows/_docs.yml +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/.github/workflows/branch-name.yml +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/.github/workflows/docs.yml +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/.github/workflows/lint.yml +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/.github/workflows/nightly.yml +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/.github/workflows/release-drafter.yml +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/.github/workflows/release.yml +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/.github/workflows/tests-integration.yml +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/.github/workflows/tests-unit.yml +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/CONTRIBUTING.md +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/LICENSE +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/README.md +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/docs/Foundations/constraint_reformulation.md +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/docs/Foundations/control_parameterization.md +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/docs/Foundations/discretization.md +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/docs/Foundations/ocp.md +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/docs/Foundations/scvx.md +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/docs/Foundations/time_dilation.md +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/docs/UnderTheHood/lowering_architecture.md +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/docs/UnderTheHood/vectorization_and_vmapping.md +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/docs/UsersGuide/00_introduction.md +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/docs/UsersGuide/01_hello_world_brachistochrone.md +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/docs/UsersGuide/02_drone_racing_constraints.md +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/docs/UsersGuide/03_obstacle_avoidance_vmap.md +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/docs/UsersGuide/04_viewpoint_constraints.md +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/docs/UsersGuide/05_visualization.md +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/docs/UsersGuide/06_logic.md +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/docs/UsersGuide/07_lie.md +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/docs/UsersGuide/08_mpcc.md +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/docs/assets/favicon.png +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/docs/assets/images/ct-scvx_dark.png +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/docs/assets/images/ct-scvx_light.png +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/docs/assets/images/ctcs_dark.png +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/docs/assets/images/ctcs_light.png +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/docs/assets/images/problem_class_dark.png +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/docs/assets/images/problem_class_light.png +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/docs/assets/logo.svg +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/docs/citation.md +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/docs/examples.md +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/docs/getting-started.md +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/docs/index.md +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/docs/javascripts/mathjax.js +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/examples/abstract/brachistochrone.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/examples/abstract/impulsive.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/examples/abstract/stl_integer_variable.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/examples/abstract/stl_or.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/examples/arm/3_dof_arm.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/examples/arm/7_dof_arm.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/examples/arm/7_dof_arm_collision.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/examples/arm/7_dof_arm_vp.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/examples/car/dubins_car.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/examples/car/dubins_car_disjoint.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/examples/car/dubins_car_obstacle_conditional.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/examples/car/dubins_car_obstacle_stl.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/examples/car/dubins_car_stl_or.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/examples/car/dubins_car_waypoint_stl.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/examples/drone/cinema_vp.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/examples/drone/dr_double_integrator.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/examples/drone/dr_vp.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/examples/drone/dr_vp_nodal.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/examples/drone/dr_vp_polytope.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/examples/drone/drone_racing.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/examples/drone/logo.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/examples/drone/logo_utils/acl_logo.svg +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/examples/drone/logo_utils/svg_path_utils.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/examples/drone/obstacle_avoidance.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/examples/drone/obstacle_avoidance_nodal.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/examples/drone/obstacle_avoidance_vmap.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/examples/mpc/double_integrator_discrete.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/examples/mpc/double_integrator_drone_racing.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/examples/mpc/dubins_car_circle_analytical.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/examples/mpc/dubins_car_circle_discrete.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/examples/mpc/realtime_double_integrator_drone_racing.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/examples/plotting.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/examples/realtime/3DoF_pdg_realtime.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/examples/realtime/6DoF_pdg_realtime.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/examples/realtime/base_problems/3DoF_pdg_realtime_base.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/examples/realtime/base_problems/6DoF_pdg_realtime_base.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/examples/realtime/base_problems/cinema_vp_realtime_base.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/examples/realtime/base_problems/drone_racing_realtime_base.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/examples/realtime/base_problems/dubins_car_realtime_base.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/examples/realtime/base_problems/obstacle_avoidance_realtime_base.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/examples/realtime/cinema_vp_realtime.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/examples/realtime/drone_racing_realtime.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/examples/realtime/dubins_car_realtime.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/examples/realtime/obstacle_avoidance_realtime.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/examples/rocket/3DoF_pdg.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/examples/rocket/6DoF_pdg.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/examples/spacecraft/halo_orbit.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/examples/spacecraft/hohmann_transfer.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/examples/spacecraft/proxops_cw.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/figures/ctlos_cine.gif +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/figures/ctlos_dr.gif +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/figures/dtlos_cine.gif +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/figures/dtlos_dr.gif +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/figures/openscvx_logo.svg +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/figures/openscvx_logo_square.png +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/figures/oscvx_structure_full_dark.svg +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/figures/video_preview.png +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/material/__init__.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/material/overrides/assets/images/layers/1-background.avif +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/material/overrides/assets/images/layers/1-background@1x.avif +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/material/overrides/assets/images/layers/1-background@2x.avif +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/material/overrides/assets/images/layers/1-background@3x.avif +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/material/overrides/assets/images/layers/1-background@4x.avif +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/material/overrides/assets/images/layers/2-mars.avif +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/material/overrides/assets/images/layers/2-mars@1x.avif +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/material/overrides/assets/images/layers/2-mars@2x.avif +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/material/overrides/assets/images/layers/2-mars@3x.avif +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/material/overrides/assets/images/layers/2-mars@4x.avif +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/material/overrides/assets/images/layers/3-moon.avif +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/material/overrides/assets/images/layers/3-moon@1x.avif +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/material/overrides/assets/images/layers/3-moon@2x.avif +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/material/overrides/assets/images/layers/3-moon@3x.avif +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/material/overrides/assets/images/layers/3-moon@4x.avif +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/material/overrides/assets/images/layers/4-sat1.avif +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/material/overrides/assets/images/layers/4-sat1@1x.avif +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/material/overrides/assets/images/layers/4-sat1@2x.avif +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/material/overrides/assets/images/layers/4-sat1@3x.avif +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/material/overrides/assets/images/layers/4-sat1@4x.avif +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/material/overrides/assets/images/layers/5-space.avif +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/material/overrides/assets/images/layers/5-space@1x.avif +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/material/overrides/assets/images/layers/5-space@2x.avif +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/material/overrides/assets/images/layers/5-space@3x.avif +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/material/overrides/assets/images/layers/5-space@4x.avif +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/material/overrides/assets/images/layers/6-earth.avif +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/material/overrides/assets/images/layers/6-earth@1x.avif +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/material/overrides/assets/images/layers/6-earth@2x.avif +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/material/overrides/assets/images/layers/6-earth@3x.avif +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/material/overrides/assets/images/layers/6-earth@4x.avif +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/material/overrides/assets/javascripts/parallax.js +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/material/overrides/assets/logo.svg +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/material/overrides/assets/stylesheets/custom.css +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/material/overrides/assets/stylesheets/parallax.css +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/material/overrides/home.html +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/material/overrides/main.html +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/material/overrides/partials/parallax/hero.html +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/material/overrides/partials/parallax.html +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/mkdocs.yml +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/__init__.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/__main__.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/algorithms/__init__.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/algorithms/augmented_lagrangian.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/algorithms/base.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/algorithms/constant_proximal_weight.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/algorithms/optimization_results.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/algorithms/penalized_trust_region.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/algorithms/ramp_proximal_weight.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/algorithms/weights.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/config.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/discretization/__init__.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/discretization/base.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/discretization/discretize_linearize.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/discretization/linearize_discretize.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/discretization/linearize_discretize_sparse.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/discretization/sparse_utils/__init__.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/discretization/sparse_utils/bcoo_helpers.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/discretization/sparse_utils/sparse_jacobian.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/expert/__init__.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/expert/byof.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/expert/lowering.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/expert/validation.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/init/__init__.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/init/interpolation.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/init/inverse_kinematics.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/integrators/__init__.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/integrators/diffrax.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/integrators/runge_kutta.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/loader.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/lowered/__init__.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/lowered/cvxpy_constraints.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/lowered/cvxpy_variables.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/lowered/dynamics.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/lowered/jax_constraints.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/lowered/parameters.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/lowered/problem.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/lowered/unified.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/plotting/__init__.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/plotting/plotting.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/plotting/scp_iteration.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/plotting/viser/__init__.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/plotting/viser/orbits.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/plotting/viser/plotly_integration.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/plotting/viser/primitives.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/plotting/viser/scp.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/plotting/viser/server.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/problem.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/propagation/__init__.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/propagation/post_processing.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/propagation/propagation.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/solvers/__init__.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/solvers/base.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/solvers/ptr_solver.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/symbolic/__init__.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/symbolic/augmentation.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/symbolic/builder.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/symbolic/constraint_set.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/symbolic/expr/__init__.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/symbolic/expr/arithmetic.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/symbolic/expr/array.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/symbolic/expr/constraint.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/symbolic/expr/control.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/symbolic/expr/expr.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/symbolic/expr/lie/__init__.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/symbolic/expr/lie/adjoint.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/symbolic/expr/lie/se3.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/symbolic/expr/lie/so3.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/symbolic/expr/linalg.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/symbolic/expr/logic.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/symbolic/expr/math.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/symbolic/expr/parameter.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/symbolic/expr/spatial.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/symbolic/expr/state.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/symbolic/expr/stl.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/symbolic/expr/stljax.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/symbolic/expr/time.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/symbolic/expr/variable.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/symbolic/expr/vmap.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/symbolic/hashing.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/symbolic/lower.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/symbolic/lowerers/__init__.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/symbolic/lowerers/cvxpy/__init__.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/symbolic/lowerers/cvxpy/_lowerer.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/symbolic/lowerers/cvxpy/_registry.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/symbolic/lowerers/cvxpy/arithmetic.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/symbolic/lowerers/cvxpy/array.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/symbolic/lowerers/cvxpy/constraint.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/symbolic/lowerers/cvxpy/control.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/symbolic/lowerers/cvxpy/expr.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/symbolic/lowerers/cvxpy/linalg.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/symbolic/lowerers/cvxpy/logic.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/symbolic/lowerers/cvxpy/math.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/symbolic/lowerers/cvxpy/state.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/symbolic/lowerers/jax/__init__.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/symbolic/lowerers/jax/_lowerer.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/symbolic/lowerers/jax/_registry.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/symbolic/lowerers/jax/arithmetic.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/symbolic/lowerers/jax/array.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/symbolic/lowerers/jax/constraint.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/symbolic/lowerers/jax/control.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/symbolic/lowerers/jax/expr.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/symbolic/lowerers/jax/lie.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/symbolic/lowerers/jax/linalg.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/symbolic/lowerers/jax/logic.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/symbolic/lowerers/jax/math.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/symbolic/lowerers/jax/spatial.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/symbolic/lowerers/jax/state.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/symbolic/lowerers/jax/stl.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/symbolic/lowerers/jax/stljax.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/symbolic/lowerers/jax/vmap.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/symbolic/parser/__init__.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/symbolic/parser/_registry.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/symbolic/parser/array.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/symbolic/parser/constraint.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/symbolic/parser/lie.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/symbolic/parser/linalg.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/symbolic/parser/logic.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/symbolic/parser/math.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/symbolic/parser/parser.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/symbolic/parser/spatial.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/symbolic/parser/stl.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/symbolic/parser/stljax.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/symbolic/parser/tokenizer.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/symbolic/preprocessing.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/symbolic/problem.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/symbolic/sparsity.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/symbolic/unified.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/utils/__init__.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/utils/cache.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/utils/caching.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/utils/printing.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/utils/profiling.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx/utils/utils.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx.egg-info/dependency_links.txt +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx.egg-info/entry_points.txt +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx.egg-info/requires.txt +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/openscvx.egg-info/top_level.txt +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/pyproject.toml +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/scripts/gen_example_pages.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/scripts/gen_ref_pages.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/setup.cfg +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/tests/__init__.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/tests/brachistochrone_analytical.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/tests/expr/__init__.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/tests/expr/test_gmsr.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/tests/fixtures/brachistochrone.json +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/tests/fixtures/brachistochrone.yaml +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/tests/hohmann_analytical.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/tests/symbolic/__init__.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/tests/symbolic/expr/__init__.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/tests/symbolic/expr/test_arithmetic.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/tests/symbolic/expr/test_array.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/tests/symbolic/expr/test_constraint.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/tests/symbolic/expr/test_expr.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/tests/symbolic/expr/test_lie.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/tests/symbolic/expr/test_linalg.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/tests/symbolic/expr/test_logic.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/tests/symbolic/expr/test_math.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/tests/symbolic/expr/test_node_reference.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/tests/symbolic/expr/test_parameters.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/tests/symbolic/expr/test_scaling.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/tests/symbolic/expr/test_spatial.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/tests/symbolic/expr/test_stl.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/tests/symbolic/expr/test_variable.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/tests/symbolic/expr/test_vmap.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/tests/symbolic/parser/__init__.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/tests/symbolic/parser/test_array.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/tests/symbolic/parser/test_constraint.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/tests/symbolic/parser/test_lie.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/tests/symbolic/parser/test_linalg.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/tests/symbolic/parser/test_load.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/tests/symbolic/parser/test_logic.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/tests/symbolic/parser/test_math.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/tests/symbolic/parser/test_parser.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/tests/symbolic/parser/test_spatial.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/tests/symbolic/parser/test_stl.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/tests/symbolic/parser/test_tokenizer.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/tests/symbolic/parser/test_vmap.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/tests/symbolic/test_augmentation.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/tests/symbolic/test_hashing.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/tests/symbolic/test_lower_cvxpy.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/tests/symbolic/test_lower_jax.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/tests/symbolic/test_preprocessing.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/tests/symbolic/test_sparsity.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/tests/symbolic/test_unified.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/tests/test_autotuning.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/tests/test_brachistochrone.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/tests/test_cvxpygen_optional.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/tests/test_discretization.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/tests/test_expert.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/tests/test_impulsive.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/tests/test_init.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/tests/test_integrators.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/tests/test_loader.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/tests/test_optimization_results.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/tests/test_plotting.py +0 -0
- {openscvx-0.4.1.dev171 → openscvx-0.4.1.dev172}/tests/test_propagation.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: openscvx
|
|
3
|
-
Version: 0.4.1.
|
|
3
|
+
Version: 0.4.1.dev172
|
|
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,225 @@
|
|
|
1
|
+
"""Cinematic offline render of the 7-DOF arm pick-and-place example.
|
|
2
|
+
|
|
3
|
+
The trajectory optimization problem itself lives in
|
|
4
|
+
``examples/arm/7_dof_arm.py``; this file imports that ``problem`` and the
|
|
5
|
+
robot parameters, solves it, builds a viser scene with arm links / joint
|
|
6
|
+
frames / EE trail, and drives it frame-by-frame while piping raw RGB into
|
|
7
|
+
ffmpeg to produce an mp4.
|
|
8
|
+
|
|
9
|
+
Run it with::
|
|
10
|
+
|
|
11
|
+
python examples/animations/7_dof_arm.py
|
|
12
|
+
|
|
13
|
+
The script prints a viser URL and waits. Open the URL in a browser — as soon
|
|
14
|
+
as the client connects, the render begins. Requires ``ffmpeg`` on ``PATH``;
|
|
15
|
+
``openscvx`` does not depend on it.
|
|
16
|
+
|
|
17
|
+
Only one camera mode is provided — ``"overview"`` — a static elevated camera
|
|
18
|
+
that frames the full pick-and-place workspace.
|
|
19
|
+
|
|
20
|
+
Tweak ``OUTPUT_PATH`` / ``WIDTH`` / ``HEIGHT`` / ``FPS`` below for different
|
|
21
|
+
output variants.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
import importlib
|
|
25
|
+
import os
|
|
26
|
+
import sys
|
|
27
|
+
|
|
28
|
+
import numpy as np
|
|
29
|
+
|
|
30
|
+
# Add the project root so `examples.*` imports resolve.
|
|
31
|
+
current_dir = os.path.dirname(os.path.abspath(__file__))
|
|
32
|
+
grandparent_dir = os.path.dirname(os.path.dirname(current_dir))
|
|
33
|
+
sys.path.append(grandparent_dir)
|
|
34
|
+
|
|
35
|
+
from examples.animations._camera import overview_pose
|
|
36
|
+
from examples.animations._render import render_animation_to_video
|
|
37
|
+
from examples.plotting_viser import AnimatedServerHandle
|
|
38
|
+
from openscvx.plotting.viser import (
|
|
39
|
+
add_animated_trail,
|
|
40
|
+
add_ellipsoid_obstacles,
|
|
41
|
+
add_ghost_trajectory,
|
|
42
|
+
add_position_marker,
|
|
43
|
+
add_target_markers,
|
|
44
|
+
compute_velocity_colors,
|
|
45
|
+
create_server,
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
# Import the arm example by path (filename starts with a digit).
|
|
49
|
+
_arm_spec = importlib.util.spec_from_file_location(
|
|
50
|
+
"arm_7dof", os.path.join(grandparent_dir, "examples", "arm", "7_dof_arm_collision.py")
|
|
51
|
+
)
|
|
52
|
+
_arm_mod = importlib.util.module_from_spec(_arm_spec)
|
|
53
|
+
_arm_spec.loader.exec_module(_arm_mod)
|
|
54
|
+
|
|
55
|
+
problem = _arm_mod.problem
|
|
56
|
+
d1 = _arm_mod.d1
|
|
57
|
+
a2 = _arm_mod.a2
|
|
58
|
+
a3 = _arm_mod.a3
|
|
59
|
+
a4 = _arm_mod.a4
|
|
60
|
+
joint_names = _arm_mod.joint_names
|
|
61
|
+
waypoint_names = _arm_mod.waypoint_names
|
|
62
|
+
waypoint_positions = _arm_mod.waypoint_positions
|
|
63
|
+
obstacle_center = _arm_mod.obstacle_center
|
|
64
|
+
obstacle_radius = _arm_mod.obstacle_radius
|
|
65
|
+
|
|
66
|
+
# Keypoint home positions (derived from link lengths — defined in __main__ of
|
|
67
|
+
# the arm example, so we replicate them here).
|
|
68
|
+
keypoint_home_positions = {
|
|
69
|
+
"base": np.array([0.0, 0.0, 0.0]),
|
|
70
|
+
"shoulder": np.array([0.0, 0.0, d1]),
|
|
71
|
+
"elbow": np.array([a2, 0.0, d1]),
|
|
72
|
+
"wrist": np.array([a2 + a3, 0.0, d1]),
|
|
73
|
+
"ee": np.array([a2 + a3 + a4, 0.0, d1]),
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
# --- Render settings ---------------------------------------------------------
|
|
77
|
+
OUTPUT_PATH = os.path.join(current_dir, "mp4", "7_dof_arm_overview.mp4")
|
|
78
|
+
WIDTH = 1080
|
|
79
|
+
HEIGHT = 1080
|
|
80
|
+
FPS = 60
|
|
81
|
+
CRF = 16
|
|
82
|
+
|
|
83
|
+
# Oversampling for smooth trails.
|
|
84
|
+
STRIDE = 4
|
|
85
|
+
PROPAGATION_HZ = FPS * STRIDE
|
|
86
|
+
|
|
87
|
+
# --- Camera settings ---------------------------------------------------------
|
|
88
|
+
OVERVIEW_AZIMUTH = np.radians(60.0)
|
|
89
|
+
OVERVIEW_ELEVATION = np.radians(15.0)
|
|
90
|
+
OVERVIEW_RADIUS_MARGIN = 1.0
|
|
91
|
+
OVERVIEW_FOV_DEG = 60.0
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
if __name__ == "__main__":
|
|
95
|
+
import jaxlie
|
|
96
|
+
|
|
97
|
+
problem.settings.prp.dt = 1.0 / PROPAGATION_HZ
|
|
98
|
+
problem.initialize()
|
|
99
|
+
problem.solve()
|
|
100
|
+
results = problem.post_process()
|
|
101
|
+
|
|
102
|
+
ee_pos = np.asarray(results.trajectory["ee_position"], dtype=np.float64)
|
|
103
|
+
n_frames = len(ee_pos)
|
|
104
|
+
|
|
105
|
+
# -- Extract joint keypoint positions from propagated transforms -----------
|
|
106
|
+
keypoints = np.zeros((n_frames, 5, 3))
|
|
107
|
+
joint_quats = np.zeros((n_frames, 5, 4))
|
|
108
|
+
for k, name in enumerate(joint_names):
|
|
109
|
+
T_k = np.asarray(results.trajectory[f"T_{name}"]) # (T, 4, 4)
|
|
110
|
+
p_home = np.append(keypoint_home_positions[name], 1.0)
|
|
111
|
+
keypoints[:, k] = (T_k @ p_home)[:, :3]
|
|
112
|
+
joint_quats[:, k] = np.array(
|
|
113
|
+
[jaxlie.SO3.from_matrix(T_k[t, :3, :3]).wxyz for t in range(n_frames)]
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
# -- Build viser scene (mirrors examples/arm/7_dof_arm.py) -----------------
|
|
117
|
+
server = create_server(ee_pos, show_grid=False)
|
|
118
|
+
server.scene.add_grid("/grid", width=1.5, height=1.5, cell_size=0.25)
|
|
119
|
+
server.scene.add_frame("/origin", axes_length=0.1, axes_radius=0.003)
|
|
120
|
+
|
|
121
|
+
# Waypoint markers
|
|
122
|
+
marker_colors = {
|
|
123
|
+
"home": (100, 150, 255),
|
|
124
|
+
"pre_grasp": (255, 180, 50),
|
|
125
|
+
"grasp": (255, 50, 50),
|
|
126
|
+
"pre_place": (255, 180, 50),
|
|
127
|
+
"place": (255, 50, 50),
|
|
128
|
+
}
|
|
129
|
+
add_target_markers(
|
|
130
|
+
server,
|
|
131
|
+
waypoint_positions,
|
|
132
|
+
radius=0.015,
|
|
133
|
+
colors=[marker_colors[name] for name in waypoint_names],
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
add_ellipsoid_obstacles(
|
|
137
|
+
server,
|
|
138
|
+
centers=[obstacle_center],
|
|
139
|
+
radii=[np.full(3, 1.0 / obstacle_radius)],
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
# Ghost EE trajectory + animated trail
|
|
143
|
+
ee_colors = compute_velocity_colors(np.asarray(results.trajectory.get("velocity")))
|
|
144
|
+
add_ghost_trajectory(server, ee_pos, ee_colors, point_size=0.005)
|
|
145
|
+
_, update_trail = add_animated_trail(server, ee_pos, ee_colors, point_size=0.008)
|
|
146
|
+
|
|
147
|
+
# Animated EE position marker
|
|
148
|
+
_, update_marker = add_position_marker(server, ee_pos, radius=0.015)
|
|
149
|
+
|
|
150
|
+
# Animated arm links (line segments between consecutive keypoints)
|
|
151
|
+
link_rgb = np.array(
|
|
152
|
+
[
|
|
153
|
+
[180, 180, 180], # base -> shoulder
|
|
154
|
+
[100, 180, 255], # shoulder -> elbow
|
|
155
|
+
[100, 255, 150], # elbow -> wrist
|
|
156
|
+
[255, 200, 100], # wrist -> ee
|
|
157
|
+
],
|
|
158
|
+
dtype=np.uint8,
|
|
159
|
+
)
|
|
160
|
+
link_colors = np.stack([link_rgb, link_rgb], axis=1) # (4, 2, 3)
|
|
161
|
+
|
|
162
|
+
init_points = np.stack(
|
|
163
|
+
[np.stack([keypoints[0, k], keypoints[0, k + 1]]) for k in range(4)]
|
|
164
|
+
).astype(np.float32)
|
|
165
|
+
|
|
166
|
+
arm_handle = server.scene.add_line_segments(
|
|
167
|
+
"/arm_links",
|
|
168
|
+
points=init_points,
|
|
169
|
+
colors=link_colors,
|
|
170
|
+
line_width=5.0,
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
# Joint coordinate frames
|
|
174
|
+
joint_frame_handles = []
|
|
175
|
+
for k in range(5):
|
|
176
|
+
h = server.scene.add_frame(
|
|
177
|
+
f"/joint_{joint_names[k]}",
|
|
178
|
+
wxyz=joint_quats[0, k].astype(np.float32),
|
|
179
|
+
position=keypoints[0, k].astype(np.float32),
|
|
180
|
+
axes_length=0.06,
|
|
181
|
+
axes_radius=0.002,
|
|
182
|
+
)
|
|
183
|
+
joint_frame_handles.append(h)
|
|
184
|
+
|
|
185
|
+
def update_arm(frame_idx: int) -> None:
|
|
186
|
+
pts = np.stack(
|
|
187
|
+
[np.stack([keypoints[frame_idx, k], keypoints[frame_idx, k + 1]]) for k in range(4)]
|
|
188
|
+
).astype(np.float32)
|
|
189
|
+
arm_handle.points = pts
|
|
190
|
+
for k, h in enumerate(joint_frame_handles):
|
|
191
|
+
h.position = keypoints[frame_idx, k].astype(np.float32)
|
|
192
|
+
h.wxyz = joint_quats[frame_idx, k].astype(np.float32)
|
|
193
|
+
|
|
194
|
+
# -- Manual-step handle for the renderer -----------------------------------
|
|
195
|
+
traj_time = np.asarray(results.trajectory["time"], dtype=np.float64).flatten()
|
|
196
|
+
handle = AnimatedServerHandle(
|
|
197
|
+
server=server,
|
|
198
|
+
traj_time=traj_time,
|
|
199
|
+
update_callbacks=[update_trail, update_marker, update_arm],
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
# -- Camera: overview framing the full workspace ---------------------------
|
|
203
|
+
# Frame on waypoint positions + EE path so the full motion is visible.
|
|
204
|
+
all_points = np.vstack([ee_pos, np.array(waypoint_positions)])
|
|
205
|
+
static_pose = overview_pose(
|
|
206
|
+
all_points,
|
|
207
|
+
azimuth=OVERVIEW_AZIMUTH,
|
|
208
|
+
elevation=OVERVIEW_ELEVATION,
|
|
209
|
+
radius_margin=OVERVIEW_RADIUS_MARGIN,
|
|
210
|
+
fov_deg=OVERVIEW_FOV_DEG,
|
|
211
|
+
)
|
|
212
|
+
|
|
213
|
+
def camera_pose_fn(frame_idx: int):
|
|
214
|
+
return static_pose
|
|
215
|
+
|
|
216
|
+
render_animation_to_video(
|
|
217
|
+
handle,
|
|
218
|
+
OUTPUT_PATH,
|
|
219
|
+
camera_pose_fn,
|
|
220
|
+
width=WIDTH,
|
|
221
|
+
height=HEIGHT,
|
|
222
|
+
fps=FPS,
|
|
223
|
+
crf=CRF,
|
|
224
|
+
stride=STRIDE,
|
|
225
|
+
)
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
"""Reusable camera pose helpers for viser animation rendering.
|
|
2
|
+
|
|
3
|
+
All functions return ``(cam_position, cam_wxyz, look_at)`` tuples suitable for
|
|
4
|
+
passing to ``render_animation_to_video``'s ``camera_pose_fn`` argument. They
|
|
5
|
+
depend only on numpy and ``viser.transforms`` — no openscvx imports.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import numpy as np
|
|
11
|
+
import viser.transforms as vtf
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def look_at_wxyz(pos: np.ndarray, target: np.ndarray, up: np.ndarray) -> np.ndarray:
|
|
15
|
+
"""Quaternion (w,x,y,z) for a camera at ``pos`` looking at ``target``.
|
|
16
|
+
|
|
17
|
+
Uses the OpenCV camera convention that viser expects: +X right, +Y down,
|
|
18
|
+
+Z forward. See ``examples/animations/camera_control_notes.md``.
|
|
19
|
+
"""
|
|
20
|
+
forward = target - pos
|
|
21
|
+
forward /= np.linalg.norm(forward)
|
|
22
|
+
right = np.cross(forward, up)
|
|
23
|
+
right_norm = np.linalg.norm(right)
|
|
24
|
+
if right_norm < 1e-6:
|
|
25
|
+
# Gimbal lock: forward is (nearly) parallel to up. Pick an arbitrary
|
|
26
|
+
# world axis that isn't, so the camera stays defined. The chosen axis
|
|
27
|
+
# determines the "roll" of the camera at the singularity — not great
|
|
28
|
+
# cinematically, but prevents a NaN crash.
|
|
29
|
+
fallback = np.array([1.0, 0.0, 0.0]) if abs(forward[0]) < 0.9 else np.array([0.0, 1.0, 0.0])
|
|
30
|
+
right = np.cross(forward, fallback)
|
|
31
|
+
right_norm = np.linalg.norm(right)
|
|
32
|
+
right /= right_norm
|
|
33
|
+
cam_down = np.cross(forward, right) # = -world_up projected perp to forward
|
|
34
|
+
R_world_cam = np.stack([right, cam_down, forward], axis=1)
|
|
35
|
+
return vtf.SO3.from_matrix(R_world_cam).wxyz
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def chase_pose(
|
|
39
|
+
subject: np.ndarray,
|
|
40
|
+
focus: np.ndarray,
|
|
41
|
+
*,
|
|
42
|
+
chase_distance: float = 15.0,
|
|
43
|
+
vertical_offset: float = 2.0,
|
|
44
|
+
up=(0.0, 0.0, 1.0),
|
|
45
|
+
) -> tuple[np.ndarray, np.ndarray, np.ndarray]:
|
|
46
|
+
"""Return ``(cam_pos, cam_wxyz, look_at)`` for a chase camera behind ``subject``.
|
|
47
|
+
|
|
48
|
+
The camera sits on the ray from ``focus`` through ``subject``, extended
|
|
49
|
+
``chase_distance`` units past the subject, then lifted along world up by
|
|
50
|
+
``vertical_offset``. It always looks at ``focus``.
|
|
51
|
+
"""
|
|
52
|
+
subject = np.asarray(subject, dtype=np.float64)
|
|
53
|
+
focus = np.asarray(focus, dtype=np.float64)
|
|
54
|
+
up = np.asarray(up, dtype=np.float64)
|
|
55
|
+
|
|
56
|
+
ray = subject - focus
|
|
57
|
+
ray_norm = np.linalg.norm(ray)
|
|
58
|
+
if ray_norm < 1e-6:
|
|
59
|
+
cam_pos = subject + vertical_offset * up
|
|
60
|
+
else:
|
|
61
|
+
cam_pos = subject + chase_distance * (ray / ray_norm) + vertical_offset * up
|
|
62
|
+
|
|
63
|
+
wxyz = look_at_wxyz(cam_pos, focus, up)
|
|
64
|
+
return cam_pos, wxyz, focus
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def onboard_pose(
|
|
68
|
+
position: np.ndarray,
|
|
69
|
+
attitude_wxyz: np.ndarray,
|
|
70
|
+
R_sb: np.ndarray,
|
|
71
|
+
*,
|
|
72
|
+
forward_offset: float = 0.0,
|
|
73
|
+
) -> tuple[np.ndarray, np.ndarray, np.ndarray]:
|
|
74
|
+
"""Return ``(cam_pos, cam_wxyz, look_at)`` for a sensor-mounted FPV camera.
|
|
75
|
+
|
|
76
|
+
Places the camera at ``position`` (shifted slightly forward along the sensor
|
|
77
|
+
boresight by ``forward_offset``) with orientation matching the sensor frame —
|
|
78
|
+
i.e. looking along the sensor boresight (+Z in sensor frame, mapped through
|
|
79
|
+
``R_sb`` and the body attitude to world coordinates).
|
|
80
|
+
"""
|
|
81
|
+
# R_body_to_world from the attitude quaternion (w, x, y, z)
|
|
82
|
+
w, x, y, z = attitude_wxyz
|
|
83
|
+
R_bw = np.array(
|
|
84
|
+
[
|
|
85
|
+
[1 - 2 * (y * y + z * z), 2 * (x * y - z * w), 2 * (x * z + y * w)],
|
|
86
|
+
[2 * (x * y + z * w), 1 - 2 * (x * x + z * z), 2 * (y * z - x * w)],
|
|
87
|
+
[2 * (x * z - y * w), 2 * (y * z + x * w), 1 - 2 * (x * x + y * y)],
|
|
88
|
+
],
|
|
89
|
+
dtype=np.float64,
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
# Sensor-to-world: columns are the sensor axes in world coords.
|
|
93
|
+
# R_sb is body-to-sensor, so R_sb.T is sensor-to-body.
|
|
94
|
+
R_sensor_to_world = R_bw @ R_sb.T
|
|
95
|
+
|
|
96
|
+
# Viser uses OpenCV convention (+X right, +Y DOWN, +Z forward).
|
|
97
|
+
# The sensor frame has +Y UP, so we rotate 180deg around the boresight (Z)
|
|
98
|
+
# to flip both X and Y, converting to the convention viser expects.
|
|
99
|
+
R_opencv_from_sensor = np.diag([-1.0, -1.0, 1.0])
|
|
100
|
+
R_cam_to_world = R_sensor_to_world @ R_opencv_from_sensor
|
|
101
|
+
|
|
102
|
+
wxyz = vtf.SO3.from_matrix(R_cam_to_world).wxyz
|
|
103
|
+
# Boresight is sensor +Z expressed in world frame (unchanged by the flip).
|
|
104
|
+
boresight_world = R_sensor_to_world[:, 2]
|
|
105
|
+
cam_pos = position + forward_offset * boresight_world
|
|
106
|
+
look_at = cam_pos + 10.0 * boresight_world
|
|
107
|
+
return cam_pos, wxyz, look_at
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def overview_pose(
|
|
111
|
+
positions: np.ndarray,
|
|
112
|
+
*,
|
|
113
|
+
azimuth: float = np.radians(135.0),
|
|
114
|
+
elevation: float = np.radians(25.0),
|
|
115
|
+
radius_margin: float = 0.75,
|
|
116
|
+
fov_deg: float = 60.0,
|
|
117
|
+
up=(0.0, 0.0, 1.0),
|
|
118
|
+
) -> tuple[np.ndarray, np.ndarray, np.ndarray]:
|
|
119
|
+
"""Return a static ``(cam_pos, cam_wxyz, look_at)`` that frames all ``positions``.
|
|
120
|
+
|
|
121
|
+
The camera is placed on a sphere around the centroid of ``positions``,
|
|
122
|
+
parameterized by ``azimuth`` (angle in XY from +X, CCW) and ``elevation``
|
|
123
|
+
(angle above the horizon). The radius is auto-computed so the full extent
|
|
124
|
+
fits within ``fov_deg``, then scaled by ``radius_margin``.
|
|
125
|
+
"""
|
|
126
|
+
up = np.asarray(up, dtype=np.float64)
|
|
127
|
+
center = positions.mean(axis=0)
|
|
128
|
+
max_extent = np.max(np.linalg.norm(positions - center, axis=1))
|
|
129
|
+
half_fov_rad = np.radians(fov_deg / 2.0)
|
|
130
|
+
radius = max_extent / np.sin(half_fov_rad) * radius_margin
|
|
131
|
+
|
|
132
|
+
cos_el = np.cos(elevation)
|
|
133
|
+
cam_pos = center + radius * np.array(
|
|
134
|
+
[
|
|
135
|
+
cos_el * np.cos(azimuth),
|
|
136
|
+
cos_el * np.sin(azimuth),
|
|
137
|
+
np.sin(elevation),
|
|
138
|
+
]
|
|
139
|
+
)
|
|
140
|
+
look_at = center.copy()
|
|
141
|
+
wxyz = look_at_wxyz(cam_pos, look_at, up)
|
|
142
|
+
return cam_pos, wxyz, look_at
|
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
"""Offline video rendering for animated viser examples.
|
|
2
|
+
|
|
3
|
+
Pipes raw RGB frames from ``client.get_render()`` directly into ffmpeg over
|
|
4
|
+
stdin, so there are no intermediate PNG files and no Python image library
|
|
5
|
+
required. The only runtime dependency is ``ffmpeg`` on ``PATH``; ``openscvx``
|
|
6
|
+
itself gains nothing from this module.
|
|
7
|
+
|
|
8
|
+
Typical usage (from an animation example):
|
|
9
|
+
|
|
10
|
+
.. code-block:: python
|
|
11
|
+
|
|
12
|
+
handle = create_animated_plotting_server(results, ..., controls="manual")
|
|
13
|
+
|
|
14
|
+
def camera_pose_fn(frame_idx):
|
|
15
|
+
return chase_pose(positions[frame_idx], target_center)
|
|
16
|
+
|
|
17
|
+
render_animation_to_video(handle, "out.mp4", camera_pose_fn)
|
|
18
|
+
|
|
19
|
+
The render blocks on a viser client connection, so you run the script, wait
|
|
20
|
+
for the "[render] waiting for a viser client..." line, open the printed viser
|
|
21
|
+
URL in a browser, and the render starts automatically.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
from __future__ import annotations
|
|
25
|
+
|
|
26
|
+
import shutil
|
|
27
|
+
import subprocess
|
|
28
|
+
import time
|
|
29
|
+
from pathlib import Path
|
|
30
|
+
from typing import Callable
|
|
31
|
+
|
|
32
|
+
import numpy as np
|
|
33
|
+
import viser
|
|
34
|
+
|
|
35
|
+
from examples.plotting_viser import AnimatedServerHandle
|
|
36
|
+
|
|
37
|
+
# frame_idx -> (camera_position, camera_wxyz, camera_look_at)
|
|
38
|
+
CameraPoseFn = Callable[[int], tuple[np.ndarray, np.ndarray, np.ndarray]]
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def wait_for_client(
|
|
42
|
+
server: viser.ViserServer,
|
|
43
|
+
timeout_s: float = 300.0,
|
|
44
|
+
poll_interval_s: float = 0.25,
|
|
45
|
+
) -> viser.ClientHandle:
|
|
46
|
+
"""Block until at least one viser client connects; return the first one.
|
|
47
|
+
|
|
48
|
+
Prints a reminder so the user knows they need to open the printed viser
|
|
49
|
+
URL in a browser for the render to proceed.
|
|
50
|
+
"""
|
|
51
|
+
print(
|
|
52
|
+
f"[render] waiting for a viser client to connect "
|
|
53
|
+
f"(open the viser URL above; timeout in {int(timeout_s)}s)..."
|
|
54
|
+
)
|
|
55
|
+
t0 = time.time()
|
|
56
|
+
while True:
|
|
57
|
+
clients = server.get_clients()
|
|
58
|
+
if clients:
|
|
59
|
+
client = next(iter(clients.values()))
|
|
60
|
+
print("[render] client connected.")
|
|
61
|
+
return client
|
|
62
|
+
if time.time() - t0 > timeout_s:
|
|
63
|
+
raise TimeoutError(
|
|
64
|
+
f"No viser client connected within {timeout_s:.0f}s. "
|
|
65
|
+
f"Open the viser URL in a browser to start the render."
|
|
66
|
+
)
|
|
67
|
+
time.sleep(poll_interval_s)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def render_animation_to_video(
|
|
71
|
+
handle: AnimatedServerHandle,
|
|
72
|
+
output_path: str | Path,
|
|
73
|
+
camera_pose_fn: CameraPoseFn,
|
|
74
|
+
*,
|
|
75
|
+
width: int = 1280,
|
|
76
|
+
height: int = 720,
|
|
77
|
+
fps: int = 30,
|
|
78
|
+
crf: int = 16,
|
|
79
|
+
preset: str = "slow",
|
|
80
|
+
background_color: tuple[int, int, int] = (16, 17, 19),
|
|
81
|
+
start_frame: int = 0,
|
|
82
|
+
end_frame: int | None = None,
|
|
83
|
+
stride: int = 1,
|
|
84
|
+
settle_s: float = 0.0,
|
|
85
|
+
client: viser.ClientHandle | None = None,
|
|
86
|
+
progress_every: int = 30,
|
|
87
|
+
fov_deg: float | None = None,
|
|
88
|
+
) -> Path:
|
|
89
|
+
"""Render frames of ``handle`` to an H.264 mp4 by piping raw RGB into ffmpeg.
|
|
90
|
+
|
|
91
|
+
Each frame is fetched with ``transport_format="png"`` so we get a *lossless*
|
|
92
|
+
RGBA array from the browser — no JPEG pre-compression stacked under the
|
|
93
|
+
final h.264 pass. The alpha channel is composited onto ``background_color``
|
|
94
|
+
in numpy before writing.
|
|
95
|
+
|
|
96
|
+
Why compositing is necessary: viser's dark mode puts the canvas over a
|
|
97
|
+
``theme.colors.dark[9]`` (= ``#101113``) DOM element (see viser client
|
|
98
|
+
``App.tsx:426``), and the browser compositor blends the scene over that
|
|
99
|
+
background at display time. But ``client.get_render`` returns only the
|
|
100
|
+
WebGL canvas pixels, which are transparent where nothing is drawn — so
|
|
101
|
+
we have to composite ourselves. The default ``background_color`` matches
|
|
102
|
+
Mantine's ``dark[9]`` exactly so the rendered frames are indistinguishable
|
|
103
|
+
from the live view.
|
|
104
|
+
|
|
105
|
+
The video's playback rate (``fps``) is independent of how many frames the
|
|
106
|
+
trajectory contains — ``stride`` controls the frame range from the trajectory
|
|
107
|
+
that gets written. For realtime playback, pick ``fps`` so that
|
|
108
|
+
``len(range(start_frame, end_frame, stride)) / fps`` matches the trajectory
|
|
109
|
+
duration. For slow-motion, raise ``fps`` relative to that; for time-lapse,
|
|
110
|
+
lower it.
|
|
111
|
+
|
|
112
|
+
Args:
|
|
113
|
+
handle: Handle returned by
|
|
114
|
+
``create_animated_plotting_server(..., controls="manual")``.
|
|
115
|
+
output_path: Destination mp4 file. Parent dirs are created.
|
|
116
|
+
camera_pose_fn: ``frame_idx -> (position, wxyz, look_at)``. Called once
|
|
117
|
+
per rendered frame to position the camera.
|
|
118
|
+
width: Output video width in pixels.
|
|
119
|
+
height: Output video height in pixels.
|
|
120
|
+
fps: Output video frame rate.
|
|
121
|
+
crf: H.264 constant-rate factor; lower is higher quality. 16 is
|
|
122
|
+
visually near-lossless, 18 is high quality, 23 is ffmpeg's default.
|
|
123
|
+
preset: ffmpeg x264 preset (``ultrafast``..``veryslow``). ``slow`` gives
|
|
124
|
+
noticeably better quality/size than ``medium`` for static-heavy 3D
|
|
125
|
+
scenes at modest encode-time cost.
|
|
126
|
+
background_color: RGB tuple (0..255) for the scene background. Applied
|
|
127
|
+
both to viser's scene (so the live view matches) and as the
|
|
128
|
+
composite color for alpha channel in rendered frames.
|
|
129
|
+
start_frame: First trajectory frame index to render (inclusive).
|
|
130
|
+
end_frame: One past the last trajectory frame to render. ``None`` means
|
|
131
|
+
up to ``handle.n_frames``.
|
|
132
|
+
stride: Trajectory frame stride. ``stride=2`` renders every other
|
|
133
|
+
frame, halving the output length at fixed ``fps``.
|
|
134
|
+
settle_s: Optional sleep between pushing scene state and calling
|
|
135
|
+
``get_render``. Usually 0; bump if you see torn/partial frames.
|
|
136
|
+
client: Pre-connected client. If ``None``, waits for one.
|
|
137
|
+
progress_every: Print a progress line every N rendered frames.
|
|
138
|
+
fov_deg: If given, override the client camera's vertical field of view
|
|
139
|
+
(in degrees) before rendering. Useful for matching a sensor FOV.
|
|
140
|
+
|
|
141
|
+
Returns:
|
|
142
|
+
Absolute path to the written mp4.
|
|
143
|
+
"""
|
|
144
|
+
ffmpeg = shutil.which("ffmpeg")
|
|
145
|
+
if ffmpeg is None:
|
|
146
|
+
raise RuntimeError(
|
|
147
|
+
"ffmpeg not found on PATH. Install it locally (e.g. "
|
|
148
|
+
"`brew install ffmpeg` on macOS) — openscvx does not include it "
|
|
149
|
+
"as a package dependency."
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
output_path = Path(output_path).expanduser().resolve()
|
|
153
|
+
output_path.parent.mkdir(parents=True, exist_ok=True)
|
|
154
|
+
|
|
155
|
+
# Push the background color to viser so the live browser view matches the
|
|
156
|
+
# rendered output. `configure_theme(dark_mode=True)` only styles GUI panels;
|
|
157
|
+
# the 3D canvas clear color is controlled separately by set_background_image.
|
|
158
|
+
bg_rgb = np.asarray(background_color, dtype=np.uint8).reshape(1, 1, 3)
|
|
159
|
+
bg_image = np.broadcast_to(bg_rgb, (2, 2, 3)).copy()
|
|
160
|
+
handle.server.scene.set_background_image(bg_image, format="png")
|
|
161
|
+
bg_float = np.asarray(background_color, dtype=np.float32).reshape(1, 1, 3)
|
|
162
|
+
|
|
163
|
+
if client is None:
|
|
164
|
+
client = wait_for_client(handle.server)
|
|
165
|
+
|
|
166
|
+
if fov_deg is not None:
|
|
167
|
+
client.camera.fov = np.radians(fov_deg)
|
|
168
|
+
|
|
169
|
+
n = handle.n_frames
|
|
170
|
+
if end_frame is None:
|
|
171
|
+
end_frame = n
|
|
172
|
+
frame_indices = list(range(start_frame, min(end_frame, n), max(stride, 1)))
|
|
173
|
+
if not frame_indices:
|
|
174
|
+
raise ValueError(
|
|
175
|
+
f"No frames to render: start_frame={start_frame}, "
|
|
176
|
+
f"end_frame={end_frame}, n_frames={n}, stride={stride}"
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
cmd = [
|
|
180
|
+
ffmpeg,
|
|
181
|
+
"-y",
|
|
182
|
+
"-f",
|
|
183
|
+
"rawvideo",
|
|
184
|
+
"-vcodec",
|
|
185
|
+
"rawvideo",
|
|
186
|
+
"-s",
|
|
187
|
+
f"{width}x{height}",
|
|
188
|
+
"-pix_fmt",
|
|
189
|
+
"rgb24",
|
|
190
|
+
"-r",
|
|
191
|
+
str(fps),
|
|
192
|
+
"-i",
|
|
193
|
+
"-",
|
|
194
|
+
"-c:v",
|
|
195
|
+
"libx264",
|
|
196
|
+
"-pix_fmt",
|
|
197
|
+
"yuv420p",
|
|
198
|
+
"-crf",
|
|
199
|
+
str(crf),
|
|
200
|
+
"-preset",
|
|
201
|
+
preset,
|
|
202
|
+
"-movflags",
|
|
203
|
+
"+faststart",
|
|
204
|
+
str(output_path),
|
|
205
|
+
]
|
|
206
|
+
|
|
207
|
+
print(f"[render] {len(frame_indices)} frame(s) @ {fps} fps, {width}x{height} -> {output_path}")
|
|
208
|
+
|
|
209
|
+
proc = subprocess.Popen(cmd, stdin=subprocess.PIPE)
|
|
210
|
+
assert proc.stdin is not None
|
|
211
|
+
|
|
212
|
+
try:
|
|
213
|
+
t_render_start = time.time()
|
|
214
|
+
for out_i, frame_i in enumerate(frame_indices):
|
|
215
|
+
handle.step(frame_i)
|
|
216
|
+
pos, wxyz, look_at = camera_pose_fn(frame_i)
|
|
217
|
+
with client.atomic():
|
|
218
|
+
client.camera.position = pos
|
|
219
|
+
client.camera.wxyz = wxyz
|
|
220
|
+
client.camera.look_at = look_at
|
|
221
|
+
if settle_s > 0:
|
|
222
|
+
time.sleep(settle_s)
|
|
223
|
+
# PNG transport is lossless RGBA. JPEG would introduce a second
|
|
224
|
+
# compression pass on top of h.264 and visibly hurts quality.
|
|
225
|
+
img = client.get_render(height=height, width=width, transport_format="png")
|
|
226
|
+
if img.ndim != 3 or img.shape[0] != height or img.shape[1] != width:
|
|
227
|
+
raise RuntimeError(
|
|
228
|
+
f"Unexpected get_render shape {img.shape}; "
|
|
229
|
+
f"expected ({height}, {width}, 3 or 4)."
|
|
230
|
+
)
|
|
231
|
+
if img.dtype != np.uint8:
|
|
232
|
+
img = img.astype(np.uint8)
|
|
233
|
+
if img.shape[2] == 4:
|
|
234
|
+
# Composite RGBA onto the chosen background color. Empty scene
|
|
235
|
+
# pixels come back alpha=0 so they'd otherwise be undefined.
|
|
236
|
+
alpha = img[:, :, 3:4].astype(np.float32) * (1.0 / 255.0)
|
|
237
|
+
rgb = img[:, :, :3].astype(np.float32)
|
|
238
|
+
composed = rgb * alpha + bg_float * (1.0 - alpha)
|
|
239
|
+
img = composed.astype(np.uint8)
|
|
240
|
+
proc.stdin.write(img.tobytes())
|
|
241
|
+
if progress_every > 0 and (out_i + 1) % progress_every == 0:
|
|
242
|
+
elapsed = time.time() - t_render_start
|
|
243
|
+
done = out_i + 1
|
|
244
|
+
rate = done / max(elapsed, 1e-6)
|
|
245
|
+
eta = (len(frame_indices) - done) / max(rate, 1e-6)
|
|
246
|
+
print(
|
|
247
|
+
f"[render] frame {done}/{len(frame_indices)} ({rate:.1f} fps, eta {eta:.0f}s)"
|
|
248
|
+
)
|
|
249
|
+
proc.stdin.close()
|
|
250
|
+
except BrokenPipeError as e:
|
|
251
|
+
proc.wait()
|
|
252
|
+
raise RuntimeError(
|
|
253
|
+
"ffmpeg closed its input pipe unexpectedly. See ffmpeg stderr above."
|
|
254
|
+
) from e
|
|
255
|
+
|
|
256
|
+
ret = proc.wait()
|
|
257
|
+
if ret != 0:
|
|
258
|
+
raise RuntimeError(f"ffmpeg exited with code {ret}")
|
|
259
|
+
|
|
260
|
+
print(f"[render] done: {output_path}")
|
|
261
|
+
return output_path
|