From c8528ce0013f7a58f300cd8e0937b88542d0b752 Mon Sep 17 00:00:00 2001 From: Andrej Orsula Date: Sun, 20 Oct 2024 20:00:00 +0200 Subject: [PATCH] Big Bang Signed-off-by: Andrej Orsula --- .devcontainer/devcontainer.json | 57 + .devcontainer/open.bash | 30 + .docker/build.bash | 40 + .docker/demo.bash | 31 + .docker/dev.bash | 30 + .docker/host/install_docker.bash | 130 + .docker/hpc/build.bash | 51 + .docker/hpc/run.bash | 94 + .docker/hpc/submit.bash | 65 + .docker/join.bash | 96 + .docker/run.bash | 201 + .dockerignore | 36 + .github/dependabot.yml | 11 + .github/workflows/dependabot.yml | 25 + .github/workflows/docker.yml | 90 + .github/workflows/docs.yml | 50 + .github/workflows/pre-commit.yml | 32 + .github/workflows/python.yml | 100 + .github/workflows/rust.yml | 123 + .gitignore | 27 + .gitmodules | 4 + .pre-commit-config.yaml | 80 + .vscode/settings.json | 582 ++ CMakeLists.txt | 31 + Cargo.lock | 4873 +++++++++++++++++ Cargo.toml | 57 + Dockerfile | 223 + LICENSE-APACHE | 176 + LICENSE-MIT | 21 + README.md | 26 + apps/srb.headless.kit | 171 + apps/srb.headless.rendering.kit | 34 + apps/srb.kit | 44 + apps/srb.rendering.kit | 35 + assets/srb_assets | 1 + config/env.yaml | 12 + config/rviz/default.rviz | 310 ++ crates/space_robotics_bench/Cargo.toml | 35 + .../src/envs/cfg/assets.rs | 48 + .../src/envs/cfg/config.rs | 241 + .../src/envs/cfg/enums/asset_variant.rs | 24 + .../src/envs/cfg/enums/mod.rs | 5 + .../src/envs/cfg/enums/scenario.rs | 240 + .../space_robotics_bench/src/envs/cfg/mod.rs | 61 + crates/space_robotics_bench/src/envs/mod.rs | 3 + crates/space_robotics_bench/src/error.rs | 33 + crates/space_robotics_bench/src/lib.rs | 5 + crates/space_robotics_bench/src/utils/mod.rs | 1 + .../src/utils/sampling/mod.rs | 3 + .../src/utils/sampling/poisson_disk.rs | 85 + crates/space_robotics_bench_gui/Cargo.toml | 54 + .../assets/fonts/Inter-Regular.otf | Bin 0 -> 605092 bytes .../fonts/MaterialIconsRound-Regular.otf | Bin 0 -> 360616 bytes .../assets/fonts/MonaspaceNeon-Medium.otf | Bin 0 -> 88192 bytes crates/space_robotics_bench_gui/build.rs | 4 + .../space_robotics_bench_gui/content/_images | 1 + .../space_robotics_bench_gui/content/about.md | 7 + .../content/controls.md | 1 + crates/space_robotics_bench_gui/src/app.rs | 1713 ++++++ crates/space_robotics_bench_gui/src/config.rs | 185 + crates/space_robotics_bench_gui/src/consts.rs | 3 + crates/space_robotics_bench_gui/src/lib.rs | 10 + .../src/macros/fonts.rs | 47 + .../src/macros/include.rs | 24 + .../src/macros/mod.rs | 5 + crates/space_robotics_bench_gui/src/main.rs | 28 + crates/space_robotics_bench_gui/src/page.rs | 33 + .../src/style/fonts.rs | 20 + .../space_robotics_bench_gui/src/style/mod.rs | 23 + .../src/style/text.rs | 24 + .../src/style/visuals.rs | 207 + .../src/utils/egui.rs | 104 + .../src/utils/enums.rs | 116 + .../space_robotics_bench_gui/src/utils/mod.rs | 6 + crates/space_robotics_bench_py/Cargo.toml | 23 + .../space_robotics_bench_py/src/envs/mod.rs | 14 + crates/space_robotics_bench_py/src/lib.rs | 15 + crates/space_robotics_bench_py/src/macros.rs | 29 + .../space_robotics_bench_py/src/utils/mod.rs | 10 + .../src/utils/sampling/mod.rs | 16 + .../tests/import_test.py | 2 + .../space_robotics_bench_py/tests/pytest.rs | 19 + deny.toml | 62 + docs/.gitignore | 2 + docs/README.md | 11 + docs/book.toml | 9 + docs/src/README.md | 31 + docs/src/SUMMARY.md | 62 + .../src/_images/envs/debris_capture_orbit.jpg | Bin 0 -> 37482 bytes docs/src/_images/envs/gateway.jpg | Bin 0 -> 51998 bytes docs/src/_images/envs/ingenuity.jpg | Bin 0 -> 38861 bytes docs/src/_images/envs/peg_in_hole_moon.jpg | Bin 0 -> 52953 bytes docs/src/_images/envs/peg_in_hole_orbit.jpg | Bin 0 -> 36794 bytes docs/src/_images/envs/perseverance.jpg | Bin 0 -> 40442 bytes .../_images/envs/sample_collection_mars.jpg | Bin 0 -> 26592 bytes .../_images/envs/sample_collection_moon.jpg | Bin 0 -> 48932 bytes .../envs/solar_panel_assembly_moon.jpg | Bin 0 -> 41445 bytes docs/src/_images/perseverance_ui.jpg | Bin 0 -> 63887 bytes docs/src/_images/srb_gui.jpg | Bin 0 -> 110894 bytes docs/src/_images/srb_multi_env.jpg | Bin 0 -> 151553 bytes docs/src/development/dev_env/README.md | 6 + docs/src/development/dev_env/dev_container.md | 15 + docs/src/development/dev_env/docker.md | 11 + docs/src/development/new_assets/README.md | 5 + docs/src/development/new_assets/procgen.md | 19 + docs/src/development/new_envs.md | 19 + .../getting_started/installation/README.md | 7 + .../getting_started/installation/docker.md | 84 + docs/src/getting_started/requirements.md | 36 + docs/src/getting_started/usage.md | 91 + docs/src/instructions/benchmark/README.md | 9 + docs/src/instructions/benchmark/cfg.md | 51 + docs/src/instructions/benchmark/gui.md | 15 + .../instructions/benchmark/parallel_envs.md | 13 + .../benchmark/random_zero_agents.md | 19 + .../benchmark/visual_observations.md | 9 + docs/src/instructions/utils/README.md | 5 + docs/src/instructions/utils/clean_cache.md | 7 + docs/src/instructions/workflows/README.md | 5 + docs/src/instructions/workflows/ros2.md | 99 + docs/src/misc/attributions.md | 10 + docs/src/misc/contributors.md | 3 + docs/src/misc/troubleshooting.md | 19 + docs/src/overview/envs/README.md | 19 + docs/src/overview/envs/manipulation.md | 118 + docs/src/overview/envs/mobile.md | 6 + docs/src/overview/envs/mobile_aerial.md | 15 + docs/src/overview/envs/mobile_wheeled.md | 15 + docs/src/overview/integrations/README.md | 5 + docs/src/overview/integrations/ros2.md | 15 + docs/theme/favicon.png | Bin 0 -> 2851 bytes docs/theme/favicon.svg | 13 + hyperparams/dreamerv3.yaml | 224 + hyperparams/robomimic/bc.json | 217 + hyperparams/robomimic/bcq.json | 237 + hyperparams/robomimic/cql.json | 184 + hyperparams/robomimic/gl.json | 186 + hyperparams/robomimic/hbc.json | 293 + hyperparams/robomimic/iql.json | 194 + hyperparams/robomimic/iris.json | 465 ++ hyperparams/robomimic/td3_bc.json | 167 + hyperparams/sb3/ppo.yaml | 22 + hyperparams/sb3/ppo_lstm.yaml | 22 + hyperparams/sb3/sac.yaml | 20 + launch/demo.launch.py | 141 + package.xml | 24 + pyproject.toml | 57 + scripts/_cli_utils.py | 163 + scripts/algo/dreamerv3/play.py | 262 + scripts/algo/dreamerv3/train.py | 385 ++ scripts/algo/robomimic/collect.py | 250 + scripts/algo/robomimic/play.py | 88 + .../robomimic/tools/create_default_cfg.py | 28 + scripts/algo/robomimic/train.py | 448 ++ scripts/algo/sb3/play.py | 127 + scripts/algo/sb3/train.py | 163 + scripts/blender/__init__.py | 23 + scripts/blender/procgen_assets.py | 913 +++ scripts/gui.bash | 30 + scripts/list_envs.py | 65 + scripts/random_agent.py | 101 + scripts/ros2.py | 104 + scripts/teleop.py | 185 + scripts/utils/clean_procgen_cache.py | 29 + scripts/utils/tensorboard.bash | 23 + scripts/utils/update_assets.bash | 23 + scripts/zero_agent.py | 99 + space_robotics_bench/__init__.py | 39 + space_robotics_bench/_rs/__init__.pyi | 1 + space_robotics_bench/_rs/envs/__init__.pyi | 88 + space_robotics_bench/_rs/utils/__init__.pyi | 1 + .../_rs/utils/sampling/__init__.pyi | 22 + space_robotics_bench/assets/__init__.py | 5 + space_robotics_bench/assets/light/__init__.py | 2 + space_robotics_bench/assets/light/sky.py | 41 + space_robotics_bench/assets/light/sunlight.py | 25 + .../assets/object/__init__.py | 259 + .../assets/object/lunar_rock_procgen.py | 33 + .../assets/object/martian_rock_procgen.py | 33 + .../assets/object/peg_in_hole_procgen.py | 44 + .../assets/object/peg_in_hole_profile.py | 25 + .../assets/object/sample_tube.py | 11 + .../assets/object/solar_panel.py | 11 + space_robotics_bench/assets/robot/__init__.py | 34 + .../assets/robot/canadarm3_large.py | 125 + space_robotics_bench/assets/robot/franka.py | 141 + .../assets/robot/ingenuity.py | 87 + .../assets/robot/perseverance.py | 123 + .../assets/terrain/__init__.py | 81 + .../assets/terrain/lunar_surface_procgen.py | 32 + .../assets/terrain/martian_surface_procgen.py | 32 + .../assets/vehicle/__init__.py | 33 + .../assets/vehicle/construction_rover.py | 55 + .../assets/vehicle/gateway.py | 53 + space_robotics_bench/core/__init__.py | 0 space_robotics_bench/core/actions/__init__.py | 5 + space_robotics_bench/core/actions/aerial.py | 122 + .../core/actions/manipulator.py | 14 + space_robotics_bench/core/actions/mobile.py | 247 + space_robotics_bench/core/assets/__init__.py | 6 + .../core/assets/common/__init__.py | 2 + .../core/assets/common/asset.py | 8 + .../core/assets/common/transform.py | 13 + .../core/assets/objects/__init__.py | 0 .../core/assets/robots/__init__.py | 4 + .../core/assets/robots/aerial.py | 28 + .../core/assets/robots/base.py | 18 + .../core/assets/robots/manipulator.py | 22 + .../core/assets/robots/mobile.py | 23 + .../core/assets/robots/propelled.py | 20 + .../core/assets/terrains/__init__.py | 1 + .../core/assets/vehicles/__init__.py | 1 + .../core/assets/vehicles/base.py | 19 + space_robotics_bench/core/envs/__init__.py | 5 + .../core/envs/base/__init__.py | 2 + .../core/envs/base/direct/__init__.py | 2 + .../core/envs/base/direct/cfg.py | 31 + .../core/envs/base/direct/impl.py | 83 + .../core/envs/base/managed/__init__.py | 2 + .../core/envs/base/managed/cfg.py | 12 + .../core/envs/base/managed/impl.py | 7 + .../core/interfaces/__init__.py | 1 + space_robotics_bench/core/interfaces/ros2.py | 532 ++ .../core/managers/__init__.py | 1 + space_robotics_bench/core/markers/__init__.py | 3 + .../core/markers/frame_marker.py | 4 + space_robotics_bench/core/mdp/__init__.py | 4 + space_robotics_bench/core/mdp/events.py | 185 + space_robotics_bench/core/mdp/observations.py | 24 + space_robotics_bench/core/sim/__init__.py | 4 + .../core/sim/schemas/__init__.py | 3 + space_robotics_bench/core/sim/schemas/cfg.py | 30 + space_robotics_bench/core/sim/schemas/impl.py | 77 + .../core/sim/spawners/__init__.py | 5 + .../core/sim/spawners/from_files/__init__.py | 3 + .../core/sim/spawners/from_files/cfg.py | 19 + .../core/sim/spawners/from_files/impl.py | 80 + .../core/sim/spawners/multi/__init__.py | 1 + .../core/sim/spawners/multi/cfg.py | 76 + .../core/sim/spawners/multi/impl.py | 152 + .../core/sim/spawners/procgen/__init__.py | 1 + .../core/sim/spawners/procgen/cfg.py | 88 + .../core/sim/spawners/procgen/impl.py | 163 + .../core/teleop_devices/__init__.py | 8 + .../core/teleop_devices/combined.py | 236 + .../core/teleop_devices/se3_keyboard.py | 22 + .../core/teleop_devices/se3_ros2.py | 130 + .../core/teleop_devices/se3_spacemouse.py | 112 + .../core/teleop_devices/se3_touch.py | 226 + .../core/wrappers/__init__.py | 0 .../core/wrappers/dreamerv3.py | 350 ++ .../core/wrappers/robomimic.py | 297 + space_robotics_bench/core/wrappers/sb3.py | 350 ++ space_robotics_bench/envs/__init__.py | 3 + .../envs/aerial_robotics/__init__.py | 4 + .../envs/aerial_robotics/cfg.py | 167 + .../aerial_robotics/extensions/__init__.py | 1 + .../extensions/visual/__init__.py | 2 + .../aerial_robotics/extensions/visual/cfg.py | 74 + .../aerial_robotics/extensions/visual/impl.py | 46 + .../envs/aerial_robotics/impl.py | 14 + .../envs/manipulation/__init__.py | 4 + space_robotics_bench/envs/manipulation/cfg.py | 206 + .../envs/manipulation/extensions/__init__.py | 1 + .../extensions/visual/__init__.py | 2 + .../manipulation/extensions/visual/cfg.py | 110 + .../manipulation/extensions/visual/impl.py | 46 + .../envs/manipulation/impl.py | 21 + .../envs/mobile_robotics/__init__.py | 4 + .../envs/mobile_robotics/cfg.py | 152 + .../mobile_robotics/extensions/__init__.py | 1 + .../extensions/visual/__init__.py | 2 + .../mobile_robotics/extensions/visual/cfg.py | 74 + .../mobile_robotics/extensions/visual/impl.py | 46 + .../envs/mobile_robotics/impl.py | 14 + space_robotics_bench/paths.py | 23 + space_robotics_bench/py.typed | 0 space_robotics_bench/tasks/__init__.py | 0 space_robotics_bench/tasks/_demos/__init__.py | 0 .../tasks/_demos/gateway/__init__.py | 17 + .../tasks/_demos/gateway/task.py | 171 + .../tasks/_demos/gateway/task_visual.py | 32 + .../tasks/_demos/ingenuity/__init__.py | 17 + .../tasks/_demos/ingenuity/task.py | 130 + .../tasks/_demos/ingenuity/task_visual.py | 32 + .../tasks/_demos/perseverance/__init__.py | 17 + .../tasks/_demos/perseverance/task.py | 130 + .../tasks/_demos/perseverance/task_visual.py | 32 + .../tasks/debris_capture/__init__.py | 17 + .../tasks/debris_capture/task.py | 469 ++ .../tasks/debris_capture/task_visual.py | 32 + .../tasks/peg_in_hole/__init__.py | 17 + .../tasks/peg_in_hole/task.py | 673 +++ .../tasks/peg_in_hole/task_visual.py | 32 + .../tasks/peg_in_hole_multi/__init__.py | 17 + .../tasks/peg_in_hole_multi/task.py | 708 +++ .../tasks/peg_in_hole_multi/task_visual.py | 32 + .../tasks/sample_collection/__init__.py | 17 + .../tasks/sample_collection/task.py | 552 ++ .../tasks/sample_collection/task_visual.py | 32 + .../tasks/sample_collection_multi/__init__.py | 17 + .../tasks/sample_collection_multi/task.py | 544 ++ .../sample_collection_multi/task_visual.py | 32 + .../tasks/solar_panel_assembly/__init__.py | 17 + .../tasks/solar_panel_assembly/task.py | 928 ++++ .../tasks/solar_panel_assembly/task_visual.py | 32 + space_robotics_bench/utils/__init__.py | 0 space_robotics_bench/utils/cfg.py | 57 + space_robotics_bench/utils/color.py | 28 + space_robotics_bench/utils/image_proc.py | 47 + space_robotics_bench/utils/importer.py | 39 + space_robotics_bench/utils/math.py | 181 + space_robotics_bench/utils/parsing.py | 182 + space_robotics_bench/utils/path.py | 6 + space_robotics_bench/utils/registry.py | 37 + space_robotics_bench/utils/ros.py | 27 + space_robotics_bench/utils/sampling.py | 71 + space_robotics_bench/utils/sim_app.py | 5 + space_robotics_bench/utils/string.py | 26 + space_robotics_bench/utils/traceback.py | 20 + 320 files changed, 30237 insertions(+) create mode 100644 .devcontainer/devcontainer.json create mode 100755 .devcontainer/open.bash create mode 100755 .docker/build.bash create mode 100755 .docker/demo.bash create mode 100755 .docker/dev.bash create mode 100755 .docker/host/install_docker.bash create mode 100755 .docker/hpc/build.bash create mode 100755 .docker/hpc/run.bash create mode 100755 .docker/hpc/submit.bash create mode 100755 .docker/join.bash create mode 100755 .docker/run.bash create mode 100644 .dockerignore create mode 100644 .github/dependabot.yml create mode 100644 .github/workflows/dependabot.yml create mode 100644 .github/workflows/docker.yml create mode 100644 .github/workflows/docs.yml create mode 100644 .github/workflows/pre-commit.yml create mode 100644 .github/workflows/python.yml create mode 100644 .github/workflows/rust.yml create mode 100644 .gitignore create mode 100644 .gitmodules create mode 100644 .pre-commit-config.yaml create mode 100644 .vscode/settings.json create mode 100644 CMakeLists.txt create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 Dockerfile create mode 100644 LICENSE-APACHE create mode 100644 LICENSE-MIT create mode 100644 README.md create mode 100644 apps/srb.headless.kit create mode 100644 apps/srb.headless.rendering.kit create mode 100644 apps/srb.kit create mode 100644 apps/srb.rendering.kit create mode 160000 assets/srb_assets create mode 100644 config/env.yaml create mode 100644 config/rviz/default.rviz create mode 100644 crates/space_robotics_bench/Cargo.toml create mode 100644 crates/space_robotics_bench/src/envs/cfg/assets.rs create mode 100644 crates/space_robotics_bench/src/envs/cfg/config.rs create mode 100644 crates/space_robotics_bench/src/envs/cfg/enums/asset_variant.rs create mode 100644 crates/space_robotics_bench/src/envs/cfg/enums/mod.rs create mode 100644 crates/space_robotics_bench/src/envs/cfg/enums/scenario.rs create mode 100644 crates/space_robotics_bench/src/envs/cfg/mod.rs create mode 100644 crates/space_robotics_bench/src/envs/mod.rs create mode 100644 crates/space_robotics_bench/src/error.rs create mode 100644 crates/space_robotics_bench/src/lib.rs create mode 100644 crates/space_robotics_bench/src/utils/mod.rs create mode 100644 crates/space_robotics_bench/src/utils/sampling/mod.rs create mode 100644 crates/space_robotics_bench/src/utils/sampling/poisson_disk.rs create mode 100644 crates/space_robotics_bench_gui/Cargo.toml create mode 100644 crates/space_robotics_bench_gui/assets/fonts/Inter-Regular.otf create mode 100644 crates/space_robotics_bench_gui/assets/fonts/MaterialIconsRound-Regular.otf create mode 100644 crates/space_robotics_bench_gui/assets/fonts/MonaspaceNeon-Medium.otf create mode 100644 crates/space_robotics_bench_gui/build.rs create mode 120000 crates/space_robotics_bench_gui/content/_images create mode 100644 crates/space_robotics_bench_gui/content/about.md create mode 100644 crates/space_robotics_bench_gui/content/controls.md create mode 100644 crates/space_robotics_bench_gui/src/app.rs create mode 100644 crates/space_robotics_bench_gui/src/config.rs create mode 100644 crates/space_robotics_bench_gui/src/consts.rs create mode 100644 crates/space_robotics_bench_gui/src/lib.rs create mode 100644 crates/space_robotics_bench_gui/src/macros/fonts.rs create mode 100644 crates/space_robotics_bench_gui/src/macros/include.rs create mode 100644 crates/space_robotics_bench_gui/src/macros/mod.rs create mode 100644 crates/space_robotics_bench_gui/src/main.rs create mode 100644 crates/space_robotics_bench_gui/src/page.rs create mode 100644 crates/space_robotics_bench_gui/src/style/fonts.rs create mode 100644 crates/space_robotics_bench_gui/src/style/mod.rs create mode 100644 crates/space_robotics_bench_gui/src/style/text.rs create mode 100644 crates/space_robotics_bench_gui/src/style/visuals.rs create mode 100644 crates/space_robotics_bench_gui/src/utils/egui.rs create mode 100644 crates/space_robotics_bench_gui/src/utils/enums.rs create mode 100644 crates/space_robotics_bench_gui/src/utils/mod.rs create mode 100644 crates/space_robotics_bench_py/Cargo.toml create mode 100644 crates/space_robotics_bench_py/src/envs/mod.rs create mode 100644 crates/space_robotics_bench_py/src/lib.rs create mode 100644 crates/space_robotics_bench_py/src/macros.rs create mode 100644 crates/space_robotics_bench_py/src/utils/mod.rs create mode 100644 crates/space_robotics_bench_py/src/utils/sampling/mod.rs create mode 100644 crates/space_robotics_bench_py/tests/import_test.py create mode 100644 crates/space_robotics_bench_py/tests/pytest.rs create mode 100644 deny.toml create mode 100644 docs/.gitignore create mode 100644 docs/README.md create mode 100644 docs/book.toml create mode 100644 docs/src/README.md create mode 100644 docs/src/SUMMARY.md create mode 100644 docs/src/_images/envs/debris_capture_orbit.jpg create mode 100644 docs/src/_images/envs/gateway.jpg create mode 100644 docs/src/_images/envs/ingenuity.jpg create mode 100644 docs/src/_images/envs/peg_in_hole_moon.jpg create mode 100644 docs/src/_images/envs/peg_in_hole_orbit.jpg create mode 100644 docs/src/_images/envs/perseverance.jpg create mode 100644 docs/src/_images/envs/sample_collection_mars.jpg create mode 100644 docs/src/_images/envs/sample_collection_moon.jpg create mode 100644 docs/src/_images/envs/solar_panel_assembly_moon.jpg create mode 100644 docs/src/_images/perseverance_ui.jpg create mode 100644 docs/src/_images/srb_gui.jpg create mode 100644 docs/src/_images/srb_multi_env.jpg create mode 100644 docs/src/development/dev_env/README.md create mode 100644 docs/src/development/dev_env/dev_container.md create mode 100644 docs/src/development/dev_env/docker.md create mode 100644 docs/src/development/new_assets/README.md create mode 100644 docs/src/development/new_assets/procgen.md create mode 100644 docs/src/development/new_envs.md create mode 100644 docs/src/getting_started/installation/README.md create mode 100644 docs/src/getting_started/installation/docker.md create mode 100644 docs/src/getting_started/requirements.md create mode 100644 docs/src/getting_started/usage.md create mode 100644 docs/src/instructions/benchmark/README.md create mode 100644 docs/src/instructions/benchmark/cfg.md create mode 100644 docs/src/instructions/benchmark/gui.md create mode 100644 docs/src/instructions/benchmark/parallel_envs.md create mode 100644 docs/src/instructions/benchmark/random_zero_agents.md create mode 100644 docs/src/instructions/benchmark/visual_observations.md create mode 100644 docs/src/instructions/utils/README.md create mode 100644 docs/src/instructions/utils/clean_cache.md create mode 100644 docs/src/instructions/workflows/README.md create mode 100644 docs/src/instructions/workflows/ros2.md create mode 100644 docs/src/misc/attributions.md create mode 100644 docs/src/misc/contributors.md create mode 100644 docs/src/misc/troubleshooting.md create mode 100644 docs/src/overview/envs/README.md create mode 100644 docs/src/overview/envs/manipulation.md create mode 100644 docs/src/overview/envs/mobile.md create mode 100644 docs/src/overview/envs/mobile_aerial.md create mode 100644 docs/src/overview/envs/mobile_wheeled.md create mode 100644 docs/src/overview/integrations/README.md create mode 100644 docs/src/overview/integrations/ros2.md create mode 100644 docs/theme/favicon.png create mode 100644 docs/theme/favicon.svg create mode 100644 hyperparams/dreamerv3.yaml create mode 100644 hyperparams/robomimic/bc.json create mode 100644 hyperparams/robomimic/bcq.json create mode 100644 hyperparams/robomimic/cql.json create mode 100644 hyperparams/robomimic/gl.json create mode 100644 hyperparams/robomimic/hbc.json create mode 100644 hyperparams/robomimic/iql.json create mode 100644 hyperparams/robomimic/iris.json create mode 100644 hyperparams/robomimic/td3_bc.json create mode 100644 hyperparams/sb3/ppo.yaml create mode 100644 hyperparams/sb3/ppo_lstm.yaml create mode 100644 hyperparams/sb3/sac.yaml create mode 100755 launch/demo.launch.py create mode 100644 package.xml create mode 100644 pyproject.toml create mode 100644 scripts/_cli_utils.py create mode 100755 scripts/algo/dreamerv3/play.py create mode 100755 scripts/algo/dreamerv3/train.py create mode 100755 scripts/algo/robomimic/collect.py create mode 100755 scripts/algo/robomimic/play.py create mode 100755 scripts/algo/robomimic/tools/create_default_cfg.py create mode 100755 scripts/algo/robomimic/train.py create mode 100755 scripts/algo/sb3/play.py create mode 100755 scripts/algo/sb3/train.py create mode 100755 scripts/blender/__init__.py create mode 100755 scripts/blender/procgen_assets.py create mode 100755 scripts/gui.bash create mode 100755 scripts/list_envs.py create mode 100755 scripts/random_agent.py create mode 100755 scripts/ros2.py create mode 100755 scripts/teleop.py create mode 100755 scripts/utils/clean_procgen_cache.py create mode 100755 scripts/utils/tensorboard.bash create mode 100755 scripts/utils/update_assets.bash create mode 100755 scripts/zero_agent.py create mode 100644 space_robotics_bench/__init__.py create mode 100644 space_robotics_bench/_rs/__init__.pyi create mode 100644 space_robotics_bench/_rs/envs/__init__.pyi create mode 100644 space_robotics_bench/_rs/utils/__init__.pyi create mode 100644 space_robotics_bench/_rs/utils/sampling/__init__.pyi create mode 100644 space_robotics_bench/assets/__init__.py create mode 100644 space_robotics_bench/assets/light/__init__.py create mode 100644 space_robotics_bench/assets/light/sky.py create mode 100644 space_robotics_bench/assets/light/sunlight.py create mode 100644 space_robotics_bench/assets/object/__init__.py create mode 100644 space_robotics_bench/assets/object/lunar_rock_procgen.py create mode 100644 space_robotics_bench/assets/object/martian_rock_procgen.py create mode 100644 space_robotics_bench/assets/object/peg_in_hole_procgen.py create mode 100644 space_robotics_bench/assets/object/peg_in_hole_profile.py create mode 100644 space_robotics_bench/assets/object/sample_tube.py create mode 100644 space_robotics_bench/assets/object/solar_panel.py create mode 100644 space_robotics_bench/assets/robot/__init__.py create mode 100644 space_robotics_bench/assets/robot/canadarm3_large.py create mode 100644 space_robotics_bench/assets/robot/franka.py create mode 100644 space_robotics_bench/assets/robot/ingenuity.py create mode 100644 space_robotics_bench/assets/robot/perseverance.py create mode 100644 space_robotics_bench/assets/terrain/__init__.py create mode 100644 space_robotics_bench/assets/terrain/lunar_surface_procgen.py create mode 100644 space_robotics_bench/assets/terrain/martian_surface_procgen.py create mode 100644 space_robotics_bench/assets/vehicle/__init__.py create mode 100644 space_robotics_bench/assets/vehicle/construction_rover.py create mode 100644 space_robotics_bench/assets/vehicle/gateway.py create mode 100644 space_robotics_bench/core/__init__.py create mode 100644 space_robotics_bench/core/actions/__init__.py create mode 100644 space_robotics_bench/core/actions/aerial.py create mode 100644 space_robotics_bench/core/actions/manipulator.py create mode 100644 space_robotics_bench/core/actions/mobile.py create mode 100644 space_robotics_bench/core/assets/__init__.py create mode 100644 space_robotics_bench/core/assets/common/__init__.py create mode 100644 space_robotics_bench/core/assets/common/asset.py create mode 100644 space_robotics_bench/core/assets/common/transform.py create mode 100644 space_robotics_bench/core/assets/objects/__init__.py create mode 100644 space_robotics_bench/core/assets/robots/__init__.py create mode 100644 space_robotics_bench/core/assets/robots/aerial.py create mode 100644 space_robotics_bench/core/assets/robots/base.py create mode 100644 space_robotics_bench/core/assets/robots/manipulator.py create mode 100644 space_robotics_bench/core/assets/robots/mobile.py create mode 100644 space_robotics_bench/core/assets/robots/propelled.py create mode 100644 space_robotics_bench/core/assets/terrains/__init__.py create mode 100644 space_robotics_bench/core/assets/vehicles/__init__.py create mode 100644 space_robotics_bench/core/assets/vehicles/base.py create mode 100644 space_robotics_bench/core/envs/__init__.py create mode 100644 space_robotics_bench/core/envs/base/__init__.py create mode 100644 space_robotics_bench/core/envs/base/direct/__init__.py create mode 100644 space_robotics_bench/core/envs/base/direct/cfg.py create mode 100644 space_robotics_bench/core/envs/base/direct/impl.py create mode 100644 space_robotics_bench/core/envs/base/managed/__init__.py create mode 100644 space_robotics_bench/core/envs/base/managed/cfg.py create mode 100644 space_robotics_bench/core/envs/base/managed/impl.py create mode 100644 space_robotics_bench/core/interfaces/__init__.py create mode 100644 space_robotics_bench/core/interfaces/ros2.py create mode 100644 space_robotics_bench/core/managers/__init__.py create mode 100644 space_robotics_bench/core/markers/__init__.py create mode 100644 space_robotics_bench/core/markers/frame_marker.py create mode 100644 space_robotics_bench/core/mdp/__init__.py create mode 100644 space_robotics_bench/core/mdp/events.py create mode 100644 space_robotics_bench/core/mdp/observations.py create mode 100644 space_robotics_bench/core/sim/__init__.py create mode 100644 space_robotics_bench/core/sim/schemas/__init__.py create mode 100644 space_robotics_bench/core/sim/schemas/cfg.py create mode 100644 space_robotics_bench/core/sim/schemas/impl.py create mode 100644 space_robotics_bench/core/sim/spawners/__init__.py create mode 100644 space_robotics_bench/core/sim/spawners/from_files/__init__.py create mode 100644 space_robotics_bench/core/sim/spawners/from_files/cfg.py create mode 100644 space_robotics_bench/core/sim/spawners/from_files/impl.py create mode 100644 space_robotics_bench/core/sim/spawners/multi/__init__.py create mode 100644 space_robotics_bench/core/sim/spawners/multi/cfg.py create mode 100644 space_robotics_bench/core/sim/spawners/multi/impl.py create mode 100644 space_robotics_bench/core/sim/spawners/procgen/__init__.py create mode 100644 space_robotics_bench/core/sim/spawners/procgen/cfg.py create mode 100644 space_robotics_bench/core/sim/spawners/procgen/impl.py create mode 100644 space_robotics_bench/core/teleop_devices/__init__.py create mode 100644 space_robotics_bench/core/teleop_devices/combined.py create mode 100644 space_robotics_bench/core/teleop_devices/se3_keyboard.py create mode 100644 space_robotics_bench/core/teleop_devices/se3_ros2.py create mode 100644 space_robotics_bench/core/teleop_devices/se3_spacemouse.py create mode 100644 space_robotics_bench/core/teleop_devices/se3_touch.py create mode 100644 space_robotics_bench/core/wrappers/__init__.py create mode 100644 space_robotics_bench/core/wrappers/dreamerv3.py create mode 100644 space_robotics_bench/core/wrappers/robomimic.py create mode 100644 space_robotics_bench/core/wrappers/sb3.py create mode 100644 space_robotics_bench/envs/__init__.py create mode 100644 space_robotics_bench/envs/aerial_robotics/__init__.py create mode 100644 space_robotics_bench/envs/aerial_robotics/cfg.py create mode 100644 space_robotics_bench/envs/aerial_robotics/extensions/__init__.py create mode 100644 space_robotics_bench/envs/aerial_robotics/extensions/visual/__init__.py create mode 100644 space_robotics_bench/envs/aerial_robotics/extensions/visual/cfg.py create mode 100644 space_robotics_bench/envs/aerial_robotics/extensions/visual/impl.py create mode 100644 space_robotics_bench/envs/aerial_robotics/impl.py create mode 100644 space_robotics_bench/envs/manipulation/__init__.py create mode 100644 space_robotics_bench/envs/manipulation/cfg.py create mode 100644 space_robotics_bench/envs/manipulation/extensions/__init__.py create mode 100644 space_robotics_bench/envs/manipulation/extensions/visual/__init__.py create mode 100644 space_robotics_bench/envs/manipulation/extensions/visual/cfg.py create mode 100644 space_robotics_bench/envs/manipulation/extensions/visual/impl.py create mode 100644 space_robotics_bench/envs/manipulation/impl.py create mode 100644 space_robotics_bench/envs/mobile_robotics/__init__.py create mode 100644 space_robotics_bench/envs/mobile_robotics/cfg.py create mode 100644 space_robotics_bench/envs/mobile_robotics/extensions/__init__.py create mode 100644 space_robotics_bench/envs/mobile_robotics/extensions/visual/__init__.py create mode 100644 space_robotics_bench/envs/mobile_robotics/extensions/visual/cfg.py create mode 100644 space_robotics_bench/envs/mobile_robotics/extensions/visual/impl.py create mode 100644 space_robotics_bench/envs/mobile_robotics/impl.py create mode 100644 space_robotics_bench/paths.py create mode 100644 space_robotics_bench/py.typed create mode 100644 space_robotics_bench/tasks/__init__.py create mode 100644 space_robotics_bench/tasks/_demos/__init__.py create mode 100644 space_robotics_bench/tasks/_demos/gateway/__init__.py create mode 100644 space_robotics_bench/tasks/_demos/gateway/task.py create mode 100644 space_robotics_bench/tasks/_demos/gateway/task_visual.py create mode 100644 space_robotics_bench/tasks/_demos/ingenuity/__init__.py create mode 100644 space_robotics_bench/tasks/_demos/ingenuity/task.py create mode 100644 space_robotics_bench/tasks/_demos/ingenuity/task_visual.py create mode 100644 space_robotics_bench/tasks/_demos/perseverance/__init__.py create mode 100644 space_robotics_bench/tasks/_demos/perseverance/task.py create mode 100644 space_robotics_bench/tasks/_demos/perseverance/task_visual.py create mode 100644 space_robotics_bench/tasks/debris_capture/__init__.py create mode 100644 space_robotics_bench/tasks/debris_capture/task.py create mode 100644 space_robotics_bench/tasks/debris_capture/task_visual.py create mode 100644 space_robotics_bench/tasks/peg_in_hole/__init__.py create mode 100644 space_robotics_bench/tasks/peg_in_hole/task.py create mode 100644 space_robotics_bench/tasks/peg_in_hole/task_visual.py create mode 100644 space_robotics_bench/tasks/peg_in_hole_multi/__init__.py create mode 100644 space_robotics_bench/tasks/peg_in_hole_multi/task.py create mode 100644 space_robotics_bench/tasks/peg_in_hole_multi/task_visual.py create mode 100644 space_robotics_bench/tasks/sample_collection/__init__.py create mode 100644 space_robotics_bench/tasks/sample_collection/task.py create mode 100644 space_robotics_bench/tasks/sample_collection/task_visual.py create mode 100644 space_robotics_bench/tasks/sample_collection_multi/__init__.py create mode 100644 space_robotics_bench/tasks/sample_collection_multi/task.py create mode 100644 space_robotics_bench/tasks/sample_collection_multi/task_visual.py create mode 100644 space_robotics_bench/tasks/solar_panel_assembly/__init__.py create mode 100644 space_robotics_bench/tasks/solar_panel_assembly/task.py create mode 100644 space_robotics_bench/tasks/solar_panel_assembly/task_visual.py create mode 100644 space_robotics_bench/utils/__init__.py create mode 100644 space_robotics_bench/utils/cfg.py create mode 100644 space_robotics_bench/utils/color.py create mode 100644 space_robotics_bench/utils/image_proc.py create mode 100644 space_robotics_bench/utils/importer.py create mode 100644 space_robotics_bench/utils/math.py create mode 100644 space_robotics_bench/utils/parsing.py create mode 100644 space_robotics_bench/utils/path.py create mode 100644 space_robotics_bench/utils/registry.py create mode 100644 space_robotics_bench/utils/ros.py create mode 100644 space_robotics_bench/utils/sampling.py create mode 100644 space_robotics_bench/utils/sim_app.py create mode 100644 space_robotics_bench/utils/string.py create mode 100644 space_robotics_bench/utils/traceback.py diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..ca3af0e --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,57 @@ +{ + "name": "${localWorkspaceFolderBasename}", + "build": { + "context": "${localWorkspaceFolder}", + "dockerfile": "${localWorkspaceFolder}/Dockerfile", + "cacheFrom": "andrejorsula/space_robotics_bench" + }, + "workspaceFolder": "/root/ws", + "workspaceMount": "type=bind,source=${localWorkspaceFolder},target=/root/ws", + "runArgs": [ + // Network mode + "--network=host", + "--ipc=host", + // NVIDIA GPU + "--gpus=all", + // Other GPUs + "--device=/dev/dri:/dev/dri", + "--group-add=video" + ], + "mounts": [ + /// Common + // Time + "type=bind,source=/etc/localtime,target=/etc/localtime,readonly", + "type=bind,source=/etc/timezone,target=/etc/timezone,readonly", + // GUI (X11) + "type=bind,source=/tmp/.X11-unix,target=/tmp/.X11-unix", + "type=bind,source=${localEnv:TMPDIR:/tmp}/xauth_docker_vsc_${localWorkspaceFolderBasename},target=${localEnv:TMPDIR:/tmp}/xauth_docker_vsc_${localWorkspaceFolderBasename}", + /// Isaac Sim + // Data + "type=bind,source=${localEnv:HOME}/.nvidia-omniverse/data/isaac-sim,target=/root/isaac-sim/kit/data", + // Cache + "type=bind,source=${localEnv:HOME}/.cache/isaac-sim,target=/root/isaac-sim/kit/cache", + "type=bind,source=${localEnv:HOME}/.cache/nvidia/GLCache,target=/root/.cache/nvidia/GLCache", + "type=bind,source=${localEnv:HOME}/.cache/ov,target=/root/.cache/ov", + "type=bind,source=${localEnv:HOME}/.nv/ComputeCache,target=/root/.nv/ComputeCache", + // Logs + "type=bind,source=${localEnv:HOME}/.nvidia-omniverse/logs,target=/root/.nvidia-omniverse/logs", + "type=bind,source=${localEnv:HOME}/.nvidia-omniverse/logs/isaac-sim,target=/root/isaac-sim/kit/logs", + /// Project + // Cache + "type=bind,source=${localEnv:HOME}/.cache/srb,target=/root/.cache/srb" + ], + "containerEnv": { + // GUI (X11) + "DISPLAY": "${localEnv:DISPLAY}", + "XAUTHORITY": "${localEnv:TMPDIR:/tmp}/xauth_docker_vsc_${localWorkspaceFolderBasename}", + // NVIDIA GPU + "NVIDIA_VISIBLE_DEVICES": "all", + "NVIDIA_DRIVER_CAPABILITIES": "all" + }, + "initializeCommand": "XAUTH=\"${localEnv:TMPDIR:/tmp}/xauth_docker_vsc_${localWorkspaceFolderBasename}\"; touch \"${XAUTH}\"; chmod a+r \"${XAUTH}\"; XAUTH_LIST=$(xauth nlist \"${localEnv:DISPLAY}\"); if [ -n \"${XAUTH_LIST}\" ]; then echo \"${XAUTH_LIST}\" | sed -e 's/^..../ffff/' | xauth -f \"${XAUTH}\" nmerge -; fi", + "customizations": { + "vscode": { + "extensions": [] + } + } +} diff --git a/.devcontainer/open.bash b/.devcontainer/open.bash new file mode 100755 index 0000000..e84bab9 --- /dev/null +++ b/.devcontainer/open.bash @@ -0,0 +1,30 @@ +#!/usr/bin/env bash +### Open the Dev Container in VS Code +### Usage: open.bash +set -e + +SCRIPT_DIR="$(cd "$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")" &>/dev/null && pwd)" +REPOSITORY_DIR="$(dirname "${SCRIPT_DIR}")" + +## Determine the workspace folder +if [[ -n "$1" ]]; then + # Use the first argument as the workspace folder if provided + WORKSPACE_FOLDER="$1" +else + # Otherwise, try to extract the workspace folder from `./devcontainer.json` + WORKSPACE_FOLDER="$(grep -Po '"workspaceFolder":.*?[^\\]",' "${SCRIPT_DIR}/devcontainer.json" | cut -d'"' -f4 || true)" + if [[ -z "${WORKSPACE_FOLDER}" ]]; then + # If `./devcontainer.json` does not contain the workspace folder, default to the root + WORKSPACE_FOLDER="/" + fi +fi + +## Open the Dev Container in VS Code +CODE_REMOTE_CMD=( + code --remote + "dev-container+$(printf "%s" "${REPOSITORY_DIR}" | xxd -p | tr -d "[:space:]")" + "${WORKSPACE_FOLDER}" +) +echo -e "\033[1;90m${CODE_REMOTE_CMD[*]}\033[0m" | xargs +# shellcheck disable=SC2048 +exec ${CODE_REMOTE_CMD[*]} diff --git a/.docker/build.bash b/.docker/build.bash new file mode 100755 index 0000000..42e4b6a --- /dev/null +++ b/.docker/build.bash @@ -0,0 +1,40 @@ +#!/usr/bin/env bash +### Build the Docker image +### Usage: build.bash [TAG] [BUILD_ARGS...] +set -e + +SCRIPT_DIR="$(cd "$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")" &>/dev/null && pwd)" +REPOSITORY_DIR="$(dirname "${SCRIPT_DIR}")" + +## If the current user is not in the docker group, all docker commands will be run as root +if ! grep -qi /etc/group -e "docker.*${USER}"; then + echo "[INFO] The current user '${USER}' is not detected in the docker group. All docker commands will be run as root." + WITH_SUDO="sudo" +fi + +## Determine the name of the image to build +DOCKERHUB_USER="$(${WITH_SUDO} docker info 2>/dev/null | sed '/Username:/!d;s/.* //')" +PROJECT_NAME="$(basename "${REPOSITORY_DIR}")" +IMAGE_NAME="${DOCKERHUB_USER:+${DOCKERHUB_USER}/}${PROJECT_NAME}" + +## Parse TAG and forward additional build arguments +if [ "${#}" -gt "0" ]; then + if [[ "${1}" != "-"* ]]; then + IMAGE_NAME+=":${1}" + BUILD_ARGS=${*:2} + else + BUILD_ARGS=${*:1} + fi +fi + +## Build the image +DOCKER_BUILD_CMD=( + "${WITH_SUDO}" docker build + "${REPOSITORY_DIR}" + --file "${REPOSITORY_DIR}/Dockerfile" + --tag "${IMAGE_NAME}" + "${BUILD_ARGS}" +) +echo -e "\033[1;90m[TRACE] ${DOCKER_BUILD_CMD[*]}\033[0m" | xargs +# shellcheck disable=SC2048 +exec ${DOCKER_BUILD_CMD[*]} diff --git a/.docker/demo.bash b/.docker/demo.bash new file mode 100755 index 0000000..1ac7eac --- /dev/null +++ b/.docker/demo.bash @@ -0,0 +1,31 @@ +#!/usr/bin/env bash +set -e + +SCRIPT_DIR="$(cd "$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")" &>/dev/null && pwd)" + +## Config +# Additional volumes to mount inside the container +EXTRA_DOCKER_VOLUMES=( + "${HOME}/Videos:/root/Videos" +) +# Additional environment variables to set inside the container +EXTRA_DOCKER_ENVIRON=( + SRB_SKIP_EXT_MOD_UPDATE="1" +) + +## Parse arguments +DEFAULT_CMD="cargo run --release --package space_robotics_bench_gui" +if [ "${#}" -gt "0" ]; then + CMD=${*:1} +fi + +## Run the container +DOCKER_RUN_CMD=( + "${SCRIPT_DIR}/run.bash" + "${EXTRA_DOCKER_VOLUMES[@]/#/"-v "}" + "${EXTRA_DOCKER_ENVIRON[@]/#/"-e "}" + "${CMD:-${DEFAULT_CMD}}" +) +echo -e "\033[1;90m[TRACE] ${DOCKER_RUN_CMD[*]}\033[0m" | xargs +# shellcheck disable=SC2048 +exec ${DOCKER_RUN_CMD[*]} diff --git a/.docker/dev.bash b/.docker/dev.bash new file mode 100755 index 0000000..37bdbab --- /dev/null +++ b/.docker/dev.bash @@ -0,0 +1,30 @@ +#!/usr/bin/env bash +### Run a Docker container with additional development volumes mounted +### Usage: dev.bash [-v HOST_DIR:DOCKER_DIR:OPTIONS] [-e ENV=VALUE] [TAG] [CMD] +set -e + +SCRIPT_DIR="$(cd "$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")" &>/dev/null && pwd)" +REPOSITORY_DIR="$(dirname "${SCRIPT_DIR}")" +WS_DIR="$(dirname "${REPOSITORY_DIR}")" + +## Config +# Development volumes to mount inside the container +DOCKER_DEV_VOLUMES=( + "${WS_DIR}/isaaclab:/root/isaaclab:rw" + # "${WS_DIR}/dreamerv3:/root/dreamerv3:rw" +) +# Development environment variables to set inside the container +DOCKER_DEV_ENVIRON=( + SRB_WITH_TRACEBACK="${SRB_WITH_TRACEBACK:-true}" +) + +## Run the container with development volumes +DOCKER_DEV_CMD=( + WITH_DEV_VOLUME=true "${SCRIPT_DIR}/run.bash" + "${DOCKER_DEV_VOLUMES[@]/#/"-v "}" + "${DOCKER_DEV_ENVIRON[@]/#/"-e "}" + "${*:1}" +) +echo -e "\033[1;90m[TRACE] ${DOCKER_DEV_CMD[*]}\033[0m" | xargs +# shellcheck disable=SC2048 +exec ${DOCKER_DEV_CMD[*]} diff --git a/.docker/host/install_docker.bash b/.docker/host/install_docker.bash new file mode 100755 index 0000000..38f7357 --- /dev/null +++ b/.docker/host/install_docker.bash @@ -0,0 +1,130 @@ +#!/usr/bin/env bash +### Install Docker and NVIDIA Container Toolkit +### Usage: install_docker.bash +set -e + +## Install curl if missing +if ! command -v curl >/dev/null 2>&1; then + if command -v apt-get >/dev/null 2>&1; then + sudo apt-get install -y curl + elif command -v dnf >/dev/null 2>&1; then + sudo dnf install -y curl + elif command -v yum >/dev/null 2>&1; then + sudo yum install -y curl + fi +fi + +## Install Docker via the convenience script +curl -fsSL https://get.docker.com | sh +sudo systemctl enable --now docker + +## (Optional) Install support for NVIDIA if an NVIDIA GPU is detected and the installation is requested +check_nvidia_gpu() { + if ! lshw -C display 2>/dev/null | grep -qi "vendor.*nvidia"; then + return 1 # NVIDIA GPU is not present + elif ! command -v nvidia-smi >/dev/null 2>&1; then + return 1 # NVIDIA GPU is present but nvidia-utils not installed + elif ! nvidia-smi -L &>/dev/null; then + return 1 # NVIDIA GPU is present but is not working properly + else + return 0 # NVIDIA GPU is present and appears to be working + fi +} +configure_nvidia_apt_repository() { + curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | + sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg && + curl -sL https://nvidia.github.io/libnvidia-container/stable/deb/nvidia-container-toolkit.list | + sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' | + sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list + sudo apt-get update +} +if check_nvidia_gpu; then + echo -e "[INFO] NVIDIA GPU detected." + DOCKER_VERSION="$(sudo docker version --format '{{.Server.Version}}' 2>/dev/null)" + MIN_VERSION_FOR_TOOLKIT="19.3" + if [ "$(printf '%s\n' "${MIN_VERSION_FOR_TOOLKIT}" "${DOCKER_VERSION}" | sort -V | head -n1)" = "$MIN_VERSION_FOR_TOOLKIT" ]; then + if ! command -v nvidia-container-toolkit >/dev/null 2>&1; then + while true; do + read -erp "Do you want to install NVIDIA Container Toolkit? [Y/n]: " INSTALL_NVIDIA_CONTAINER_TOOLKIT + case "${INSTALL_NVIDIA_CONTAINER_TOOLKIT,,}" in + "" | y | yes) + INSTALL_NVIDIA_CONTAINER_TOOLKIT=true + break + ;; + n | no) + INSTALL_NVIDIA_CONTAINER_TOOLKIT=false + break + ;; + esac + done + if [[ "${INSTALL_NVIDIA_CONTAINER_TOOLKIT}" = true ]]; then + if command -v apt-get >/dev/null 2>&1; then + configure_nvidia_apt_repository + sudo apt-get install -y nvidia-container-toolkit + elif command -v yum >/dev/null 2>&1; then + curl -s -L https://nvidia.github.io/libnvidia-container/stable/rpm/nvidia-container-toolkit.repo | + sudo tee /etc/yum.repos.d/nvidia-container-toolkit.repo + sudo yum install -y nvidia-container-toolkit + else + echo >&2 -e "\033[1;31m[ERROR] Supported package manager not found. Please install nvidia-container-toolkit manually.\033[0m" + fi + sudo systemctl restart --now docker + fi + else + echo -e "[INFO] NVIDIA Container Toolkit is already installed." + fi + else + if ! command -v nvidia-docker >/dev/null 2>&1; then + while true; do + read -erp "Do you want to install NVIDIA Docker [Y/n]: " INSTALL_NVIDIA_DOCKER + case "${INSTALL_NVIDIA_DOCKER,,}" in + "" | y | yes) + INSTALL_NVIDIA_DOCKER=true + break + ;; + n | no) + INSTALL_NVIDIA_DOCKER=false + break + ;; + esac + done + if [[ "${INSTALL_NVIDIA_DOCKER}" = true ]]; then + if command -v apt-get >/dev/null 2>&1; then + configure_nvidia_apt_repository + sudo apt-get install -y nvidia-docker2 + else + echo >&2 -e "\033[1;31m[ERROR] Supported package manager not found. Please install nvidia-docker2 manually.\033[0m" + fi + sudo systemctl restart --now docker + fi + else + echo -e "[INFO] NVIDIA Docker is already installed." + fi + fi +fi + +if [[ $(grep /etc/group -e "docker") != *"${USER}"* ]]; then + [ -z "${PS1}" ] + ## (Optional) Add user to docker group + while true; do + read -erp "Do you want to add the current user ${USER} to the docker group? [Y/n]: " ADD_USER_TO_DOCKER_GROUP + case "${ADD_USER_TO_DOCKER_GROUP,,}" in + "" | y | yes) + ADD_USER_TO_DOCKER_GROUP=true + break + ;; + n | no) + ADD_USER_TO_DOCKER_GROUP=false + break + ;; + esac + done + if [[ "${ADD_USER_TO_DOCKER_GROUP}" = true ]]; then + sudo groupadd -f docker + sudo usermod -aG docker "${USER}" + newgrp docker + echo -e "[INFO] The current user ${USER} was added to the docker group." + fi +else + echo -e "[INFO] The current user ${USER} is already in the docker group." +fi diff --git a/.docker/hpc/build.bash b/.docker/hpc/build.bash new file mode 100755 index 0000000..1bd604a --- /dev/null +++ b/.docker/hpc/build.bash @@ -0,0 +1,51 @@ +#!/usr/bin/env bash +### Build a Singularity image from the Docker image +### Usage: build.bash [TAG] [BUILD_ARGS...] +set -e + +SCRIPT_DIR="$(cd "$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")" &>/dev/null && pwd)" +DOT_DOCKER_DIR="$(dirname "${SCRIPT_DIR}")" +REPOSITORY_DIR="$(dirname "${DOT_DOCKER_DIR}")" +IMAGES_DIR="${SCRIPT_DIR}/images" +DOCKER_BUILD_SCRIPT="${DOT_DOCKER_DIR}/build.bash" +export APPTAINER_TMPDIR="${APPTAINER_TMPDIR:-"${HOME}/.apptainer/tmp"}" + +## If the current user is not in the docker group, all docker commands will be run as root +if ! grep -qi /etc/group -e "docker.*${USER}"; then + echo "[INFO] The current user '${USER}' is not detected in the docker group. All docker commands will be run as root." + WITH_SUDO="sudo" +fi + +## Determine the name of the image to build and the output path +DOCKERHUB_USER="$(${WITH_SUDO} docker info 2>/dev/null | sed '/Username:/!d;s/.* //')" +PROJECT_NAME="$(basename "${REPOSITORY_DIR}")" +IMAGE_NAME="${DOCKERHUB_USER:+${DOCKERHUB_USER}/}${PROJECT_NAME}" +OUTPUT_PATH="${IMAGES_DIR}/${PROJECT_NAME}.sif" + +## Parse TAG and forward additional build arguments +if [ "${#}" -gt "0" ]; then + if [[ "${1}" != "-"* ]]; then + TAG="${1}" + BUILD_ARGS=${*:2} + else + BUILD_ARGS=${*:1} + fi +fi +TAG="${TAG:-"latest"}" +IMAGE_NAME+=":${TAG}" + +## Create the temporary directory for the Singularity image +mkdir -p "${APPTAINER_TMPDIR}" + +## Build the Docker image +"${DOCKER_BUILD_SCRIPT}" "${TAG}" "${BUILD_ARGS}" + +## Convert the Docker image to a Singularity image +APPTAINER_BUILD_CMD=( + apptainer build + "${OUTPUT_PATH}" + "docker-daemon:${IMAGE_NAME}" +) +echo -e "\033[1;90m[TRACE] ${APPTAINER_BUILD_CMD[*]}\033[0m" | xargs +# shellcheck disable=SC2048 +exec ${APPTAINER_BUILD_CMD[*]} diff --git a/.docker/hpc/run.bash b/.docker/hpc/run.bash new file mode 100755 index 0000000..d226076 --- /dev/null +++ b/.docker/hpc/run.bash @@ -0,0 +1,94 @@ +#!/usr/bin/env bash +### Run a command inside the Singularity container +### Usage: run.bash [CMD] +set -e + +SCRIPT_DIR="$(cd "$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")" &>/dev/null && pwd)" +DOT_DOCKER_DIR="$(dirname "${SCRIPT_DIR}")" +REPOSITORY_DIR="$(dirname "${DOT_DOCKER_DIR}")" +IMAGES_DIR="${SCRIPT_DIR}/images" +PROJECT_NAME="$(basename "${REPOSITORY_DIR}")" + +## Configuration +# Path to the image to run +IMAGE_PATH="${IMAGE_PATH:-"${IMAGES_DIR}/${PROJECT_NAME}.sif"}" +# Options for running the container +SINGULARITY_EXEC_OPTS="${SINGULARITY_EXEC_OPTS:- + --no-home +}" +# Flag to enable GPU +WITH_GPU="${WITH_GPU:-true}" +# Volumes to mount inside the container +VOLUMES_ROOT="${SCRATCH:-${HOME}}/volumes/${PROJECT_NAME}" +SINGULARITY_VOLUMES=( + ## Isaac Sim + # Data + "${VOLUMES_ROOT}/.nvidia-omniverse/data/isaac-sim:/root/isaac-sim/kit/data:rw" + # Cache + "${VOLUMES_ROOT}/.cache/isaac-sim:/root/isaac-sim/kit/cache:rw" + "${VOLUMES_ROOT}/.cache/nvidia/GLCache:/root/.cache/nvidia/GLCache:rw" + "${VOLUMES_ROOT}/.cache/ov:/root/.cache/ov:rw" + "${VOLUMES_ROOT}/.nv/ComputeCache:/root/.nv/ComputeCache:rw" + # Logs + "${VOLUMES_ROOT}/.nvidia-omniverse/logs:/root/.nvidia-omniverse/logs:rw" + "${VOLUMES_ROOT}/.nvidia-omniverse/logs/isaac-sim:/root/isaac-sim/kit/logs:rw" + ## Project + # Source + "${REPOSITORY_DIR}:/root/ws:rw" + # Cache + "${VOLUMES_ROOT}/.cache/srb:/root/.cache/srb:rw" + + + "${VOLUMES_ROOT}/home/users:/home/users:rw" +) + +## Ensure the image exists +if [ ! -f "${IMAGE_PATH}" ]; then + echo >&2 -e "\033[1;31m[ERROR] Singularity image not found at ${IMAGE_PATH}\033[0m" + exit 1 +fi + +## Parse CMD +if [ "${#}" -gt "0" ]; then + CMD=${*:1} +else + echo >&2 -e "\033[1;31m[ERROR] No command provided.\033[0m" + exit 1 +fi + +## Ensure the host directories exist +for volume in "${SINGULARITY_VOLUMES[@]}"; do + if [[ "${volume}" =~ ^([^:]+):([^:]+).*$ ]]; then + host_dir="${BASH_REMATCH[1]}" + if [ ! -d "${host_dir}" ]; then + mkdir -p "${host_dir}" + echo -e "\033[1;90m[INFO] Created directory ${host_dir}\033[0m" + fi + fi +done + +## GPU +if [[ "${WITH_GPU,,}" = true ]]; then + SINGULARITY_EXEC_OPTS+=" --nv" +fi + +## Environment +if ! command -v module >/dev/null 2>&1; then + echo >&2 -e "\033[1;31m[ERROR] The 'module' command is not available. Please run this script on a compute node.\033[0m" + exit 1 +fi +# Load the Singularity module +module purge +module load tools/Singularity + +## Run the container +SINGULARITY_EXEC_CMD=( + singularity exec + "${SINGULARITY_EXEC_OPTS}" + "${SINGULARITY_VOLUMES[@]/#/"--bind "}" + "${IMAGE_PATH}" + "${CMD}" +) +echo -e "\033[1;90m[TRACE] ${SINGULARITY_EXEC_CMD[*]}\033[0m" | xargs +# shellcheck disable=SC2048 +exec ${SINGULARITY_EXEC_CMD[*]} diff --git a/.docker/hpc/submit.bash b/.docker/hpc/submit.bash new file mode 100755 index 0000000..82d12b8 --- /dev/null +++ b/.docker/hpc/submit.bash @@ -0,0 +1,65 @@ +#!/usr/bin/env bash +### Submit a job using Slurm +### Usage: submit.bash [CMD] +set -e + +SCRIPT_DIR="$(cd "$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")" &>/dev/null && pwd)" +DOT_DOCKER_DIR="$(dirname "${SCRIPT_DIR}")" +REPOSITORY_DIR="$(dirname "${DOT_DOCKER_DIR}")" +IMAGES_DIR="${SCRIPT_DIR}/images" +PROJECT_NAME="$(basename "${REPOSITORY_DIR}")" + +## Configuration +# Path to the image to run +IMAGE_PATH="${IMAGE_PATH:-"${IMAGES_DIR}/${PROJECT_NAME}.sif"}" +JOBS_DIR="${JOBS_DIR:-"${HOME}/jobs"}" + +## Ensure the image exists +if [ ! -f "${IMAGE_PATH}" ]; then + echo >&2 -e "\033[1;31m[ERROR] Singularity image not found at ${IMAGE_PATH}\033[0m" + exit 1 +fi + +## Parse CMD +if [ "${#}" -gt "0" ]; then + CMD=${*:1} +else + echo >&2 -e "\033[1;31m[ERROR] No command provided.\033[0m" + exit 1 +fi + +# Create a job file directory if it does not exist +if [ ! -d "${JOBS_DIR}" ]; then + mkdir -p "${JOBS_DIR}" + echo -e "\033[1;90m[INFO] Created directory ${JOBS_DIR}\033[0m" +fi + +# Create a job file +TIMESTAMP="$(date +%Y-%m-%d_%H-%M-%S)" +JOB_PATH="${JOBS_DIR}/job_${TIMESTAMP}.bash" +cat <"${JOB_PATH}" +#!/bin/bash -l +#SBATCH --job-name="job_${TIMESTAMP}" +#SBATCH --time=1-12:00:00 +#SBATCH --qos=normal +#SBATCH --nodes=1 +#SBATCH --ntasks-per-node=1 +#SBATCH --cpus-per-task=3 +#SBATCH --mem=168GB +#SBATCH --partition=gpu +#SBATCH --constraint volta32 +#SBATCH --gpus=1 +#SBATCH --gpus-per-node=1 +#SBATCH --gpus-per-task=1 +#SBATCH --signal=B:SIGKILL@60 + +bash "${SCRIPT_DIR}/run.bash" "${CMD}" +EOT + +## Submit the job +SBATCH_CMD=( + sbatch "${JOB_PATH}" +) +echo -e "\033[1;90m[TRACE] ${SBATCH_CMD[*]}\033[0m" | xargs +# shellcheck disable=SC2048 +exec ${SBATCH_CMD[*]} diff --git a/.docker/join.bash b/.docker/join.bash new file mode 100755 index 0000000..eb9fb7a --- /dev/null +++ b/.docker/join.bash @@ -0,0 +1,96 @@ +#!/usr/bin/env bash +### Join a running Docker container +### Usage: join.bash [ID] [CMD] +set -e + +SCRIPT_DIR="$(cd "$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")" &>/dev/null && pwd)" +REPOSITORY_DIR="$(dirname "${SCRIPT_DIR}")" + +## If the current user is not in the docker group, all docker commands will be run as root +if ! grep -qi /etc/group -e "docker.*${USER}"; then + echo "[INFO] The current user '${USER}' is not detected in the docker group. All docker commands will be run as root." + WITH_SUDO="sudo" +fi + +## Config +# Name of the Docker image to join if an image with locally-defined name does not exist +DOCKERHUB_IMAGE_NAME="${DOCKERHUB_IMAGE_NAME:-"andrejorsula/space_robotics_bench"}" +# Options for executing a command inside the container +DOCKER_EXEC_OPTS="${DOCKER_EXEC_OPTS:- + --interactive + --tty +}" +# Default command to execute inside the container +DEFAULT_CMD="${DEFAULT_CMD:-"bash"}" + +## Parse ID and CMD +if [ "${#}" -gt "0" ]; then + if [[ "${1}" =~ ^[0-9]+$ ]]; then + ID="${1}" + if [ "${#}" -gt "1" ]; then + CMD=${*:2} + else + CMD="${DEFAULT_CMD}" + fi + else + CMD=${*:1} + fi +else + CMD="${DEFAULT_CMD}" +fi + +## Determine the name of the container to join +DOCKERHUB_USER="$(${WITH_SUDO} docker info 2>/dev/null | sed '/Username:/!d;s/.* //')" +PROJECT_NAME="$(basename "${REPOSITORY_DIR}")" +IMAGE_NAME="${DOCKERHUB_USER:+${DOCKERHUB_USER}/}${PROJECT_NAME}" +if [[ -z "$(${WITH_SUDO} docker images -q "${IMAGE_NAME}" 2>/dev/null)" ]] && [[ -n "$(curl -fsSL "https://registry.hub.docker.com/v2/repositories/${DOCKERHUB_IMAGE_NAME}" 2>/dev/null)" ]]; then + IMAGE_NAME="${DOCKERHUB_IMAGE_NAME}" +fi +CONTAINER_NAME="${IMAGE_NAME##*/}" +CONTAINER_NAME="${CONTAINER_NAME//[^a-zA-Z0-9]/_}" + +## Verify/select the appropriate container to join +RELEVANT_CONTAINERS=$(${WITH_SUDO} docker container list --all --format "{{.Names}}" | grep -i "${CONTAINER_NAME}" || :) +RELEVANT_CONTAINERS_COUNT=$(echo "${RELEVANT_CONTAINERS}" | wc -w) +if [ "${RELEVANT_CONTAINERS_COUNT}" -eq "0" ]; then + echo >&2 -e "\033[1;31m[ERROR] No containers with the name '${CONTAINER_NAME}' found. Run the container first.\033[0m" + exit 1 +elif [ "${RELEVANT_CONTAINERS_COUNT}" -eq "1" ]; then + CONTAINER_NAME="${RELEVANT_CONTAINERS}" +else + print_usage_with_relevant_containers() { + echo >&2 "Usage: ${0} [ID] [CMD]" + echo "${RELEVANT_CONTAINERS}" | sort --version-sort | while read -r container; do + id=$(echo "${container}" | grep -oE '[0-9]+$' || :) + if [ -z "${id}" ]; then + id=0 + fi + echo >&2 -e " ${container}\t(ID=${id})" + done + } + if [[ -n "${ID}" ]]; then + if [ "${ID}" -gt "0" ]; then + CONTAINER_NAME="${CONTAINER_NAME}${ID}" + fi + if ! echo "${RELEVANT_CONTAINERS}" | grep -qi "${CONTAINER_NAME}"; then + echo >&2 -e "\033[1;31m[ERROR] Container with 'ID=${ID}' does not exist. Specify the correct ID as the first argument.\033[0m" + print_usage_with_relevant_containers + exit 2 + fi + else + echo >&2 -e "\033[1;31m[ERROR] Multiple containers with the name '${CONTAINER_NAME}' found. ID of the container must be specified as the first argument.\033[0m" + print_usage_with_relevant_containers + exit 2 + fi +fi + +## Execute command inside the container +DOCKER_EXEC_CMD=( + "${WITH_SUDO}" docker exec + "${DOCKER_EXEC_OPTS}" + "${CONTAINER_NAME}" + "${CMD}" +) +echo -e "\033[1;90m[TRACE] ${DOCKER_EXEC_CMD[*]}\033[0m" | xargs +# shellcheck disable=SC2048 +exec ${DOCKER_EXEC_CMD[*]} diff --git a/.docker/run.bash b/.docker/run.bash new file mode 100755 index 0000000..0bae2b0 --- /dev/null +++ b/.docker/run.bash @@ -0,0 +1,201 @@ +#!/usr/bin/env bash +### Run a Docker container +### Usage: run.bash [-v HOST_DIR:DOCKER_DIR:OPTIONS] [-e ENV=VALUE] [TAG] [CMD] +set -e + +SCRIPT_DIR="$(cd "$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")" &>/dev/null && pwd)" +REPOSITORY_DIR="$(dirname "${SCRIPT_DIR}")" + +## If the current user is not in the docker group, all docker commands will be run as root +if ! grep -qi /etc/group -e "docker.*${USER}"; then + echo "[INFO] The current user '${USER}' is not detected in the docker group. All docker commands will be run as root." + WITH_SUDO="sudo" +fi + +## Config +# Name of the Docker image to run if an image with locally-defined name does not exist +DOCKERHUB_IMAGE_NAME="${DOCKERHUB_IMAGE_NAME:-"andrejorsula/space_robotics_bench"}" +# Options for running the container +DOCKER_RUN_OPTS="${DOCKER_RUN_OPTS:- + --interactive + --tty + --rm + --network host + --ipc host + --privileged +}" +# Flag to enable GPU +WITH_GPU="${WITH_GPU:-true}" +# Flag to enable GUI (X11) +WITH_GUI="${WITH_GUI:-true}" +# Flag to enable mounting the source code as a volume +WITH_DEV_VOLUME="${WITH_DEV_VOLUME:-true}" +# Volumes to mount inside the container +DOCKER_VOLUMES=( + ## Common + # Time + "/etc/localtime:/etc/localtime:ro" + "/etc/timezone:/etc/timezone:ro" + # Hot-pluggable IO devices + "/dev:/dev" + ## Isaac Sim + # Data + "${HOME}/.nvidia-omniverse/data/isaac-sim:/root/isaac-sim/kit/data:rw" + # Cache + "${HOME}/.cache/isaac-sim:/root/isaac-sim/kit/cache:rw" + "${HOME}/.cache/nvidia/GLCache:/root/.cache/nvidia/GLCache:rw" + "${HOME}/.cache/ov:/root/.cache/ov:rw" + "${HOME}/.nv/ComputeCache:/root/.nv/ComputeCache:rw" + # Logs + "${HOME}/.nvidia-omniverse/logs:/root/.nvidia-omniverse/logs:rw" + "${HOME}/.nvidia-omniverse/logs/isaac-sim:/root/isaac-sim/kit/logs:rw" + ## Project + # Cache + "${HOME}/.cache/srb:/root/.cache/srb:rw" +) +if [[ "${WITH_DEV_VOLUME,,}" = true ]]; then + DOCKER_VOLUMES+=( + "${REPOSITORY_DIR}:/root/ws:rw" + ) +fi +# Environment variables to set inside the container +DOCKER_ENVIRON=( + ROS_DOMAIN_ID="${ROS_DOMAIN_ID:-"0"}" + ROS_LOCALHOST_ONLY="${ROS_LOCALHOST_ONLY:-"1"}" + RMW_IMPLEMENTATION="${RMW_IMPLEMENTATION:-"rmw_cyclonedds_cpp"}" +) + +## DDS config +if [ -n "${CYCLONEDDS_URI}" ]; then + DOCKER_VOLUMES+=("${CYCLONEDDS_URI//file:\/\//}:/root/.ros/cyclonedds.xml:ro") + DOCKER_ENVIRON+=("CYCLONEDDS_URI=file:///root/.ros/cyclonedds.xml") +fi +if [ -n "${FASTRTPS_DEFAULT_PROFILES_FILE}" ]; then + DOCKER_VOLUMES+=("${FASTRTPS_DEFAULT_PROFILES_FILE}:/root/.ros/fastrtps.xml:ro") + DOCKER_ENVIRON+=("FASTRTPS_DEFAULT_PROFILES_FILE=/root/.ros/fastrtps.xml") +fi + +## Determine the name of the image to run +DOCKERHUB_USER="$(${WITH_SUDO} docker info 2>/dev/null | sed '/Username:/!d;s/.* //')" +PROJECT_NAME="$(basename "${REPOSITORY_DIR}")" +IMAGE_NAME="${DOCKERHUB_USER:+${DOCKERHUB_USER}/}${PROJECT_NAME}" +if [[ -z "$(${WITH_SUDO} docker images -q "${IMAGE_NAME}" 2>/dev/null)" ]] && [[ -n "$(curl -fsSL "https://registry.hub.docker.com/v2/repositories/${DOCKERHUB_IMAGE_NAME}" 2>/dev/null)" ]]; then + IMAGE_NAME="${DOCKERHUB_IMAGE_NAME}" +fi + +## Generate a unique container name +CONTAINER_NAME="${IMAGE_NAME##*/}" +CONTAINER_NAME="${CONTAINER_NAME//[^a-zA-Z0-9]/_}" +ALL_CONTAINER_NAMES=$(${WITH_SUDO} docker container list --all --format "{{.Names}}") +if echo "${ALL_CONTAINER_NAMES}" | grep -qi "${CONTAINER_NAME}"; then + ID=1 + while echo "${ALL_CONTAINER_NAMES}" | grep -qi "${CONTAINER_NAME}${ID}"; do + ID=$((ID + 1)) + done + CONTAINER_NAME="${CONTAINER_NAME}${ID}" +fi +DOCKER_RUN_OPTS+=" --name ${CONTAINER_NAME}" + +## Parse volumes and environment variables +while getopts ":v:e:" opt; do + case "${opt}" in + v) DOCKER_VOLUMES+=("${OPTARG}") ;; + e) DOCKER_ENVIRON+=("${OPTARG}") ;; + *) + echo >&2 "Usage: ${0} [-v HOST_DIR:DOCKER_DIR:OPTIONS] [-e ENV=VALUE] [TAG] [CMD]" + exit 2 + ;; + esac +done +shift "$((OPTIND - 1))" + +## Parse TAG and forward CMD arguments +if [ "${#}" -gt "0" ]; then + if [[ $(${WITH_SUDO} docker images --format "{{.Tag}}" "${IMAGE_NAME}") =~ (^|[[:space:]])${1}($|[[:space:]]) || $(curl -fsSL "https://registry.hub.docker.com/v2/repositories/${IMAGE_NAME}/tags" 2>/dev/null | grep -Poe '(?<=(\"name\":\")).*?(?=\")') =~ (^|[[:space:]])${1}($|[[:space:]]) ]]; then + IMAGE_NAME+=":${1}" + CMD=${*:2} + else + CMD=${*:1} + fi +fi + +## GPU +if [[ "${WITH_GPU,,}" = true ]]; then + check_nvidia_gpu() { + if [[ -n "${WITH_GPU_FORCE_NVIDIA}" ]]; then + if [[ "${WITH_GPU_FORCE_NVIDIA,,}" = true ]]; then + echo "[INFO] NVIDIA GPU is force-enabled via 'WITH_GPU_FORCE_NVIDIA=true'." + return 0 # NVIDIA GPU is force-enabled + else + echo "[INFO] NVIDIA GPU is force-disabled via 'WITH_GPU_FORCE_NVIDIA=false'." + return 1 # NVIDIA GPU is force-disabled + fi + elif ! lshw -C display 2>/dev/null | grep -qi "vendor.*nvidia"; then + return 1 # NVIDIA GPU is not present + elif ! command -v nvidia-smi >/dev/null 2>&1; then + echo >&2 -e "\e[33m[WARNING] NVIDIA GPU is detected, but its functionality cannot be verified. This container will not be able to use the GPU. Please install nvidia-utils on the host system or force-enable NVIDIA GPU via 'WITH_GPU_FORCE_NVIDIA=true'.\e[0m" + return 1 # NVIDIA GPU is present but nvidia-utils not installed + elif ! nvidia-smi -L &>/dev/null; then + echo >&2 -e "\e[33m[WARNING] NVIDIA GPU is detected, but it does not seem to be working properly. This container will not be able to use the GPU. Please ensure the NVIDIA drivers are properly installed on the host system.\e[0m" + return 1 # NVIDIA GPU is present but is not working properly + else + return 0 # NVIDIA GPU is present and appears to be working + fi + } + if check_nvidia_gpu; then + # Enable GPU either via NVIDIA Container Toolkit or NVIDIA Docker (depending on Docker version) + DOCKER_VERSION="$(${WITH_SUDO} docker version --format '{{.Server.Version}}')" + MIN_VERSION_FOR_TOOLKIT="19.3" + if [ "$(printf '%s\n' "${MIN_VERSION_FOR_TOOLKIT}" "${DOCKER_VERSION}" | sort -V | head -n1)" = "$MIN_VERSION_FOR_TOOLKIT" ]; then + DOCKER_RUN_OPTS+=" --gpus all" + else + DOCKER_RUN_OPTS+=" --runtime nvidia" + fi + DOCKER_ENVIRON+=( + NVIDIA_VISIBLE_DEVICES="all" + NVIDIA_DRIVER_CAPABILITIES="all" + ) + fi + if [[ -e /dev/dri ]]; then + DOCKER_RUN_OPTS+=" --device=/dev/dri:/dev/dri" + if [[ $(getent group video) ]]; then + DOCKER_RUN_OPTS+=" --group-add video" + fi + fi +fi + +## GUI +if [[ "${WITH_GUI,,}" = true ]]; then + # To enable GUI, make sure processes in the container can connect to the x server + XAUTH="${TMPDIR:-"/tmp"}/xauth_docker_${PROJECT_NAME}" + touch "${XAUTH}" + chmod a+r "${XAUTH}" + XAUTH_LIST=$(xauth nlist "${DISPLAY}") + if [ -n "${XAUTH_LIST}" ]; then + echo "${XAUTH_LIST}" | sed -e 's/^..../ffff/' | xauth -f "${XAUTH}" nmerge - + fi + # GUI-enabling volumes + DOCKER_VOLUMES+=( + "${XAUTH}:${XAUTH}" + "/tmp/.X11-unix:/tmp/.X11-unix" + "/dev/input:/dev/input" + ) + # GUI-enabling environment variables + DOCKER_ENVIRON+=( + DISPLAY="${DISPLAY}" + XAUTHORITY="${XAUTH}" + ) +fi + +## Run the container +DOCKER_RUN_CMD=( + "${WITH_SUDO}" docker run + "${DOCKER_RUN_OPTS}" + "${DOCKER_VOLUMES[@]/#/"--volume "}" + "${DOCKER_ENVIRON[@]/#/"--env "}" + "${IMAGE_NAME}" + "${CMD}" +) +echo -e "\033[1;90m[TRACE] ${DOCKER_RUN_CMD[*]}\033[0m" | xargs +# shellcheck disable=SC2048 +exec ${DOCKER_RUN_CMD[*]} diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..d86a7e0 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,36 @@ +## Python +**/__pycache__/ +**/.pytest_cache/ +**/*.pcd +**/*.py[cod] +**/*.pyc +**/*$py.class + +## Rust +target/ +**/*.rs.bk +**/*.pdb +**/.cargo/config.toml +!.cargo/config.toml + +## Shared Objects +**/*.so + +## Dump files +**/core.* + +## Logs +logs/ +*.log + +## Apptainer +**/*.sif + +## Docker +Dockerfile* +.docker/ +.dockerignore + +## Git +.git/ +.gitignore diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..a1cd7d2 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,11 @@ +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + + - package-ecosystem: "cargo" + directory: "/" + schedule: + interval: "weekly" diff --git a/.github/workflows/dependabot.yml b/.github/workflows/dependabot.yml new file mode 100644 index 0000000..17b7161 --- /dev/null +++ b/.github/workflows/dependabot.yml @@ -0,0 +1,25 @@ +name: Dependabot automation +on: pull_request + +permissions: + contents: write + pull-requests: write + +jobs: + dependabot_automation: + runs-on: ubuntu-latest + if: ${{ github.actor == 'dependabot[bot]' }} + env: + GH_TOKEN: ${{secrets.GITHUB_TOKEN}} + PR_URL: ${{github.event.pull_request.html_url}} + steps: + - name: Fetch metadata + id: metadata + uses: dependabot/fetch-metadata@v2 + with: + github-token: "${{ secrets.GITHUB_TOKEN }}" + - name: Approve the PR + run: gh pr review --approve "$PR_URL" + continue-on-error: true + - name: Enable auto-merge for the PR + run: gh pr merge --auto --squash "$PR_URL" diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml new file mode 100644 index 0000000..65eb15b --- /dev/null +++ b/.github/workflows/docker.yml @@ -0,0 +1,90 @@ +name: Docker + +on: + push: + branches: + - main + paths-ignore: + - "docs/**" + pull_request: + release: + types: [published] + workflow_dispatch: + +concurrency: + group: "${{ github.workflow }} @ ${{ github.event.pull_request.head.label || github.head_ref || github.ref }}" + cancel-in-progress: true + +jobs: + docker: + permissions: + packages: write + contents: read + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + dockerfile: + - Dockerfile + steps: + ## Free up space by removing unnecessary files + - name: Maximize build space + uses: AdityaGarg8/remove-unwanted-software@v4 + with: + remove-android: "true" + remove-cached-tools: "true" + remove-codeql: "true" + remove-docker-images: "true" + remove-dotnet: "true" + remove-haskell: "true" + remove-large-packages: "true" + + - uses: actions/checkout@v4 + + ## Login to Docker Hub and/or GitHub container registry + - name: Login to GitHub container registry + if: ${{ github.event_name == 'release' }} + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Login to Docker Hub + env: + DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }} + DOCKERHUB_PASSWORD: ${{ secrets.DOCKERHUB_PASSWORD }} + if: ${{ github.event_name == 'release' && env.DOCKERHUB_USERNAME != '' && env.DOCKERHUB_PASSWORD != '' }} + id: login-dockerhub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_PASSWORD }} + - name: Login to NGC + env: + NVCR_USERNAME: ${{ secrets.NVCR_USERNAME }} + NVCR_PASSWORD: ${{ secrets.NVCR_PASSWORD }} + if: ${{ env.NVCR_USERNAME != '' && env.NVCR_PASSWORD != '' }} + uses: docker/login-action@v3 + with: + registry: nvcr.io + username: ${{ secrets.NVCR_USERNAME }} + password: ${{ secrets.NVCR_PASSWORD }} + + ## Extract metadata (tags, labels) from Git reference and GitHub events for the Docker image + - name: Extract metadata + id: metadata + uses: docker/metadata-action@v5 + with: + images: | + name=ghcr.io/${{ github.repository }},enable=true + name=${{ github.repository }},enable=${{ steps.login-dockerhub.outcome == 'success' }} + + ## Build and push if the workflow was triggered by a release + - name: Build (and push on release) + uses: docker/build-push-action@v6 + with: + context: . + file: ${{ matrix.dockerfile }} + push: ${{ github.event_name == 'release' && matrix.dockerfile == 'Dockerfile' }} + tags: ${{ steps.metadata.outputs.tags }} + labels: ${{ steps.metadata.outputs.labels }} diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 0000000..868b988 --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,50 @@ +name: Docs + +on: + push: + branches: + - main + paths: + - ".github/workflows/docs.yml" + - "docs/**" + pull_request: + release: + types: [published] + workflow_dispatch: + +permissions: + contents: read + pages: write + id-token: write + +concurrency: + group: "${{ github.workflow }} @ ${{ github.event.pull_request.head.label || github.head_ref || github.ref }}" + cancel-in-progress: true + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Setup mdBook + uses: peaceiris/actions-mdbook@v2 + with: + mdbook-version: latest + - name: Build + run: mdbook build docs + - name: Upload artifact + uses: actions/upload-pages-artifact@v3 + with: + path: docs/book + + deploy: + if: ${{ github.event_name != 'pull_request' }} + needs: build + runs-on: ubuntu-latest + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + steps: + - name: Deploy + id: deployment + uses: actions/deploy-pages@v4 diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml new file mode 100644 index 0000000..ac43c8e --- /dev/null +++ b/.github/workflows/pre-commit.yml @@ -0,0 +1,32 @@ +name: pre-commit + +on: + push: + branches: + - main + pull_request: + workflow_dispatch: + +concurrency: + group: "${{ github.workflow }} @ ${{ github.event.pull_request.head.label || github.head_ref || github.ref }}" + cancel-in-progress: true + +env: + PYTHON_VERSION: "3.10" + SKIP: cargo-fmt,cargo-update,cargo-clippy,cargo-check,cargo-test,cargo-test-doc,cargo-doc,cargo-miri-test,cargo-miri-run,cargo-deny-check + +jobs: + pre-commit: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: ${{ env.PYTHON_VERSION }} + + ## Run pre-commit and try to apply fixes + - name: Run pre-commit + uses: pre-commit/action@v3.0.1 + - name: Apply fixes from pre-commit + uses: pre-commit-ci/lite-action@v1.1.0 + if: always() diff --git a/.github/workflows/python.yml b/.github/workflows/python.yml new file mode 100644 index 0000000..aa42b81 --- /dev/null +++ b/.github/workflows/python.yml @@ -0,0 +1,100 @@ +name: Python + +on: + push: + branches: + - main + paths-ignore: + - "docs/**" + pull_request: + release: + types: [published] + workflow_dispatch: + +concurrency: + group: "${{ github.workflow }} @ ${{ github.event.pull_request.head.label || github.head_ref || github.ref }}" + cancel-in-progress: true + +env: + CARGO_TERM_COLOR: always + PYTHON_VERSION: "3.10" + +jobs: + pytest: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: Swatinem/rust-cache@v2 + with: + save-if: ${{ github.event_name == 'push'}} + - uses: actions/setup-python@v5 + with: + python-version: ${{ env.PYTHON_VERSION }} + + - name: pip install + run: pip install .[tests] + - name: pytest + run: pytest + + wheels_sdist: + if: ${{ github.event_name == 'release' }} + needs: pytest + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Build sdist + uses: PyO3/maturin-action@v1 + with: + command: sdist + args: --out dist + - name: Upload sdist + uses: actions/upload-artifact@v4 + with: + name: wheels + path: dist + + wheels_linux: + if: ${{ github.event_name == 'release' }} + needs: pytest + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: ${{ env.PYTHON_VERSION }} + + - name: Build wheels + uses: PyO3/maturin-action@v1 + with: + args: --release --out dist --find-interpreter + sccache: "true" + manylinux: auto + - name: Upload wheels + uses: actions/upload-artifact@v4 + with: + name: wheels + path: dist + + pypi_release: + permissions: + contents: read + if: ${{ github.event_name == 'release' }} + needs: + - wheels_sdist + - wheels_linux + runs-on: ubuntu-latest + env: + MATURIN_PYPI_TOKEN: ${{ secrets.PYPI_TOKEN }} + steps: + - name: Download wheels + if: ${{ env.MATURIN_PYPI_TOKEN != '' }} + uses: actions/download-artifact@v4 + with: + name: wheels + - name: Publish to PyPI + if: ${{ env.MATURIN_PYPI_TOKEN != '' }} + uses: PyO3/maturin-action@v1 + with: + command: upload + args: --skip-existing * diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml new file mode 100644 index 0000000..579af8d --- /dev/null +++ b/.github/workflows/rust.yml @@ -0,0 +1,123 @@ +name: Rust + +on: + push: + branches: + - main + paths-ignore: + - "docs/**" + pull_request: + release: + types: [published] + workflow_dispatch: + +concurrency: + group: "${{ github.workflow }} @ ${{ github.event.pull_request.head.label || github.head_ref || github.ref }}" + cancel-in-progress: true + +env: + MSRV: "1.80" + CARGO_TERM_COLOR: always + PYTHON_PACKAGE_NAME: space_robotics_bench_py + GUI_PACKAGE_NAME: space_robotics_bench_gui + +jobs: + rustfmt: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + with: + components: rustfmt + + ## cargo fmt + - name: cargo fmt + run: cargo fmt --all --check --verbose + + cargo: + needs: rustfmt + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + toolchain: + - MSRV + - stable + - beta + steps: + - uses: actions/checkout@v4 + - uses: Swatinem/rust-cache@v2 + with: + save-if: ${{ github.event_name == 'push'}} + - uses: dtolnay/rust-toolchain@master + if: ${{ matrix.toolchain != 'MSRV' && matrix.toolchain != 'stable' }} + with: + toolchain: ${{ matrix.toolchain }} + - uses: dtolnay/rust-toolchain@master + if: ${{ matrix.toolchain == 'MSRV' }} + with: + toolchain: ${{ env.MSRV }} + - uses: dtolnay/rust-toolchain@master + if: ${{ matrix.toolchain == 'stable' }} + with: + toolchain: ${{ matrix.toolchain }} + components: clippy + + ## cargo check + - name: cargo check + run: cargo check --workspace --exclude ${{ env.PYTHON_PACKAGE_NAME }} --exclude ${{ env.GUI_PACKAGE_NAME }} --all-targets --verbose + - name: cargo check --no-default-features + run: cargo check --workspace --exclude ${{ env.PYTHON_PACKAGE_NAME }} --exclude ${{ env.GUI_PACKAGE_NAME }} --all-targets --no-default-features --verbose + - name: cargo check --all-features + run: cargo check --workspace --exclude ${{ env.PYTHON_PACKAGE_NAME }} --exclude ${{ env.GUI_PACKAGE_NAME }} --all-targets --all-features --verbose + + ## cargo test + - name: cargo test + run: cargo test --workspace --exclude ${{ env.PYTHON_PACKAGE_NAME }} --exclude ${{ env.GUI_PACKAGE_NAME }} --all-targets --verbose + - name: cargo test --no-default-features + run: cargo test --workspace --exclude ${{ env.PYTHON_PACKAGE_NAME }} --exclude ${{ env.GUI_PACKAGE_NAME }} --all-targets --no-default-features --verbose + - name: cargo test --all-features + run: cargo test --workspace --exclude ${{ env.PYTHON_PACKAGE_NAME }} --exclude ${{ env.GUI_PACKAGE_NAME }} --all-targets --all-features --verbose + + ## cargo test --doc + - name: cargo test --doc + run: cargo test --workspace --exclude ${{ env.PYTHON_PACKAGE_NAME }} --exclude ${{ env.GUI_PACKAGE_NAME }} --doc --verbose + - name: cargo test --doc --no-default-features + run: cargo test --workspace --exclude ${{ env.PYTHON_PACKAGE_NAME }} --exclude ${{ env.GUI_PACKAGE_NAME }} --doc --no-default-features --verbose + - name: cargo test --doc --all-features + run: cargo test --workspace --exclude ${{ env.PYTHON_PACKAGE_NAME }} --exclude ${{ env.GUI_PACKAGE_NAME }} --doc --all-features --verbose + + ## [stable] cargo clippy + - name: stable | cargo clippy + if: ${{ matrix.toolchain == 'stable' }} + run: cargo clippy --workspace --exclude ${{ env.PYTHON_PACKAGE_NAME }} --exclude ${{ env.GUI_PACKAGE_NAME }} --all-targets --all-features --no-deps --verbose -- --deny warnings + + ## [stable] cargo doc + - name: stable | cargo doc --document-private-items + if: ${{ matrix.toolchain == 'stable' }} + run: cargo doc --workspace --exclude ${{ env.PYTHON_PACKAGE_NAME }} --exclude ${{ env.GUI_PACKAGE_NAME }} --all-features --no-deps --document-private-items --verbose + + ## [stable] Code coverage + - name: stable | Install cargo llvm-cov for code coverage + uses: taiki-e/install-action@cargo-llvm-cov + if: ${{ matrix.toolchain == 'stable' }} + ## [stable] Generate coverage with cargo llvm-cov + - name: stable | Generate coverage + if: ${{ matrix.toolchain == 'stable' }} + run: cargo llvm-cov --workspace --exclude ${{ env.PYTHON_PACKAGE_NAME }} --exclude ${{ env.GUI_PACKAGE_NAME }} --all-features --lcov --output-path lcov.info + ## [stable] Upload coverage to codecov.io + - name: stable | Upload coverage + if: ${{ matrix.toolchain == 'stable' }} + uses: codecov/codecov-action@v4 + with: + token: ${{ secrets.CODECOV_TOKEN }} + files: lcov.info + fail_ci_if_error: false + + deny: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: EmbarkStudios/cargo-deny-action@v1 + with: + command: check bans licenses sources diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fdf9f8a --- /dev/null +++ b/.gitignore @@ -0,0 +1,27 @@ +## Python +**/__pycache__/ +**/.pytest_cache/ +**/*.pcd +**/*.py[cod] +**/*.pyc +**/*$py.class + +## Rust +target/ +**/*.rs.bk +**/*.pdb +**/.cargo/config.toml +!.cargo/config.toml + +## Shared Objects +**/*.so + +## Dump files +**/core.* + +## Logs +logs/ +*.log + +## Apptainer +**/*.sif diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..1a423f6 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,4 @@ +[submodule "assets/srb_assets"] + path = assets/srb_assets + url = https://github.com/AndrejOrsula/srb_assets + branch = main diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..38f359d --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,80 @@ +# See https://pre-commit.com for more information +# See https://pre-commit.com/hooks.html for more hooks + +exclude: Cargo.lock|.*/patches/.*|.*hdr|.*svg|.*png|.*jpg +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.6.0 + hooks: + - id: check-added-large-files + args: ["--maxkb=1024"] + - id: check-ast + - id: check-builtin-literals + - id: check-case-conflict + - id: check-docstring-first + - id: check-executables-have-shebangs + - id: check-merge-conflict + - id: check-shebang-scripts-are-executable + exclude: \.rs$ + - id: check-symlinks + - id: check-toml + - id: check-vcs-permalinks + - id: check-xml + - id: check-yaml + - id: debug-statements + - id: destroyed-symlinks + - id: detect-private-key + - id: end-of-file-fixer + - id: fix-byte-order-marker + - id: mixed-line-ending + - id: name-tests-test + - id: trailing-whitespace + + - repo: https://github.com/lovesegfault/beautysh + rev: v6.2.1 + hooks: + - id: beautysh + + - repo: https://github.com/psf/black + rev: 24.10.0 + hooks: + - id: black + + - repo: https://github.com/codespell-project/codespell + rev: v2.3.0 + hooks: + - id: codespell + args: ["--ignore-words-list", "crate"] + + - repo: https://github.com/hadolint/hadolint + rev: v2.13.1-beta + hooks: + - id: hadolint-docker + + - repo: https://github.com/pycqa/isort + rev: 5.13.2 + hooks: + - id: isort + args: ["--profile", "black"] + + - repo: https://github.com/executablebooks/mdformat + rev: 0.7.18 + hooks: + - id: mdformat + + - repo: https://github.com/AndrejOrsula/pre-commit-cargo + rev: 0.3.0 + hooks: + - id: cargo-fmt + - id: cargo-update + - id: cargo-clippy + args: ["--workspace", "--all-targets", "--", "--deny=warnings"] + - id: cargo-check + args: ["--workspace", "--all-targets"] + - id: cargo-test + args: ["--workspace", "--all-targets"] + - id: cargo-test-doc + args: ["--workspace"] + - id: cargo-doc + args: ["--workspace", "--no-deps", "--document-private-items"] + - id: cargo-deny-check diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..5561ba2 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,582 @@ +{ + "files.associations": { + "*.kit": "toml" + }, + "python.defaultInterpreterPath": "~/isaac-sim/kit/python/bin/python3", + "python.analysis.extraPaths": [ + "scripts", + "${workspaceFolder}/../isaaclab/source/extensions/omni.isaac.lab", + "${workspaceFolder}/../isaaclab/source/extensions/omni.isaac.lab_assets", + "${workspaceFolder}/../isaaclab/source/extensions/omni.isaac.lab_tasks", + "~/isaac-sim/exts/omni.exporter.urdf", + "~/isaac-sim/exts/omni.exporter.urdf/pip_prebundle", + "~/isaac-sim/exts/omni.isaac.app.selector", + "~/isaac-sim/exts/omni.isaac.app.setup", + "~/isaac-sim/exts/omni.isaac.articulation_inspector", + "~/isaac-sim/exts/omni.isaac.asset_browser", + "~/isaac-sim/exts/omni.isaac.assets_check", + "~/isaac-sim/exts/omni.isaac.benchmark.services", + "~/isaac-sim/exts/omni.isaac.benchmarks", + "~/isaac-sim/exts/omni.isaac.block_world", + "~/isaac-sim/exts/omni.isaac.camera_inspector", + "~/isaac-sim/exts/omni.isaac.cloner", + "~/isaac-sim/exts/omni.isaac.common_includes", + "~/isaac-sim/exts/omni.isaac.conveyor", + "~/isaac-sim/exts/omni.isaac.conveyor.ui", + "~/isaac-sim/exts/omni.isaac.core", + "~/isaac-sim/exts/omni.isaac.core_archive", + "~/isaac-sim/exts/omni.isaac.core_archive/pip_prebundle", + "~/isaac-sim/exts/omni.isaac.core_nodes", + "~/isaac-sim/exts/omni.isaac.cortex", + "~/isaac-sim/exts/omni.isaac.cortex.sample_behaviors", + "~/isaac-sim/exts/omni.isaac.cortex_sync", + "~/isaac-sim/exts/omni.isaac.debug_draw", + "~/isaac-sim/exts/omni.isaac.doctest", + "~/isaac-sim/exts/omni.isaac.dynamic_control", + "~/isaac-sim/exts/omni.isaac.examples", + "~/isaac-sim/exts/omni.isaac.examples_nodes", + "~/isaac-sim/exts/omni.isaac.extension_templates", + "~/isaac-sim/exts/omni.isaac.franka", + "~/isaac-sim/exts/omni.isaac.gain_tuner", + "~/isaac-sim/exts/omni.isaac.grasp_editor", + "~/isaac-sim/exts/omni.isaac.import_wizard", + "~/isaac-sim/exts/omni.isaac.internal_tools", + "~/isaac-sim/exts/omni.isaac.jupyter_notebook", + "~/isaac-sim/exts/omni.isaac.kit", + "~/isaac-sim/exts/omni.isaac.lula", + "~/isaac-sim/exts/omni.isaac.lula/pip_prebundle", + "~/isaac-sim/exts/omni.isaac.lula_test_widget", + "~/isaac-sim/exts/omni.isaac.manipulators", + "~/isaac-sim/exts/omni.isaac.manipulators.ui", + "~/isaac-sim/exts/omni.isaac.menu", + "~/isaac-sim/exts/omni.isaac.merge_mesh", + "~/isaac-sim/exts/omni.isaac.ml_archive", + "~/isaac-sim/exts/omni.isaac.ml_archive/pip_prebundle", + "~/isaac-sim/exts/omni.isaac.motion_generation", + "~/isaac-sim/exts/omni.isaac.nucleus", + "~/isaac-sim/exts/omni.isaac.occupancy_map", + "~/isaac-sim/exts/omni.isaac.occupancy_map.ui", + "~/isaac-sim/exts/omni.isaac.ocs2", + "~/isaac-sim/exts/omni.isaac.physics_inspector", + "~/isaac-sim/exts/omni.isaac.physics_utilities", + "~/isaac-sim/exts/omni.isaac.proximity_sensor", + "~/isaac-sim/exts/omni.isaac.quadruped", + "~/isaac-sim/exts/omni.isaac.range_sensor", + "~/isaac-sim/exts/omni.isaac.range_sensor.examples", + "~/isaac-sim/exts/omni.isaac.range_sensor.ui", + "~/isaac-sim/exts/omni.isaac.repl", + "~/isaac-sim/exts/omni.isaac.robot_assembler", + "~/isaac-sim/exts/omni.isaac.robot_description_editor", + "~/isaac-sim/exts/omni.isaac.ros2_bridge", + "~/isaac-sim/exts/omni.isaac.ros2_bridge.robot_description", + "~/isaac-sim/exts/omni.isaac.ros_bridge", + "~/isaac-sim/exts/omni.isaac.scene_blox", + "~/isaac-sim/exts/omni.isaac.sensor", + "~/isaac-sim/exts/omni.isaac.surface_gripper", + "~/isaac-sim/exts/omni.isaac.surface_gripper.ui", + "~/isaac-sim/exts/omni.isaac.synthetic_recorder", + "~/isaac-sim/exts/omni.isaac.tests", + "~/isaac-sim/exts/omni.isaac.tf_viewer", + "~/isaac-sim/exts/omni.isaac.throttling", + "~/isaac-sim/exts/omni.isaac.ui", + "~/isaac-sim/exts/omni.isaac.ui_template", + "~/isaac-sim/exts/omni.isaac.universal_robots", + "~/isaac-sim/exts/omni.isaac.utils", + "~/isaac-sim/exts/omni.isaac.version", + "~/isaac-sim/exts/omni.isaac.vscode", + "~/isaac-sim/exts/omni.isaac.wheeled_robots", + "~/isaac-sim/exts/omni.isaac.wheeled_robots.ui", + "~/isaac-sim/exts/omni.isaac.window.about", + "~/isaac-sim/exts/omni.kit.loop-isaac", + "~/isaac-sim/exts/omni.kit.property.isaac", + "~/isaac-sim/exts/omni.pip.cloud", + "~/isaac-sim/exts/omni.pip.cloud/pip_prebundle", + "~/isaac-sim/exts/omni.pip.compute", + "~/isaac-sim/exts/omni.pip.compute/pip_prebundle", + "~/isaac-sim/exts/omni.replicator.isaac", + "~/isaac-sim/exts/omni.usd.schema.isaac", + "~/isaac-sim/extsPhysics/omni.convexdecomposition", + "~/isaac-sim/extsPhysics/omni.kit.property.physx", + "~/isaac-sim/extsPhysics/omni.kvdb", + "~/isaac-sim/extsPhysics/omni.localcache", + "~/isaac-sim/extsPhysics/omni.physics.tensors", + "~/isaac-sim/extsPhysics/omni.physics.tensors.tests", + "~/isaac-sim/extsPhysics/omni.physx", + "~/isaac-sim/extsPhysics/omni.physx.bundle", + "~/isaac-sim/extsPhysics/omni.physx.camera", + "~/isaac-sim/extsPhysics/omni.physx.cct", + "~/isaac-sim/extsPhysics/omni.physx.commands", + "~/isaac-sim/extsPhysics/omni.physx.cooking", + "~/isaac-sim/extsPhysics/omni.physx.demos", + "~/isaac-sim/extsPhysics/omni.physx.fabric", + "~/isaac-sim/extsPhysics/omni.physx.forcefields", + "~/isaac-sim/extsPhysics/omni.physx.foundation", + "~/isaac-sim/extsPhysics/omni.physx.graph", + "~/isaac-sim/extsPhysics/omni.physx.internal", + "~/isaac-sim/extsPhysics/omni.physx.pvd", + "~/isaac-sim/extsPhysics/omni.physx.stageupdate", + "~/isaac-sim/extsPhysics/omni.physx.supportui", + "~/isaac-sim/extsPhysics/omni.physx.telemetry", + "~/isaac-sim/extsPhysics/omni.physx.tensors", + "~/isaac-sim/extsPhysics/omni.physx.tests", + "~/isaac-sim/extsPhysics/omni.physx.tests.mini", + "~/isaac-sim/extsPhysics/omni.physx.tests.visual", + "~/isaac-sim/extsPhysics/omni.physx.ui", + "~/isaac-sim/extsPhysics/omni.physx.vehicle", + "~/isaac-sim/extsPhysics/omni.physx.vehicle.tests", + "~/isaac-sim/extsPhysics/omni.physx.zerogravity", + "~/isaac-sim/extsPhysics/omni.usd.schema.forcefield", + "~/isaac-sim/extsPhysics/omni.usd.schema.physx", + "~/isaac-sim/extsPhysics/omni.usdphysics", + "~/isaac-sim/extsPhysics/omni.usdphysics.tests", + "~/isaac-sim/extsPhysics/omni.usdphysics.ui", + "~/isaac-sim/extscache/carb.audio-0.1.0+10a4b5c0.lx64.r.cp310", + "~/isaac-sim/extscache/carb.imaging.python-0.1.0+10a4b5c0.lx64.r.cp310", + "~/isaac-sim/extscache/carb.windowing.plugins-1.0.0+10a4b5c0.lx64.r", + "~/isaac-sim/extscache/omni.activity.core-1.0.1+10a4b5c0.lx64.r.cp310", + "~/isaac-sim/extscache/omni.anim.asset-106.1.0+106.1.0.lx64.r", + "~/isaac-sim/extscache/omni.anim.behavior.schema-106.1.0+106.1.0.lx64.r.cp310", + "~/isaac-sim/extscache/omni.anim.curve.bundle-1.2.3+106.1.0.ub3f", + "~/isaac-sim/extscache/omni.anim.curve.core-1.1.14+106.1.0.lx64.r.cp310.ub3f", + "~/isaac-sim/extscache/omni.anim.curve.ui-1.3.17+106.1.0.ub3f", + "~/isaac-sim/extscache/omni.anim.curve_editor-105.17.10+106.0.1.ub3f", + "~/isaac-sim/extscache/omni.anim.graph.bundle-106.1.0+106.1.0", + "~/isaac-sim/extscache/omni.anim.graph.core-106.1.2+106.1.0.lx64.r.cp310", + "~/isaac-sim/extscache/omni.anim.graph.schema-106.1.0+106.1.0.lx64.r.cp310", + "~/isaac-sim/extscache/omni.anim.graph.ui-106.1.1+106.1.0", + "~/isaac-sim/extscache/omni.anim.navigation.bundle-106.1.0+106.1.0", + "~/isaac-sim/extscache/omni.anim.navigation.core-106.1.3+106.1.0.lx64.r.cp310", + "~/isaac-sim/extscache/omni.anim.navigation.navmesh.recast-106.1.4+106.1.0.lx64.r.cp310", + "~/isaac-sim/extscache/omni.anim.navigation.schema-106.1.0+106.1.0.lx64.r.cp310", + "~/isaac-sim/extscache/omni.anim.navigation.ui-106.1.1+106.1.0", + "~/isaac-sim/extscache/omni.anim.people-0.5.0", + "~/isaac-sim/extscache/omni.anim.retarget.bundle-106.1.0+106.1.0", + "~/isaac-sim/extscache/omni.anim.retarget.core-106.1.2+106.1.0.lx64.r.cp310", + "~/isaac-sim/extscache/omni.anim.retarget.ui-106.1.1+106.1.0", + "~/isaac-sim/extscache/omni.anim.shared.core-106.0.1+106.0.1.lx64.r.cp310", + "~/isaac-sim/extscache/omni.anim.skelJoint-106.1.2+106.1.0.lx64.r.cp310", + "~/isaac-sim/extscache/omni.anim.timeline-105.0.23+106.1.0.lx64.r.cp310.ub3f", + "~/isaac-sim/extscache/omni.anim.window.timeline-105.13.5+106.0.1.ub3f", + "~/isaac-sim/extscache/omni.appwindow-1.1.8+10a4b5c0.lx64.r.cp310", + "~/isaac-sim/extscache/omni.asset_validator.core-0.11.8", + "~/isaac-sim/extscache/omni.asset_validator.ui-0.11.8", + "~/isaac-sim/extscache/omni.blobkey-1.1.2+10a4b5c0.lx64.r", + "~/isaac-sim/extscache/omni.command.usd-1.0.3+10a4b5c0", + "~/isaac-sim/extscache/omni.cuopt.examples-1.0.0+106.0.0", + "~/isaac-sim/extscache/omni.cuopt.service-1.0.0+106.0.0", + "~/isaac-sim/extscache/omni.cuopt.visualization-1.0.0+106.0.0", + "~/isaac-sim/extscache/omni.curve.creator-105.0.4+106.1.0.lx64.r.cp310", + "~/isaac-sim/extscache/omni.curve.manipulator-105.2.8+106.1.0.lx64.r.cp310", + "~/isaac-sim/extscache/omni.datastore-0.0.0+10a4b5c0.lx64.r", + "~/isaac-sim/extscache/omni.debugdraw-0.1.3+10a4b5c0.lx64.r.cp310", + "~/isaac-sim/extscache/omni.extended.materials-105.0.9", + "~/isaac-sim/extscache/omni.fabric.commands-1.1.5+10a4b5c0.lx64.r.cp310", + "~/isaac-sim/extscache/omni.flowusd-106.1.1+106.1.0.lx64.r.cp310.ub3f", + "~/isaac-sim/extscache/omni.genproc.core-106.1.0+106.1.0.lx64.r.cp310", + "~/isaac-sim/extscache/omni.gpu_foundation-0.0.0+10a4b5c0.lx64.r.cp310", + "~/isaac-sim/extscache/omni.gpu_foundation.shadercache.vulkan-1.0.0+10a4b5c0.lx64.r", + "~/isaac-sim/extscache/omni.gpucompute.plugins-0.0.0+10a4b5c0.lx64.r", + "~/isaac-sim/extscache/omni.graph-1.140.0+10a4b5c0.lx64.r.cp310", + "~/isaac-sim/extscache/omni.graph.action-1.102.1+106.0.3", + "~/isaac-sim/extscache/omni.graph.action_core-1.1.6+10a4b5c0.lx64.r.cp310", + "~/isaac-sim/extscache/omni.graph.action_nodes-1.24.0+106.1.0.lx64.r.cp310", + "~/isaac-sim/extscache/omni.graph.bundle.action-2.4.1+106.0.3", + "~/isaac-sim/extscache/omni.graph.core-2.179.2+10a4b5c0.lx64.r", + "~/isaac-sim/extscache/omni.graph.exec-0.9.4+10a4b5c0.lx64.r", + "~/isaac-sim/extscache/omni.graph.image.core-0.4.5+10a4b5c0.lx64.r", + "~/isaac-sim/extscache/omni.graph.image.nodes-1.1.0+10a4b5c0.lx64.r", + "~/isaac-sim/extscache/omni.graph.io-1.9.1+106.0.3", + "~/isaac-sim/extscache/omni.graph.nodes-1.146.1+106.1.0.lx64.r.cp310", + "~/isaac-sim/extscache/omni.graph.scriptnode-1.20.1+106.1.0", + "~/isaac-sim/extscache/omni.graph.telemetry-2.15.0+106.1.0.lx64.r.cp310", + "~/isaac-sim/extscache/omni.graph.tools-1.79.0+10a4b5c0", + "~/isaac-sim/extscache/omni.graph.tutorials-1.29.0+106.1.0.lx64.r.cp310", + "~/isaac-sim/extscache/omni.graph.ui-1.70.2+106.1.0.lx64.r.cp310", + "~/isaac-sim/extscache/omni.graph.ui_nodes-1.26.0+106.1.0.lx64.r.cp310", + "~/isaac-sim/extscache/omni.graph.visualization.nodes-2.1.1", + "~/isaac-sim/extscache/omni.graph.window.action-1.28.0+106.1.0", + "~/isaac-sim/extscache/omni.graph.window.core-1.113.1+106.1.0", + "~/isaac-sim/extscache/omni.graph.window.generic-1.26.0+106.1.0", + "~/isaac-sim/extscache/omni.hsscclient-1.1.1+10a4b5c0.lx64.r.cp310", + "~/isaac-sim/extscache/omni.hydra.engine.stats-1.0.2+10a4b5c0.lx64.r.cp310", + "~/isaac-sim/extscache/omni.hydra.iray.shadercache.vulkan-1.0.0+10a4b5c0.lx64.r", + "~/isaac-sim/extscache/omni.hydra.rtx-1.0.0+10a4b5c0.lx64.r", + "~/isaac-sim/extscache/omni.hydra.rtx.shadercache.vulkan-1.0.0+10a4b5c0.lx64.r", + "~/isaac-sim/extscache/omni.hydra.scene_api-0.1.2+10a4b5c0.lx64.r.cp310", + "~/isaac-sim/extscache/omni.hydra.scene_delegate-0.3.3+10a4b5c0.lx64.r", + "~/isaac-sim/extscache/omni.hydra.usdrt_delegate-7.5.1+10a4b5c0.lx64.r.cp310", + "~/isaac-sim/extscache/omni.importer.mjcf-1.1.1+106.0.0.lx64.r.cp310", + "~/isaac-sim/extscache/omni.importer.onshape-0.7.3+106.0.0", + "~/isaac-sim/extscache/omni.importer.urdf-1.14.1+106.0.0.lx64.r.cp310", + "~/isaac-sim/extscache/omni.inspect-1.0.1+10a4b5c0.lx64.r.cp310", + "~/isaac-sim/extscache/omni.iray.libs-0.0.0+10a4b5c0.lx64.r", + "~/isaac-sim/extscache/omni.kit.actions.core-1.0.0+10a4b5c0.lx64.r.cp310", + "~/isaac-sim/extscache/omni.kit.actions.window-1.1.1+10a4b5c0", + "~/isaac-sim/extscache/omni.kit.agent.watcher-0.2.1", + "~/isaac-sim/extscache/omni.kit.asset_converter-2.1.21+lx64.r.cp310", + "~/isaac-sim/extscache/omni.kit.audiodeviceenum-1.0.1+10a4b5c0.lx64.r.cp310", + "~/isaac-sim/extscache/omni.kit.browser.asset-1.3.10", + "~/isaac-sim/extscache/omni.kit.browser.asset_provider.local-1.0.9", + "~/isaac-sim/extscache/omni.kit.browser.core-2.3.11", + "~/isaac-sim/extscache/omni.kit.browser.deepsearch-1.1.8", + "~/isaac-sim/extscache/omni.kit.browser.folder.core-1.9.13", + "~/isaac-sim/extscache/omni.kit.browser.material-1.6.0", + "~/isaac-sim/extscache/omni.kit.browser.sample-1.4.8", + "~/isaac-sim/extscache/omni.kit.browser.texture-1.2.1", + "~/isaac-sim/extscache/omni.kit.capture.viewport-1.5.1", + "~/isaac-sim/extscache/omni.kit.clipboard-1.0.4+10a4b5c0.lx64.r.cp310", + "~/isaac-sim/extscache/omni.kit.collaboration.channel_manager-1.0.12+10a4b5c0", + "~/isaac-sim/extscache/omni.kit.collaboration.presence_layer-1.0.9+10a4b5c0", + "~/isaac-sim/extscache/omni.kit.collaboration.telemetry-1.0.0+10a4b5c0.lx64.r.cp310", + "~/isaac-sim/extscache/omni.kit.commands-1.4.9+10a4b5c0.lx64.r.cp310", + "~/isaac-sim/extscache/omni.kit.context_menu-1.8.1+10a4b5c0", + "~/isaac-sim/extscache/omni.kit.converter.cad-201.1.0+106.0.1", + "~/isaac-sim/extscache/omni.kit.converter.common-500.0.10+106.1.0.lx64.r.cp310", + "~/isaac-sim/extscache/omni.kit.converter.dgn-500.0.6+106.1.0", + "~/isaac-sim/extscache/omni.kit.converter.dgn_core-500.0.19+106.1.0.lx64.r.cp310", + "~/isaac-sim/extscache/omni.kit.converter.geojson-0.0.10+lx64.r.cp310", + "~/isaac-sim/extscache/omni.kit.converter.hoops-500.0.7+106.1.0", + "~/isaac-sim/extscache/omni.kit.converter.hoops_core-500.0.17+106.1.0.lx64.r.cp310", + "~/isaac-sim/extscache/omni.kit.converter.jt-500.0.7+106.1.0", + "~/isaac-sim/extscache/omni.kit.converter.jt_core-500.0.16+106.1.0.lx64.r.cp310", + "~/isaac-sim/extscache/omni.kit.converter.lib3mf-1.1.3", + "~/isaac-sim/extscache/omni.kit.converter.ogc-1.1.22+lx64.r.cp310", + "~/isaac-sim/extscache/omni.kit.converter.stl-0.1.1+105.1", + "~/isaac-sim/extscache/omni.kit.converter.vtk-2.3.1+105.2.lx64.r.cp310", + "~/isaac-sim/extscache/omni.kit.core.collection-0.1.7", + "~/isaac-sim/extscache/omni.kit.data2ui.core-1.0.27+106.0", + "~/isaac-sim/extscache/omni.kit.data2ui.usd-1.0.27+106.0", + "~/isaac-sim/extscache/omni.kit.environment.core-1.3.14", + "~/isaac-sim/extscache/omni.kit.exec.core-0.13.4+10a4b5c0.lx64.r.cp310", + "~/isaac-sim/extscache/omni.kit.gfn-106.0.5+106.0.0.lx64.r.cp310", + "~/isaac-sim/extscache/omni.kit.graph.delegate.default-1.2.2", + "~/isaac-sim/extscache/omni.kit.graph.delegate.modern-1.10.6", + "~/isaac-sim/extscache/omni.kit.graph.delegate.neo-1.1.3", + "~/isaac-sim/extscache/omni.kit.graph.editor.core-1.5.3", + "~/isaac-sim/extscache/omni.kit.graph.editor.example-1.0.24", + "~/isaac-sim/extscache/omni.kit.graph.usd.commands-1.3.1", + "~/isaac-sim/extscache/omni.kit.graph.widget.variables-2.1.0", + "~/isaac-sim/extscache/omni.kit.helper.file_utils-0.1.8+10a4b5c0", + "~/isaac-sim/extscache/omni.kit.hotkeys.core-1.3.5+10a4b5c0", + "~/isaac-sim/extscache/omni.kit.hotkeys.window-1.4.5+10a4b5c0", + "~/isaac-sim/extscache/omni.kit.hydra_texture-1.3.9+10a4b5c0.lx64.r.cp310", + "~/isaac-sim/extscache/omni.kit.livestream.core-3.2.0+105.2.lx64.r.cp310", + "~/isaac-sim/extscache/omni.kit.livestream.core-4.3.6+106.0.0.lx64.r.cp310", + "~/isaac-sim/extscache/omni.kit.livestream.messaging-1.1.1", + "~/isaac-sim/extscache/omni.kit.livestream.native-4.1.0+106.0.0.lx64.r.cp310", + "~/isaac-sim/extscache/omni.kit.livestream.webrtc-4.1.1+106.0.0.lx64.r.cp310", + "~/isaac-sim/extscache/omni.kit.mainwindow-1.0.3+10a4b5c0", + "~/isaac-sim/extscache/omni.kit.manipulator.camera-105.0.5+10a4b5c0", + "~/isaac-sim/extscache/omni.kit.manipulator.prim-107.0.0+10a4b5c0", + "~/isaac-sim/extscache/omni.kit.manipulator.prim.core-107.0.4+10a4b5c0", + "~/isaac-sim/extscache/omni.kit.manipulator.prim.fabric-107.0.3+10a4b5c0", + "~/isaac-sim/extscache/omni.kit.manipulator.prim.usd-107.0.2+10a4b5c0", + "~/isaac-sim/extscache/omni.kit.manipulator.selection-106.0.1+10a4b5c0", + "~/isaac-sim/extscache/omni.kit.manipulator.selector-1.1.1+10a4b5c0", + "~/isaac-sim/extscache/omni.kit.manipulator.tool.mesh_snap-1.4.5+106.0.0", + "~/isaac-sim/extscache/omni.kit.manipulator.tool.snap-1.5.11+10a4b5c0", + "~/isaac-sim/extscache/omni.kit.manipulator.transform-104.7.4+10a4b5c0", + "~/isaac-sim/extscache/omni.kit.manipulator.viewport-107.0.0+10a4b5c0", + "~/isaac-sim/extscache/omni.kit.material.library-1.5.6+10a4b5c0", + "~/isaac-sim/extscache/omni.kit.menu.aov-1.1.4+10a4b5c0", + "~/isaac-sim/extscache/omni.kit.menu.common-1.1.7+10a4b5c0", + "~/isaac-sim/extscache/omni.kit.menu.core-1.0.4+10a4b5c0", + "~/isaac-sim/extscache/omni.kit.menu.create-1.0.16+10a4b5c0", + "~/isaac-sim/extscache/omni.kit.menu.edit-1.1.24+10a4b5c0", + "~/isaac-sim/extscache/omni.kit.menu.file-1.1.14+10a4b5c0", + "~/isaac-sim/extscache/omni.kit.menu.stage-1.2.5", + "~/isaac-sim/extscache/omni.kit.menu.utils-1.5.27+10a4b5c0", + "~/isaac-sim/extscache/omni.kit.mesh.raycast-105.4.0+106.0.0.lx64.r.cp310", + "~/isaac-sim/extscache/omni.kit.ngsearch-0.3.3", + "~/isaac-sim/extscache/omni.kit.notification_manager-1.0.9+10a4b5c0", + "~/isaac-sim/extscache/omni.kit.numpy.common-0.1.2+10a4b5c0.lx64.r.cp310", + "~/isaac-sim/extscache/omni.kit.pip_archive-0.0.0+10a4b5c0.lx64.cp310", + "~/isaac-sim/extscache/omni.kit.pip_archive-0.0.0+10a4b5c0.lx64.cp310/pip_prebundle", + "~/isaac-sim/extscache/omni.kit.pipapi-0.0.0+10a4b5c0", + "~/isaac-sim/extscache/omni.kit.playlist.core-1.3.4", + "~/isaac-sim/extscache/omni.kit.pointclouds-1.4.3+cp310", + "~/isaac-sim/extscache/omni.kit.preferences.animation-1.1.8+106.1.0.ub3f", + "~/isaac-sim/extscache/omni.kit.prim.icon-1.0.13", + "~/isaac-sim/extscache/omni.kit.primitive.mesh-1.0.17+10a4b5c0", + "~/isaac-sim/extscache/omni.kit.profiler.tracy-1.1.5+106.0.0.lx64", + "~/isaac-sim/extscache/omni.kit.profiler.window-2.2.3", + "~/isaac-sim/extscache/omni.kit.property.adapter.core-1.0.1+10a4b5c0", + "~/isaac-sim/extscache/omni.kit.property.adapter.fabric-1.0.1+10a4b5c0", + "~/isaac-sim/extscache/omni.kit.property.adapter.usd-1.0.1+10a4b5c0", + "~/isaac-sim/extscache/omni.kit.property.audio-1.0.14+10a4b5c0", + "~/isaac-sim/extscache/omni.kit.property.bundle-1.3.1+10a4b5c0", + "~/isaac-sim/extscache/omni.kit.property.camera-1.0.8+10a4b5c0", + "~/isaac-sim/extscache/omni.kit.property.collection-0.1.17", + "~/isaac-sim/extscache/omni.kit.property.environment-1.2.1", + "~/isaac-sim/extscache/omni.kit.property.geometry-1.3.1+10a4b5c0", + "~/isaac-sim/extscache/omni.kit.property.layer-1.1.8+10a4b5c0", + "~/isaac-sim/extscache/omni.kit.property.light-1.0.10+10a4b5c0", + "~/isaac-sim/extscache/omni.kit.property.material-1.10.8+10a4b5c0", + "~/isaac-sim/extscache/omni.kit.property.render-1.1.2+10a4b5c0", + "~/isaac-sim/extscache/omni.kit.property.sbsar-107.0.0", + "~/isaac-sim/extscache/omni.kit.property.transform-1.5.9+10a4b5c0", + "~/isaac-sim/extscache/omni.kit.property.usd-4.2.8+10a4b5c0", + "~/isaac-sim/extscache/omni.kit.property.visualization-104.0.5", + "~/isaac-sim/extscache/omni.kit.quicklayout-1.0.7+10a4b5c0", + "~/isaac-sim/extscache/omni.kit.raycast.query-1.0.5+10a4b5c0.lx64.r.cp310", + "~/isaac-sim/extscache/omni.kit.renderer.capture-0.0.0+10a4b5c0.lx64.r.cp310", + "~/isaac-sim/extscache/omni.kit.renderer.core-1.0.2+10a4b5c0.lx64.r.cp310", + "~/isaac-sim/extscache/omni.kit.renderer.cuda_interop-1.0.1+10a4b5c0.lx64.r.cp310", + "~/isaac-sim/extscache/omni.kit.renderer.imgui-1.0.1+10a4b5c0.lx64.r.cp310", + "~/isaac-sim/extscache/omni.kit.renderer.init-0.0.0+10a4b5c0.lx64.r", + "~/isaac-sim/extscache/omni.kit.scripting-106.1.1+106.1.0", + "~/isaac-sim/extscache/omni.kit.search.files-1.0.4", + "~/isaac-sim/extscache/omni.kit.search.service-0.1.12", + "~/isaac-sim/extscache/omni.kit.search_core-1.0.5+10a4b5c0", + "~/isaac-sim/extscache/omni.kit.selection-0.1.4+10a4b5c0", + "~/isaac-sim/extscache/omni.kit.sequencer.core-103.4.2+106.0.0", + "~/isaac-sim/extscache/omni.kit.sequencer.usd-103.4.5+106.0.0", + "~/isaac-sim/extscache/omni.kit.stage.copypaste-1.2.2+10a4b5c0", + "~/isaac-sim/extscache/omni.kit.stage.mdl_converter-1.0.6+10a4b5c0", + "~/isaac-sim/extscache/omni.kit.stage_column.payload-2.0.0", + "~/isaac-sim/extscache/omni.kit.stage_column.variant-1.0.13", + "~/isaac-sim/extscache/omni.kit.stage_template.core-1.1.22+10a4b5c0", + "~/isaac-sim/extscache/omni.kit.stage_templates-1.2.5+10a4b5c0", + "~/isaac-sim/extscache/omni.kit.stagerecorder.bundle-105.0.2+106.1.0", + "~/isaac-sim/extscache/omni.kit.stagerecorder.core-105.0.5+106.1.0.lx64.r.cp310", + "~/isaac-sim/extscache/omni.kit.stagerecorder.ui-105.0.6+106.1.0", + "~/isaac-sim/extscache/omni.kit.streamsdk.plugins-3.2.1+105.2.lx64.r", + "~/isaac-sim/extscache/omni.kit.streamsdk.plugins-4.5.2+106.0.0.lx64.r", + "~/isaac-sim/extscache/omni.kit.streamsdk.plugins-4.5.3+106.0.0.lx64.r", + "~/isaac-sim/extscache/omni.kit.telemetry-0.5.0+10a4b5c0.lx64.r.cp310", + "~/isaac-sim/extscache/omni.kit.test-1.1.0+10a4b5c0.lx64.r.cp310", + "~/isaac-sim/extscache/omni.kit.test_helpers_gfx-0.0.0+10a4b5c0", + "~/isaac-sim/extscache/omni.kit.thumbnails.mdl-1.0.24", + "~/isaac-sim/extscache/omni.kit.thumbnails.usd-1.0.9", + "~/isaac-sim/extscache/omni.kit.timeline.minibar-1.2.9", + "~/isaac-sim/extscache/omni.kit.tool.asset_exporter-1.3.3", + "~/isaac-sim/extscache/omni.kit.tool.asset_importer-2.5.5", + "~/isaac-sim/extscache/omni.kit.tool.collect-2.2.14", + "~/isaac-sim/extscache/omni.kit.tool.measure-105.2.6+106.0.1", + "~/isaac-sim/extscache/omni.kit.tool.remove_unused.controller-0.1.3", + "~/isaac-sim/extscache/omni.kit.tool.remove_unused.core-0.1.2", + "~/isaac-sim/extscache/omni.kit.ui.actions-1.0.2+10a4b5c0", + "~/isaac-sim/extscache/omni.kit.ui_test-1.3.2+10a4b5c0", + "~/isaac-sim/extscache/omni.kit.uiapp-0.0.0+10a4b5c0", + "~/isaac-sim/extscache/omni.kit.usd.collect-2.2.21+10a4b5c0", + "~/isaac-sim/extscache/omni.kit.usd.layers-2.1.36+10a4b5c0.lx64.r.cp310", + "~/isaac-sim/extscache/omni.kit.usd.mdl-1.0.1+10a4b5c0.lx64.r.cp310", + "~/isaac-sim/extscache/omni.kit.usd_undo-0.1.8+10a4b5c0", + "~/isaac-sim/extscache/omni.kit.usda_edit-1.1.14+10a4b5c0", + "~/isaac-sim/extscache/omni.kit.variant.editor-106.1.0+106.1.0", + "~/isaac-sim/extscache/omni.kit.variant.presenter-106.0.0", + "~/isaac-sim/extscache/omni.kit.viewport.actions-106.0.2+10a4b5c0", + "~/isaac-sim/extscache/omni.kit.viewport.bundle-104.0.1+10a4b5c0", + "~/isaac-sim/extscache/omni.kit.viewport.legacy_gizmos-1.1.0+10a4b5c0.lx64.r.cp310", + "~/isaac-sim/extscache/omni.kit.viewport.manipulator.transform-107.0.2+10a4b5c0", + "~/isaac-sim/extscache/omni.kit.viewport.menubar.camera-107.0.2+10a4b5c0", + "~/isaac-sim/extscache/omni.kit.viewport.menubar.core-106.1.0+10a4b5c0", + "~/isaac-sim/extscache/omni.kit.viewport.menubar.display-107.0.2+10a4b5c0", + "~/isaac-sim/extscache/omni.kit.viewport.menubar.lighting-106.0.2+ub3f", + "~/isaac-sim/extscache/omni.kit.viewport.menubar.render-106.1.1+10a4b5c0", + "~/isaac-sim/extscache/omni.kit.viewport.menubar.settings-107.0.3+10a4b5c0", + "~/isaac-sim/extscache/omni.kit.viewport.menubar.waypoint-104.2.16", + "~/isaac-sim/extscache/omni.kit.viewport.registry-104.0.6+10a4b5c0", + "~/isaac-sim/extscache/omni.kit.viewport.rtx-104.0.1+10a4b5c0", + "~/isaac-sim/extscache/omni.kit.viewport.utility-1.0.17+10a4b5c0", + "~/isaac-sim/extscache/omni.kit.viewport.window-107.0.6+10a4b5c0", + "~/isaac-sim/extscache/omni.kit.viewport_widgets_manager-1.0.8+10a4b5c0", + "~/isaac-sim/extscache/omni.kit.waypoint.bundle-1.0.4", + "~/isaac-sim/extscache/omni.kit.waypoint.core-1.4.54", + "~/isaac-sim/extscache/omni.kit.waypoint.playlist-1.0.8", + "~/isaac-sim/extscache/omni.kit.widget.browser_bar-2.0.10+10a4b5c0", + "~/isaac-sim/extscache/omni.kit.widget.cache_indicator-2.0.10+10a4b5c0", + "~/isaac-sim/extscache/omni.kit.widget.calendar-1.0.8", + "~/isaac-sim/extscache/omni.kit.widget.collection-0.1.18", + "~/isaac-sim/extscache/omni.kit.widget.context_menu-1.2.2+10a4b5c0", + "~/isaac-sim/extscache/omni.kit.widget.extended_searchfield-1.0.28", + "~/isaac-sim/extscache/omni.kit.widget.filebrowser-2.10.51+10a4b5c0", + "~/isaac-sim/extscache/omni.kit.widget.filter-1.1.4+10a4b5c0", + "~/isaac-sim/extscache/omni.kit.widget.graph-1.12.15+10a4b5c0", + "~/isaac-sim/extscache/omni.kit.widget.highlight_label-1.0.2+10a4b5c0", + "~/isaac-sim/extscache/omni.kit.widget.imageview-1.0.3+10a4b5c0", + "~/isaac-sim/extscache/omni.kit.widget.inspector-1.0.3+10a4b5c0.lx64.r.cp310", + "~/isaac-sim/extscache/omni.kit.widget.layers-1.8.0+10a4b5c0", + "~/isaac-sim/extscache/omni.kit.widget.live-2.1.8+10a4b5c0", + "~/isaac-sim/extscache/omni.kit.widget.live_session_management-1.2.20+10a4b5c0", + "~/isaac-sim/extscache/omni.kit.widget.live_session_management.ui-1.0.1+10a4b5c0", + "~/isaac-sim/extscache/omni.kit.widget.material_preview-1.0.16", + "~/isaac-sim/extscache/omni.kit.widget.nucleus_connector-1.1.9+10a4b5c0", + "~/isaac-sim/extscache/omni.kit.widget.nucleus_info-1.0.2+10a4b5c0", + "~/isaac-sim/extscache/omni.kit.widget.opengl-1.0.8+10a4b5c0.lx64.r.cp310", + "~/isaac-sim/extscache/omni.kit.widget.options_button-1.0.3+10a4b5c0", + "~/isaac-sim/extscache/omni.kit.widget.options_menu-1.1.6+10a4b5c0", + "~/isaac-sim/extscache/omni.kit.widget.path_field-2.0.10+10a4b5c0", + "~/isaac-sim/extscache/omni.kit.widget.prompt-1.0.7+10a4b5c0", + "~/isaac-sim/extscache/omni.kit.widget.search_delegate-1.0.5+10a4b5c0", + "~/isaac-sim/extscache/omni.kit.widget.searchable_combobox-1.0.6+10a4b5c0", + "~/isaac-sim/extscache/omni.kit.widget.searchfield-1.1.8+10a4b5c0", + "~/isaac-sim/extscache/omni.kit.widget.settings-1.2.2+10a4b5c0", + "~/isaac-sim/extscache/omni.kit.widget.sliderbar-1.0.10", + "~/isaac-sim/extscache/omni.kit.widget.stage-2.11.2+10a4b5c0", + "~/isaac-sim/extscache/omni.kit.widget.stage_icons-1.0.5+10a4b5c0", + "~/isaac-sim/extscache/omni.kit.widget.text_editor-1.0.2+10a4b5c0.lx64.r.cp310", + "~/isaac-sim/extscache/omni.kit.widget.timeline-105.0.1+105.0", + "~/isaac-sim/extscache/omni.kit.widget.toolbar-1.7.2+10a4b5c0", + "~/isaac-sim/extscache/omni.kit.widget.versioning-1.4.7+10a4b5c0", + "~/isaac-sim/extscache/omni.kit.widget.viewport-106.1.0+10a4b5c0", + "~/isaac-sim/extscache/omni.kit.widget.zoombar-1.0.5", + "~/isaac-sim/extscache/omni.kit.widgets.custom-1.0.8", + "~/isaac-sim/extscache/omni.kit.window.collection-0.1.22", + "~/isaac-sim/extscache/omni.kit.window.commands-0.2.6+10a4b5c0", + "~/isaac-sim/extscache/omni.kit.window.console-0.2.13+10a4b5c0.lx64.r.cp310", + "~/isaac-sim/extscache/omni.kit.window.content_browser-2.9.18+10a4b5c0", + "~/isaac-sim/extscache/omni.kit.window.content_browser_registry-0.0.6+10a4b5c0", + "~/isaac-sim/extscache/omni.kit.window.cursor-1.1.2+10a4b5c0", + "~/isaac-sim/extscache/omni.kit.window.drop_support-1.0.3+10a4b5c0", + "~/isaac-sim/extscache/omni.kit.window.extensions-1.4.11+10a4b5c0", + "~/isaac-sim/extscache/omni.kit.window.file-1.3.54+10a4b5c0", + "~/isaac-sim/extscache/omni.kit.window.file_exporter-1.0.30+10a4b5c0", + "~/isaac-sim/extscache/omni.kit.window.file_importer-1.1.12+10a4b5c0", + "~/isaac-sim/extscache/omni.kit.window.filepicker-2.10.40+10a4b5c0", + "~/isaac-sim/extscache/omni.kit.window.material-1.6.2", + "~/isaac-sim/extscache/omni.kit.window.material_graph-1.8.18", + "~/isaac-sim/extscache/omni.kit.window.movie_capture-2.4.2", + "~/isaac-sim/extscache/omni.kit.window.popup_dialog-2.0.24+10a4b5c0", + "~/isaac-sim/extscache/omni.kit.window.preferences-1.6.0+10a4b5c0", + "~/isaac-sim/extscache/omni.kit.window.property-1.11.3+10a4b5c0", + "~/isaac-sim/extscache/omni.kit.window.quicksearch-2.4.4", + "~/isaac-sim/extscache/omni.kit.window.script_editor-1.7.6+10a4b5c0.lx64.r.cp310", + "~/isaac-sim/extscache/omni.kit.window.section-107.0.1", + "~/isaac-sim/extscache/omni.kit.window.stage-2.5.10+10a4b5c0", + "~/isaac-sim/extscache/omni.kit.window.stats-0.1.6+10a4b5c0", + "~/isaac-sim/extscache/omni.kit.window.status_bar-0.1.7+10a4b5c0.lx64.r.cp310", + "~/isaac-sim/extscache/omni.kit.window.title-1.1.5+10a4b5c0", + "~/isaac-sim/extscache/omni.kit.window.toolbar-1.6.1+10a4b5c0", + "~/isaac-sim/extscache/omni.kit.window.usd_paths-1.0.7+10a4b5c0", + "~/isaac-sim/extscache/omni.kit.window.usddebug-1.0.2", + "~/isaac-sim/extscache/omni.kit.xr.advertise-106.1.24+106.1.0", + "~/isaac-sim/extscache/omni.kit.xr.core-106.1.24+106.1.0.lx64.r.cp310", + "~/isaac-sim/extscache/omni.kit.xr.example.usd_scene_ui-106.0.116+106.0.1", + "~/isaac-sim/extscache/omni.kit.xr.profile.ar-106.1.24+106.1.0", + "~/isaac-sim/extscache/omni.kit.xr.profile.common-106.1.24+106.1.0", + "~/isaac-sim/extscache/omni.kit.xr.profile.tabletar-106.1.24+106.1.0", + "~/isaac-sim/extscache/omni.kit.xr.profile.vr-106.1.24+106.1.0", + "~/isaac-sim/extscache/omni.kit.xr.scene_view.core-106.1.24+106.1.0.lx64.r.cp310", + "~/isaac-sim/extscache/omni.kit.xr.scene_view.utils-106.1.24+106.1.0", + "~/isaac-sim/extscache/omni.kit.xr.system.cloudxr-106.1.24+106.1.0.lx64.r.cp310", + "~/isaac-sim/extscache/omni.kit.xr.system.cloudxr41-106.1.24+106.1.0.lx64.r.cp310", + "~/isaac-sim/extscache/omni.kit.xr.system.openxr-106.1.24+106.1.0.lx64.r.cp310", + "~/isaac-sim/extscache/omni.kit.xr.system.playback-106.1.24+106.1.0.lx64.r.cp310", + "~/isaac-sim/extscache/omni.kit.xr.system.steamvr-106.1.24+106.1.0.lx64.r.cp310", + "~/isaac-sim/extscache/omni.kit.xr.telemetry-106.1.24+106.1.0.lx64.r.cp310", + "~/isaac-sim/extscache/omni.kit.xr.ui.config.common-106.1.24+106.1.0", + "~/isaac-sim/extscache/omni.kit.xr.ui.config.generic-106.1.24+106.1.0", + "~/isaac-sim/extscache/omni.kit.xr.ui.config.htcvive-106.1.24+106.1.0", + "~/isaac-sim/extscache/omni.kit.xr.ui.config.magicleap-106.1.24+106.1.0", + "~/isaac-sim/extscache/omni.kit.xr.ui.config.metaquest-106.1.24+106.1.0", + "~/isaac-sim/extscache/omni.kit.xr.ui.stage.common-106.1.24+106.1.0", + "~/isaac-sim/extscache/omni.kit.xr.ui.window.profile-106.1.24+106.1.0", + "~/isaac-sim/extscache/omni.kit.xr.ui.window.viewport-106.1.24+106.1.0", + "~/isaac-sim/extscache/omni.materialx.libs-1.0.4+10a4b5c0.lx64.r.cp310", + "~/isaac-sim/extscache/omni.mdl-55.0.1+10a4b5c0.lx64.r.cp310", + "~/isaac-sim/extscache/omni.mdl.neuraylib-0.2.8+10a4b5c0.lx64.r.cp310", + "~/isaac-sim/extscache/omni.mdl.usd_converter-1.0.21+10a4b5c0", + "~/isaac-sim/extscache/omni.net-0.0.1-isaac-1+lx64.r", + "~/isaac-sim/extscache/omni.no_code_ui.bundle-1.0.27+106.0", + "~/isaac-sim/extscache/omni.product_configurator.panel-1.0.15", + "~/isaac-sim/extscache/omni.product_configurator.utils-1.2.2", + "~/isaac-sim/extscache/omni.ramp-105.1.15+106.1.0.lx64.r.cp310", + "~/isaac-sim/extscache/omni.replicator.agent.camera_calibration-0.4.0", + "~/isaac-sim/extscache/omni.replicator.agent.core-0.4.1", + "~/isaac-sim/extscache/omni.replicator.agent.ui-0.4.0", + "~/isaac-sim/extscache/omni.replicator.core-1.11.20+106.1.0.lx64.r.cp310", + "~/isaac-sim/extscache/omni.replicator.object-0.3.8+lx64", + "~/isaac-sim/extscache/omni.replicator.replicator_yaml-2.0.6+lx64", + "~/isaac-sim/extscache/omni.resourcemonitor-105.0.1+10a4b5c0.lx64.r.cp310", + "~/isaac-sim/extscache/omni.rtx.settings.core-0.6.3+10a4b5c0", + "~/isaac-sim/extscache/omni.rtx.shadercache.vulkan-1.0.0+10a4b5c0.lx64.r", + "~/isaac-sim/extscache/omni.rtx.window.settings-0.6.17+10a4b5c0.lx64.r.cp310", + "~/isaac-sim/extscache/omni.scene.optimizer.bundle-106.1.1+106.1.0", + "~/isaac-sim/extscache/omni.scene.optimizer.core-106.1.1+106.1.0.lx64.r.cp310.ub3f", + "~/isaac-sim/extscache/omni.scene.optimizer.ui-106.1.1+106.1.0", + "~/isaac-sim/extscache/omni.scene.visualization.bundle-105.1.0+106.1.0", + "~/isaac-sim/extscache/omni.scene.visualization.core-105.4.13+106.1.0.lx64.r.cp310", + "~/isaac-sim/extscache/omni.scene.visualization.ui-105.1.2+106.1.0", + "~/isaac-sim/extscache/omni.sensors.nv.camera-0.10.0-isaac-1+lx64.r", + "~/isaac-sim/extscache/omni.sensors.nv.common-1.2.2-isaac-1+lx64.r.cp310", + "~/isaac-sim/extscache/omni.sensors.nv.ids-1.1.0-isaac-1+lx64.r.cp310", + "~/isaac-sim/extscache/omni.sensors.nv.lidar-1.2.2-isaac-1+lx64.r.cp310", + "~/isaac-sim/extscache/omni.sensors.nv.materials-1.2.1-isaac-1+lx64.r.cp310", + "~/isaac-sim/extscache/omni.sensors.nv.radar-1.2.1-isaac-1+lx64.r.cp310", + "~/isaac-sim/extscache/omni.sensors.nv.ultrasonic-1.2.1-isaac-1+lx64.r.cp310", + "~/isaac-sim/extscache/omni.sensors.nv.visualizer-1.0.1-isaac-1+lx64.r.cp310", + "~/isaac-sim/extscache/omni.sensors.nv.wpm-1.2.1-isaac-1+lx64.r", + "~/isaac-sim/extscache/omni.sensors.tiled-0.0.6+106.1.0.lx64.r", + "~/isaac-sim/extscache/omni.services.browser.asset-1.3.3+106.0.0", + "~/isaac-sim/extscache/omni.services.carb.event_stream-1.0.0", + "~/isaac-sim/extscache/omni.services.client-0.5.3", + "~/isaac-sim/extscache/omni.services.core-1.9.0", + "~/isaac-sim/extscache/omni.services.facilities.base-1.0.4", + "~/isaac-sim/extscache/omni.services.facilities.workqueue-1.1.2", + "~/isaac-sim/extscache/omni.services.pip_archive-0.13.6+lx64", + "~/isaac-sim/extscache/omni.services.starfleet.auth-0.1.5", + "~/isaac-sim/extscache/omni.services.streamclient.webrtc-1.3.8", + "~/isaac-sim/extscache/omni.services.streamclient.websocket-2.0.0", + "~/isaac-sim/extscache/omni.services.streaming.manager-0.3.10", + "~/isaac-sim/extscache/omni.services.thumbnails.images-1.3.2", + "~/isaac-sim/extscache/omni.services.transport.client.base-1.2.4", + "~/isaac-sim/extscache/omni.services.transport.client.http_async-1.3.6", + "~/isaac-sim/extscache/omni.services.transport.server.base-1.1.1", + "~/isaac-sim/extscache/omni.services.transport.server.http-1.3.1", + "~/isaac-sim/extscache/omni.services.transport.server.zeroconf-1.0.9", + "~/isaac-sim/extscache/omni.services.usd-1.1.0", + "~/isaac-sim/extscache/omni.simready.explorer-1.1.1", + "~/isaac-sim/extscache/omni.slangnode-106.1.0+106.1.0.lx64.r.cp310.ub3f", + "~/isaac-sim/extscache/omni.stats-1.0.1+10a4b5c0.lx64.r.cp310", + "~/isaac-sim/extscache/omni.syntheticdata-0.6.9+10a4b5c0.lx64.r.cp310", + "~/isaac-sim/extscache/omni.timeline-1.0.10+10a4b5c0.lx64.r.cp310", + "~/isaac-sim/extscache/omni.timeline.live_session-1.0.8+10a4b5c0", + "~/isaac-sim/extscache/omni.tools.array-105.0.4", + "~/isaac-sim/extscache/omni.ui-2.25.22+10a4b5c0.lx64.r.cp310", + "~/isaac-sim/extscache/omni.ui.scene-1.10.3+10a4b5c0.lx64.r.cp310", + "~/isaac-sim/extscache/omni.ui_query-1.1.4+10a4b5c0", + "~/isaac-sim/extscache/omni.uiaudio-1.0.0+10a4b5c0.lx64.r.cp310", + "~/isaac-sim/extscache/omni.ujitso.client-0.0.0+10a4b5c0.lx64.r", + "~/isaac-sim/extscache/omni.ujitso.default-1.0.0+10a4b5c0.lx64.r", + "~/isaac-sim/extscache/omni.ujitso.processor.texture-1.0.0+10a4b5c0.lx64.r", + "~/isaac-sim/extscache/omni.usd-1.12.2+10a4b5c0.lx64.r.cp310", + "~/isaac-sim/extscache/omni.usd.config-1.0.4+10a4b5c0", + "~/isaac-sim/extscache/omni.usd.core-1.4.1+10a4b5c0.lx64.r", + "~/isaac-sim/extscache/omni.usd.fileformat.e57-1.2.4+106.0.0.lx64.r.cp310.ub3f", + "~/isaac-sim/extscache/omni.usd.fileformat.pts-106.0.1+106.0.0.lx64.r.cp310.ub3f", + "~/isaac-sim/extscache/omni.usd.fileformat.sbsar-107.0.2+lx64.r.cp310.ub3f", + "~/isaac-sim/extscache/omni.usd.libs-1.0.1+10a4b5c0.lx64.r.cp310", + "~/isaac-sim/extscache/omni.usd.metrics.assembler-106.1.0+106.1.0.lx64.r.cp310", + "~/isaac-sim/extscache/omni.usd.metrics.assembler.physics-106.1.0+106.1.0", + "~/isaac-sim/extscache/omni.usd.metrics.assembler.ui-106.1.0+106.1.0", + "~/isaac-sim/extscache/omni.usd.schema.anim-0.0.0+10a4b5c0.lx64.r.cp310", + "~/isaac-sim/extscache/omni.usd.schema.audio-0.0.0+10a4b5c0.lx64.r.cp310", + "~/isaac-sim/extscache/omni.usd.schema.destruction-0.7.0+106.0.1.lx64.r", + "~/isaac-sim/extscache/omni.usd.schema.flow-106.0.8+106.0.0.ub3f", + "~/isaac-sim/extscache/omni.usd.schema.geospatial-0.0.0+10a4b5c0.lx64.r.cp310", + "~/isaac-sim/extscache/omni.usd.schema.metrics.assembler-106.1.0+106.1.0.lx64.r.cp310", + "~/isaac-sim/extscache/omni.usd.schema.omnigraph-1.0.0+10a4b5c0.lx64.r.cp310", + "~/isaac-sim/extscache/omni.usd.schema.omniscripting-1.0.0+10a4b5c0.lx64.r.cp310", + "~/isaac-sim/extscache/omni.usd.schema.scene.visualization-2.0.2+106.1.0", + "~/isaac-sim/extscache/omni.usd.schema.semantics-0.0.0+10a4b5c0.lx64.r.cp310", + "~/isaac-sim/extscache/omni.usd.schema.sequence-2.3.1+106.0.0.lx64.r.cp310", + "~/isaac-sim/extscache/omni.usd_resolver-1.0.0+10a4b5c0.lx64.r.cp310", + "~/isaac-sim/extscache/omni.vdb_timesample_editor-0.1.10", + "~/isaac-sim/extscache/omni.volume-0.5.0+10a4b5c0.lx64.r.cp310", + "~/isaac-sim/extscache/omni.warehouse_creator-0.4.2", + "~/isaac-sim/extscache/omni.warp-1.2.1", + "~/isaac-sim/extscache/omni.warp.core-1.2.1", + "~/isaac-sim/extscache/semantics.schema.editor-0.3.8", + "~/isaac-sim/extscache/semantics.schema.property-1.0.4", + "~/isaac-sim/extscache/usdrt.scenegraph-7.5.0+10a4b5c0.lx64.r.cp310", + "~/isaac-sim/kit/extscore/omni.assets.plugins", + "~/isaac-sim/kit/extscore/omni.client", + "~/isaac-sim/kit/extscore/omni.kit.async_engine", + "~/isaac-sim/kit/extscore/omni.kit.registry.nucleus", + "~/isaac-sim/kit/kernel/py", + "~/isaac-sim/kit/plugins/bindings-python", + "~/isaac-sim/kit/python/lib/python3.10/site-packages", + "~/isaac-sim/python_packages", + ] +} diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..6bc78d5 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,31 @@ +cmake_minimum_required(VERSION 3.5) +project(space_robotics_bench) + +## Find dependencies +find_package(ament_cmake REQUIRED) +find_package(ament_cmake_python REQUIRED) + +## Install Python package +ament_python_install_package(space_robotics_bench) + +## Install Python scripts +set(SCRIPTS_DIR scripts) +install(PROGRAMS + ${SCRIPTS_DIR}/blender/procgen_assets.py + ${SCRIPTS_DIR}/utils/clean_procgen_cache.py + ${SCRIPTS_DIR}/utils/tensorboard.bash + ${SCRIPTS_DIR}/utils/update_assets.bash + ${SCRIPTS_DIR}/gui.bash + ${SCRIPTS_DIR}/list_envs.py + ${SCRIPTS_DIR}/random_agent.py + ${SCRIPTS_DIR}/ros2.py + ${SCRIPTS_DIR}/teleop.py + ${SCRIPTS_DIR}/zero_agent.py + DESTINATION lib/${PROJECT_NAME} +) + +## Install directories +install(DIRECTORY config launch DESTINATION share/${PROJECT_NAME}) + +## Setup the project +ament_package() diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..7fb31cb --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,4873 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "ab_glyph" +version = "0.2.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec3672c180e71eeaaac3a541fbbc5f5ad4def8b747c595ad30d674e43049f7b0" +dependencies = [ + "ab_glyph_rasterizer", + "owned_ttf_parser", +] + +[[package]] +name = "ab_glyph_rasterizer" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c71b1793ee61086797f5c80b6efa2b8ffa6d5dd703f118545808a7f2e27f7046" + +[[package]] +name = "accesskit" +version = "0.16.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99b76d84ee70e30a4a7e39ab9018e2b17a6a09e31084176cc7c0b2dec036ba45" +dependencies = [ + "enumn", + "serde", +] + +[[package]] +name = "accesskit_atspi_common" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5393c75d4666f580f4cac0a968bc97c36076bb536a129f28210dac54ee127ed" +dependencies = [ + "accesskit", + "accesskit_consumer", + "atspi-common", + "serde", + "thiserror", + "zvariant", +] + +[[package]] +name = "accesskit_consumer" +version = "0.24.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a12dc159d52233c43d9fe5415969433cbdd52c3d6e0df51bda7d447427b9986" +dependencies = [ + "accesskit", + "immutable-chunkmap", +] + +[[package]] +name = "accesskit_macos" +version = "0.17.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49509c722e8da39e6c569f7d13ec51620208988913e738abbc67e3c65f06e6d5" +dependencies = [ + "accesskit", + "accesskit_consumer", + "objc2", + "objc2-app-kit", + "objc2-foundation", + "once_cell", +] + +[[package]] +name = "accesskit_unix" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be7f5cf6165be10a54b2655fa2e0e12b2509f38ed6fc43e11c31fdb7ee6230bb" +dependencies = [ + "accesskit", + "accesskit_atspi_common", + "async-channel", + "async-executor", + "async-task", + "atspi", + "futures-lite", + "futures-util", + "serde", + "zbus", +] + +[[package]] +name = "accesskit_windows" +version = "0.23.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "974e96c347384d9133427167fb8a58c340cb0496988dacceebdc1ed27071023b" +dependencies = [ + "accesskit", + "accesskit_consumer", + "paste", + "static_assertions", + "windows 0.58.0", + "windows-core 0.58.0", +] + +[[package]] +name = "accesskit_winit" +version = "0.22.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9987e852fe7e4e5a493f8c8af0b729b47da2750f0dea10a4c8984fe1117ebaec" +dependencies = [ + "accesskit", + "accesskit_macos", + "accesskit_unix", + "accesskit_windows", + "raw-window-handle", + "winit", +] + +[[package]] +name = "adler2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" + +[[package]] +name = "ahash" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +dependencies = [ + "cfg-if", + "getrandom", + "once_cell", + "serde", + "version_check", + "zerocopy", +] + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "aligned" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "377e4c0ba83e4431b10df45c1d4666f178ea9c552cac93e60c3a88bf32785923" +dependencies = [ + "as-slice", +] + +[[package]] +name = "aligned-array" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e05c92d086290f52938013f6242ac62bf7d401fab8ad36798a609faa65c3fd2c" +dependencies = [ + "generic-array", +] + +[[package]] +name = "android-activity" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef6978589202a00cd7e118380c448a08b6ed394c3a8df3a430d0898e3a42d046" +dependencies = [ + "android-properties", + "bitflags 2.6.0", + "cc", + "cesu8", + "jni", + "jni-sys", + "libc", + "log", + "ndk", + "ndk-context", + "ndk-sys", + "num_enum", + "thiserror", +] + +[[package]] +name = "android-properties" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc7eb209b1518d6bb87b283c20095f5228ecda460da70b44f0802523dea6da04" + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "arboard" +version = "3.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df099ccb16cd014ff054ac1bf392c67feeef57164b05c42f037cd40f5d4357f4" +dependencies = [ + "clipboard-win", + "log", + "objc2", + "objc2-app-kit", + "objc2-foundation", + "parking_lot", + "x11rb", +] + +[[package]] +name = "arrayref" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" + +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + +[[package]] +name = "as-raw-xcb-connection" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "175571dd1d178ced59193a6fc02dde1b972eb0bc56c892cde9beeceac5bf0f6b" + +[[package]] +name = "as-slice" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "516b6b4f0e40d50dcda9365d53964ec74560ad4284da2e7fc97122cd83174516" +dependencies = [ + "stable_deref_trait", +] + +[[package]] +name = "async-broadcast" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20cd0e2e25ea8e5f7e9df04578dc6cf5c83577fd09b1a46aaf5c85e1c33f2a7e" +dependencies = [ + "event-listener", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-channel" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b47800b0be77592da0afd425cc03468052844aff33b84e33cc696f64e77b6a" +dependencies = [ + "concurrent-queue", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-executor" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30ca9a001c1e8ba5149f91a74362376cc6bc5b919d92d988668657bd570bdcec" +dependencies = [ + "async-task", + "concurrent-queue", + "fastrand", + "futures-lite", + "slab", +] + +[[package]] +name = "async-fs" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebcd09b382f40fcd159c2d695175b2ae620ffa5f3bd6f664131efff4e8b9e04a" +dependencies = [ + "async-lock", + "blocking", + "futures-lite", +] + +[[package]] +name = "async-io" +version = "2.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "444b0228950ee6501b3568d3c93bf1176a1fdbc3b758dcd9475046d30f4dc7e8" +dependencies = [ + "async-lock", + "cfg-if", + "concurrent-queue", + "futures-io", + "futures-lite", + "parking", + "polling", + "rustix", + "slab", + "tracing", + "windows-sys 0.59.0", +] + +[[package]] +name = "async-lock" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18" +dependencies = [ + "event-listener", + "event-listener-strategy", + "pin-project-lite", +] + +[[package]] +name = "async-process" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63255f1dc2381611000436537bbedfe83183faa303a5a0edaf191edef06526bb" +dependencies = [ + "async-channel", + "async-io", + "async-lock", + "async-signal", + "async-task", + "blocking", + "cfg-if", + "event-listener", + "futures-lite", + "rustix", + "tracing", +] + +[[package]] +name = "async-recursion" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.82", +] + +[[package]] +name = "async-signal" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "637e00349800c0bdf8bfc21ebbc0b6524abea702b0da4168ac00d070d0c0b9f3" +dependencies = [ + "async-io", + "async-lock", + "atomic-waker", + "cfg-if", + "futures-core", + "futures-io", + "rustix", + "signal-hook-registry", + "slab", + "windows-sys 0.59.0", +] + +[[package]] +name = "async-task" +version = "4.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" + +[[package]] +name = "async-trait" +version = "0.1.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.82", +] + +[[package]] +name = "atomic" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d818003e740b63afc82337e3160717f4f63078720a810b7b903e70a5d1d2994" +dependencies = [ + "bytemuck", +] + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "atspi" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be534b16650e35237bb1ed189ba2aab86ce65e88cc84c66f4935ba38575cecbf" +dependencies = [ + "atspi-common", + "atspi-connection", + "atspi-proxies", +] + +[[package]] +name = "atspi-common" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1909ed2dc01d0a17505d89311d192518507e8a056a48148e3598fef5e7bb6ba7" +dependencies = [ + "enumflags2", + "serde", + "static_assertions", + "zbus", + "zbus-lockstep", + "zbus-lockstep-macros", + "zbus_names", + "zvariant", +] + +[[package]] +name = "atspi-connection" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "430c5960624a4baaa511c9c0fcc2218e3b58f5dbcc47e6190cafee344b873333" +dependencies = [ + "atspi-common", + "atspi-proxies", + "futures-lite", + "zbus", +] + +[[package]] +name = "atspi-proxies" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e6c5de3e524cf967569722446bcd458d5032348554d9a17d7d72b041ab7496" +dependencies = [ + "atspi-common", + "serde", + "zbus", + "zvariant", +] + +[[package]] +name = "autocfg" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" + +[[package]] +name = "az" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b7e4c2464d97fe331d41de9d5db0def0a96f4d823b8b32a2efd503578988973" + +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + +[[package]] +name = "bindgen" +version = "0.63.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36d860121800b2a9a94f9b5604b332d5cffb234ce17609ea479d723dbc9d3885" +dependencies = [ + "bitflags 1.3.2", + "cexpr", + "clang-sys", + "lazy_static", + "lazycell", + "log", + "peeking_take_while", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn 1.0.109", + "which", +] + +[[package]] +name = "bit-set" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" +dependencies = [ + "serde", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "block2" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c132eebf10f5cad5289222520a4a058514204aed6d791f1cf4fe8088b82d15f" +dependencies = [ + "objc2", +] + +[[package]] +name = "blocking" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "703f41c54fc768e63e091340b424302bb1c29ef4aa0c7f10fe849dfb114d29ea" +dependencies = [ + "async-channel", + "async-task", + "futures-io", + "futures-lite", + "piper", +] + +[[package]] +name = "bumpalo" +version = "3.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" + +[[package]] +name = "bytemuck" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8334215b81e418a0a7bdb8ef0849474f40bb10c8b71f1c4ed315cff49f32494d" +dependencies = [ + "bytemuck_derive", +] + +[[package]] +name = "bytemuck_derive" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcfcc3cd946cb52f0bbfdbbcfa2f4e24f75ebb6c0e1002f7c25904fada18b9ec" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.82", +] + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "byteorder-lite" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" + +[[package]] +name = "bytes" +version = "1.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3" + +[[package]] +name = "calloop" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b99da2f8558ca23c71f4fd15dc57c906239752dd27ff3c00a1d56b685b7cbfec" +dependencies = [ + "bitflags 2.6.0", + "log", + "polling", + "rustix", + "slab", + "thiserror", +] + +[[package]] +name = "calloop-wayland-source" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95a66a987056935f7efce4ab5668920b5d0dac4a7c99991a67395f13702ddd20" +dependencies = [ + "calloop", + "rustix", + "wayland-backend", + "wayland-client", +] + +[[package]] +name = "cc" +version = "1.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2e7962b54006dcfcc61cb72735f4d89bb97061dd6a7ed882ec6b8ee53714c6f" +dependencies = [ + "jobserver", + "libc", + "shlex", +] + +[[package]] +name = "cesu8" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" + +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + +[[package]] +name = "cgl" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ced0551234e87afee12411d535648dd89d2e7f34c78b753395567aff3d447ff" +dependencies = [ + "libc", +] + +[[package]] +name = "chrono" +version = "0.4.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "js-sys", + "num-traits", + "wasm-bindgen", + "windows-targets 0.52.6", +] + +[[package]] +name = "clang-sys" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" +dependencies = [ + "glob", + "libc", + "libloading", +] + +[[package]] +name = "clipboard-win" +version = "5.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15efe7a882b08f34e38556b14f2fb3daa98769d06c7f0c1b076dfd0d983bc892" +dependencies = [ + "error-code", +] + +[[package]] +name = "color_quant" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" + +[[package]] +name = "combine" +version = "4.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" +dependencies = [ + "bytes", + "memchr", +] + +[[package]] +name = "concurrent-queue" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "const_format" +version = "0.2.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50c655d81ff1114fb0dcdea9225ea9f0cc712a6f8d189378e82bdf62a473a64b" +dependencies = [ + "const_format_proc_macros", + "konst", +] + +[[package]] +name = "const_format_proc_macros" +version = "0.2.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eff1a44b93f47b1bac19a27932f5c591e43d1ba357ee4f61526c8a25603f0eb1" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b55271e5c8c478ad3f38ad24ef34923091e0548492a266d19b3c0b4d82574c63" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "core-graphics" +version = "0.23.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c07782be35f9e1140080c6b96f0d44b739e2278479f64e02fdab4e32dfd8b081" +dependencies = [ + "bitflags 1.3.2", + "core-foundation 0.9.4", + "core-graphics-types", + "foreign-types", + "libc", +] + +[[package]] +name = "core-graphics-types" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf" +dependencies = [ + "bitflags 1.3.2", + "core-foundation 0.9.4", + "libc", +] + +[[package]] +name = "cpufeatures" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" +dependencies = [ + "libc", +] + +[[package]] +name = "crc32fast" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "cursor-icon" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96a6ac251f4a2aca6b3f91340350eab87ae57c3f127ffeb585e92bd336717991" + +[[package]] +name = "data-url" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c297a1c74b71ae29df00c3e22dd9534821d60eb9af5a0192823fa2acea70c2a" + +[[package]] +name = "deranged" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +dependencies = [ + "powerfmt", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "dispatch" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" + +[[package]] +name = "display_json" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e8619be5104f2a5fe15929a69a960db3dc3f71a879f15e1c4255083d9b57a1c" +dependencies = [ + "proc-macro2", + "quote", + "serde", + "serde_json", + "syn 1.0.109", +] + +[[package]] +name = "divrem" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69dde51e8fef5e12c1d65e0929b03d66e4c0c18282bc30ed2ca050ad6f44dd82" + +[[package]] +name = "dlib" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412" +dependencies = [ + "libloading", +] + +[[package]] +name = "doc-comment" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" + +[[package]] +name = "document-features" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb6969eaabd2421f8a2775cfd2471a2b634372b4a25d41e3bd647b79912850a0" +dependencies = [ + "litrs", +] + +[[package]] +name = "downcast-rs" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" + +[[package]] +name = "dpi" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f25c0e292a7ca6d6498557ff1df68f32c99850012b6ea401cf8daf771f22ff53" + +[[package]] +name = "ecolor" +version = "0.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "775cfde491852059e386c4e1deb4aef381c617dc364184c6f6afee99b87c402b" +dependencies = [ + "bytemuck", + "emath", + "serde", +] + +[[package]] +name = "eframe" +version = "0.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ac2645a9bf4826eb4e91488b1f17b8eaddeef09396706b2f14066461338e24f" +dependencies = [ + "ahash", + "bytemuck", + "document-features", + "egui", + "egui-winit", + "egui_glow", + "glow", + "glutin", + "glutin-winit", + "home", + "image", + "js-sys", + "log", + "objc2", + "objc2-app-kit", + "objc2-foundation", + "parking_lot", + "percent-encoding", + "raw-window-handle", + "ron", + "serde", + "static_assertions", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "web-time", + "winapi", + "windows-sys 0.52.0", + "winit", +] + +[[package]] +name = "egui" +version = "0.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53eafabcce0cb2325a59a98736efe0bf060585b437763f8c476957fb274bb974" +dependencies = [ + "accesskit", + "ahash", + "emath", + "epaint", + "log", + "nohash-hasher", + "ron", + "serde", +] + +[[package]] +name = "egui-winit" +version = "0.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a9c430f4f816340e8e8c1b20eec274186b1be6bc4c7dfc467ed50d57abc36c6" +dependencies = [ + "accesskit_winit", + "ahash", + "arboard", + "egui", + "log", + "raw-window-handle", + "serde", + "smithay-clipboard", + "web-time", + "webbrowser", + "winit", +] + +[[package]] +name = "egui_commonmark" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f169228b94d1c8eb9330b7ea1b5f65b1193b6bea95159c87f574ed4aff8c172" +dependencies = [ + "egui", + "egui_commonmark_backend", + "egui_commonmark_macros", + "egui_extras", + "pulldown-cmark", +] + +[[package]] +name = "egui_commonmark_backend" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcd08f95abeb137e59c9bfdd0880d362bff74a83afe13805fde7a2d014ef773d" +dependencies = [ + "egui", + "egui_extras", + "pulldown-cmark", + "syntect", +] + +[[package]] +name = "egui_commonmark_macros" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb1af8e42cb5ab90dc11600ac50fc81f4f87e46f1f6fce6b602f70b45d625fdd" +dependencies = [ + "egui", + "egui_commonmark_backend", + "proc-macro-crate", + "proc-macro2", + "pulldown-cmark", + "quote", + "syn 2.0.82", +] + +[[package]] +name = "egui_extras" +version = "0.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf3c1f5cd8dfe2ade470a218696c66cf556fcfd701e7830fa2e9f4428292a2a1" +dependencies = [ + "ahash", + "egui", + "ehttp", + "enum-map", + "image", + "log", + "mime_guess2", + "resvg", + "syntect", +] + +[[package]] +name = "egui_glow" +version = "0.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e39bccc683cd43adab530d8f21a13eb91e80de10bcc38c3f1c16601b6f62b26" +dependencies = [ + "ahash", + "bytemuck", + "egui", + "glow", + "log", + "memoffset", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "ehttp" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59a81c221a1e4dad06cb9c9deb19aea1193a5eea084e8cd42d869068132bf876" +dependencies = [ + "document-features", + "js-sys", + "ureq", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" + +[[package]] +name = "elapsed" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f4e5af126dafd0741c2ad62d47f68b28602550102e5f0dd45c8a97fc8b49c29" + +[[package]] +name = "emath" +version = "0.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1fe0049ce51d0fb414d029e668dd72eb30bc2b739bf34296ed97bd33df544f3" +dependencies = [ + "bytemuck", + "serde", +] + +[[package]] +name = "endi" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3d8a32ae18130a3c84dd492d4215c3d913c3b07c6b63c2eb3eb7ff1101ab7bf" + +[[package]] +name = "enum-map" +version = "2.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6866f3bfdf8207509a033af1a75a7b08abda06bbaaeae6669323fd5a097df2e9" +dependencies = [ + "enum-map-derive", + "serde", +] + +[[package]] +name = "enum-map-derive" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f282cfdfe92516eb26c2af8589c274c7c17681f5ecc03c18255fe741c6aa64eb" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.82", +] + +[[package]] +name = "enumflags2" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d232db7f5956f3f14313dc2f87985c58bd2c695ce124c8cdd984e08e15ac133d" +dependencies = [ + "enumflags2_derive", + "serde", +] + +[[package]] +name = "enumflags2_derive" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de0d48a183585823424a4ce1aa132d174a6a81bd540895822eb4c8373a8e49e8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.82", +] + +[[package]] +name = "enumn" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f9ed6b3789237c8a0c1c505af1c7eb2c560df6186f01b098c3a1064ea532f38" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.82", +] + +[[package]] +name = "epaint" +version = "0.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a32af8da821bd4f43f2c137e295459ee2e1661d87ca8779dfa0eaf45d870e20f" +dependencies = [ + "ab_glyph", + "ahash", + "bytemuck", + "ecolor", + "emath", + "log", + "nohash-hasher", + "parking_lot", + "serde", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "errno" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "error-code" +version = "3.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5d9305ccc6942a704f4335694ecd3de2ea531b114ac2d51f5f843750787a92f" + +[[package]] +name = "event-listener" +version = "5.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6032be9bd27023a771701cc49f9f053c751055f71efb2e0ae5c15809093675ba" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f214dc438f977e6d4e3500aaa277f5ad94ca83fbbd9b1a15713ce2344ccc5a1" +dependencies = [ + "event-listener", + "pin-project-lite", +] + +[[package]] +name = "fancy-regex" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b95f7c0680e4142284cf8b22c14a476e87d61b004a3a0861872b32ef7ead40a2" +dependencies = [ + "bit-set", + "regex", +] + +[[package]] +name = "fast_poisson" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2472baa9796d2ee497bd61690e3093a26935390d8ce0dd0ddc2db9b47a65898f" +dependencies = [ + "kiddo", + "rand", + "rand_distr", + "rand_xoshiro", +] + +[[package]] +name = "fastrand" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" + +[[package]] +name = "fdeflate" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8090f921a24b04994d9929e204f50b498a33ea6ba559ffaa05e04f7ee7fb5ab" +dependencies = [ + "simd-adler32", +] + +[[package]] +name = "figment" +version = "0.10.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cb01cd46b0cf372153850f4c6c272d9cbea2da513e07538405148f95bd789f3" +dependencies = [ + "atomic", + "parking_lot", + "pear", + "serde", + "serde_json", + "serde_yaml", + "tempfile", + "toml", + "uncased", + "version_check", +] + +[[package]] +name = "fixed" +version = "1.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85c6e0b89bf864acd20590dbdbad56f69aeb898abfc9443008fd7bd48b2cc85a" +dependencies = [ + "az", + "bytemuck", + "half", + "num-traits", + "typenum", +] + +[[package]] +name = "flate2" +version = "1.0.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1b589b4dc103969ad3cf85c950899926ec64300a1a46d76c03a6072957036f0" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "float-cmp" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "force-send-sync" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32688dcc448aa684426ecc398f9af78c55b0769515ccd12baa565daf6f32feb6" + +[[package]] +name = "foreign-types" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" +dependencies = [ + "foreign-types-macros", + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-macros" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.82", +] + +[[package]] +name = "foreign-types-shared" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" + +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futures" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-executor" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" + +[[package]] +name = "futures-lite" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52527eb5074e35e9339c6b4e8d12600c7128b68fb25dcb9fa9dec18f7c25f3a5" +dependencies = [ + "fastrand", + "futures-core", + "futures-io", + "parking", + "pin-project-lite", +] + +[[package]] +name = "futures-macro" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.82", +] + +[[package]] +name = "futures-sink" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" + +[[package]] +name = "futures-task" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + +[[package]] +name = "futures-util" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "gethostname" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0176e0459c2e4a1fe232f984bca6890e681076abb9934f6cea7c326f3fc47818" +dependencies = [ + "libc", + "windows-targets 0.48.5", +] + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "gif" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fb2d69b19215e18bb912fa30f7ce15846e301408695e44e0ef719f1da9e19f2" +dependencies = [ + "color_quant", + "weezl", +] + +[[package]] +name = "gl_generator" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a95dfc23a2b4a9a2f5ab41d194f8bfda3cabec42af4e39f08c339eb2a0c124d" +dependencies = [ + "khronos_api", + "log", + "xml-rs", +] + +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + +[[package]] +name = "glow" +version = "0.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d51fa363f025f5c111e03f13eda21162faeacb6911fe8caa0c0349f9cf0c4483" +dependencies = [ + "js-sys", + "slotmap", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "glutin" +version = "0.32.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec69412a0bf07ea7607e638b415447857a808846c2b685a43c8aa18bc6d5e499" +dependencies = [ + "bitflags 2.6.0", + "cfg_aliases", + "cgl", + "core-foundation 0.9.4", + "dispatch", + "glutin_egl_sys", + "glutin_glx_sys", + "glutin_wgl_sys", + "libloading", + "objc2", + "objc2-app-kit", + "objc2-foundation", + "once_cell", + "raw-window-handle", + "wayland-sys", + "windows-sys 0.52.0", + "x11-dl", +] + +[[package]] +name = "glutin-winit" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85edca7075f8fc728f28cb8fbb111a96c3b89e930574369e3e9c27eb75d3788f" +dependencies = [ + "cfg_aliases", + "glutin", + "raw-window-handle", + "winit", +] + +[[package]] +name = "glutin_egl_sys" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cae99fff4d2850dbe6fb8c1fa8e4fead5525bab715beaacfccf3fb994e01c827" +dependencies = [ + "gl_generator", + "windows-sys 0.52.0", +] + +[[package]] +name = "glutin_glx_sys" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c2b2d3918e76e18e08796b55eb64e8fe6ec67d5a6b2e2a7e2edce224ad24c63" +dependencies = [ + "gl_generator", + "x11-dl", +] + +[[package]] +name = "glutin_wgl_sys" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a4e1951bbd9434a81aa496fe59ccc2235af3820d27b85f9314e279609211e2c" +dependencies = [ + "gl_generator", +] + +[[package]] +name = "half" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888" +dependencies = [ + "cfg-if", + "crunchy", +] + +[[package]] +name = "hashbrown" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hermit-abi" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "home" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core 0.52.0", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "idna" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "image" +version = "0.25.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc144d44a31d753b02ce64093d532f55ff8dc4ebf2ffb8a63c0dda691385acae" +dependencies = [ + "bytemuck", + "byteorder-lite", + "color_quant", + "gif", + "num-traits", + "png", + "zune-core", + "zune-jpeg", +] + +[[package]] +name = "imagesize" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "029d73f573d8e8d63e6d5020011d3255b28c3ba85d6cf870a07184ed23de9284" + +[[package]] +name = "immutable-chunkmap" +version = "2.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4419f022e55cc63d5bbd6b44b71e1d226b9c9480a47824c706e9d54e5c40c5eb" +dependencies = [ + "arrayvec", +] + +[[package]] +name = "indexmap" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "indoc" +version = "2.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b248f5224d1d606005e02c97f5aa4e88eeb230488bcc03bc9ca4d7991399f2b5" + +[[package]] +name = "inlinable_string" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8fae54786f62fb2918dcfae3d568594e50eb9b5c25bf04371af6fe7516452fb" + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + +[[package]] +name = "jni" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" +dependencies = [ + "cesu8", + "cfg-if", + "combine", + "jni-sys", + "log", + "thiserror", + "walkdir", + "windows-sys 0.45.0", +] + +[[package]] +name = "jni-sys" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" + +[[package]] +name = "jobserver" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" +dependencies = [ + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a88f1bda2bd75b0452a14784937d796722fdebfe50df998aeb3f0b7603019a9" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "khronos_api" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" + +[[package]] +name = "kiddo" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e1c5ea778d68eacd5c33f29537ba0b7b6c2595e74ee013a69cedc20ab4d3177" +dependencies = [ + "aligned", + "aligned-array", + "az", + "divrem", + "doc-comment", + "elapsed", + "fixed", + "log", + "min-max-heap", + "num-traits", + "rand", + "rayon", +] + +[[package]] +name = "konst" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "330f0e13e6483b8c34885f7e6c9f19b1a7bd449c673fbb948a51c99d66ef74f4" +dependencies = [ + "konst_macro_rules", +] + +[[package]] +name = "konst_macro_rules" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4933f3f57a8e9d9da04db23fb153356ecaf00cbd14aee46279c33dc80925c37" + +[[package]] +name = "kurbo" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd85a5776cd9500c2e2059c8c76c3b01528566b7fcbaf8098b55a33fc298849b" +dependencies = [ + "arrayvec", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + +[[package]] +name = "libc" +version = "0.2.161" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1" + +[[package]] +name = "libloading" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" +dependencies = [ + "cfg-if", + "windows-targets 0.52.6", +] + +[[package]] +name = "libm" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" + +[[package]] +name = "libredox" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +dependencies = [ + "bitflags 2.6.0", + "libc", + "redox_syscall 0.5.7", +] + +[[package]] +name = "linked-hash-map" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" + +[[package]] +name = "linux-raw-sys" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" + +[[package]] +name = "litrs" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ce301924b7887e9d637144fdade93f9dfff9b60981d4ac161db09720d39aa5" + +[[package]] +name = "lock_api" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "memmap2" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd3f7eed9d3848f8b98834af67102b720745c4ec028fcd0aa0239277e7de374f" +dependencies = [ + "libc", +] + +[[package]] +name = "memoffset" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" +dependencies = [ + "autocfg", +] + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "mime_guess2" +version = "2.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25a3333bb1609500601edc766a39b4c1772874a4ce26022f4d866854dc020c41" +dependencies = [ + "mime", + "unicase", +] + +[[package]] +name = "min-max-heap" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2687e6cf9c00f48e9284cf9fd15f2ef341d03cc7743abf9df4c5f07fdee50b18" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "miniz_oxide" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" +dependencies = [ + "adler2", + "simd-adler32", +] + +[[package]] +name = "ndk" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3f42e7bbe13d351b6bead8286a43aac9534b82bd3cc43e47037f012ebfd62d4" +dependencies = [ + "bitflags 2.6.0", + "jni-sys", + "log", + "ndk-sys", + "num_enum", + "raw-window-handle", + "thiserror", +] + +[[package]] +name = "ndk-context" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" + +[[package]] +name = "ndk-sys" +version = "0.6.0+11769913" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee6cda3051665f1fb8d9e08fc35c96d5a244fb1be711a03b71118828afc9a873" +dependencies = [ + "jni-sys", +] + +[[package]] +name = "nix" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" +dependencies = [ + "bitflags 2.6.0", + "cfg-if", + "cfg_aliases", + "libc", + "memoffset", +] + +[[package]] +name = "nohash-hasher" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451" + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "ntapi" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8a3895c6391c39d7fe7ebc444a87eb2991b2a0bc718fdabd071eec617fc68e4" +dependencies = [ + "winapi", +] + +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", + "libm", +] + +[[package]] +name = "num_enum" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e613fc340b2220f734a8595782c551f1250e969d87d3be1ae0579e8d4065179" +dependencies = [ + "num_enum_derive", +] + +[[package]] +name = "num_enum_derive" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.82", +] + +[[package]] +name = "objc-sys" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb91bdd390c7ce1a8607f35f3ca7151b65afc0ff5ff3b34fa350f7d7c7e4310" + +[[package]] +name = "objc2" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46a785d4eeff09c14c487497c162e92766fbb3e4059a71840cecc03d9a50b804" +dependencies = [ + "objc-sys", + "objc2-encode", +] + +[[package]] +name = "objc2-app-kit" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4e89ad9e3d7d297152b17d39ed92cd50ca8063a89a9fa569046d41568891eff" +dependencies = [ + "bitflags 2.6.0", + "block2", + "libc", + "objc2", + "objc2-core-data", + "objc2-core-image", + "objc2-foundation", + "objc2-quartz-core", +] + +[[package]] +name = "objc2-cloud-kit" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74dd3b56391c7a0596a295029734d3c1c5e7e510a4cb30245f8221ccea96b009" +dependencies = [ + "bitflags 2.6.0", + "block2", + "objc2", + "objc2-core-location", + "objc2-foundation", +] + +[[package]] +name = "objc2-contacts" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5ff520e9c33812fd374d8deecef01d4a840e7b41862d849513de77e44aa4889" +dependencies = [ + "block2", + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-data" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "617fbf49e071c178c0b24c080767db52958f716d9eabdf0890523aeae54773ef" +dependencies = [ + "bitflags 2.6.0", + "block2", + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-image" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55260963a527c99f1819c4f8e3b47fe04f9650694ef348ffd2227e8196d34c80" +dependencies = [ + "block2", + "objc2", + "objc2-foundation", + "objc2-metal", +] + +[[package]] +name = "objc2-core-location" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "000cfee34e683244f284252ee206a27953279d370e309649dc3ee317b37e5781" +dependencies = [ + "block2", + "objc2", + "objc2-contacts", + "objc2-foundation", +] + +[[package]] +name = "objc2-encode" +version = "4.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7891e71393cd1f227313c9379a26a584ff3d7e6e7159e988851f0934c993f0f8" + +[[package]] +name = "objc2-foundation" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8" +dependencies = [ + "bitflags 2.6.0", + "block2", + "dispatch", + "libc", + "objc2", +] + +[[package]] +name = "objc2-link-presentation" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1a1ae721c5e35be65f01a03b6d2ac13a54cb4fa70d8a5da293d7b0020261398" +dependencies = [ + "block2", + "objc2", + "objc2-app-kit", + "objc2-foundation", +] + +[[package]] +name = "objc2-metal" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6" +dependencies = [ + "bitflags 2.6.0", + "block2", + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-quartz-core" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a" +dependencies = [ + "bitflags 2.6.0", + "block2", + "objc2", + "objc2-foundation", + "objc2-metal", +] + +[[package]] +name = "objc2-symbols" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a684efe3dec1b305badae1a28f6555f6ddd3bb2c2267896782858d5a78404dc" +dependencies = [ + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-ui-kit" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8bb46798b20cd6b91cbd113524c490f1686f4c4e8f49502431415f3512e2b6f" +dependencies = [ + "bitflags 2.6.0", + "block2", + "objc2", + "objc2-cloud-kit", + "objc2-core-data", + "objc2-core-image", + "objc2-core-location", + "objc2-foundation", + "objc2-link-presentation", + "objc2-quartz-core", + "objc2-symbols", + "objc2-uniform-type-identifiers", + "objc2-user-notifications", +] + +[[package]] +name = "objc2-uniform-type-identifiers" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44fa5f9748dbfe1ca6c0b79ad20725a11eca7c2218bceb4b005cb1be26273bfe" +dependencies = [ + "block2", + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-user-notifications" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76cfcbf642358e8689af64cee815d139339f3ed8ad05103ed5eaf73db8d84cb3" +dependencies = [ + "bitflags 2.6.0", + "block2", + "objc2", + "objc2-core-location", + "objc2-foundation", +] + +[[package]] +name = "once_cell" +version = "1.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" + +[[package]] +name = "orbclient" +version = "0.3.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba0b26cec2e24f08ed8bb31519a9333140a6599b867dac464bb150bdb796fd43" +dependencies = [ + "libredox", +] + +[[package]] +name = "ordered-stream" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aa2b01e1d916879f73a53d01d1d6cee68adbb31d6d9177a8cfce093cced1d50" +dependencies = [ + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "os_str_bytes" +version = "6.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2355d85b9a3786f481747ced0e0ff2ba35213a1f9bd406ed906554d7af805a1" +dependencies = [ + "memchr", +] + +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + +[[package]] +name = "owned_ttf_parser" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22ec719bbf3b2a81c109a4e20b1f129b5566b7dce654bc3872f6a05abf82b2c4" +dependencies = [ + "ttf-parser", +] + +[[package]] +name = "parking" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" + +[[package]] +name = "parking_lot" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall 0.5.7", + "smallvec", + "windows-targets 0.52.6", +] + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "pear" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdeeaa00ce488657faba8ebf44ab9361f9365a97bd39ffb8a60663f57ff4b467" +dependencies = [ + "inlinable_string", + "pear_codegen", + "yansi", +] + +[[package]] +name = "pear_codegen" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bab5b985dc082b345f812b7df84e1bef27e7207b39e448439ba8bd69c93f147" +dependencies = [ + "proc-macro2", + "proc-macro2-diagnostics", + "quote", + "syn 2.0.82", +] + +[[package]] +name = "peeking_take_while" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "phf" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" +dependencies = [ + "phf_macros", + "phf_shared", +] + +[[package]] +name = "phf_generator" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0" +dependencies = [ + "phf_shared", + "rand", +] + +[[package]] +name = "phf_macros" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3444646e286606587e49f3bcf1679b8cef1dc2c5ecc29ddacaffc305180d464b" +dependencies = [ + "phf_generator", + "phf_shared", + "proc-macro2", + "quote", + "syn 2.0.82", +] + +[[package]] +name = "phf_shared" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b" +dependencies = [ + "siphasher", +] + +[[package]] +name = "pico-args" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315" + +[[package]] +name = "pin-project" +version = "1.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf123a161dde1e524adf36f90bc5d8d3462824a9c43553ad07a8183161189ec" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4502d8515ca9f32f1fb543d987f63d95a14934883db45bdb48060b6b69257f8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.82", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "piper" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96c8c490f422ef9a4efd2cb5b42b76c8613d7e7dfc1caf667b8a3350a5acc066" +dependencies = [ + "atomic-waker", + "fastrand", + "futures-io", +] + +[[package]] +name = "pkg-config" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" + +[[package]] +name = "plist" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42cf17e9a1800f5f396bc67d193dc9411b59012a5876445ef450d449881e1016" +dependencies = [ + "base64 0.22.1", + "indexmap", + "quick-xml 0.32.0", + "serde", + "time", +] + +[[package]] +name = "png" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52f9d46a34a05a6a57566bc2bfae066ef07585a6e3fa30fbbdff5936380623f0" +dependencies = [ + "bitflags 1.3.2", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide", +] + +[[package]] +name = "polling" +version = "3.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc2790cd301dec6cd3b7a025e4815cf825724a51c98dccfe6a3e55f05ffb6511" +dependencies = [ + "cfg-if", + "concurrent-queue", + "hermit-abi", + "pin-project-lite", + "rustix", + "tracing", + "windows-sys 0.59.0", +] + +[[package]] +name = "portable-atomic" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc9c68a3f6da06753e9335d63e27f6b9754dd1920d941135b7ea8224f141adb2" + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "ppv-lite86" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "prettyplease" +version = "0.2.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "910d41a655dac3b764f1ade94821093d3610248694320cd072303a8eedcf221d" +dependencies = [ + "proc-macro2", + "syn 2.0.82", +] + +[[package]] +name = "proc-macro-crate" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b" +dependencies = [ + "toml_edit", +] + +[[package]] +name = "proc-macro2" +version = "1.0.88" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c3a7fc5db1e57d5a779a352c8cdb57b29aa4c40cc69c3a68a7fedc815fbf2f9" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "proc-macro2-diagnostics" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.82", + "version_check", + "yansi", +] + +[[package]] +name = "pulldown-cmark" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f86ba2052aebccc42cbbb3ed234b8b13ce76f75c3551a303cb2bcffcff12bb14" +dependencies = [ + "bitflags 2.6.0", + "memchr", + "unicase", +] + +[[package]] +name = "pyo3" +version = "0.22.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d922163ba1f79c04bc49073ba7b32fd5a8d3b76a87c955921234b8e77333c51" +dependencies = [ + "cfg-if", + "indoc", + "libc", + "memoffset", + "once_cell", + "portable-atomic", + "pyo3-build-config", + "pyo3-ffi", + "pyo3-macros", + "unindent", +] + +[[package]] +name = "pyo3-build-config" +version = "0.22.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc38c5feeb496c8321091edf3d63e9a6829eab4b863b4a6a65f26f3e9cc6b179" +dependencies = [ + "once_cell", + "target-lexicon", +] + +[[package]] +name = "pyo3-ffi" +version = "0.22.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94845622d88ae274d2729fcefc850e63d7a3ddff5e3ce11bd88486db9f1d357d" +dependencies = [ + "libc", + "pyo3-build-config", +] + +[[package]] +name = "pyo3-macros" +version = "0.22.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e655aad15e09b94ffdb3ce3d217acf652e26bbc37697ef012f5e5e348c716e5e" +dependencies = [ + "proc-macro2", + "pyo3-macros-backend", + "quote", + "syn 2.0.82", +] + +[[package]] +name = "pyo3-macros-backend" +version = "0.22.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae1e3f09eecd94618f60a455a23def79f79eba4dc561a97324bf9ac8c6df30ce" +dependencies = [ + "heck", + "proc-macro2", + "pyo3-build-config", + "quote", + "syn 2.0.82", +] + +[[package]] +name = "quick-xml" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eff6510e86862b57b210fd8cbe8ed3f0d7d600b9c2863cd4549a2e033c66e956" +dependencies = [ + "memchr", + "serde", +] + +[[package]] +name = "quick-xml" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d3a6e5838b60e0e8fa7a43f22ade549a37d61f8bdbe636d0d7816191de969c2" +dependencies = [ + "memchr", +] + +[[package]] +name = "quick-xml" +version = "0.36.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7649a7b4df05aed9ea7ec6f628c67c9953a43869b8bc50929569b2999d443fe" +dependencies = [ + "memchr", +] + +[[package]] +name = "quote" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r2r" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71a6ecd5ceee58172c691c22ecfc013ef4621d38eb6804eb5bea0271f6f53daa" +dependencies = [ + "force-send-sync", + "futures", + "indexmap", + "lazy_static", + "log", + "phf", + "prettyplease", + "proc-macro2", + "quote", + "r2r_actions", + "r2r_common", + "r2r_macros", + "r2r_msg_gen", + "r2r_rcl", + "rayon", + "serde", + "serde_json", + "syn 2.0.82", + "thiserror", + "uuid", +] + +[[package]] +name = "r2r_actions" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57386bf86b3db4e77bd91d6faa33bce0dba4389cefd4ba808a5b3dc5f870dc96" +dependencies = [ + "bindgen", + "r2r_common", + "r2r_msg_gen", + "r2r_rcl", +] + +[[package]] +name = "r2r_common" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f663cf4827e641a11b670844ce1cfa67d7ecf625ada3b91246515ee26c896b18" +dependencies = [ + "bindgen", + "os_str_bytes", + "regex", + "sha2", +] + +[[package]] +name = "r2r_macros" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7092302580ef2a6febeda5db5fd6af4eb28400eb5e0da6c729dcc8b41990d137" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.82", +] + +[[package]] +name = "r2r_msg_gen" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c538acb9f8150028d1a8163eda1c48008551374f6a317cf52c30a3f06d4bef5b" +dependencies = [ + "bindgen", + "force-send-sync", + "itertools 0.10.5", + "phf", + "proc-macro2", + "quote", + "r2r_common", + "r2r_rcl", + "rayon", + "syn 2.0.82", +] + +[[package]] +name = "r2r_rcl" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6a333ef3afc5cda5ddbf2c0d0b0c0ff01e5f8dae542b109112dad0142b47531" +dependencies = [ + "bindgen", + "paste", + "r2r_common", + "widestring", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rand_distr" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32cb0b9bc82b0a0876c2dd994a7e7a2683d3e7390ca40e6886785ef0c7e3ee31" +dependencies = [ + "num-traits", + "rand", +] + +[[package]] +name = "rand_xoshiro" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f97cdb2a36ed4183de61b2f824cc45c9f1037f28afe0a322e9fff4c108b5aaa" +dependencies = [ + "rand_core", +] + +[[package]] +name = "raw-window-handle" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539" + +[[package]] +name = "rayon" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "rctree" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b42e27ef78c35d3998403c1d26f3efd9e135d3e5121b0a4845cc5cc27547f4f" + +[[package]] +name = "redox_syscall" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "redox_syscall" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" +dependencies = [ + "bitflags 2.6.0", +] + +[[package]] +name = "regex" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38200e5ee88914975b69f657f0801b6f6dccafd44fd9326302a4aaeecfacb1d8" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + +[[package]] +name = "resvg" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cadccb3d99a9efb8e5e00c16fbb732cbe400db2ec7fc004697ee7d97d86cf1f4" +dependencies = [ + "log", + "pico-args", + "rgb", + "svgtypes", + "tiny-skia", + "usvg", +] + +[[package]] +name = "rgb" +version = "0.8.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57397d16646700483b67d2dd6511d79318f9d057fdbd21a4066aeac8b41d310a" +dependencies = [ + "bytemuck", +] + +[[package]] +name = "ring" +version = "0.17.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" +dependencies = [ + "cc", + "cfg-if", + "getrandom", + "libc", + "spin", + "untrusted", + "windows-sys 0.52.0", +] + +[[package]] +name = "ron" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b91f7eff05f748767f183df4320a63d6936e9c6107d97c9e6bdd9784f4289c94" +dependencies = [ + "base64 0.21.7", + "bitflags 2.6.0", + "serde", + "serde_derive", +] + +[[package]] +name = "roxmltree" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3cd14fd5e3b777a7422cca79358c57a8f6e3a703d9ac187448d0daf220c2407f" + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustix" +version = "0.38.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811" +dependencies = [ + "bitflags 2.6.0", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustls" +version = "0.23.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fbb44d7acc4e873d613422379f69f237a1b141928c02f6bc6ccfddddc2d7993" +dependencies = [ + "log", + "once_cell", + "ring", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-pki-types" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16f1201b3c9a7ee8039bcadc17b7e605e2945b27eee7631788c1bd2b0643674b" + +[[package]] +name = "rustls-webpki" +version = "0.102.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "scoped-tls" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "serde" +version = "1.0.210" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.210" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.82", +] + +[[package]] +name = "serde_json" +version = "1.0.132" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "serde_repr" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.82", +] + +[[package]] +name = "serde_spanned" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_yaml" +version = "0.9.34+deprecated" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" +dependencies = [ + "indexmap", + "itoa", + "ryu", + "serde", + "unsafe-libyaml", +] + +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "signal-hook-registry" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +dependencies = [ + "libc", +] + +[[package]] +name = "simd-adler32" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" + +[[package]] +name = "simplecss" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a11be7c62927d9427e9f40f3444d5499d868648e2edbc4e2116de69e7ec0e89d" +dependencies = [ + "log", +] + +[[package]] +name = "siphasher" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "slotmap" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbff4acf519f630b3a3ddcfaea6c06b42174d9a44bc70c620e9ed1649d58b82a" +dependencies = [ + "version_check", +] + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "smithay-client-toolkit" +version = "0.19.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3457dea1f0eb631b4034d61d4d8c32074caa6cd1ab2d59f2327bd8461e2c0016" +dependencies = [ + "bitflags 2.6.0", + "calloop", + "calloop-wayland-source", + "cursor-icon", + "libc", + "log", + "memmap2", + "rustix", + "thiserror", + "wayland-backend", + "wayland-client", + "wayland-csd-frame", + "wayland-cursor", + "wayland-protocols", + "wayland-protocols-wlr", + "wayland-scanner", + "xkeysym", +] + +[[package]] +name = "smithay-clipboard" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc8216eec463674a0e90f29e0ae41a4db573ec5b56b1c6c1c71615d249b6d846" +dependencies = [ + "libc", + "smithay-client-toolkit", + "wayland-backend", +] + +[[package]] +name = "smol_str" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd538fb6910ac1099850255cf94a94df6551fbdd602454387d0adb2d1ca6dead" +dependencies = [ + "serde", +] + +[[package]] +name = "space_robotics_bench" +version = "0.0.1" +dependencies = [ + "display_json", + "fast_poisson", + "figment", + "paste", + "pyo3", + "rand", + "rand_xoshiro", + "rayon", + "serde", + "serde_json", + "serde_yaml", + "thiserror", + "toml", +] + +[[package]] +name = "space_robotics_bench_gui" +version = "0.0.1" +dependencies = [ + "chrono", + "const_format", + "display_json", + "eframe", + "egui", + "egui_commonmark", + "egui_extras", + "home", + "image", + "itertools 0.13.0", + "nix", + "paste", + "r2r", + "serde", + "serde_json", + "space_robotics_bench", + "subprocess", + "sysinfo", + "tracing", + "tracing-subscriber", + "typed-builder", +] + +[[package]] +name = "space_robotics_bench_py" +version = "0.0.1" +dependencies = [ + "const_format", + "pyo3", + "space_robotics_bench", +] + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "strict-num" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731" +dependencies = [ + "float-cmp", +] + +[[package]] +name = "subprocess" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c2e86926081dda636c546d8c5e641661049d7562a68f5488be4a1f7f66f6086" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "svgtypes" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e44e288cd960318917cbd540340968b90becc8bc81f171345d706e7a89d9d70" +dependencies = [ + "kurbo", + "siphasher", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.82" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83540f837a8afc019423a8edb95b52a8effe46957ee402287f4292fae35be021" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syntect" +version = "5.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "874dcfa363995604333cf947ae9f751ca3af4522c60886774c4963943b4746b1" +dependencies = [ + "bincode", + "bitflags 1.3.2", + "fancy-regex", + "flate2", + "fnv", + "once_cell", + "plist", + "regex-syntax", + "serde", + "serde_derive", + "serde_json", + "thiserror", + "walkdir", + "yaml-rust", +] + +[[package]] +name = "sysinfo" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3b5ae3f4f7d64646c46c4cae4e3f01d1c5d255c7406fdd7c7f999a94e488791" +dependencies = [ + "core-foundation-sys", + "libc", + "memchr", + "ntapi", + "rayon", + "windows 0.57.0", +] + +[[package]] +name = "target-lexicon" +version = "0.12.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" + +[[package]] +name = "tempfile" +version = "3.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f2c9fc62d0beef6951ccffd757e241266a2c833136efbe35af6cd2567dca5b" +dependencies = [ + "cfg-if", + "fastrand", + "once_cell", + "rustix", + "windows-sys 0.59.0", +] + +[[package]] +name = "thiserror" +version = "1.0.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.82", +] + +[[package]] +name = "thread_local" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +dependencies = [ + "cfg-if", + "once_cell", +] + +[[package]] +name = "time" +version = "0.3.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" +dependencies = [ + "deranged", + "itoa", + "num-conv", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" + +[[package]] +name = "time-macros" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tiny-skia" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83d13394d44dae3207b52a326c0c85a8bf87f1541f23b0d143811088497b09ab" +dependencies = [ + "arrayref", + "arrayvec", + "bytemuck", + "cfg-if", + "log", + "png", + "tiny-skia-path", +] + +[[package]] +name = "tiny-skia-path" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c9e7fc0c2e86a30b117d0462aa261b72b7a99b7ebd7deb3a14ceda95c5bdc93" +dependencies = [ + "arrayref", + "bytemuck", + "strict-num", +] + +[[package]] +name = "tinyvec" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "toml" +version = "0.8.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.22.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.82", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" +dependencies = [ + "nu-ansi-term", + "sharded-slab", + "smallvec", + "thread_local", + "tracing-core", + "tracing-log", +] + +[[package]] +name = "ttf-parser" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5902c5d130972a0000f60860bfbf46f7ca3db5391eddfedd1b8728bd9dc96c0e" + +[[package]] +name = "typed-builder" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e14ed59dc8b7b26cacb2a92bad2e8b1f098806063898ab42a3bd121d7d45e75" +dependencies = [ + "typed-builder-macro", +] + +[[package]] +name = "typed-builder-macro" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "560b82d656506509d43abe30e0ba64c56b1953ab3d4fe7ba5902747a7a3cedd5" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.82", +] + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "uds_windows" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89daebc3e6fd160ac4aa9fc8b3bf71e1f74fbf92367ae71fb83a037e8bf164b9" +dependencies = [ + "memoffset", + "tempfile", + "winapi", +] + +[[package]] +name = "uncased" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1b88fcfe09e89d3866a5c11019378088af2d24c3fbd4f0543f96b479ec90697" +dependencies = [ + "version_check", +] + +[[package]] +name = "unicase" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e51b68083f157f853b6379db119d1c1be0e6e4dec98101079dec41f6f5cf6df" + +[[package]] +name = "unicode-bidi" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ab17db44d7388991a428b2ee655ce0c212e862eff1768a455c58f9aad6e7893" + +[[package]] +name = "unicode-ident" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" + +[[package]] +name = "unicode-normalization" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-segmentation" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" + +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + +[[package]] +name = "unindent" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7de7d73e1754487cb58364ee906a499937a0dfabd86bcb980fa99ec8c8fa2ce" + +[[package]] +name = "unsafe-libyaml" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "ureq" +version = "2.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b74fc6b57825be3373f7054754755f03ac3a8f5d70015ccad699ba2029956f4a" +dependencies = [ + "base64 0.22.1", + "flate2", + "log", + "once_cell", + "rustls", + "rustls-pki-types", + "url", + "webpki-roots", +] + +[[package]] +name = "url" +version = "2.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "usvg" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38b0a51b72ab80ca511d126b77feeeb4fb1e972764653e61feac30adc161a756" +dependencies = [ + "base64 0.21.7", + "log", + "pico-args", + "usvg-parser", + "usvg-tree", + "xmlwriter", +] + +[[package]] +name = "usvg-parser" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bd4e3c291f45d152929a31f0f6c819245e2921bfd01e7bd91201a9af39a2bdc" +dependencies = [ + "data-url", + "flate2", + "imagesize", + "kurbo", + "log", + "roxmltree", + "simplecss", + "siphasher", + "svgtypes", + "usvg-tree", +] + +[[package]] +name = "usvg-tree" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ee3d202ebdb97a6215604b8f5b4d6ef9024efd623cf2e373a6416ba976ec7d3" +dependencies = [ + "rctree", + "strict-num", + "svgtypes", + "tiny-skia-path", +] + +[[package]] +name = "uuid" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8c5f0a0af699448548ad1a2fbf920fb4bee257eae39953ba95cb84891a0446a" +dependencies = [ + "getrandom", + "serde", +] + +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e" +dependencies = [ + "cfg-if", + "once_cell", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb6dd4d3ca0ddffd1dd1c9c04f94b868c37ff5fac97c30b97cff2d74fce3a358" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.82", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc7ec4f8827a71586374db3e87abdb5a2bb3a15afed140221307c3ec06b1f63b" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e79384be7f8f5a9dd5d7167216f022090cf1f9ec128e6e6a482a2cb5c5422c56" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.82", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d" + +[[package]] +name = "wayland-backend" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "056535ced7a150d45159d3a8dc30f91a2e2d588ca0b23f70e56033622b8016f6" +dependencies = [ + "cc", + "downcast-rs", + "rustix", + "scoped-tls", + "smallvec", + "wayland-sys", +] + +[[package]] +name = "wayland-client" +version = "0.31.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3f45d1222915ef1fd2057220c1d9d9624b7654443ea35c3877f7a52bd0a5a2d" +dependencies = [ + "bitflags 2.6.0", + "rustix", + "wayland-backend", + "wayland-scanner", +] + +[[package]] +name = "wayland-csd-frame" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "625c5029dbd43d25e6aa9615e88b829a5cad13b2819c4ae129fdbb7c31ab4c7e" +dependencies = [ + "bitflags 2.6.0", + "cursor-icon", + "wayland-backend", +] + +[[package]] +name = "wayland-cursor" +version = "0.31.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a94697e66e76c85923b0d28a0c251e8f0666f58fc47d316c0f4da6da75d37cb" +dependencies = [ + "rustix", + "wayland-client", + "xcursor", +] + +[[package]] +name = "wayland-protocols" +version = "0.32.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b5755d77ae9040bb872a25026555ce4cb0ae75fd923e90d25fba07d81057de0" +dependencies = [ + "bitflags 2.6.0", + "wayland-backend", + "wayland-client", + "wayland-scanner", +] + +[[package]] +name = "wayland-protocols-plasma" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a0a41a6875e585172495f7a96dfa42ca7e0213868f4f15c313f7c33221a7eff" +dependencies = [ + "bitflags 2.6.0", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "wayland-scanner", +] + +[[package]] +name = "wayland-protocols-wlr" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dad87b5fd1b1d3ca2f792df8f686a2a11e3fe1077b71096f7a175ab699f89109" +dependencies = [ + "bitflags 2.6.0", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "wayland-scanner", +] + +[[package]] +name = "wayland-scanner" +version = "0.31.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597f2001b2e5fc1121e3d5b9791d3e78f05ba6bfa4641053846248e3a13661c3" +dependencies = [ + "proc-macro2", + "quick-xml 0.36.2", + "quote", +] + +[[package]] +name = "wayland-sys" +version = "0.31.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "efa8ac0d8e8ed3e3b5c9fc92c7881406a268e11555abe36493efabe649a29e09" +dependencies = [ + "dlib", + "log", + "once_cell", + "pkg-config", +] + +[[package]] +name = "web-sys" +version = "0.3.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6488b90108c040df0fe62fa815cbdee25124641df01814dd7282749234c6112" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webbrowser" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e5f07fb9bc8de2ddfe6b24a71a75430673fd679e568c48b52716cef1cfae923" +dependencies = [ + "block2", + "core-foundation 0.10.0", + "home", + "jni", + "log", + "ndk-context", + "objc2", + "objc2-foundation", + "url", + "web-sys", +] + +[[package]] +name = "webpki-roots" +version = "0.26.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841c67bff177718f1d4dfefde8d8f0e78f9b6589319ba88312f567fc5841a958" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "weezl" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53a85b86a771b1c87058196170769dd264f66c0782acf1ae6cc51bfd64b39082" + +[[package]] +name = "which" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" +dependencies = [ + "either", + "home", + "once_cell", + "rustix", +] + +[[package]] +name = "widestring" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7219d36b6eac893fa81e84ebe06485e7dcbb616177469b142df14f1f4deb1311" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows" +version = "0.57.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12342cb4d8e3b046f3d80effd474a7a02447231330ef77d71daa6fbc40681143" +dependencies = [ + "windows-core 0.57.0", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd04d41d93c4992d421894c18c8b43496aa748dd4c081bac0dc93eb0489272b6" +dependencies = [ + "windows-core 0.58.0", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-core" +version = "0.57.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2ed2439a290666cd67ecce2b0ffaad89c2a56b976b736e6ece670297897832d" +dependencies = [ + "windows-implement 0.57.0", + "windows-interface 0.57.0", + "windows-result 0.1.2", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-core" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99" +dependencies = [ + "windows-implement 0.58.0", + "windows-interface 0.58.0", + "windows-result 0.2.0", + "windows-strings", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-implement" +version = "0.57.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9107ddc059d5b6fbfbffdfa7a7fe3e22a226def0b2608f72e9d552763d3e1ad7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.82", +] + +[[package]] +name = "windows-implement" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.82", +] + +[[package]] +name = "windows-interface" +version = "0.57.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29bee4b38ea3cde66011baa44dba677c432a78593e202392d1e9070cf2a7fca7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.82", +] + +[[package]] +name = "windows-interface" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.82", +] + +[[package]] +name = "windows-result" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-result" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-strings" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" +dependencies = [ + "windows-result 0.2.0", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "winit" +version = "0.30.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0be9e76a1f1077e04a411f0b989cbd3c93339e1771cb41e71ac4aee95bfd2c67" +dependencies = [ + "ahash", + "android-activity", + "atomic-waker", + "bitflags 2.6.0", + "block2", + "bytemuck", + "calloop", + "cfg_aliases", + "concurrent-queue", + "core-foundation 0.9.4", + "core-graphics", + "cursor-icon", + "dpi", + "js-sys", + "libc", + "memmap2", + "ndk", + "objc2", + "objc2-app-kit", + "objc2-foundation", + "objc2-ui-kit", + "orbclient", + "percent-encoding", + "pin-project", + "raw-window-handle", + "redox_syscall 0.4.1", + "rustix", + "smithay-client-toolkit", + "smol_str", + "tracing", + "unicode-segmentation", + "wasm-bindgen", + "wasm-bindgen-futures", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "wayland-protocols-plasma", + "web-sys", + "web-time", + "windows-sys 0.52.0", + "x11-dl", + "x11rb", + "xkbcommon-dl", +] + +[[package]] +name = "winnow" +version = "0.6.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" +dependencies = [ + "memchr", +] + +[[package]] +name = "x11-dl" +version = "2.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38735924fedd5314a6e548792904ed8c6de6636285cb9fec04d5b1db85c1516f" +dependencies = [ + "libc", + "once_cell", + "pkg-config", +] + +[[package]] +name = "x11rb" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d91ffca73ee7f68ce055750bf9f6eca0780b8c85eff9bc046a3b0da41755e12" +dependencies = [ + "as-raw-xcb-connection", + "gethostname", + "libc", + "libloading", + "once_cell", + "rustix", + "x11rb-protocol", +] + +[[package]] +name = "x11rb-protocol" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec107c4503ea0b4a98ef47356329af139c0a4f7750e621cf2973cd3385ebcb3d" + +[[package]] +name = "xcursor" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ef33da6b1660b4ddbfb3aef0ade110c8b8a781a3b6382fa5f2b5b040fd55f61" + +[[package]] +name = "xdg-home" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec1cdab258fb55c0da61328dc52c8764709b249011b2cad0454c72f0bf10a1f6" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] + +[[package]] +name = "xkbcommon-dl" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d039de8032a9a8856a6be89cea3e5d12fdd82306ab7c94d74e6deab2460651c5" +dependencies = [ + "bitflags 2.6.0", + "dlib", + "log", + "once_cell", + "xkeysym", +] + +[[package]] +name = "xkeysym" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9cc00251562a284751c9973bace760d86c0276c471b4be569fe6b068ee97a56" + +[[package]] +name = "xml-rs" +version = "0.8.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af4e2e2f7cba5a093896c1e150fbfe177d1883e7448200efb81d40b9d339ef26" + +[[package]] +name = "xmlwriter" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec7a2a501ed189703dba8b08142f057e887dfc4b2cc4db2d343ac6376ba3e0b9" + +[[package]] +name = "yaml-rust" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" +dependencies = [ + "linked-hash-map", +] + +[[package]] +name = "yansi" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" + +[[package]] +name = "zbus" +version = "4.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb97012beadd29e654708a0fdb4c84bc046f537aecfde2c3ee0a9e4b4d48c725" +dependencies = [ + "async-broadcast", + "async-executor", + "async-fs", + "async-io", + "async-lock", + "async-process", + "async-recursion", + "async-task", + "async-trait", + "blocking", + "enumflags2", + "event-listener", + "futures-core", + "futures-sink", + "futures-util", + "hex", + "nix", + "ordered-stream", + "rand", + "serde", + "serde_repr", + "sha1", + "static_assertions", + "tracing", + "uds_windows", + "windows-sys 0.52.0", + "xdg-home", + "zbus_macros", + "zbus_names", + "zvariant", +] + +[[package]] +name = "zbus-lockstep" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ca2c5dceb099bddaade154055c926bb8ae507a18756ba1d8963fd7b51d8ed1d" +dependencies = [ + "zbus_xml", + "zvariant", +] + +[[package]] +name = "zbus-lockstep-macros" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "709ab20fc57cb22af85be7b360239563209258430bccf38d8b979c5a2ae3ecce" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.82", + "zbus-lockstep", + "zbus_xml", + "zvariant", +] + +[[package]] +name = "zbus_macros" +version = "4.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "267db9407081e90bbfa46d841d3cbc60f59c0351838c4bc65199ecd79ab1983e" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.82", + "zvariant_utils", +] + +[[package]] +name = "zbus_names" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b9b1fef7d021261cc16cba64c351d291b715febe0fa10dc3a443ac5a5022e6c" +dependencies = [ + "serde", + "static_assertions", + "zvariant", +] + +[[package]] +name = "zbus_xml" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab3f374552b954f6abb4bd6ce979e6c9b38fb9d0cd7cc68a7d796e70c9f3a233" +dependencies = [ + "quick-xml 0.30.0", + "serde", + "static_assertions", + "zbus_names", + "zvariant", +] + +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "byteorder", + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.82", +] + +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" + +[[package]] +name = "zune-core" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a" + +[[package]] +name = "zune-jpeg" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16099418600b4d8f028622f73ff6e3deaabdff330fb9a2a131dea781ee8b0768" +dependencies = [ + "zune-core", +] + +[[package]] +name = "zvariant" +version = "4.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2084290ab9a1c471c38fc524945837734fbf124487e105daec2bb57fd48c81fe" +dependencies = [ + "endi", + "enumflags2", + "serde", + "static_assertions", + "zvariant_derive", +] + +[[package]] +name = "zvariant_derive" +version = "4.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73e2ba546bda683a90652bac4a279bc146adad1386f25379cf73200d2002c449" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.82", + "zvariant_utils", +] + +[[package]] +name = "zvariant_utils" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c51bcff7cc3dbb5055396bcf774748c3dab426b4b8659046963523cee4808340" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.82", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..910c153 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,57 @@ +[workspace.package] +authors = ["Andrej Orsula "] +description = "Comprehensive benchmark for space robotics" +categories = ["science::robotics"] +keywords = ["benchmark", "robotics", "simulation", "space"] +readme = "README.md" +license = "MIT OR Apache-2.0" +homepage = "https://github.com/AndrejOrsula/space_robotics_bench" +repository = "https://github.com/AndrejOrsula/space_robotics_bench" +documentation = "https://andrejorsula.github.io/space_robotics_bench" +edition = "2021" +rust-version = "1.80" +version = "0.0.1" +publish = false + +[workspace] +resolver = "2" +members = [ + "crates/space_robotics_bench", + "crates/space_robotics_bench_py", + "crates/space_robotics_bench_gui", +] +default-members = [ + "crates/space_robotics_bench", + "crates/space_robotics_bench_py", +] + +[workspace.dependencies] +space_robotics_bench = { path = "crates/space_robotics_bench", version = "0.0.1" } + +chrono = { version = "0.4" } +const_format = { version = "0.2", features = ["more_str_macros"] } +display_json = { version = "0.2" } +fast_poisson = { version = "1.0", features = ["single_precision"] } +figment = { version = "0.10", features = ["env", "test"] } +home = { version = "0.5.9" } +image = { version = "0.25", default-features = false, features = [ + "jpeg", + "png", +] } +itertools = { version = "0.13" } +nix = { version = "0.29", features = ["signal"] } +paste = { version = "1.0" } +pyo3 = { version = "0.22", features = ["abi3-py310", "auto-initialize"] } +rand = { version = "0.8" } +rand_xoshiro = { version = "0.6" } +rayon = { version = "1.5" } +serde = { version = "1", features = ["derive"] } +serde_json = { version = "1" } +serde_yaml = { version = "0.9" } +subprocess = { version = "0.2" } +sysinfo = { version = "0.32" } +thiserror = { version = "1" } +toml = { version = "0.8" } +tracing = { version = "0.1" } +tracing-subscriber = { version = "0.3" } +typed-builder = { version = "0.20" } diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..c1f29de --- /dev/null +++ b/Dockerfile @@ -0,0 +1,223 @@ +## Base +ARG BASE_IMAGE_NAME="ubuntu" +ARG BASE_IMAGE_TAG="jammy" + +## Isaac Sim +## Label as isaac-sim for copying into the final image +ARG ISAAC_SIM_IMAGE_NAME="nvcr.io/nvidia/isaac-sim" +ARG ISAAC_SIM_IMAGE_TAG="4.2.0" +FROM ${ISAAC_SIM_IMAGE_NAME}:${ISAAC_SIM_IMAGE_TAG} AS isaac-sim + +## Continue with the base image +FROM ${BASE_IMAGE_NAME}:${BASE_IMAGE_TAG} + +## Use bash as the default shell +SHELL ["/bin/bash", "-o", "pipefail", "-c"] + +## Create a barebones entrypoint that is conditionally updated throughout the Dockerfile +RUN echo "#!/usr/bin/env bash" >> /entrypoint.bash && \ + chmod +x /entrypoint.bash + +## Copy Isaac Sim into the base image +ARG ISAAC_SIM_PATH="/root/isaac-sim" +ENV ISAAC_SIM_PYTHON="${ISAAC_SIM_PATH}/python.sh" +COPY --from=isaac-sim /isaac-sim "${ISAAC_SIM_PATH}" +COPY --from=isaac-sim /root/.nvidia-omniverse/config /root/.nvidia-omniverse/config +COPY --from=isaac-sim /etc/vulkan/icd.d/nvidia_icd.json /etc/vulkan/icd.d/nvidia_icd.json +RUN ISAAC_SIM_VERSION="$(cut -d'-' -f1 < "${ISAAC_SIM_PATH}/VERSION")" && \ + echo -e "\n# Isaac Sim ${ISAAC_SIM_VERSION}" >> /entrypoint.bash && \ + echo "export ISAAC_SIM_PATH=\"${ISAAC_SIM_PATH}\"" >> /entrypoint.bash && \ + echo "export OMNI_KIT_ALLOW_ROOT=\"1\"" >> /entrypoint.bash +## Fix cosmetic issues in `isaac-sim/setup_python_env.sh` that append nonsense paths to `PYTHONPATH` and `LD_LIBRARY_PATH` +# hadolint ignore=SC2016 +RUN sed -i 's|$SCRIPT_DIR/../../../$LD_LIBRARY_PATH:||' "${ISAAC_SIM_PATH}/setup_python_env.sh" && \ + sed -i 's|$SCRIPT_DIR/../../../$PYTHONPATH:||' "${ISAAC_SIM_PATH}/setup_python_env.sh" + +## Optimization: Build Python to improve the runtime performance of training +ARG PYTHON_VERSION="3.10.14" +ENV PYTHONEXE="/usr/local/bin/python${PYTHON_VERSION%%.*}" +# hadolint ignore=DL3003,DL3008 +RUN PYTHON_DL_PATH="/tmp/Python-${PYTHON_VERSION}.tar.xz" && \ + PYTHON_SRC_DIR="/tmp/python${PYTHON_VERSION}" && \ + apt-get update && \ + DEBIAN_FRONTEND=noninteractive apt-get install -yq --no-install-recommends \ + build-essential \ + ca-certificates \ + curl \ + libbz2-dev \ + libdb4o-cil-dev \ + libgdm-dev \ + libhidapi-dev \ + liblzma-dev \ + libncurses5-dev \ + libpcap-dev \ + libreadline-dev \ + libsqlite3-dev \ + libssl-dev \ + libtk8.6 \ + lzma \ + xz-utils && \ + rm -rf /var/lib/apt/lists/* && \ + curl --proto "=https" --tlsv1.2 -sSfL "https://www.python.org/ftp/python/${PYTHON_VERSION}/Python-${PYTHON_VERSION}.tar.xz" -o "${PYTHON_DL_PATH}" && \ + mkdir -p "${PYTHON_SRC_DIR}" && \ + tar xf "${PYTHON_DL_PATH}" -C "${PYTHON_SRC_DIR}" --strip-components=1 && \ + rm "${PYTHON_DL_PATH}" && \ + cd "${PYTHON_SRC_DIR}" && \ + "${PYTHON_SRC_DIR}/configure" --enable-shared --enable-optimizations --with-lto --prefix="/usr/local" && \ + make -j "$(nproc)" && \ + make install && \ + cd - && \ + rm -rf "${PYTHON_SRC_DIR}" +## Fix `PYTHONEXE` by disabling the append of "isaac-sim/kit/kernel/plugins" to `LD_LIBRARY_PATH` inside `isaac-sim/setup_python_env.sh` +# hadolint ignore=SC2016 +RUN sed -i 's|$SCRIPT_DIR/kit/kernel/plugins:||' "${ISAAC_SIM_PATH}/setup_python_env.sh" + +## Install system dependencies +# hadolint ignore=DL3008 +RUN apt-get update && \ + DEBIAN_FRONTEND=noninteractive apt-get install -yq --no-install-recommends \ + # Common + build-essential \ + ca-certificates \ + cmake \ + curl \ + git \ + mold \ + xz-utils \ + # Isaac Sim + libgl1 \ + libglu1 \ + libxt-dev \ + # Blender + libegl1 \ + # egui + libxkbcommon-x11-0 \ + # r2r + clang \ + # Video recording/processing + ffmpeg && \ + rm -rf /var/lib/apt/lists/* + +## Upgrade pip +RUN "${ISAAC_SIM_PYTHON}" -m pip install --no-input --no-cache-dir --upgrade pip + +## Install Rust +ARG RUST_VERSION="1.80" +RUN echo -e "\n# Rust ${RUST_VERSION}" >> /entrypoint.bash && \ + echo "export PATH=\"${HOME}/.cargo/bin\${PATH:+:\${PATH}}\"" >> /entrypoint.bash && \ + echo "export CARGO_TARGET_DIR=\"${HOME}/.cargo/target\"" >> /entrypoint.bash && \ + echo "export CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_RUSTFLAGS=\"-Clink-arg=-fuse-ld=mold -Ctarget-cpu=native\"" >> /entrypoint.bash && \ + echo -e "\n# PyO3" >> /entrypoint.bash && \ + echo "export PYO3_PYTHON=\"${ISAAC_SIM_PYTHON}\"" >> /entrypoint.bash && \ + curl --proto "=https" --tlsv1.2 -sSfL "https://sh.rustup.rs" | sh -s -- --no-modify-path -y --default-toolchain "${RUST_VERSION}" --profile default + +## Install ROS +ARG ROS_DISTRO="humble" +# hadolint ignore=SC1091,DL3008 +RUN curl --proto "=https" --tlsv1.2 -sSfL "https://raw.githubusercontent.com/ros/rosdistro/master/ros.key" -o /usr/share/keyrings/ros-archive-keyring.gpg && \ + echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/ros-archive-keyring.gpg] http://packages.ros.org/ros2/ubuntu $(source /etc/os-release && echo "${UBUNTU_CODENAME}") main" > /etc/apt/sources.list.d/ros2.list && \ + apt-get update && \ + DEBIAN_FRONTEND=noninteractive apt-get install -yq --no-install-recommends \ + ros-dev-tools \ + "ros-${ROS_DISTRO}-ros-base" \ + "ros-${ROS_DISTRO}-rmw-cyclonedds-cpp" && \ + rm -rf /var/lib/apt/lists/* && \ + "${ISAAC_SIM_PYTHON}" -m pip install --no-input --no-cache-dir catkin_pkg && \ + rosdep init --rosdistro "${ROS_DISTRO}" && \ + echo -e "\n# ROS ${ROS_DISTRO^}" >> /entrypoint.bash && \ + echo "source \"/opt/ros/${ROS_DISTRO}/setup.bash\" --" >> /entrypoint.bash + +## Install Blender +ARG BLENDER_PATH="/root/blender" +ARG BLENDER_VERSION="4.2.2" +# hadolint ignore=SC2016 +RUN echo -e "\n# Blender ${BLENDER_VERSION}" >> /entrypoint.bash && \ + echo "export PATH=\"${BLENDER_PATH}\${PATH:+:\${PATH}}\"" >> /entrypoint.bash && \ + curl --proto "=https" --tlsv1.2 -sSfL "https://download.blender.org/release/Blender${BLENDER_VERSION%.*}/blender-${BLENDER_VERSION}-linux-x64.tar.xz" -o "/tmp/blender_${BLENDER_VERSION}.tar.xz" && \ + mkdir -p "${BLENDER_PATH}" && \ + tar xf "/tmp/blender_${BLENDER_VERSION}.tar.xz" -C "${BLENDER_PATH}" --strip-components=1 && \ + rm "/tmp/blender_${BLENDER_VERSION}.tar.xz" + +## Install Isaac Lab +ARG ISAACLAB_PATH="/root/isaaclab" +ARG ISAACLAB_REMOTE="https://github.com/isaac-sim/IsaacLab.git" +ARG ISAACLAB_BRANCH="main" +ARG ISAACLAB_COMMIT_SHA="0ef582badf6f257bb3c320c63d5d6d899604a138" # Oct 5, 2024 +# hadolint ignore=SC2044 +RUN echo -e "\n# Isaac Lab ${ISAACLAB_COMMIT_SHA}" >> /entrypoint.bash && \ + echo "export ISAACLAB_PATH=\"${ISAACLAB_PATH}\"" >> /entrypoint.bash && \ + git clone "${ISAACLAB_REMOTE}" "${ISAACLAB_PATH}" --branch "${ISAACLAB_BRANCH}" && \ + git -C "${ISAACLAB_PATH}" reset --hard "${ISAACLAB_COMMIT_SHA}" && \ + for extension in $(find -L "${ISAACLAB_PATH}/source/extensions" -mindepth 1 -maxdepth 1 -type d); do \ + if [ -f "${extension}/setup.py" ]; then \ + "${ISAAC_SIM_PYTHON}" -m pip install --no-input --no-cache-dir --editable "${extension}" ; \ + fi ; \ + done + +## Finalize the entrypoint +# hadolint ignore=SC2016 +RUN echo -e "\n# Execute command" >> /entrypoint.bash && \ + echo -en 'exec "${@}"\n' >> /entrypoint.bash && \ + sed -i '$a source /entrypoint.bash --' ~/.bashrc +ENTRYPOINT ["/entrypoint.bash"] + +## Define the workspace of the project +ARG WS="/root/ws" +WORKDIR "${WS}" + +## [Optional] Install Python dependencies in advance to cache the layers (speeds up rebuilds) +COPY ./pyproject.toml "${WS}/" +# hadolint ignore=SC2046 +RUN "${ISAAC_SIM_PYTHON}" -m pip install --no-input --no-cache-dir toml==0.10.2 && \ + "${ISAAC_SIM_PYTHON}" -m pip install --no-input --no-cache-dir $("${ISAAC_SIM_PYTHON}" -c "import toml, itertools; data = toml.load(open('${WS}/pyproject.toml')); project_name = data['project']['name']; deps = [dep for dep in data['project'].get('dependencies', []) if project_name not in dep]; opt_deps = list(itertools.chain(*[opt for opt in data['project'].get('optional-dependencies', {}).values() if not any(project_name in dep for dep in opt)])); print(' '.join(deps + opt_deps))") + +## Install ROS dependencies in advance to cache the layers (speeds up rebuilds) +COPY ./package.xml "${WS}/" +RUN apt-get update && \ + rosdep update --rosdistro "${ROS_DISTRO}" && \ + DEBIAN_FRONTEND=noninteractive rosdep install --default-yes --ignore-src --rosdistro "${ROS_DISTRO}" --from-paths "${WS}" && \ + rm -rf /var/lib/apt/lists/* /root/.ros/rosdep/sources.cache + +## Copy the source code into the image +COPY . "${WS}" + +## Build Rust targets +# hadolint ignore=SC1091 +RUN source /entrypoint.bash -- && \ + cargo build --release --workspace --all-targets + +## Install project as editable Python module +# hadolint ignore=SC1091 +RUN source /entrypoint.bash -- && \ + "${ISAAC_SIM_PYTHON}" -m pip install --no-input --no-cache-dir --editable "${WS}[all]" + +## Install project as ROS package +ARG ROS_WS="/opt/ros/${ROS_DISTRO}/ws" +# hadolint ignore=SC1091 +RUN source /entrypoint.bash -- && \ + colcon build --merge-install --symlink-install --cmake-args -DPython3_EXECUTABLE=${ISAAC_SIM_PYTHON} --paths "${WS}" --build-base "${ROS_WS}/build" --install-base "${ROS_WS}/install" && \ + rm -rf ./log && \ + sed -i "s|source \"/opt/ros/${ROS_DISTRO}/setup.bash\" --|source \"${ROS_WS}/install/setup.bash\" --|" /entrypoint.bash + +## Set the default command +CMD ["bash"] + +############ +### Misc ### +############ + +## Skip writing Python bytecode to the disk to avoid polluting mounted host volume with `__pycache__` directories +ENV PYTHONDONTWRITEBYTECODE=1 + +############### +### Develop ### +############### + +# ## Install DreamerV3 locally to enable mounting the source code into the container +# ARG DREAMERV3_PATH="/root/dreamerv3" +# ARG DREAMERV3_REMOTE="https://github.com/AndrejOrsula/dreamerv3.git" +# ARG DREAMERV3_BRANCH="dev" +# RUN git clone "${DREAMERV3_REMOTE}" "${DREAMERV3_PATH}" --branch "${DREAMERV3_BRANCH}" && \ +# "${ISAAC_SIM_PYTHON}" -m pip install --no-input --no-cache-dir -r "${DREAMERV3_PATH}/embodied/requirements.txt" && \ +# "${ISAAC_SIM_PYTHON}" -m pip install --no-input --no-cache-dir -r "${DREAMERV3_PATH}/dreamerv3/requirements.txt" -f "https://storage.googleapis.com/jax-releases/jax_cuda_releases.html" && \ +# "${ISAAC_SIM_PYTHON}" -m pip install --no-input --no-cache-dir --editable "${DREAMERV3_PATH}" diff --git a/LICENSE-APACHE b/LICENSE-APACHE new file mode 100644 index 0000000..d9a10c0 --- /dev/null +++ b/LICENSE-APACHE @@ -0,0 +1,176 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS diff --git a/LICENSE-MIT b/LICENSE-MIT new file mode 100644 index 0000000..1133824 --- /dev/null +++ b/LICENSE-MIT @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 Andrej Orsula + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..2e44ab7 --- /dev/null +++ b/README.md @@ -0,0 +1,26 @@ +# Space Robotics Bench + +![](docs/src/_images/srb_multi_env.jpg) + +

+ Rust + Python + Docker + Docs +

+ +The **Space Robotics Bench** aims to be a comprehensive collection of environments and tasks for robotics research in the challenging domain of space. The benchmark covers a wide range of applications and scenarios while providing a unified framework for experimenting with new tasks. Although the primary focus is on the application of robot learning techniques, the benchmark is designed to be flexible and extensible to accommodate a variety of research directions. + +## Documentation + +The full documentation is available in its raw form inside the [docs](docs) directory. The compiled version is hosted [online](https://AndrejOrsula.github.io/space_robotics_bench) in a more accessible format. + + HTML + +## License + +This project is dual-licensed under either the [MIT](LICENSE-MIT) or [Apache 2.0](LICENSE-APACHE) licenses. + +All assets created by contributors of this repository and those generated from the included procedural pipelines are licensed under the [CC0 1.0 Universal](https://github.com/AndrejOrsula/srb_assets/blob/main/LICENSE-CC0) license. Some assets are based on modified third-party resources, which might require you to give appropriate credit to the original author. Please review [`srb_assets` repository](https://github.com/AndrejOrsula/srb_assets) for more information. + + diff --git a/apps/srb.headless.kit b/apps/srb.headless.kit new file mode 100644 index 0000000..3b6e71b --- /dev/null +++ b/apps/srb.headless.kit @@ -0,0 +1,171 @@ +[package] +title = "Space Robotics Bench | Headless" +description = "Headless experience for the Space Robotics Bench" +version = "0.0.1" +keywords = ["benchmark", "robotics", "simulation", "space"] + + +[dependencies] +## Essentials +"omni.isaac.cloner" = {} +"omni.isaac.core" = {} +"omni.kit.loop-isaac" = {} +"omni.kit.viewport.rtx" = {} +"omni.kit.window.title" = {} +"omni.physx.bundle" = {} + + +[settings] +## Name +app.name = "Space Robotics Bench" +app.window.title = "Space Robotics Bench" +crashreporter.data.experience = "Space Robotics Bench" +exts."omni.kit.window.modifier.titlebar".titleFormatString = "Space Robotics Bench" + +## Extensions +app.exts.folders = [ + "${app}", + "${exe-path}/exts", + "${exe-path}/extscore", + "${exe-path}/../exts", + "${exe-path}/../extscache", + "${exe-path}/../extsPhysics", + "${exe-path}/../isaacsim/exts", + "${exe-path}/../isaacsim/extscache", + "${exe-path}/../isaacsim/extsPhysics", +] + +## UI +app.window.hideUi = false +app.window.iconPath = "${app}/../docs/theme/favicon.png" +app.file.ignoreUnsavedOnExit = true + +## Viewport +app.viewport.defaultCamPos.x = 5 +app.viewport.defaultCamPos.y = 5 +app.viewport.defaultCamPos.z = 5 + +## Rendering +app.asyncRendering = false +app.asyncRenderingLowLatency = false +app.hydraEngine.waitIdle = true +app.renderer.resolution.height = 720 +app.renderer.resolution.width = 1280 +app.renderer.skipWhileMinimized = false +app.renderer.sleepMsOnFocus = 0 +app.renderer.sleepMsOutOfFocus = 0 +app.renderer.waitIdle = true +app.updateOrder.checkForHydraRenderComplete = 1000 +app.vsync = false +ngx.enabled = true +omni.replicator.asyncRendering = false +renderer.active = "rtx" +renderer.enabled = "rtx" +renderer.multiGpu.autoEnable = false +renderer.multiGpu.enabled = false +renderer.multiGpu.maxGpuCount = 1 +rtx-transient.dlssg.enabled = false +rtx-transient.resourcemanager.enableTextureStreaming = true +rtx.hydra.enableSemanticSchema = true +rtx.indirectDiffuse.enabled = false +rtx.mdltranslator.mdlDistilling = false +rtx.newDenoiser.enabled = false +rtx.pathtracing.optixDenoiser.enabled = false +rtx.post.dlss.execMode = 0 # 0 (Performance) | 1 (Balanced) | 2 (Quality) | 3 (Auto) +rtx.post.histogram.enabled = true # Auto exposure +rtx.raytracing.fractionalCutoutOpacity = false +rtx.raytracing.subsurface.denoiser.enabled = false +rtx.reflections.enabled = false +rtx.sceneDb.ambientLightIntensity = 1.0 +rtx.transient.dlssg.enabled = false +rtx.translucency.enabled = true +rtx.translucency.worldEps = 0.005 + +# Note: Sampled lighting works better for static scenes +rtx.directLighting.sampledLighting.enabled = false +rtx.directLighting.sampledLighting.samplesPerPixel = 1 + +## Physics +physics.autoPopupSimulationOutputWindow = false +physics.fabricUpdateForceSensors = false +physics.fabricUpdateJointStates = false +physics.fabricUpdateTransformations = false +physics.fabricUpdateVelocities = false +physics.outputVelocitiesLocalSpace = false +physics.updateForceSensorsToUsd = false +physics.updateParticlesToUsd = false +physics.updateToUsd = false +physics.updateVelocitiesToUsd = false +physics.updateVelocitiesToUsd = false +physics.useFastCache = false +physics.visualizationDisplayJoints = false +physics.visualizationSimulationOutput = false + +## Thread settings +app.runLoops.main.rateLimitEnabled = false +app.runLoops.main.rateLimitFrequency = 120 +app.runLoops.main.rateLimitUsePrecisionSleep = true +app.runLoops.main.syncToPresent = false +app.runLoops.present.rateLimitFrequency = 120 +app.runLoops.present.rateLimitUsePrecisionSleep = true +app.runLoops.rendering_0.rateLimitFrequency = 120 +app.runLoops.rendering_0.rateLimitUsePrecisionSleep = true +app.runLoops.rendering_0.syncToPresent = false +app.runLoops.rendering_1.rateLimitFrequency = 120 +app.runLoops.rendering_1.rateLimitUsePrecisionSleep = true +app.runLoops.rendering_1.syncToPresent = false +app.runLoopsGlobal.syncToPresent = false + +## Audio +app.audio.enabled = false + +## Suppress errors and warnings +app.settings.fabricDefaultStageFrameHistoryCount = 3 +app.vulkan = true +rtx.pathtracing.maxSamplesPerLaunch = 1000000 + +## Performance +exts."omni.replicator.core".Orchestrator.enabled = false +exts."omni.kit.renderer.core".present.enabled = false +exts."omni.kit.renderer.core".present.presentAfterRendering = false + + +app.settings.persistent = true +[settings.persistent] +## Stage +app.primCreation.DefaultXformOpOrder = "xformOp:translate, xformOp:orient, xformOp:scale" +app.primCreation.DefaultXformOpType = "Scale, Orient, Translate" +app.primCreation.typedDefaults.camera.clippingRange = [0.01, 10000000.0] +app.stage.instanceableOnCreatingReference = false +app.stage.materialStrength = "weakerThanDescendants" +app.stage.movePrimInPlace = false +app.stage.upAxis = "Z" +simulation.defaultMetersPerUnit = 1.0 +simulation.minFrameRate = 15 + +## UI +app.file.recentFiles = [] +app.window.uiStyle = "NvidiaDark" + +## Viewport +app.transform.gizmoUseSRT = true +app.viewport.camMoveVelocity = 0.05 +app.viewport.defaults.tickRate = 120 +app.viewport.displayOptions = 31887 +app.viewport.gizmo.scale = 0.01 +app.viewport.grid.scale = 1.0 +app.viewport.pickingMode = "kind:model.ALL" +app.viewport.previewOnPeek = false +app.viewport.snapToSurface = false + +## Rendering +omni.replicator.captureOnPlay = true +renderer.startupMessageDisplayed = false + +## Omnigraph +omnigraph.disablePrimNodes = true +omnigraph.updateToUsd = false +omnigraph.useSchemaPrims = true + +## Miscellaneous +resourcemonitor.timeBetweenQueries = 100 diff --git a/apps/srb.headless.rendering.kit b/apps/srb.headless.rendering.kit new file mode 100644 index 0000000..948ad1c --- /dev/null +++ b/apps/srb.headless.rendering.kit @@ -0,0 +1,34 @@ +[package] +title = "Space Robotics Bench | Headless + Rendering" +description = "Headless experience with rendering support for the Space Robotics Bench" +version = "0.0.1" +keywords = ["benchmark", "robotics", "simulation", "space"] + + +[dependencies] +## Rendering +"omni.replicator.core" = {} +"omni.replicator.isaac" = {} + +## Inheritance +"srb.headless" = {} + +[settings] +## Extensions +app.exts.folders = [ + "${app}", + "${exe-path}/exts", + "${exe-path}/extscore", + "${exe-path}/../exts", + "${exe-path}/../extscache", + "${exe-path}/../extsPhysics", + "${exe-path}/../isaacsim/exts", + "${exe-path}/../isaacsim/extscache", + "${exe-path}/../isaacsim/extsPhysics", +] + +## Rendering +isaaclab.cameras_enabled = true +rtx.raytracing.cached.enabled = false +rtx.raytracing.lightcache.spatialCache.enabled = false +rtx.descriptorSets = 30000 # Note: Increase for scenes with more cameras diff --git a/apps/srb.kit b/apps/srb.kit new file mode 100644 index 0000000..664dc14 --- /dev/null +++ b/apps/srb.kit @@ -0,0 +1,44 @@ +[package] +title = "Space Robotics Bench" +description = "Regular experience for the Space Robotics Bench" +version = "0.0.1" +keywords = ["benchmark", "robotics", "simulation", "space"] + + +[dependencies] +## Basic UI +"omni.kit.uiapp" = {} +"omni.kit.window.stage" = {} + +## Menu bar +"omni.kit.viewport.menubar.camera" = {} +"omni.kit.viewport.menubar.display" = {} +"omni.kit.viewport.menubar.lighting" = {} +"omni.kit.viewport.menubar.render" = {} +"omni.kit.viewport.menubar.settings" = {} + +## Property widgets +"omni.kit.property.bundle" = {} + +## Basic manipulation +"omni.kit.manipulator.camera" = {} +"omni.kit.manipulator.prim" = {} +"omni.kit.manipulator.selection" = {} + +## Inheritance +"srb.headless" = {} + + +[settings] +## Extensions +app.exts.folders = [ + "${app}", + "${exe-path}/exts", + "${exe-path}/extscore", + "${exe-path}/../exts", + "${exe-path}/../extscache", + "${exe-path}/../extsPhysics", + "${exe-path}/../isaacsim/exts", + "${exe-path}/../isaacsim/extscache", + "${exe-path}/../isaacsim/extsPhysics", +] diff --git a/apps/srb.rendering.kit b/apps/srb.rendering.kit new file mode 100644 index 0000000..5e21673 --- /dev/null +++ b/apps/srb.rendering.kit @@ -0,0 +1,35 @@ +[package] +title = "Space Robotics Bench | Rendering" +description = "Experience with rendering support for the Space Robotics Bench" +version = "0.0.1" +keywords = ["benchmark", "robotics", "simulation", "space"] + + +[dependencies] +## Rendering +"omni.replicator.core" = {} +"omni.replicator.isaac" = {} + +## Inheritance +"srb" = {} + + +[settings] +## Extensions +app.exts.folders = [ + "${app}", + "${exe-path}/exts", + "${exe-path}/extscore", + "${exe-path}/../exts", + "${exe-path}/../extscache", + "${exe-path}/../extsPhysics", + "${exe-path}/../isaacsim/exts", + "${exe-path}/../isaacsim/extscache", + "${exe-path}/../isaacsim/extsPhysics", +] + +## Rendering +isaaclab.cameras_enabled = true +rtx.raytracing.cached.enabled = false +rtx.raytracing.lightcache.spatialCache.enabled = false +rtx.descriptorSets = 30000 # Note: Increase for scenes with more cameras diff --git a/assets/srb_assets b/assets/srb_assets new file mode 160000 index 0000000..dc48f1c --- /dev/null +++ b/assets/srb_assets @@ -0,0 +1 @@ +Subproject commit dc48f1ce0b28d8cdc7a6cde563309c28aee479b2 diff --git a/config/env.yaml b/config/env.yaml new file mode 100644 index 0000000..a4d1982 --- /dev/null +++ b/config/env.yaml @@ -0,0 +1,12 @@ +seed: 42 # SRB_SEED [int] +scenario: mars # SRB_SCENARIO [mars, moon, orbit] +detail: 0.5 # SRB_DETAIL [float] +assets: + robot: + variant: dataset # SRB_ASSETS_ROBOT_VARIANT [dataset] + object: + variant: procedural # SRB_ASSETS_OBJECT_VARIANT [primitive, dataset, procedural] + terrain: + variant: procedural # SRB_ASSETS_TERRAIN_VARIANT [none, primitive, dataset, procedural] + vehicle: + variant: dataset # SRB_ASSETS_VEHICLE_VARIANT [none, dataset] diff --git a/config/rviz/default.rviz b/config/rviz/default.rviz new file mode 100644 index 0000000..9a5ce4b --- /dev/null +++ b/config/rviz/default.rviz @@ -0,0 +1,310 @@ +Panels: + - Class: rviz_common/Displays + Help Height: 0 + Name: Displays + Property Tree Widget: + Expanded: ~ + Splitter Ratio: 0.75 + Tree Height: 294 + - Class: rviz_common/Selection + Name: Selection + - Class: rviz_common/Tool Properties + Expanded: + - /2D Goal Pose1 + - /Publish Point1 + Name: Tool Properties + Splitter Ratio: 0.6 + - Class: rviz_common/Views + Expanded: + - /Current View1 + Name: Views + Splitter Ratio: 0.5 + - Class: rviz_common/Time + Experimental: false + Name: Time + SyncMode: 0 + SyncSource: "" +Visualization Manager: + Class: "" + Displays: + - Alpha: 0.25 + Cell Size: 1 + Class: rviz_default_plugins/Grid + Color: 127; 127; 127 + Enabled: true + Line Style: + Line Width: 0.029999999329447746 + Value: Lines + Name: Grid + Normal Cell Count: 0 + Offset: + X: 0 + Y: 0 + Z: 0 + Plane: XY + Plane Cell Count: 100 + Reference Frame: + Value: true + - Class: rviz_default_plugins/TF + Enabled: true + Frame Timeout: 15 + Frames: + All Enabled: true + robot/base: + Value: true + world: + Value: true + Marker Scale: 1 + Name: TF + Show Arrows: true + Show Axes: true + Show Names: false + Tree: + world: + robot/base: {} + Update Interval: 0 + Value: true + - Class: rviz_default_plugins/Image + Enabled: true + Max Value: 1 + Median window: 5 + Min Value: 0 + Name: Scene Camera (RGB) + Normalize Range: true + Topic: + Depth: 5 + Durability Policy: Volatile + History Policy: Keep Last + Reliability Policy: Reliable + Value: /camera_scene/image_raw + Value: true + - Class: rviz_default_plugins/Image + Enabled: true + Max Value: 1 + Median window: 5 + Min Value: 0 + Name: Scene Camera (Depth) + Normalize Range: true + Topic: + Depth: 5 + Durability Policy: Volatile + History Policy: Keep Last + Reliability Policy: Reliable + Value: /camera_scene/depth/image_raw + Value: true + - Class: rviz_default_plugins/Image + Enabled: true + Max Value: 1 + Median window: 5 + Min Value: 0 + Name: Robot Base Camera (RGB) + Normalize Range: true + Topic: + Depth: 5 + Durability Policy: Volatile + History Policy: Keep Last + Reliability Policy: Reliable + Value: /robot/camera_base/image_raw + Value: true + - Class: rviz_default_plugins/Image + Enabled: true + Max Value: 1 + Median window: 5 + Min Value: 0 + Name: Robot Base Camera (Depth) + Normalize Range: true + Topic: + Depth: 5 + Durability Policy: Volatile + History Policy: Keep Last + Reliability Policy: Reliable + Value: /robot/camera_base/depth/image_raw + Value: true + - Class: rviz_default_plugins/Image + Enabled: true + Max Value: 1 + Median window: 5 + Min Value: 0 + Name: Wrist Camera (RGB) + Normalize Range: true + Topic: + Depth: 5 + Durability Policy: Volatile + History Policy: Keep Last + Reliability Policy: Reliable + Value: /robot/camera_wrist/image_raw + Value: true + - Class: rviz_default_plugins/Image + Enabled: true + Max Value: 1 + Median window: 5 + Min Value: 0 + Name: Wrist Camera (Depth) + Normalize Range: true + Topic: + Depth: 5 + Durability Policy: Volatile + History Policy: Keep Last + Reliability Policy: Reliable + Value: /robot/camera_wrist/depth/image_raw + Value: true + - Class: rviz_default_plugins/Image + Enabled: true + Max Value: 1 + Median window: 5 + Min Value: 0 + Name: Front Camera (RGB) + Normalize Range: true + Topic: + Depth: 5 + Durability Policy: Volatile + History Policy: Keep Last + Reliability Policy: Reliable + Value: /robot/camera_front/image_raw + Value: true + - Class: rviz_default_plugins/Image + Enabled: true + Max Value: 1 + Median window: 5 + Min Value: 0 + Name: Front Camera (Depth) + Normalize Range: true + Topic: + Depth: 5 + Durability Policy: Volatile + History Policy: Keep Last + Reliability Policy: Reliable + Value: /robot/camera_front/depth/image_raw + Value: true + - Class: rviz_default_plugins/Image + Enabled: true + Max Value: 1 + Median window: 5 + Min Value: 0 + Name: Bottom Camera (RGB) + Normalize Range: true + Topic: + Depth: 5 + Durability Policy: Volatile + History Policy: Keep Last + Reliability Policy: Reliable + Value: /robot/camera_bottom/image_raw + Value: true + - Class: rviz_default_plugins/Image + Enabled: true + Max Value: 1 + Median window: 5 + Min Value: 0 + Name: Bottom Camera (Depth) + Normalize Range: true + Topic: + Depth: 5 + Durability Policy: Volatile + History Policy: Keep Last + Reliability Policy: Reliable + Value: /robot/camera_bottom/depth/image_raw + Value: true + Enabled: true + Global Options: + Background Color: 31; 31; 31 + Fixed Frame: world + Frame Rate: 30 + Name: root + Tools: + - Class: rviz_default_plugins/Interact + Hide Inactive Objects: true + - Class: rviz_default_plugins/MoveCamera + - Class: rviz_default_plugins/Select + - Class: rviz_default_plugins/FocusCamera + - Class: rviz_default_plugins/Measure + Line color: 128; 128; 0 + - Class: rviz_default_plugins/SetInitialPose + Covariance x: 0.25 + Covariance y: 0.25 + Covariance yaw: 0.06853891909122467 + Topic: + Depth: 5 + Durability Policy: Volatile + History Policy: Keep Last + Reliability Policy: Reliable + Value: /initialpose + - Class: rviz_default_plugins/SetGoal + Topic: + Depth: 5 + Durability Policy: Volatile + History Policy: Keep Last + Reliability Policy: Reliable + Value: /goal_pose + - Class: rviz_default_plugins/PublishPoint + Single click: true + Topic: + Depth: 5 + Durability Policy: Volatile + History Policy: Keep Last + Reliability Policy: Reliable + Value: /clicked_point + Transformation: + Current: + Class: rviz_default_plugins/TF + Value: true + Views: + Current: + Class: rviz_default_plugins/Orbit + Distance: 15 + Enable Stereo Rendering: + Stereo Eye Separation: 0.05999999865889549 + Stereo Focal Distance: 1 + Swap Stereo Eyes: false + Value: false + Focal Point: + X: 0 + Y: 0 + Z: 0 + Focal Shape Fixed Size: true + Focal Shape Size: 0.05000000074505806 + Invert Z Axis: false + Name: Current View + Near Clip Distance: 0.009999999776482582 + Pitch: 0.785398006439209 + Target Frame: + Value: Orbit (rviz) + Yaw: 0.785398006439209 + Saved: ~ +Window Geometry: + Bottom Camera (Depth): + collapsed: false + Bottom Camera (RGB): + collapsed: false + Displays: + collapsed: false + Front Camera (Depth): + collapsed: false + Front Camera (RGB): + collapsed: false + Height: 1131 + Hide Left Dock: false + Hide Right Dock: false + QMainWindow State: 000000ff00000000fd0000000400000000000001560000043cfc020000000dfb0000001200530065006c0065006300740069006f006e00000001e10000009b0000005c00fffffffb0000001e0054006f006f006c002000500072006f007000650072007400690065007302000001ed000001df00000185000000a3fb000000120056006900650077007300200054006f006f02000001df000002110000018500000122fb000000200054006f006f006c002000500072006f0070006500720074006900650073003203000002880000011d000002210000017afb000000100044006900730070006c006100790073010000001400000161000000c700fffffffb0000002000730065006c0065006300740069006f006e00200062007500660066006500720200000138000000aa0000023a00000294fb00000014005700690064006500530074006500720065006f02000000e6000000d2000003ee0000030bfb0000000c004b0069006e0065006300740200000186000001060000030c00000261fb0000000a0049006d006100670065000000037e000000d20000000000000000fb0000002400460072006f006e0074002000430061006d006500720061002000280052004700420029010000017b000000b80000002800fffffffb0000002800460072006f006e0074002000430061006d006500720061002000280044006500700074006800290100000239000000a90000002800fffffffb000000260042006f00740074006f006d002000430061006d00650072006100200028005200470042002901000002e8000000af0000002800fffffffb0000002a0042006f00740074006f006d002000430061006d00650072006100200028004400650070007400680029010000039d000000b30000002800ffffff00000001000001340000043cfc0200000009fb00000024005300630065006e0065002000430061006d0065007200610020002800520047004200290100000014000000a50000002800fffffffb00000028005300630065006e0065002000430061006d0065007200610020002800440065007000740068002901000000bf000000a80000002800fffffffb0000002e0052006f0062006f007400200042006100730065002000430061006d006500720061002000280052004700420029010000016d000000ad0000002800fffffffb000000320052006f0062006f007400200042006100730065002000430061006d006500720061002000280044006500700074006800290100000220000000b80000002800fffffffb0000002400570072006900730074002000430061006d00650072006100200028005200470042002901000002de000000b40000002800fffffffb0000002800570072006900730074002000430061006d006500720061002000280044006500700074006800290100000398000000b80000002800fffffffb0000001e0054006f006f006c002000500072006f00700065007200740069006500730100000041000000780000000000000000fb0000000a00560069006500770073000000003b000003d2000000a000fffffffb0000001200530065006c0065006300740069006f006e010000025a000000b200000000000000000000000200000490000000a9fc0100000001fb0000000a00560069006500770073030000004e00000080000002e10000019700000003000007800000003dfc0100000002fb0000000800540069006d00650000000000000007800000025300fffffffb0000000800540069006d00650100000000000004500000000000000000000004ea0000043c00000004000000040000000800000008fc0000000100000002000000010000000a0054006f006f006c00730000000000ffffffff0000000000000000 + Robot Base Camera (Depth): + collapsed: false + Robot Base Camera (RGB): + collapsed: false + Scene Camera (Depth): + collapsed: false + Scene Camera (RGB): + collapsed: false + Selection: + collapsed: false + Time: + collapsed: false + Tool Properties: + collapsed: false + Views: + collapsed: false + Width: 1920 + Wrist Camera (Depth): + collapsed: false + Wrist Camera (RGB): + collapsed: false + X: 0 + Y: 0 diff --git a/crates/space_robotics_bench/Cargo.toml b/crates/space_robotics_bench/Cargo.toml new file mode 100644 index 0000000..43aa2d8 --- /dev/null +++ b/crates/space_robotics_bench/Cargo.toml @@ -0,0 +1,35 @@ +[package] +name = "space_robotics_bench" +description.workspace = true +categories.workspace = true +keywords.workspace = true +readme.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true +documentation.workspace = true +edition.workspace = true +rust-version.workspace = true +version.workspace = true +publish.workspace = true + +[dependencies] +display_json = { workspace = true, optional = true } +fast_poisson = { workspace = true } +figment = { workspace = true } +paste = { workspace = true } +pyo3 = { workspace = true } +rand = { workspace = true } +rand_xoshiro = { workspace = true } +rayon = { workspace = true } +serde = { workspace = true } +serde_json = { workspace = true, optional = true } +serde_yaml = { workspace = true, optional = true } +thiserror = { workspace = true } +toml = { workspace = true, optional = true } + +[features] +default = ["yaml"] +json = ["dep:serde_json", "figment/json", "dep:display_json"] +toml = ["dep:toml", "figment/toml"] +yaml = ["dep:serde_yaml", "figment/yaml"] diff --git a/crates/space_robotics_bench/src/envs/cfg/assets.rs b/crates/space_robotics_bench/src/envs/cfg/assets.rs new file mode 100644 index 0000000..d955019 --- /dev/null +++ b/crates/space_robotics_bench/src/envs/cfg/assets.rs @@ -0,0 +1,48 @@ +use super::AssetVariant; +use pyo3::prelude::*; +use serde::{Deserialize, Serialize}; + +#[derive(Deserialize, Serialize, Debug, Clone, Copy, PartialEq, Eq, Hash, Default)] +#[cfg_attr(feature = "json", derive(display_json::DisplayAsJson))] +#[pyclass(eq, get_all, set_all)] +pub struct Assets { + pub robot: Asset, + pub object: Asset, + pub terrain: Asset, + pub vehicle: Asset, +} + +#[pymethods] +impl Assets { + fn __repr__(&self) -> String { + format!("{self:?}") + } + + #[new] + fn new(robot: Asset, object: Asset, terrain: Asset, vehicle: Asset) -> Self { + Self { + robot, + object, + terrain, + vehicle, + } + } +} + +#[derive(Deserialize, Serialize, Debug, Clone, Copy, PartialEq, Eq, Hash, Default)] +#[pyclass(eq, get_all, set_all)] +pub struct Asset { + pub variant: AssetVariant, +} + +#[pymethods] +impl Asset { + fn __repr__(&self) -> String { + format!("{self:?}") + } + + #[new] + fn new(variant: AssetVariant) -> Self { + Self { variant } + } +} diff --git a/crates/space_robotics_bench/src/envs/cfg/config.rs b/crates/space_robotics_bench/src/envs/cfg/config.rs new file mode 100644 index 0000000..15f4462 --- /dev/null +++ b/crates/space_robotics_bench/src/envs/cfg/config.rs @@ -0,0 +1,241 @@ +use super::{Asset, AssetVariant, Assets, Scenario}; +use crate::Result; +#[cfg(any(feature = "json", feature = "toml", feature = "yaml"))] +use figment::providers::Format; +use figment::Figment; +use pyo3::prelude::*; +use serde::{Deserialize, Serialize}; +#[cfg(any(feature = "json", feature = "toml", feature = "yaml"))] +use std::io::Write; + +#[derive(Deserialize, Serialize, Debug, Clone, Copy, PartialEq)] +#[cfg_attr(feature = "json", derive(display_json::DisplayAsJson))] +#[pyclass(eq, get_all, set_all)] +pub struct EnvironmentConfig { + pub scenario: Scenario, + pub assets: Assets, + pub seed: u64, + pub detail: f32, +} + +impl Default for EnvironmentConfig { + fn default() -> Self { + Self { + scenario: Scenario::Moon, + assets: Assets { + robot: Asset { + variant: AssetVariant::Dataset, + }, + object: Asset { + variant: AssetVariant::Procedural, + }, + terrain: Asset { + variant: AssetVariant::Procedural, + }, + vehicle: Asset { + variant: AssetVariant::Dataset, + }, + }, + seed: 0, + detail: 1.0, + } + } +} + +const SUPPORTED_FILE_EXTENSIONS: &[&str] = &[ + #[cfg(feature = "json")] + "json", + #[cfg(feature = "toml")] + "toml", + #[cfg(feature = "yaml")] + "yaml", + #[cfg(feature = "yaml")] + "yml", +]; + +impl EnvironmentConfig { + pub fn extract( + cfg_path: Option>, + env_prefix: Option<&str>, + other: Option, + ) -> Result { + let mut figment = Figment::new(); + + // 1. (Optional) Load configuration from file + if let Some(path) = cfg_path { + let path = path.as_ref(); + if !path.exists() { + return Err(std::io::Error::new( + std::io::ErrorKind::NotFound, + format!("File not found: {}", path.display()), + ) + .into()); + } + match path.extension().and_then(|ext| ext.to_str()) { + #[cfg(feature = "json")] + Some("json") => figment = figment.merge(figment::providers::Json::file(path)), + #[cfg(feature = "toml")] + Some("toml") => figment = figment.merge(figment::providers::Toml::file(path)), + #[cfg(feature = "yaml")] + Some("yaml" | "yml") => { + figment = figment.merge(figment::providers::Yaml::file(path)); + } + Some(_) => { + return Err(figment::Error::from(format!( + "Unsupported file extension: {} (supported=[{}])", + path.display(), + SUPPORTED_FILE_EXTENSIONS.join(", "), + )) + .into()) + } + None => { + return Err(figment::Error::from(format!( + "Missing file extension: {}", + path.display() + )) + .into()) + } + } + } + + // 2. (Optional) Load configuration from environment variables + if let Some(env_prefix) = env_prefix { + figment = figment.merge(figment::providers::Env::prefixed(env_prefix).split('_')); + } + + // 3. (Optional) Load configuration from other sources + if let Some(other) = other { + figment = figment.merge(figment::providers::Serialized::defaults(other)); + } + + // Finally, apply default values for missing fields + figment = figment.join(figment::providers::Serialized::defaults( + EnvironmentConfig::default(), + )); + + Ok(figment.extract()?) + } + + pub fn write(&self, path: impl AsRef) -> Result<()> { + let path = path.as_ref(); + + // Return error if none or if not supported extension + match path.extension().and_then(|ext| ext.to_str()) { + #[cfg(feature = "json")] + Some("json") => { + let file = std::fs::File::create(path)?; + let mut writer = std::io::BufWriter::new(file); + serde_json::to_writer_pretty(&mut writer, self).map_err(|e| { + std::io::Error::new(std::io::ErrorKind::InvalidInput, e.to_string()) + })?; + writer.flush()?; + Ok(()) + } + #[cfg(feature = "toml")] + Some("toml") => { + let mut file = std::fs::File::create(path)?; + let content = toml::to_string_pretty(self).map_err(|e| { + std::io::Error::new(std::io::ErrorKind::InvalidInput, e.to_string()) + })?; + file.write_all(content.as_bytes())?; + Ok(()) + } + #[cfg(feature = "yaml")] + Some("yaml" | "yml") => { + let file = std::fs::File::create(path)?; + let mut writer = std::io::BufWriter::new(file); + serde_yaml::to_writer(&mut writer, self).map_err(|e| { + std::io::Error::new(std::io::ErrorKind::InvalidInput, e.to_string()) + })?; + writer.flush()?; + Ok(()) + } + Some(_) | None => Err(std::io::Error::new( + std::io::ErrorKind::InvalidInput, + format!( + "Unsupported file extension: {} (supported=[{}])", + path.display(), + SUPPORTED_FILE_EXTENSIONS.join(", "), + ), + ) + .into()), + } + } +} + +#[pymethods] +impl EnvironmentConfig { + fn __repr__(&self) -> String { + format!("{self:?}") + } + + #[new] + fn new(scenario: Scenario, assets: Assets, seed: u64, detail: f32) -> Self { + Self { + scenario, + assets, + seed, + detail, + } + } + + #[staticmethod] + #[pyo3( + name = "extract", + signature = (cfg_path=None, env_prefix=Some("SRB_"), other=None), + )] + fn py_extract( + cfg_path: Option<&str>, + env_prefix: Option<&str>, + other: Option, + ) -> PyResult { + Ok(Self::extract(cfg_path, env_prefix, other)?) + } + + #[pyo3(name = "write")] + fn py_write(&self, path: &str) -> PyResult<()> { + Ok(self.write(path)?) + } + + #[cfg(any(feature = "json", feature = "toml", feature = "yaml"))] + fn __reduce__(&self) -> PyResult<(PyObject, PyObject)> { + Python::with_gil(|py| { + py.run_bound("import space_robotics_bench", None, None) + .unwrap(); + let deserialize = py + .eval_bound( + "space_robotics_bench._rs.envs.EnvironmentConfig._deserialize", + None, + None, + ) + .unwrap(); + + #[cfg(feature = "json")] + let data = serde_json::to_vec(self).unwrap(); + #[cfg(all(feature = "yaml", not(feature = "json")))] + let data = serde_yaml::to_string(self).unwrap().as_bytes().to_vec(); + #[cfg(all(feature = "toml", not(any(feature = "json", feature = "yaml"))))] + let data = toml::to_string(self).unwrap().as_bytes().to_vec(); + + Ok((deserialize.to_object(py), (data,).to_object(py))) + }) + } + + #[staticmethod] + #[pyo3(name = "_deserialize")] + #[cfg(any(feature = "json", feature = "toml", feature = "yaml"))] + fn deserialize(data: Vec) -> PyResult { + #[cfg(feature = "json")] + { + Ok(serde_json::from_slice(&data).unwrap()) + } + #[cfg(all(feature = "yaml", not(feature = "json")))] + { + Ok(serde_yaml::from_slice(&data).unwrap()) + } + #[cfg(all(feature = "toml", not(any(feature = "json", feature = "yaml"))))] + { + Ok(toml::from_str(std::str::from_utf8(&data).unwrap()).unwrap()) + } + } +} diff --git a/crates/space_robotics_bench/src/envs/cfg/enums/asset_variant.rs b/crates/space_robotics_bench/src/envs/cfg/enums/asset_variant.rs new file mode 100644 index 0000000..8eb7eda --- /dev/null +++ b/crates/space_robotics_bench/src/envs/cfg/enums/asset_variant.rs @@ -0,0 +1,24 @@ +use pyo3::prelude::*; +use serde::{Deserialize, Serialize}; + +#[derive(Deserialize, Serialize, Debug, Clone, Copy, PartialEq, Eq, Hash, Default)] +#[cfg_attr(feature = "json", derive(display_json::DisplayAsJson))] +#[pyclass(frozen, eq, eq_int, hash, rename_all = "SCREAMING_SNAKE_CASE")] +#[serde(rename_all = "SCREAMING_SNAKE_CASE")] +pub enum AssetVariant { + #[serde(alias = "none")] + None, + #[default] + #[serde(alias = "primitive", alias = "PRIM", alias = "prim")] + Primitive, + #[serde(alias = "dataset", alias = "DB", alias = "db")] + Dataset, + #[serde( + alias = "procedural", + alias = "PROC", + alias = "proc", + alias = "PROCGEN", + alias = "procgen" + )] + Procedural, +} diff --git a/crates/space_robotics_bench/src/envs/cfg/enums/mod.rs b/crates/space_robotics_bench/src/envs/cfg/enums/mod.rs new file mode 100644 index 0000000..b830c36 --- /dev/null +++ b/crates/space_robotics_bench/src/envs/cfg/enums/mod.rs @@ -0,0 +1,5 @@ +mod asset_variant; +mod scenario; + +pub use asset_variant::*; +pub use scenario::*; diff --git a/crates/space_robotics_bench/src/envs/cfg/enums/scenario.rs b/crates/space_robotics_bench/src/envs/cfg/enums/scenario.rs new file mode 100644 index 0000000..a11d775 --- /dev/null +++ b/crates/space_robotics_bench/src/envs/cfg/enums/scenario.rs @@ -0,0 +1,240 @@ +use pyo3::prelude::*; +use serde::{Deserialize, Serialize}; + +#[derive(Deserialize, Serialize, Debug, Clone, Copy, PartialEq, Eq, Hash, Default)] +#[cfg_attr(feature = "json", derive(display_json::DisplayAsJson))] +#[pyclass(frozen, eq, eq_int, hash, rename_all = "SCREAMING_SNAKE_CASE")] +#[serde(rename_all = "SCREAMING_SNAKE_CASE")] +pub enum Scenario { + #[serde(alias = "asteroid")] + Asteroid, + #[serde(alias = "earth", alias = "TERRESTRIAL", alias = "terrestrial")] + Earth, + #[serde(alias = "mars", alias = "MARTIAN", alias = "martian")] + Mars, + #[default] + #[serde(alias = "moon", alias = "LUNAR", alias = "lunar")] + Moon, + #[serde(alias = "orbit", alias = "ORBITAL", alias = "orbital")] + Orbit, +} + +impl Scenario { + /// Magnitude of gravitational acceleration in m/s². + /// + /// # Assumptions + /// + /// - Asteroid: 50% gravitational acceleration of Ceres (largest body in the asteroid belt). + /// - Orbit: No gravitational acceleration. + #[must_use] + pub fn gravity_magnitude(self) -> f64 { + match self { + Self::Asteroid => 0.14219, + Self::Earth => 9.80665, + Self::Mars => 3.72076, + Self::Moon => 1.62496, + Self::Orbit => 0.0, + } + } + + /// Difference between the maximum and minimum of gravitational acceleration in m/s². + /// + /// # Assumptions + /// + /// - Asteroid: Ceres is considered as the maximum (largest body in the asteroid belt). + /// - Orbit: No gravitational acceleration. + #[must_use] + pub fn gravity_variation(self) -> f64 { + match self { + Self::Asteroid => 2.0 * Self::Asteroid.gravity_magnitude(), + Self::Earth => 0.0698, + Self::Mars => 0.0279, + Self::Moon => 0.0253, + Self::Orbit => 0.0, + } + } + + /// Range of gravitational acceleration in m/s² calculated as the magnitude ± variation/2. + #[must_use] + pub fn gravity_range(self) -> (f64, f64) { + let magnitude = self.gravity_magnitude(); + let delta = self.gravity_variation() / 2.0; + (magnitude - delta, magnitude + delta) + } + + /// Intensity of Solar light in W/m². + /// + /// # Notes + /// + /// - Asteroid: Taken at 2.7 AU. + /// - Earth | Mars: Taken at the surface. The peak value (sunny day) is subtracted by half of the variation. + /// - Moon | Orbit: Taken at 1 AU. + #[must_use] + pub fn light_intensity(self) -> f64 { + match self { + Self::Asteroid => 190.0, + Self::Earth => 1000.0 - Self::Earth.light_intensity_variation() / 2.0, + Self::Mars => 842.0 - Self::Mars.light_intensity_variation() / 2.0, + Self::Moon | Self::Orbit => 1361.0, + } + } + + /// Difference between the maximum and minimum of Solar light intensity in W/m². + /// + /// # Notes + /// + /// - Asteroid: Approximate range between 2.55 and 2.97 AU. + /// - Earth | Mars: Guesstimated effect of atmosphere and weather. + /// - Moon | Orbit: Minor variation due to elliptical orbit. + #[must_use] + pub fn light_intensity_variation(self) -> f64 { + match self { + Self::Asteroid => 50.0, + Self::Earth => 450.0, + Self::Mars => 226.0, + Self::Moon | Self::Orbit => 0.5, + } + } + + /// Range of Solar light intensity in W/m² calculated as the intensity ± variation/2. + #[must_use] + pub fn light_intensity_range(self) -> (f64, f64) { + let intensity = self.light_intensity(); + let delta = self.light_intensity_variation() / 2.0; + (intensity - delta, intensity + delta) + } + + /// Angular diameter of the Solar light source in degrees. + /// + /// # Assumptions + /// + /// - Earth | Mars: Taken at their distance from the Sun. + /// - Asteroid | Moon | Orbit: Approximated as a point source due to lack of atmosphere. + #[must_use] + pub fn light_angular_diameter(self) -> f64 { + match self { + Self::Earth => 0.53, + Self::Mars => 0.35, + Self::Asteroid | Self::Moon | Self::Orbit => 0.0, + } + } + + /// Variation of the angular diameter of the Solar light source in degrees. + #[must_use] + pub fn light_angular_diameter_variation(self) -> f64 { + match self { + Self::Earth => 0.021, + Self::Mars => 0.08, + Self::Asteroid | Self::Moon | Self::Orbit => 0.0, + } + } + + /// Range of the angular diameter of the Solar light source in degrees calculated as the diameter ± variation/2. + #[must_use] + pub fn light_angular_diameter_range(self) -> (f64, f64) { + let diameter = self.light_angular_diameter(); + let delta = self.light_angular_diameter_variation() / 2.0; + (diameter - delta, diameter + delta) + } + + /// Temperature of the Solar light source in K. + /// + /// # Assumptions + /// + /// - Earth | Mars: Guesstimated effect atmosphere and weather. + /// - Asteroid | Moon | Orbit: Intrinsic color temperature of the Sun. + #[must_use] + pub fn light_color_temperature(self) -> f64 { + match self { + Self::Earth => 5750.0, + Self::Mars => 6250.0, + Self::Asteroid | Self::Moon | Self::Orbit => 5778.0, + } + } + + /// Variation of the temperature of the Solar light source in K. + /// + /// # Assumptions + /// + /// - Earth | Mars: Guesstimated effect atmosphere and weather. + /// - Asteroid | Moon | Orbit: No significant variation. + #[must_use] + pub fn light_color_temperature_variation(self) -> f64 { + match self { + Self::Earth => 1500.0, + Self::Mars => 500.0, + Self::Asteroid | Self::Moon | Self::Orbit => 0.0, + } + } + + /// Range of the temperature of the Solar light source in K calculated as the temperature ± variation/2. + #[must_use] + pub fn light_color_temperature_range(self) -> (f64, f64) { + let temperature = self.light_color_temperature(); + let delta = self.light_color_temperature_variation() / 2.0; + (temperature - delta, temperature + delta) + } +} + +#[pymethods] +impl Scenario { + #[getter("gravity_magnitude")] + fn py_gravity_magnitude(&self) -> PyResult { + Ok(self.gravity_magnitude()) + } + + #[getter("gravity_variation")] + fn py_gravity_variation(&self) -> PyResult { + Ok(self.gravity_variation()) + } + + #[getter("gravity_range")] + fn py_gravity_range(&self) -> PyResult<(f64, f64)> { + Ok(self.gravity_range()) + } + + #[getter("light_intensity")] + fn py_light_intensity(&self) -> PyResult { + Ok(self.light_intensity()) + } + + #[getter("light_intensity_variation")] + fn py_light_intensity_variation(&self) -> PyResult { + Ok(self.light_intensity_variation()) + } + + #[getter("light_intensity_range")] + fn py_light_intensity_range(&self) -> PyResult<(f64, f64)> { + Ok(self.light_intensity_range()) + } + + #[getter("light_angular_diameter")] + fn py_light_angular_diameter(&self) -> PyResult { + Ok(self.light_angular_diameter()) + } + + #[getter("light_angular_diameter_variation")] + fn py_light_angular_diameter_variation(&self) -> PyResult { + Ok(self.light_angular_diameter_variation()) + } + + #[getter("light_angular_diameter_range")] + fn py_light_angular_diameter_range(&self) -> PyResult<(f64, f64)> { + Ok(self.light_angular_diameter_range()) + } + + #[getter("light_color_temperature")] + fn py_light_color_temperature(&self) -> PyResult { + Ok(self.light_color_temperature()) + } + + #[getter("light_color_temperature_variation")] + fn py_light_color_temperature_variation(&self) -> PyResult { + Ok(self.light_color_temperature_variation()) + } + + #[getter("light_color_temperature_range")] + fn py_light_color_temperature_range(&self) -> PyResult<(f64, f64)> { + Ok(self.light_color_temperature_range()) + } +} diff --git a/crates/space_robotics_bench/src/envs/cfg/mod.rs b/crates/space_robotics_bench/src/envs/cfg/mod.rs new file mode 100644 index 0000000..324e372 --- /dev/null +++ b/crates/space_robotics_bench/src/envs/cfg/mod.rs @@ -0,0 +1,61 @@ +mod assets; +mod config; +mod enums; + +pub use assets::*; +pub use config::*; +pub use enums::*; + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + #[cfg(feature = "yaml")] + fn extract() { + figment::Jail::expect_with(|jail| { + // Arrange + let cfg_path = if cfg!(feature = "yaml") { + jail.create_file( + "config.yaml", + r" + seed: 42 + assets: + vehicle: + variant: dataset + ", + )?; + Some("config.yaml") + } else { + None + }; + jail.set_env("SRB_SCENARIO", "ORBIT"); + jail.set_env("SRB_ASSETS_TERRAIN_VARIANT", "PROCEDURAL"); + jail.set_env("SRB_DETAIL", "0.2"); + + // Act + let config = EnvironmentConfig::extract(cfg_path, Some("SRB_"), None)?; + + // Assert + assert_eq!( + config, + EnvironmentConfig { + scenario: Scenario::Orbit, + assets: Assets { + vehicle: Asset { + variant: AssetVariant::Dataset, + }, + terrain: Asset { + variant: AssetVariant::Procedural, + }, + ..EnvironmentConfig::default().assets + }, + seed: 42, + detail: 0.2, + } + ); + + Ok(()) + }); + } +} diff --git a/crates/space_robotics_bench/src/envs/mod.rs b/crates/space_robotics_bench/src/envs/mod.rs new file mode 100644 index 0000000..a96d5f1 --- /dev/null +++ b/crates/space_robotics_bench/src/envs/mod.rs @@ -0,0 +1,3 @@ +mod cfg; + +pub use cfg::*; diff --git a/crates/space_robotics_bench/src/error.rs b/crates/space_robotics_bench/src/error.rs new file mode 100644 index 0000000..d9c2070 --- /dev/null +++ b/crates/space_robotics_bench/src/error.rs @@ -0,0 +1,33 @@ +/// Alias for [`std::result::Result`] that wraps our [Error] type. +pub type Result = std::result::Result; + +/// Error type for this crate. +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error(transparent)] + Config(#[from] figment::Error), + #[error(transparent)] + Io(#[from] std::io::Error), + #[error(transparent)] + Python(#[from] pyo3::PyErr), +} + +/// Convert [Error] to [`figment::Error`]. +impl From for figment::Error { + fn from(e: Error) -> Self { + match e { + Error::Config(e) => e, + _ => figment::Error::from(e.to_string()), + } + } +} + +/// Convert [Error] to [`pyo3::PyErr`]. +impl From for pyo3::PyErr { + fn from(e: Error) -> Self { + match e { + Error::Python(e) => e, + _ => pyo3::exceptions::PyException::new_err(e.to_string()), + } + } +} diff --git a/crates/space_robotics_bench/src/lib.rs b/crates/space_robotics_bench/src/lib.rs new file mode 100644 index 0000000..66928a5 --- /dev/null +++ b/crates/space_robotics_bench/src/lib.rs @@ -0,0 +1,5 @@ +pub mod envs; +pub mod utils; + +mod error; +pub use error::{Error, Result}; diff --git a/crates/space_robotics_bench/src/utils/mod.rs b/crates/space_robotics_bench/src/utils/mod.rs new file mode 100644 index 0000000..4aee55b --- /dev/null +++ b/crates/space_robotics_bench/src/utils/mod.rs @@ -0,0 +1 @@ +pub mod sampling; diff --git a/crates/space_robotics_bench/src/utils/sampling/mod.rs b/crates/space_robotics_bench/src/utils/sampling/mod.rs new file mode 100644 index 0000000..c01dd83 --- /dev/null +++ b/crates/space_robotics_bench/src/utils/sampling/mod.rs @@ -0,0 +1,3 @@ +mod poisson_disk; + +pub use poisson_disk::*; diff --git a/crates/space_robotics_bench/src/utils/sampling/poisson_disk.rs b/crates/space_robotics_bench/src/utils/sampling/poisson_disk.rs new file mode 100644 index 0000000..8c2b61e --- /dev/null +++ b/crates/space_robotics_bench/src/utils/sampling/poisson_disk.rs @@ -0,0 +1,85 @@ +use pyo3::prelude::*; +use rand::seq::SliceRandom; +use rand_xoshiro::rand_core::SeedableRng; +use rayon::prelude::*; + +const RADIUS_DECAY: f32 = 0.98; + +macro_rules! sample_poisson_disk_impl { + ($dim:expr) => { + paste::paste! { + #[pyfunction] + #[must_use] pub fn [< sample_poisson_disk_ $dim d >]( + num_samples: usize, + bounds: [[f32; $dim]; 2], + radius: f32, + ) -> Vec<[f32; $dim]> { + let dimensions = { + let mut dims = bounds[1]; + for (i, d) in dims.iter_mut().enumerate() { + *d -= bounds[0][i]; + } + dims + }; + + let mut distribution = fast_poisson::Poisson::<$dim>::new().with_dimensions(dimensions, radius); + + let mut decay_factor = RADIUS_DECAY; + loop { + let points = distribution.generate(); + match points.len() { + n if n < num_samples => { + distribution.set_dimensions(dimensions, decay_factor * radius); + decay_factor *= RADIUS_DECAY; + continue; + } + n if n == num_samples => { + break points + .into_iter() + .map(|mut point| { + for (i, p) in point.iter_mut().enumerate() { + *p += bounds[0][i]; + } + point + }) + .collect() + } + _ => { + break points + .choose_multiple( + &mut rand_xoshiro::Xoshiro128StarStar::from_entropy(), + num_samples, + ) + .cloned() + .map(|mut point| { + for (i, p) in point.iter_mut().enumerate() { + *p += bounds[0][i]; + } + point + }) + .collect() + } + } + } + } + + #[pyfunction] + #[must_use] pub fn [< sample_poisson_disk_ $dim d_looped >]( + num_samples: [usize; 2], + bounds: [[f32; $dim]; 2], + radius: f32, + ) -> Vec> { + (0..num_samples[0]) + .into_par_iter() + .map(|_| [< sample_poisson_disk_ $dim d >](num_samples[1], bounds, radius)) + .collect() + } + + } + }; + [$( $dim:expr ),*] => { + $( sample_poisson_disk_impl!($dim); )* + }; +} + +sample_poisson_disk_impl![2, 3]; diff --git a/crates/space_robotics_bench_gui/Cargo.toml b/crates/space_robotics_bench_gui/Cargo.toml new file mode 100644 index 0000000..c3f6251 --- /dev/null +++ b/crates/space_robotics_bench_gui/Cargo.toml @@ -0,0 +1,54 @@ +[package] +name = "space_robotics_bench_gui" +description.workspace = true +categories.workspace = true +keywords.workspace = true +readme.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true +documentation.workspace = true +edition.workspace = true +rust-version.workspace = true +version.workspace = true +publish.workspace = true + +[[bin]] +name = "gui" +path = "src/main.rs" + +[dependencies] +space_robotics_bench = { workspace = true, features = ["json"] } + +eframe = { version = "0.29", default-features = false, features = [ + "accesskit", + "glow", + "persistence", +] } +egui = { version = "0.29", default-features = false } +egui_extras = { version = "0.29", default-features = false, features = [ + "all_loaders", + "syntect", +] } +egui_commonmark = { version = "0.18", default-features = false, features = [ + "better_syntax_highlighting", + "macros", + "pulldown_cmark", +] } +r2r = { version = "0.9" } + +display_json = { workspace = true } +chrono = { workspace = true } +const_format = { workspace = true } +home = { workspace = true } +image = { workspace = true } +itertools = { workspace = true } +nix = { workspace = true } +paste = { workspace = true } +serde = { workspace = true } +serde_json = { workspace = true } +subprocess = { workspace = true } +sysinfo = { workspace = true } +tracing = { workspace = true } +tracing-subscriber = { workspace = true } +typed-builder = { workspace = true } diff --git a/crates/space_robotics_bench_gui/assets/fonts/Inter-Regular.otf b/crates/space_robotics_bench_gui/assets/fonts/Inter-Regular.otf new file mode 100644 index 0000000000000000000000000000000000000000..2d0bd1d645a9afaa4cf47afc4e1dc38698809f78 GIT binary patch literal 605092 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%0A^E#jArOFLB=rZemIsf)CP<(fmv4} z;~6?O%LGOZfvInRj??Rzw-7-Ebv__FY+- z!3=fTw;0R5$#@v$s*J+1PgY|L^}9Nws0}s3&^Xs*)DgH=7>u8+&8U-b9md=R-xh}A zU6)Z;;d)^x_VpR{E!-dsjYC65U4w6D48^_?qrQO~hoRUvVbpoJX&CDF9gL=YY!;>p z+?>%daEmY$kCu#%gWL+iFih)PoSxfv`-T2z+gtx zzV<+v_3(p?raGw)#0Eg^26`z>Z6!7WY9G+c;Nf8?&qpwNIXp7VCU_L1SHPpgd=8Ic z^l5l(m{Tx~8PMOs+6cypLy2TVLnaR;gyOtA(g7oNi4o{x}I89J|*Pcw84A*V5_4g3s4=iu^LhR*Tj zbjBpZGZ;nr^&EqHNP?~n%B~Nn-!CvG4W>K;xWmM`h5Qm!_iP4torIjj2zuXK#;OR< zV{lJO$QKzpr;wB%Kzs(z50eTnVDLR&Ar~_00=$SZt>MLtDS#<=iQ0g23+O1A@`<2a zpd2D7=9D);XTp>#z+}U(Fs3cMG7OC~wHxSm@G8dS!>hwk8`m&;9Q+z%s4r_7QwM&X z(NyOfVQ4#VGNu#!Rv6mOx-e7+?LR!QU}D4=!a)2K+r^>cc-UiemXAgS)>% z{>0FHf&7`l9bqAVVFb1LI)l5!Lf&BLdQtw$m_qosFx6nndw{#i0(S<>5C;B(QK|5s z49y48wF;rK;lINSfNwFl6D^cr1nR^c1I7pA1wtThN{2zeDE|nWd!Xx|GDgAI z4KoUkVCY&+p{-?n1bYl!v*B(yvj>i3=sbn;gV6bKG(+bs3OPjR0vNecmM>Fa>I2Zd zVQM#^&l%ilFGKf%6B#-#(6tAl*TBgP9Vh6TjnJ>bh-q0|=fG)<9uB87bX=xuKSEO* zG8vNvhrwNYf%|f0(AI`32E8BOa}VCWoBRb({1rxHWwgQ_y4sg5cPog1pEjHddk zF|^;S>WnUhYlOK1*JQL0uEpqyaP2Uk!gauHI8NRR*JWsqRn=nzTRC9*ru~dsNgW;A; z+4bk0C=>fYD@IO&?_xB?joJ<9{DsbY2u)+%mZ4)FT@MrbTbT9@f^v=ek1;ef4m4&2 zwTs3H7+S9j=nAOLFot4JF})A#Aij$Fd_O*m$HUzj>ruD|V?6@*4D%V>E6iM&_BUWC zetp6$f%`JnCAc4BC@=blSp^RO1Hmg`P?+s7#SvIk&x2tu!ViVH3=auI^$%qX#cvoG zj#$xtF@n(&9?9rPcod@zJciLUhT|AR@upY+Louc?2AX0b~f#~sx&G~%cpXEfrd zD35`rxIW3~2jIz!p?rUeF_afm8AG}MG^3w@r!o35_!&kogr5b|(FTg;3`QS=pJVhX z_<2U(gl96A1H4TqF63w z42>;~C2<(M9Htmv#%ScLTF#g|;1!H%4pV**eZk5w)b~}4=?brAOhbuKYp1NbuI-40)2 zyoT^Mj8`AN3cf`=Q{ijiJG6(!y_B&je%~`*4*Uc773-9Te`7p~?eE|gmRHAdM=%cB zMaMqEL3`<#NO)6WtVB3ym$n!O?bQzBO@m#=n*v8L-czt)yk}vL@t%Pr8SiO0hH(%N zoxph0VdN9x&44o)?>QKKBOJtr&iM!jv7z%kIydp2hmmK5HxsT6>YzLe#u(_PD35}h zgFYzFf%}4CD7Sz|gE1)6HpenFpQj&T4Dy5SW6`nJ?1dj^Y--O$hUSuK{)ljpr*zIo zILIA6l@Zx6^&i+*;AxDQ08@;BSPxU|fH(=z>0;JGc?ac*PnzH2d`vk?t!i|%dRyq!mAi79$w9q zU5nCsui?FvQ)?NzucKdQ==x8;!C3dgZ!#8*`CE)d<3{}l7PXhg16bSO4UBaUyb-*E zdzR9Vod7+i?+W%OhcH~0~WRA3&uJChcVc;J_x=j%P#* z7&%RtQE+0Is&En`rohQzs=+B?YQU)s-RrQ@7`neFt@{nfa<;jR0Ng5U4Z%z==#{AzEuavZHxLq zTm#frK;-+D9OFEbJSEZV6LcfsMSe z8Zo*9+?Y|F;U69hfVp? znz1R5+JJUgPUGJm+>JO)hN(?}&Wo*%j9CnKVr&|7YAc}6H;eMVE7t!UzK5Z6C^{xu z_o4oHxEn*~-PZk#p>lV|EQ5P6v|n018QX<>G3If&H)E)6eHb$l?#tMe@BJ7v3GUCB zSKtARc>*5Dn3eD##yklRX3VSb1B{`VJjj?;@I#Dw3Le6k)$mZpOo4|nW(_=?F;n3Y zjCl!LW9(S?d4}%!ST8WTEj%+!Wq4MY ztMF{bP@m>7=4E(p7<%tK#!i7>3{xF`DNHFmpD}2+wSY0N!_;R$_sK14EAb1UJSJ*^ zC1I|^%NV-HZY>XU175)hs*`ev_zO_p0P!YFxkCI6C_f17R~F?2uv1~m1LAH#V-IYK zKaC$S6mQxFFmJ$N%yg7#8^Dm?VhoMLI>ykl^^AEA-oO}oeIp}}!f!LS4Q~oV^?VqnHvCbTpW!`>nFW6w<~R5gMo?V$hPefQ$_R?42|b0#>|CJGls_Y3}fcOXBk7|TpXqoT*BB{@Hs}-htD&H za^V6a8^9MC`af)G-i@I9$z`9fmvJ1s6Xh$690PyD7|NZi4BcC{zGdkD(k=TuzJ{@Q z5#{d~LpfE-Xo~;$jQ$n=fuVCW>qkaY4*bN>`I_}JL+5v8`~NSf|0R^KGiC>TgE7=j zsu!3gFx3Uje3;%3%ue`bm?|)}iJ*Q^JAga})3$+a!IUe&EPyE&f!T%EDl=4X^zDq@ zK!~iSjDy_EYQZ?=;FbV!bt=GZ7zcG^wPT#>aC^q733p(e+He=fsRMTf_n@Boa5u(j z2=`$ea$m-I5bg*1WBsA8(YGw^QkAwE-(pIO*{32y_u2Yib1 zRv7yu;UN#R_5tia9^#yJ7$BFt&M*4)=>`sJ8 zge61wQv8Y$Xs2H@G^g&zI1vJ|_uCB3ulpTF+yT1`&9nO>7=d{E4MTI{evc7|vERq2 z?rI}{E`D-weVqcS?`96OwMpB$= zGwM6I4kIaMw=pyi=da7iAY6~3IXQoQMp7&rFzN@mAtNbHw=*=y=5NGEidkbuQ(jZv z14;3tTn97{=coJzlKRw)(Ufb=8A<(X!O$F=za=B7ue3h^O)+W3Nb2`p49&s$TQl@s zE`J+_=GXjf8AY(ib`3Owg z0GjHfdVzczrn&&l;rZ`lk0bLAJjDS1@Q(S;9fhiAwJOWQ= z^aXeZBfo;5WAsJ%d4|5L;(vkB-@`K*c^#g`&|Hy!HY0Dqa~PU8^3P@HJ01Rc49z9^ zDW8B^4^s{SnhW+*-T-|LUck^?ntvff-&61}V(6Zpe+grH!Y?zDUSGZE)E^qmR+N=Br>uQJMkS1}?LUd<@#`x-{1!LKp&JrDm{Mx?`(bAZ10 z;io(UA_Jz}0?LIcpMc1O*D>^66+guYh%9&mL*G^LZ)E6%!v8iy-&yf*VnhIbhfx~d z%!nYog~95=zm*Z#m;CQCbbr^sjS(Z^?F`-T_0v8E1npcB#jjv3jp0G@Lyu+yMX@74Ba#EUtuJT;WrH3L-1c^ zB#q~{4Bb!gUt=VV?RSi({4Hf9jotT*-UR=^NE*8z8M-Io|A~>*zn>YJbNBzk(D&*5 z*BP3F_upXXyLJ9w8Jdsx|Hep)!S4*s&HHaMrXu_YV`wb?WK1RaFUHVV{LPrk@GZt* zECK?!xV}QZ1#r9w#9%=V91D`L9Q#fn6=Y%gV{i}@VEI(I5THJRcm}Efj6cl-1u))$ z+p!$uA7})cVT|y5pe1O7u^SAx2VJrLPWWC%VyptlZ$e_M0^J#ju?qBLB*rSx3k<}2 z55j}MFqBi^;S9}j21YREFg%i>`Mba<#vFl1Gc*qu7{i#aVC?e*%{K+cG3F>dp3&$} z;9{%`wh)02CzQ_K0$dQycc|m`e}U}-vawkeiz0ujSzTG0LL`K z8xMcMcoe?_j5h&32o9mnwJ?nzFvnmT1DNLZ0u)okQe1>jGv326jx&V!D14UjDE7tR z9NtUqJ-agGl9PtkMj0!#v3O@5Zw;?Ft>p1 z2V+4T+LH{&GxioZfw4Eki2!xmTVYxcc=$b-!g#4Lk%o0JPQi4LfimJA%mjXvF(yH@ zm9W2p(THFOZSdf1#@+_!Fg9{4n9F#yP99@p41)QLjaUT>7|+0!83!>ARsmH}KXNEo z4b;GPkHR%UZIm%~!8(lRz_)?ASbi0*$9R!&ea3zfZot?};D(HcI0kQLylA))<3+%Y z8PA2AFrE!JW$YdB9gH&_ZpPRv;O30I5^e!nqHPP|I~jXE+={X1!gnza#ke(SgY|d8 zZ5b~HZpV00aC>k!#*xOq1LM&)JA%$wPPx#Pv5&(&83(x=9L_k@59A`@AlAXrj6>}j z1IA)GwPhT57-ic2BVYo`RQF@xag=GB6B(x#Jc)6rz9$$5bp@XUld=AI_$kK0ej1zt zreZnO_cVA0Wvb&@Fazaj@N?jKl<9pl8RtHD7UT4QXEV+qcn;%G|0uq|42I`1PH&iU zVgc&x2h-R9ht^pH7Nh(Syo~W^Y?m`O>_v#W7maWW9$m>TE?avf1ObS;5QgMAAXau%foMhb!ZRm9~&6E9K4aSX?))Xo3KtL z_#MWs4sT`bLik@dz3uwk6P-~`4)ZikW?=MOlAapGY= zBavGn>~Dm`{u>H{5Vo5LXEP4fhgcI9Ek{g4h_~}Mj5rc5+8RPm5Q6GNEC`3T--dBM zhubsGhw$B?1J>CKcLbeKM(jhK!F?zr#-VPY2g)D8JsD>++zU)ZKA(jrGv*fj6l40q zQ@~Ul2hgO@GmL|A4$Wj-1ep<>b>TUTn*`5g+*o)X<5DbN1lV`nNEmq=LcY4G z@Ops!bGyTDGw%KHCdQ?HAomED`iC3~ZATgX4;^G&8s|fdiw=a2Fm61I+zFxIZXfsp zxQH^v<1*v6g0C{}T|#7|pM;YQqh7*6Zf7HZ2>NbFb`0a>!f}j~17mxHeH~6`9OOYZ z@`rGcE7`~e!pVbcF*e$i-IB3?htWR5{u##DWcNq;2N?0pMjYLR@Jo!l1fI{hi*YKh z85en!V=*p{!#U^&;kJblcfxHA=P@qwD+g`OL0{djFxp7CUEoQK+a7+JaXY}%85jAO zgZ+wdk&8Ll-w5|!A#xEH!n*@TpK=Fb`BU&_K(*rlB5;O99$LcovL2 zCR`2QVB84!SH?x|7yQP!Xlns-mvDXXO~yrAio&?bLX<-+2=`@NZs0wHdnb(jraabj z(bw{sj5`3%Wn8qk!UV?Y3r}R^A3{{T#rQCel~NfS^{-mWcq4^ajdciv@@p7-!b87` z5p%+A3}e49M%%?pFpg8j=tDhF@fCnL}F-vDo-+!KC_ zaj1>!7-u-V9&Ermh;8vk@HWa*VI0E=X9oNZ<2(;<23xRvCcKq#sO|3p#MMC`im`7L zW8ZY`jhM)_|@55ceeJE2Kx&h>q zi+-2%WgNV}H^|p*vE)ZVZ@n`ufkCP z@m0^j@r-%_M!X62GMvaLqLqqe{}1`+BYjBz0p`d^CiAr$&vO6>-kUPp`weHWYo zGEp7~XMsGFXTbTO8Oq4((&mh&dRj1cKe#0rfqifQjQxagj=`fCcR4(oagmdyV;C3X zR63S%FTmr#qlncdcmm^IgdYQsWBIr6MDQfarSN3N{SJPLaesoRfTuC;_26j$@pFHH zu}_vR!19LhLdLrSFJgSukHw7pGrR<>!uof@tHB}U^EdEe##bFa0?uHajxhBHxPQXP zy;91(9zxteeiF`kA^yI_cuL40E0-ahIJgR^h4-$4YXfZ4eG7h|4B@2050)XE6d`|W zUxsikxI-DjHOMUu@bB-HboTv!{*&|#V<~0yokK_WA6cOGQ{bk2;76Pc0m zGBYwE5`{#S8JVR2_#smN8jQ63%S6j3Uzk4qA4j^G_!DY{nTcyNBTm~$m~|!wKQ|@E zzL_PQlds0pFR5|f3+ShCM6AdV*`g3LZ?!}Nafi4|bP)H7USc4=KQKm2z%1FbVwRXM zmWoy4O|eP5D?SvTi7#elgx079DciB%q zC`Xn($9<}NPR^B!dL!=HprKcy246B)DCDpgzUZ1dpjR>b$zDZm7TT z_g{QEUZ?4xF3^>9OTi>I*>)v?U;3)mDo{T4=&eSjIm+_ZiR_pb8r{1H# z#53a0>2LKf`VY&oqO3$b`!m}rvMO3N@QmRmcs6l+t0SH}+Q%Aa{dncL;Ev4Y4dA5NB^AtoqfZ;g=f;oI;l<`o}F9E zX^7|2cf`{i`{T*EwRa4T zFXH)&Kf5>GTM;V4M8rm zBIZOaj94DACSqO0=7^mUA4TkoI23U_qB!DmL}|qJh(C=so=L(p)eG??*IIaj_Z{Xg z(*aKz?}aC;55;qXAI9^PpT_fw=b6QLuJS58+xQ)`9Z!tjiziAS#nZ1Zm~YKb=6Cal zr|@La1UwZx8_(9Rf~RLU^qP6Cy^h{}UT<$8o~}F&&p4gp&G6=UOTAUzo8EdnWq3QD zCA`=B+&kfwcvrmdy&K+NKFjCx#rx8HK|I^LlCLJ7G1=7D%6GT#9$!!20N)VbXy2p0 zslFM$*}g@-6~5Pe>wQ~%yL@|mdwmCdCwwKoE51_Sb>E+nI?{`bi_D6w5LrF4Ze-)g zmXYluyF_-6>=*f9eBcF_XCUR!vOOcBrmqxCNd?WJh$Ze7DNA8V05P3B6bmWD| zZzF$-{5?uUxlz$kNl}?mIZ@@Jsz%j`x;^U7s7_JcqIyLQj2ap>CTc>|WcH;h_bQ_B*Cs;I<)W)c*N<)%-6pzAbkFEP(IcWC ziGCvbx#$<8Uyfc8{aW<;=q=H^qW47ak3JlIJo-X(Y4k7Af5fO56B84Y9FrB38&f`} zYD}G&+hdx?w2A2y(=Db?%;1=zF(YFhj+qoQHD+eaOEF7gR>r&@voYq~nB6g-#C#F+ zRm`cF^D$RrevJ7o=2ol|8x@-vn-QBGTNGOuC)`Ywi4lo0i3y3RiGjqt#A=DR zB{oWIk=Qn|bK?DpeG?x@9FaIaabn`s#OD&{CN4@`k@#BT`ot}XyAt;#?oT|Ncp|YR z@mk`~iMNuRq^P9Cq>QBOq@tuMNwt&eB{fO9GpTJ-=cN0S`X)V)G$Lty(!`{xNzWzC zO>rpi<+H8M3RH7hkgwR&p()MlyeQ}0dflloxl=+wtj zr=~ui`cmr4sjE}hr@ou|LF&HL6RGD@uccm3{X5N0i%LsQ3#8?yl~1ddRxhnl+FfaP zr*%#1k=8%$p|nwHkEA`3_Eg&RwApD((pILup0+V6OxJq&G}&mfkwOWBNVmJ=6Q84@e)BJ|um3`l$3r(w|75 zo<2K$LHe@v)#-1gzmvWr{loOn(!WgqI{j?=rS$L8e@Xu%LuHtZxQx_{Kt_H>g^cPM zbu$`gw9IIi(IumMMxTtq8N)NiWjvlSC1Xa$oQ#DT%QMzwtjpM(u`}bNjC~o0GLB~y zXI#!G&A6WNXQs~dGUGB+GXt6VnH4i@WY)vq*ts*aZRS0hJu?Sn4#^yy`Do^ona^a- z%zP>H<;+*{H+bI8+?M%%=HAQ$nMX5EXI{wsHuI;<-?K!Pm1VM`vy!qxS(UPCWi`g% zzv`VeC~H{O*sRC0p30h@H9PCYtOZ%iv({v-%i5f^GwY+QefY~~$Fqv@*Uw6`uH!Fb zX}{->^QZa){(OH$e+_>(EkbQWokRDB`i33|jR=h|`>Slvh31A9 zg;tdP)vhg}U7?g9PW6FteXHd?FoQHF!=FHA{Ip_79Ejb_M9Lg!l`6<`RP0B6E zt()66w`*?y+=p_<=02G_D|bQe%G}p;-_G5Y`$_KM+>+eV+~4!;yu`e`yxa1c=XJ^J zlQ%4HV%~FkFXwH@`!Mft-pRa+dEe*#p0Dyver$e9zCS-NzgB+B{4V$#abxnQ=FiQ4 zC4X)HhWzdMALJj*KbC(c|6=|R`F|961xW?Lg31N;3fdOjS1_PpWWmIO7YgPUEG<}7 z@Mgg~1s@iCS@3ni*@ABit{41SXctBnCKZMXiwbKLHYmKauxsIod4(Qie6m$S-6 zmCG#m|M2!6;7wNT!#JZ!z(8fCLfa&O%#JkANYa*;Hcj$$?*%QZBOTCX2g`+uiseS zi9(_)F*DJVn4eghSe;m(*pS$o*qPXuxF+%Z#ETOzPrN4ahQwPFwyvIvx-aRGq-T;|OL`~i%cLVo z$C8I7k4`=#d1~^UWJhvVa(;3}^0MUi4(IVpuHRVno;jVUWq`cl@VT$*xa%JnH*QnsaRPuZ2S zC*?rOhbdpAe3$Zb%I_&B?8EKj?NjYZcFCS@_t=Zlx zB#stM;xsW)bcyptpI9VTigjXx*d}(1E5!@ME5z%?E#fwDySPi-BOVYx6u%R{7k?H1 z5~(yo8ZC{J&X6q9Oes~$k`$>}s*>uZMyXxukyc6PNf%33NY_a>OZQ0+Nl!?7q!*i=cc*it{-QjkWI~pB5j&+V}9k)An zI-YU7;&{jLvEwVpFOEN*QBIR{hSTB9buM&vI#)W+cW!WA_tIl_v zpE|#F9(DfZ{2$jS*LYW~>r7XY%i)^u%5{~w7P=Z;U9L5*^{#7Nx4Q0kJ?8qi>rK}u zuAg1ORAXv-%sh6g1PTiLJQ0lX(ucv;N`eW)~+G%N1(h|}#(n``A z)4I~mN!yUNDed;OooTz%UQByC?d!CoX@lvy^zrG|^u%;Wx+lFfeOY>M`nvS1((g!r zBK_6$Ptp&kAD=gD-k5ol=gpXBpO-POa9-`arg`1-&YgF~yc_4;HSf`Rd*>aP_u;(n z<{irzmN6z{a>k4ddqze^UPe{MvW$+5wHcRWT$^!Q#`cUSGWKP>nej=+;f&)MCo@mW zjLDpyY0J#W%*(9GT$b69xjJ)w=GB>7GVjZLB6DBno0*?v9?tw@{;>ID=Eu#SJwJ7R z-u#;Rt@8)wUoih)^KY4d@BByS@11{O{)h7q&p$r@WY%d}le1=LNm*H0C0TV@Em^%; ztFtc2x;E>!tnFD3XFZ$sO4h-w&$E8W`ZIfY_Sozx*)y}H?5ymX_n1A2 zo-|Lcr_!_3v%<5=bCKsN&n=#NJ&$_!dJcF#_I&3#<{9#i_L{uYyvg1)ug_cKUGBZe zdzJSV@4enfy?ecHct7$U^8V~S;WPLq_^dwBH{Vz8TjuNXo$cG;yUDl1_oVMt-}}C= zeMfzRicT4?n3cJTL&;IflqRKDxmdYY*`_?Lysmtu{FXZ`cS^39TbSFNdv5OL+=p{t z%l$U@WZoHh_PmU|g1nl%#=Ne)wRxB1U7L4X-a~oMA(!!R)uEI5i>k2O|yt456!Yzf{ z3bz;TD%@STxA5h{Hwxb?{Iu{;;SYts6_TQnMW+>wFESOy7tJb4E^-yQi}H$!iYkj1 z7d02HDC#X*Q?#z=(xNMit}ohBw5@1+(XOK1MSF{0E_$Qry`oQx4i)`S^ji@rI$5kM z9#cHAIJS6N@toq6VrOw?vA4LexT1Jr@v`F9;;!O>;&X~GD&AOpP4SJzTZ`{0exUfV z;%AEY6~9{icJYVBUle~={B!Z|#V1OJmy9Yoy(Ff@Tr#61u|zCME6Fa&Eh#CfE?HF4 zRIM!joT~&I1>1CzYm2N4$tMsALr%PWf zeY^Cl(xat+l+v;hWuwc+m7P&$DVtfAR3??Bm$}RG%1X;>$`+S3m#rx4En8EzuI$pX zE6c7g+fufzY-ic-vb|+5m%UN;UfHK*hsu5^`>l+Woh%vet^8p5N9A9ZA1*&y{zo~j7*R30 zVqC=;6_$#b6-gD&itLJlimHkw75<8jiq#e8S6p6kZN*I$w^!U-@nFT{70*_@Q1M#D z!HSP6zN|Q0akS!(3R*d$a&+am$}=i0l`|`oDy7QwN_S;mWoczi<>Jcb$`zHpm1`>3 zRbEglR~SG`j8R@Dbp zpI3cb^;6aHs=?}E)rRWmYEyN5^_kTP)k1Y@byl@fU0hvNU0>Z;-Co^Oy{h`W>Wizd zsJ^cH=IT4D@2h^O`ibg2)h|{bsD7vV`jxE9%av`&Zqzx~J;itoyc()Q_pR)X%I>s+a21>)rKv z^`-SS^-Jno>$~a)>d&dasQ#MzTkG$yf4u(1`giI-sQ<41hx()SgNvdTO*p zQPHA>i&_@-En2r|*y2%(V;9d}>|E?y zT)nt)aqr@D7hk$~)8e}pKfL(a#qTUWx@6dru}h{bNm`P*B!5Zmk`+tNUvlA+olD+Z z^7WF#wyuGW4tH;RS8GGlK);{Ix?8)u{43m@4Nbk>UGBzS|4P3b^9H)w?SjY41Fw$< zK06CUyBdf*B|0Gxd^}U|xmg(mtPBDf1FYgs#grQZ4+gAj!Iy*WaCfxDf~}9|wWp6GU%gLoc6$poreJywy@4;)##r zi7zy`4>884bqy%OmIMJVN#qdp3Aj!Hu|ZHXaS{Sf0{6(rHwTbH%|sOOAc}YpXddj$ zqXNz8>mTUt^7Zt!cXVq(7-euQMZhf-_>hW#TZkho0&bxPH`~L(B?}I=3>gt#w&Rv~ z`H(^yO97yK3L7~Wpv57aCgq0rCzk^%Hv}kNLlkpEfD#m3hG@^V39Jtrf;>%vWVjzz z#*QY0p}y3bqR48p%~vxNnMVNQ>{=u>FWeQIf+!$FMFgd&&H?vDbmysSkv%R}Tjqz^ zE~_x%XWA|(0PzO+KMZ8tWkp0FiwI;9fh^)V5fR8Do)Zzzi3e+XIKIm2KBVD3q+w@i zSS1ZROT*66u(LGmEKTjqlW%M89%$_F!_)x=zWnBPf3Lr&e z&quV*=P=GkWX=x(n!!NuuIEL(BZm-|Jf<-t~SVSA0ji*Ss zC9oVGe*mi==6h zP*IMS1CvB}d9?^G1fYat35r+i$E)?@)%x*bKX7cFyg5W*`32!s@-3A4PD(DmuQKkd zAmP#Wad`VABsdbD5{aD>I1I=eI7R{@r4}~y^fVNONV_2A6lteh@bUg2Q2Rry;X4iV zne8+nmRiO)+$ZCZG8+<3oVR#@MI?o_1oq@92@fPYO0+bC;MVu(*7*X0b0CGg{ zKxaq8KtJnI@FMB*@wvj5vrLgQ7YNw$>>?)$5MUW60{{qh&HMoPWCVl*GaU$f50-hb zjI(FK=fyIWmvX8MnFt6xBR(&-;q{gHXEii}5aHAX?-()T~*af#-(bf%c z5)_}Od}(h#+qH`DhC7@Up$!*NQV|7&fdg0VP&>y}I~cS?K~O4IbKuLKO5Ryz_#{*| z`J3B2UuKKz|h=X6)`=H0yuF>PMnexH_*j~ zfc3IW7T6ja`utV6U_KSeSJg03&2X-o)1zv{ylRel)l7n|jzque2#M8;;*%Gr=;a(t z;FJ?El#@)6%ONmfT$2kI>cWM()Jz1Z3un)d1t1QVsnkK;Nsi}GhAUI}%0%@Xx^UGl zUu_uVwVe9a@|~!Sngw&yoD|%c?1K(rbz|Dv94PZ<@8<@u}`;3vaoA6M;*~FH6Xe*gwRUxrMVkIiW84T zl-Y#_CIYhc$eftjWu%3&ii|#~!5=!uve(gI6B>9LLY2L$3r2niOV%QS76E}!T{w>b z;{cx65XJ$u*zM3FD(?hnXgMmU1c+c4q8)ktPaZ*6u zA)*2eA_y9F)HI+39AIjouA8^#`+RNMsz?z3GDaD(mqIE%6Gv&*v33>3eizd3^5IFL->VG~8D5!R+S_WMwy^r0&06L2pC zK2?xPSjQrwkKl1Oujy`WYwkvVE6OfEqLZI*wLdgNKf=kcjRj!jNG+rS)+o7LEp7 zr=hps*0Pk5<`$dKVrz&0)jbseM_T+{tu_Iat=7Ii{uV1y7BD5H zT_d4tc00#!Xaa?}F{0Pj)YRG24#T%2X0&TG4;HUrVTil{*Fv6xSS+HU!^f2|5V8%bvT?lFJj?VUG4x^5U1IgtD z5Dn!Hww4g>6eLfFh8rMOH2eF&e$xyiL6?YqGQv!j zx)37T@sS)|9W5M*aYeqaFkob7mj)Qn29Wc~h`us{2}M~SmtkSgJY6uGZYE-NBNBFV zBnazhX zo6n*2!i+(FSqVX3!~`UzkN4UaKIVXp5h?-p=0pfPac7*kK2Bi;pA$jA7Y!gtZT0td zHgq*NcJ$c<hj3hS=k(*~YDv>TZ%3$9MD1I2r}i_b^s8_eoKkIg@b|6@gZ~+VE`77v<*=vl?jA=-YUGj%ybw=1)eMTTqp>_te9*WVsE?; zL9@g7T&gSx3(|T+GY+f`?JzJQ)0@0pnDQo2K@(9G4fZ~5e|*~h_}KnLf|$2u zFoOtghT5BpwG9cQY-=E`ikkptSeZ7Q3nsRbAsuET++4~kZLzCZ*{U#PR`Erw(oQ5O zZ3ut7Bk-Z|(ua2hK7Kg>a;Kw7)7?<@k(D*!9{IMbToUgOd}(QGA~ZSNX9dSmtqgco zAag|@2ou|9&=L8@$cnT^T@aKo3>-45%{vY8GR=|O(Amk22U40`)dou3g7&iZ%J$aIhO)NyvOcb=N~nh^N=|8~ zzcs>M%#8_PN9OpfprJs)gRP*wKymV&08=v?UBP3naATOD;BAtk_U`7Iy26z`C}XDy z0&MQa=BQ38ibvITId_+ky-BF4NnV*R2| zdO4K2bsE?`gAthQIBS%-kpYZuxJ6vNw9DM?4uL9Aa;%WKW(wk^nu%NpFRNrO`#}TD zRK;JksmaKLWi+kHC@jj{P7Mth93Vh6K>jSF0xr98AiS`VIZ$DAPK{(FQ8Hfp$=pH$ zYY-dQr5yhgW~_Mqr*JJBq(lZ=2soZ7yl+Tx6jr#w8d4mS6>bUw^Cj|Se(41%Y|b@7 zNHM@bfX&fLqTqp3@Y+y8+kk?%hYFeq6ud-KI3xun$JGe<*~$>M+27IM;71#X8t{dv z@urUMK7X^C5VVk8zhDyqqNmn@492thH1YmSK@Cws`<#NzS-}f11vNUwC9Z`T;s0Sk ziUAQShQhLA6A|i&q6r84B936g09Zcu*CzwJ3OA6~5yj93mh&>Ouf>qFttDwlIaIFedRu#38B%#$=K`B+? zhITOUvQr0vtEa6U{%>XXMlg+TMj`p1@bGAlRZs>~&@8KjMQiir5cO%;$rn4#RNa7qA~ID2j_5frZJL4Y;Ukf4`A3x8M(m|QaD|1j06ISQZHU2 z$*5q<{9XWR;u=uVl2z`{Hw=svsWU)G2cd53qFv z2p12*F2MSP04_Uv*)0M@StAHid;EQHelfw?!2Kl~8G^Kq_FlDsZ5&i)8w~|)?T`|i zy1}>uVtTK?t7#49u|o?r*nUI6cnSn;?hvSD>QvYvgiHo_=*!pA+W=j`rlX<@S$xF; zI$sDmp0)noZW~wPKs;fZv(VoLCj*R4-2R1tdsyX3~BQvmrh#YN*Q?pK~*0!dnje}B+s8yLo3|0dKtpQ$rLX2hj z&l+gx2~1^8z&|S$V*dWBcDUJWZ|L%`ZUTM@Dmg!goLGT_ z4x=v+;Nj#HQ}hX`DoQZ~TYA<2+zm$J80MUcW`D!6i1s!%$;jrR3`A|M7`BNWMwV>A zfX_^DbocnX8o^}^?9qIx5@32mS8InqG81VGw=#(iw>ttG!Kwzgyu9C*_SS)3e<# z6&~f|aZU;M04|;dRU;}u3g*$HR)-X{u#3ov#2hyeKW_I0@wOKUj5ZxGtTZsR?eMoV zS`QcdzR;;u&jUws4YeasF?4hy&rPT?Uu0wtJR^Kuv<`W8=%9qrzBBTOM4XTC5$8w| zamaZ(e9SQ!aVDC&d)6@AWxy1;6$P%y?XYy7p}N`#s*))<-R{|o#Oa{BmRuvqjpO+zl=}K@D}2)OK7fVhwSV!r>{K1OVa0 znN?IbDI)TZO@h@kkw)7C05D1@TwY;)oWc%R2B!)vqHPk4$fX6A#{tm{7ZjmL7!WL* z0fL1gK(GV?;?!6e5G{ckzvF-SvIbd#rQf; ziGcf4PPgVh1VHM;W}B86Jf4TfwK`;P6Vz;gJqek9Rk;*A;q;1f$Pzb;v$N` zRVPSs-9q3>Ii$EcBXFf0ybv&j!hpaHP2ecSbrQjDZ)k1?N6k-UZwF@m*Qa2B5(n4vwV@4f`k*Ukjk?-0`Nbfx~HZ>EqOK@+r=&1qQH$* z5O84?ye;{>($aXhjotll4Z!NUw2{ME;I0Ex8US>Jpt*Y$Us0MMsoBlALatVW=Ni`L zR7w+?V0BzuS_>O%iRmKbiugu{?1tU@7N%GSnH>YBqP(C%-A zD1IWaT%kq)zlb<^1E}IEK$*vzyILEUHnsViR`6J3Lq|t<|7s@twl;V1V4#Qf>G0Tj z3hpggid!Qf;06Q-_cwtQ-!Z4#&aOB>Vuw5Y6&PLB*52=LfrI96ZtPyod*GHGkeazk zA&1*c#T-FuZf^h*537T=k@2Og)fDe4GQM- zb;wbP1iwE6w|I9C4Ce0!36LGOzJ4$t^8O@l$_7_ePE6pavmPaGwH76AkA)OpvcxS} zqJZWYv~55Yeg=Vr@OIo}3tmk8^h+N0Zh`?BMpUnCpgD)8!0p?RLc)f|8Sv<3y}(^B z54dxyh>mSy4mS~t4t^H|FhuQ$-InP)Mb6-il5kh=|~!1#%P^m27Gn zIcg}0@=`=Omy{yXC=f+tprWmHch&%-n_%csy9W-~2$~>Zl$N6@tNe&GqppSVkh+CQ zRi(lNcE9KMG(bsaU8iY%0+ZF%59VC`Vqmf z0L5{Pb39X{++L~HS4D9wLFB`D1Zt~QL#VB$HPk+3WQMtFMhLalbQrbO3>CH2R0y@T z>S5GYb5zt;QyjI`xQ5zlQkC{`A*c;tTvZ4#0j6V6yWuRUtu!px(yH-5E#*iHNFSMG zGJuw$A$?>yNBYPNM0zzR0_h`jR2CW0FL>~YjHob*#Nz;BEhRZ4;@nFNYsrpqz9U=* zWaMzZ#J$b1K02gD#v_K5!ySMC9qIsNsEBxYaNsTQ!3}MSz@T`X%dI>XHdm{4z(fMa z6_%vgvk-6*87knkoKwsHR;U@nPU-QVnSJ29(ZM`XPpKEm09b>68lO62GQh^U%bv@v z3Gu8MD*;~=AUbMX#Ko#D>{>qf3v+N{zFp?|;fIzH)quufSI`6pT< z4Mp_hjKBqF#0s1d{W~N2aYpom?x3#ff2=_8^5cZFaD{pToZ?4|ZmJf$H zq9=H05bDW};0?R{M-+v20gfgPh||Ju!NZYB_wXi^br$x>4|k9g+EV7g&hx`w>fvgU z(-b47CgRkJ;Fm%2QTkX9DKKVE!9zt#lg%7e;Gb8r|nQp2}fWq(Gi&ukdh)Q z%MnnMBc_JT29Z*ZXsaS|_bH&^QxHSIWWK%Okv$QB^+k>lQO6N6f+J#fj!>U)a3lJ0 zMjRPuL|bRXYQUK@1QKUNpWvn$&X4HdsrLVml`-eVPzzV6SHQ&3p@ewoh(SE!7=kZj zs4aLahT`CK7>*+(LT-fNlGKQ~f*WS2MQQ{rQn9Bsf1fuT=m-ddnS!+rJL-iygGc0{ zj@gT%a0(6~rozWCRLT!xIK_@)D8&zBL`r>k5bl`04B;saNke2Z8rJOF>x#ErJwyrACy2YpRw)|I~1b`JjeV%$Zb6 zffHP0JhYeacqWWFas=kc5n_iNW(DBF5mDb4#x!u%43DLfY<3o9C4{jshX`oF$RWIg zcnFIi9>NtEErhcWkEjp6wJM^(2oZIZ5N6uJGm_VdbO6Ifr+QEQr#x;#)kc7)+u
?Bhk6CIhv?Y78W> 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