diff --git a/.github/workflows/python.yml b/.github/workflows/python.yml index 36cc25318920..4e4e0e77a9c3 100644 --- a/.github/workflows/python.yml +++ b/.github/workflows/python.yml @@ -89,8 +89,8 @@ jobs: # MacOS build is really slow (>30 mins); uses up a lot of CI minutes matrix+=('{"platform": "macos", "runs_on": "macos-latest"},') fi - matrix+=('{"platform": "windows", "runs_on": "windows-latest-8-cores"},') - matrix+=('{"platform": "linux", "runs_on": "ubuntu-latest-16-cores", container: {"image": "rerunio/ci_docker:0.5"}}') + matrix+=('{"platform": "windows", "runs_on": "windows-latest"},') + matrix+=('{"platform": "linux", "runs_on": "ubuntu-latest", container: {"image": "rerunio/ci_docker:0.5"}}') echo "Matrix values: ${matrix[@]}" @@ -115,7 +115,7 @@ jobs: # docker - name: Cache APT Packages if: matrix.platform == 'linux' - uses: awalsh128/cache-apt-pkgs-action@v1.2.2 + uses: awalsh128/cache-apt-pkgs-action@v1.2.4 with: packages: ${{ env.UBUNTU_REQUIRED_PKGS }} version: 2.0 # Increment this to pull newer packages @@ -211,7 +211,7 @@ jobs: - name: Install built wheel run: | - pip install rerun-sdk --find-links pre-dist --force-reinstall + pip install depthai_viewer --find-links pre-dist --force-reinstall - name: Run tests run: cd rerun_py/tests && pytest @@ -227,22 +227,6 @@ jobs: run: | echo "pkg_folder=$(ls unpack-dist)" >> $GITHUB_ENV - - name: Cache RRD dataset - id: dataset - uses: actions/cache@v3 - with: - path: examples/python/colmap/dataset/ - # TODO(jleibs): Derive this key from the invocation below - key: colmap-dataset-colmap-fiat-v0 - - - name: Generate Embedded RRD file - shell: bash - # If you change the line below you should almost definitely change the `key:` line above by giving it a new, unique name - run: | - pip install -r examples/python/colmap/requirements.txt - python3 examples/python/colmap/main.py --dataset colmap_fiat --resize 800x600 --save colmap.rrd - cp colmap.rrd unpack-dist/${{ env.pkg_folder }}/rerun_sdk/rerun_demo/colmap.rrd - - name: Repack the wheel shell: bash run: | @@ -337,65 +321,65 @@ jobs: # --------------------------------------------------------------------------- - py-test-docs: - name: Verify the docs build - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - - name: Set up Python - uses: actions/setup-python@v4 - with: - python-version: "3.8" - cache: "pip" - cache-dependency-path: "rerun_py/requirements-doc.txt" - - - name: Install Python dependencies - run: | - pip install --upgrade pip - pip install -r rerun_py/requirements-doc.txt - - - name: Build via mkdocs - run: | - mkdocs build -f rerun_py/mkdocs.yml - - py-docs: - name: Build and deploy docs - if: ${{ github.event_name == 'push' }} - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - with: - fetch-depth: 0 # Don't do a shallow clone - - - name: Set up Python - uses: actions/setup-python@v4 - with: - python-version: "3.8" - cache: "pip" - cache-dependency-path: "rerun_py/requirements-doc.txt" - - - name: Install Python dependencies - run: | - pip install --upgrade pip - pip install -r rerun_py/requirements-doc.txt - - - name: Set up git author - run: | - remote_repo="https://${GITHUB_TOKEN}@github.com/${GITHUB_REPOSITORY}.git" - git config --global user.name "${GITHUB_ACTOR}" - git config --global user.email "${GITHUB_ACTOR}@users.noreply.github.com" - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Deploy via mike # https://github.com/jimporter/mike - if: startsWith(github.ref, 'refs/tags/v') - run: | - git fetch - mike deploy -F rerun_py/mkdocs.yml -p --rebase -b gh-pages --prefix docs/python -u ${{github.ref_name}} latest - - - name: Deploy tag via mike # https://github.com/jimporter/mike - if: github.ref == 'refs/heads/main' - run: | - git fetch - mike deploy -F rerun_py/mkdocs.yml -p --rebase -b gh-pages --prefix docs/python HEAD + # py-test-docs: + # name: Verify the docs build + # runs-on: ubuntu-latest + # steps: + # - uses: actions/checkout@v3 + + # - name: Set up Python + # uses: actions/setup-python@v4 + # with: + # python-version: "3.8" + # cache: "pip" + # cache-dependency-path: "rerun_py/requirements-doc.txt" + + # - name: Install Python dependencies + # run: | + # pip install --upgrade pip + # pip install -r rerun_py/requirements-doc.txt + + # - name: Build via mkdocs + # run: | + # mkdocs build -f rerun_py/mkdocs.yml + + # py-docs: + # name: Build and deploy docs + # if: ${{ github.event_name == 'push' }} + # runs-on: ubuntu-latest + # steps: + # - uses: actions/checkout@v3 + # with: + # fetch-depth: 0 # Don't do a shallow clone + + # - name: Set up Python + # uses: actions/setup-python@v4 + # with: + # python-version: "3.8" + # cache: "pip" + # cache-dependency-path: "rerun_py/requirements-doc.txt" + + # - name: Install Python dependencies + # run: | + # pip install --upgrade pip + # pip install -r rerun_py/requirements-doc.txt + + # - name: Set up git author + # run: | + # remote_repo="https://${GITHUB_TOKEN}@github.com/${GITHUB_REPOSITORY}.git" + # git config --global user.name "${GITHUB_ACTOR}" + # git config --global user.email "${GITHUB_ACTOR}@users.noreply.github.com" + # env: + # GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + # - name: Deploy via mike # https://github.com/jimporter/mike + # if: startsWith(github.ref, 'refs/tags/v') + # run: | + # git fetch + # mike deploy -F rerun_py/mkdocs.yml -p --rebase -b gh-pages --prefix docs/python -u ${{github.ref_name}} latest + + # - name: Deploy tag via mike # https://github.com/jimporter/mike + # if: github.ref == 'refs/heads/main' + # run: | + # git fetch + # mike deploy -F rerun_py/mkdocs.yml -p --rebase -b gh-pages --prefix docs/python HEAD diff --git a/.mypy.ini b/.mypy.ini index e1c3f9421600..de19ffda5e8e 100644 --- a/.mypy.ini +++ b/.mypy.ini @@ -1,5 +1,5 @@ [mypy] -files = rerun_py/rerun_sdk/depthai_viewer, rerun_py/tests, examples/python +files = rerun_py/depthai_viewer, rerun_py/tests, examples/python exclude = examples/python/objectron/proto|examples/python/ros namespace_packages = True show_error_codes = True diff --git a/.vscode/settings.json b/.vscode/settings.json index 1722a88e03b3..a1a7f0abc714 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -52,9 +52,9 @@ "cranky", "--target-dir=target_ra", "--workspace", - "--message-format=json", - "--all-targets", - "--all-features" // --all-features will set the `__ci` feature flag, which stops crates/re_web_viewer_server/build.rs from building the web viewer + "--message-format=json" + // "--all-targets", + // "--all-features" // --all-features will set the `__ci` feature flag, which stops crates/re_web_viewer_server/build.rs from building the web viewer ], "rust-analyzer.cargo.buildScripts.overrideCommand": [ "cargo", @@ -62,14 +62,14 @@ "--quiet", "--target-dir=target_ra", "--workspace", - "--message-format=json", - "--all-targets", - "--all-features" // --all-features will set the `__ci` feature flag, which stops crates/re_web_viewer_server/build.rs from building the web viewer + "--message-format=json" + // "--all-targets", + // "--all-features" // --all-features will set the `__ci` feature flag, which stops crates/re_web_viewer_server/build.rs from building the web viewer ], // Our build scripts are generating code. // Having Rust Analyzer do this while doing other builds can lead to catastrophic failures. // INCLUDING attempts to publish a new release! "rust-analyzer.cargo.buildScripts.enable": false, - "python.analysis.extraPaths": ["rerun_py/rerun_sdk"], + "python.analysis.extraPaths": ["rerun_py/"], "ruff.args": ["--config", "rerun_py/pyproject.toml"] } diff --git a/CHANGELOG.md b/CHANGELOG.md index 2a840fe9a9b9..268c14c09115 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Depthai Viewer changelog + +## 0.0.2 + +Patch release. + ## 0.0.1 Beta release of the new Depthai Viewer. diff --git a/Cargo.lock b/Cargo.lock index 681b3ab79782..b0369291c38a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -124,7 +124,7 @@ checksum = "216261ddc8289130e551ddcd5ce8a064710c0d064a4d2895c67151c92b5443f6" [[package]] name = "api_demo" -version = "0.6.0-alpha.0" +version = "0.0.2" dependencies = [ "anyhow", "clap 4.1.4", @@ -1267,7 +1267,7 @@ dependencies = [ [[package]] name = "depthai-viewer" -version = "0.6.0-alpha.0" +version = "0.0.2" dependencies = [ "anyhow", "backtrace", @@ -1382,7 +1382,7 @@ dependencies = [ [[package]] name = "dna" -version = "0.6.0-alpha.0" +version = "0.0.2" dependencies = [ "depthai-viewer", "itertools", @@ -2822,7 +2822,7 @@ dependencies = [ [[package]] name = "minimal" -version = "0.6.0-alpha.0" +version = "0.0.2" dependencies = [ "depthai-viewer", ] @@ -2835,7 +2835,7 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "minimal_options" -version = "0.6.0-alpha.0" +version = "0.0.2" dependencies = [ "anyhow", "clap 4.1.4", @@ -3253,7 +3253,7 @@ dependencies = [ [[package]] name = "objectron" -version = "0.6.0-alpha.0" +version = "0.0.2" dependencies = [ "anyhow", "clap 4.1.4", @@ -3871,7 +3871,7 @@ checksum = "f2ff9a1f06a88b01621b7ae906ef0211290d1c8a168a15542486a8f61c0833b9" [[package]] name = "raw_mesh" -version = "0.6.0-alpha.0" +version = "0.0.2" dependencies = [ "anyhow", "bytes", @@ -3911,7 +3911,7 @@ dependencies = [ [[package]] name = "re_analytics" -version = "0.6.0-alpha.0" +version = "0.0.2" dependencies = [ "anyhow", "crossbeam", @@ -3932,7 +3932,7 @@ dependencies = [ [[package]] name = "re_arrow_store" -version = "0.6.0-alpha.0" +version = "0.0.2" dependencies = [ "ahash 0.8.2", "anyhow", @@ -3959,7 +3959,7 @@ dependencies = [ [[package]] name = "re_build_build_info" -version = "0.6.0-alpha.0" +version = "0.0.2" dependencies = [ "anyhow", "time 0.3.20", @@ -3967,18 +3967,18 @@ dependencies = [ [[package]] name = "re_build_info" -version = "0.6.0-alpha.0" +version = "0.0.2" [[package]] name = "re_build_web_viewer" -version = "0.6.0-alpha.0" +version = "0.0.2" dependencies = [ "cargo_metadata", ] [[package]] name = "re_data_store" -version = "0.6.0-alpha.0" +version = "0.0.2" dependencies = [ "ahash 0.8.2", "criterion", @@ -4001,14 +4001,14 @@ dependencies = [ [[package]] name = "re_error" -version = "0.6.0-alpha.0" +version = "0.0.2" dependencies = [ "anyhow", ] [[package]] name = "re_format" -version = "0.6.0-alpha.0" +version = "0.0.2" dependencies = [ "arrow2", "arrow2_convert", @@ -4018,7 +4018,7 @@ dependencies = [ [[package]] name = "re_int_histogram" -version = "0.6.0-alpha.0" +version = "0.0.2" dependencies = [ "criterion", "insta", @@ -4029,7 +4029,7 @@ dependencies = [ [[package]] name = "re_log" -version = "0.6.0-alpha.0" +version = "0.0.2" dependencies = [ "env_logger", "js-sys", @@ -4042,7 +4042,7 @@ dependencies = [ [[package]] name = "re_log_encoding" -version = "0.6.0-alpha.0" +version = "0.0.2" dependencies = [ "criterion", "ehttp", @@ -4067,7 +4067,7 @@ dependencies = [ [[package]] name = "re_log_types" -version = "0.6.0-alpha.0" +version = "0.0.2" dependencies = [ "ahash 0.8.2", "array-init", @@ -4105,7 +4105,7 @@ dependencies = [ [[package]] name = "re_memory" -version = "0.6.0-alpha.0" +version = "0.0.2" dependencies = [ "ahash 0.8.2", "backtrace", @@ -4125,7 +4125,7 @@ dependencies = [ [[package]] name = "re_query" -version = "0.6.0-alpha.0" +version = "0.0.2" dependencies = [ "arrow2", "criterion", @@ -4143,7 +4143,7 @@ dependencies = [ [[package]] name = "re_renderer" -version = "0.6.0-alpha.0" +version = "0.0.2" dependencies = [ "ahash 0.8.2", "anyhow", @@ -4196,7 +4196,7 @@ dependencies = [ [[package]] name = "re_sdk" -version = "0.6.0-alpha.0" +version = "0.0.2" dependencies = [ "arrow2_convert", "document-features", @@ -4216,7 +4216,7 @@ dependencies = [ [[package]] name = "re_sdk_comms" -version = "0.6.0-alpha.0" +version = "0.0.2" dependencies = [ "ahash 0.8.2", "anyhow", @@ -4232,7 +4232,7 @@ dependencies = [ [[package]] name = "re_smart_channel" -version = "0.6.0-alpha.0" +version = "0.0.2" dependencies = [ "crossbeam", "instant", @@ -4240,7 +4240,7 @@ dependencies = [ [[package]] name = "re_string_interner" -version = "0.6.0-alpha.0" +version = "0.0.2" dependencies = [ "ahash 0.8.2", "nohash-hasher", @@ -4251,7 +4251,7 @@ dependencies = [ [[package]] name = "re_tensor_ops" -version = "0.6.0-alpha.0" +version = "0.0.2" dependencies = [ "ahash 0.8.2", "ndarray", @@ -4261,7 +4261,7 @@ dependencies = [ [[package]] name = "re_tuid" -version = "0.6.0-alpha.0" +version = "0.0.2" dependencies = [ "arrow2", "arrow2_convert", @@ -4275,7 +4275,7 @@ dependencies = [ [[package]] name = "re_ui" -version = "0.6.0-alpha.0" +version = "0.0.2" dependencies = [ "eframe", "egui", @@ -4293,7 +4293,7 @@ dependencies = [ [[package]] name = "re_viewer" -version = "0.6.0-alpha.0" +version = "0.0.2" dependencies = [ "ahash 0.8.2", "anyhow", @@ -4363,7 +4363,7 @@ dependencies = [ [[package]] name = "re_web_viewer_server" -version = "0.6.0-alpha.0" +version = "0.0.2" dependencies = [ "cargo_metadata", "ctrlc", @@ -4380,7 +4380,7 @@ dependencies = [ [[package]] name = "re_ws_comms" -version = "0.6.0-alpha.0" +version = "0.0.2" dependencies = [ "anyhow", "bincode", @@ -4452,7 +4452,7 @@ checksum = "216080ab382b992234dda86873c18d4c48358f5cfcb70fd693d7f6f2131b628b" [[package]] name = "rerun_py" -version = "0.6.0-alpha.0" +version = "0.0.2" dependencies = [ "arrow2", "depthai-viewer", @@ -4556,7 +4556,7 @@ dependencies = [ [[package]] name = "run_wasm" -version = "0.6.0-alpha.0" +version = "0.0.2" dependencies = [ "cargo-run-wasm", "pico-args", @@ -5110,7 +5110,7 @@ dependencies = [ [[package]] name = "test_image_memory" -version = "0.6.0-alpha.0" +version = "0.0.2" dependencies = [ "depthai-viewer", "mimalloc", diff --git a/Cargo.toml b/Cargo.toml index bd3b9f138eec..920cb2836f2c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,39 +16,39 @@ include = ["../../LICENSE-APACHE", "../../LICENSE-MIT", "**/*.rs", "Cargo.toml"] license = "MIT OR Apache-2.0" repository = "https://github.com/rerun-io/rerun" rust-version = "1.67" -version = "0.6.0-alpha.0" +version = "0.0.2" [workspace.dependencies] # When using alpha-release, always use exact version, e.g. `version = "=0.x.y-alpha.z" # This is because we treat alpha-releases as incompatible, but semver doesn't. # In particular: if we compile rerun 0.3.0-alpha.0 we only want it to use # re_log_types 0.3.0-alpha.0, NOT 0.3.0-alpha.4 even though it is newer and semver-compatible. -re_sdk_comms = { path = "crates/re_sdk_comms", version = "=0.6.0-alpha.0" } -re_analytics = { path = "crates/re_analytics", version = "=0.6.0-alpha.0" } -re_arrow_store = { path = "crates/re_arrow_store", version = "=0.6.0-alpha.0" } -re_build_build_info = { path = "crates/re_build_build_info", version = "=0.6.0-alpha.0" } -re_build_info = { path = "crates/re_build_info", version = "=0.6.0-alpha.0" } -re_build_web_viewer = { path = "crates/re_build_web_viewer", version = "=0.6.0-alpha.0" } -re_data_store = { path = "crates/re_data_store", version = "=0.6.0-alpha.0" } -re_error = { path = "crates/re_error", version = "=0.6.0-alpha.0" } -re_format = { path = "crates/re_format", version = "=0.6.0-alpha.0" } -re_int_histogram = { path = "crates/re_int_histogram", version = "=0.6.0-alpha.0" } -re_log = { path = "crates/re_log", version = "=0.6.0-alpha.0" } -re_log_encoding = { path = "crates/re_log_encoding", version = "=0.6.0-alpha.0" } -re_log_types = { path = "crates/re_log_types", version = "=0.6.0-alpha.0" } -re_memory = { path = "crates/re_memory", version = "=0.6.0-alpha.0" } -re_query = { path = "crates/re_query", version = "=0.6.0-alpha.0" } -re_renderer = { path = "crates/re_renderer", version = "=0.6.0-alpha.0", default-features = false } -re_sdk = { path = "crates/re_sdk", version = "=0.6.0-alpha.0" } -re_smart_channel = { path = "crates/re_smart_channel", version = "=0.6.0-alpha.0" } -re_string_interner = { path = "crates/re_string_interner", version = "=0.6.0-alpha.0" } -re_tensor_ops = { path = "crates/re_tensor_ops", version = "=0.6.0-alpha.0" } -re_tuid = { path = "crates/re_tuid", version = "=0.6.0-alpha.0" } -re_ui = { path = "crates/re_ui", version = "=0.6.0-alpha.0" } -re_viewer = { path = "crates/re_viewer", version = "=0.6.0-alpha.0", default-features = false } -re_web_viewer_server = { path = "crates/re_web_viewer_server", version = "=0.6.0-alpha.0" } -re_ws_comms = { path = "crates/re_ws_comms", version = "=0.6.0-alpha.0" } -depthai-viewer = { path = "crates/rerun", version = "=0.6.0-alpha.0" } +re_sdk_comms = { path = "crates/re_sdk_comms", version = "0.0.2" } +re_analytics = { path = "crates/re_analytics", version = "0.0.2" } +re_arrow_store = { path = "crates/re_arrow_store", version = "0.0.2" } +re_build_build_info = { path = "crates/re_build_build_info", version = "0.0.2" } +re_build_info = { path = "crates/re_build_info", version = "0.0.2" } +re_build_web_viewer = { path = "crates/re_build_web_viewer", version = "0.0.2" } +re_data_store = { path = "crates/re_data_store", version = "0.0.2" } +re_error = { path = "crates/re_error", version = "0.0.2" } +re_format = { path = "crates/re_format", version = "0.0.2" } +re_int_histogram = { path = "crates/re_int_histogram", version = "0.0.2" } +re_log = { path = "crates/re_log", version = "0.0.2" } +re_log_encoding = { path = "crates/re_log_encoding", version = "0.0.2" } +re_log_types = { path = "crates/re_log_types", version = "0.0.2" } +re_memory = { path = "crates/re_memory", version = "0.0.2" } +re_query = { path = "crates/re_query", version = "0.0.2" } +re_renderer = { path = "crates/re_renderer", version = "0.0.2", default-features = false } +re_sdk = { path = "crates/re_sdk", version = "0.0.2" } +re_smart_channel = { path = "crates/re_smart_channel", version = "0.0.2" } +re_string_interner = { path = "crates/re_string_interner", version = "0.0.2" } +re_tensor_ops = { path = "crates/re_tensor_ops", version = "0.0.2" } +re_tuid = { path = "crates/re_tuid", version = "0.0.2" } +re_ui = { path = "crates/re_ui", version = "0.0.2" } +re_viewer = { path = "crates/re_viewer", version = "0.0.2", default-features = false } +re_web_viewer_server = { path = "crates/re_web_viewer_server", version = "0.0.2" } +re_ws_comms = { path = "crates/re_ws_comms", version = "0.0.2" } +depthai-viewer = { path = "crates/rerun", version = "0.0.2" } ahash = "0.8" anyhow = "1.0" diff --git a/crates/re_log_types/src/lib.rs b/crates/re_log_types/src/lib.rs index bc2785fd7c2e..af76f0b2c4a8 100644 --- a/crates/re_log_types/src/lib.rs +++ b/crates/re_log_types/src/lib.rs @@ -272,13 +272,15 @@ impl std::fmt::Display for PythonVersion { } } +type SysExePath = String; + #[derive(Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub enum RecordingSource { Unknown, /// The official Rerun Python Logging SDK - PythonSdk(PythonVersion), + PythonSdk(PythonVersion, SysExePath), /// The official Rerun Rust Logging SDK RustSdk { @@ -294,7 +296,7 @@ impl std::fmt::Display for RecordingSource { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self::Unknown => "Unknown".fmt(f), - Self::PythonSdk(version) => write!(f, "Python {version} SDK"), + Self::PythonSdk(version, _) => write!(f, "Python {version} SDK"), Self::RustSdk { rustc_version: rust_version, llvm_version: _, diff --git a/crates/re_ui/src/design_tokens.rs b/crates/re_ui/src/design_tokens.rs index dbcfdd66af40..115a760e2fcd 100644 --- a/crates/re_ui/src/design_tokens.rs +++ b/crates/re_ui/src/design_tokens.rs @@ -20,6 +20,14 @@ pub struct DesignTokens { pub primary_bg_color: egui::Color32, pub primary_hover_bg_color: egui::Color32, pub gray_50: egui::Color32, + pub gray_100: egui::Color32, + pub gray_200: egui::Color32, + pub gray_300: egui::Color32, + pub gray_400: egui::Color32, + pub gray_500: egui::Color32, + pub gray_600: egui::Color32, + pub gray_700: egui::Color32, + pub gray_800: egui::Color32, pub gray_900: egui::Color32, pub primary_700: egui::Color32, } @@ -191,11 +199,19 @@ fn apply_design_tokens(ctx: &egui::Context) -> DesignTokens { success_hover_bg_color: get_global_color(&json, "{Global.Color.Success.300}"), warning_bg_color: get_global_color(&json, "{Global.Color.Warning.200}"), warning_hover_bg_color: get_global_color(&json, "{Global.Color.Warning.300}"), - error_bg_color: get_global_color(&json, "{Global.Color.Error.200}"), - error_hover_bg_color: get_global_color(&json, "{Global.Color.Error.300}"), + error_bg_color: get_global_color(&json, "{Global.Color.Error.500}"), + error_hover_bg_color: get_global_color(&json, "{Global.Color.Error.600}"), primary_bg_color: get_global_color(&json, "{Global.Color.Primary.Default}"), primary_hover_bg_color: get_global_color(&json, "{Global.Color.Primary.500}"), gray_50: get_global_color(&json, "{Global.Color.Gray.50}"), + gray_100: get_global_color(&json, "{Global.Color.Gray.100}"), + gray_200: get_global_color(&json, "{Global.Color.Gray.200}"), + gray_300: get_global_color(&json, "{Global.Color.Gray.300}"), + gray_400: get_global_color(&json, "{Global.Color.Gray.400}"), + gray_500: get_global_color(&json, "{Global.Color.Gray.500}"), + gray_600: get_global_color(&json, "{Global.Color.Gray.600}"), + gray_700: get_global_color(&json, "{Global.Color.Gray.700}"), + gray_800: get_global_color(&json, "{Global.Color.Gray.800}"), gray_900: get_global_color(&json, "{Global.Color.Gray.900}"), primary_700: get_global_color(&json, "{Global.Color.Primary.700}"), } diff --git a/crates/re_ui/src/lib.rs b/crates/re_ui/src/lib.rs index d8a9d782db95..a823ec04dfdd 100644 --- a/crates/re_ui/src/lib.rs +++ b/crates/re_ui/src/lib.rs @@ -51,6 +51,13 @@ use parking_lot::Mutex; use egui::{pos2, Align2, Color32, Mesh, NumExt, Rect, Shape, Vec2}; +#[derive(Clone, Copy, Debug, PartialEq)] +pub enum ScrollAreaDirection { + Vertical, + Horizontal, + Both, +} + #[derive(Clone)] pub struct ReUi { pub egui_ctx: egui::Context, @@ -189,22 +196,61 @@ impl ReUi { }); } + /// A scroll bar that is more visible than the default. + pub fn styled_scrollbar( + &self, + ui: &mut egui::Ui, + direction: ScrollAreaDirection, + auto_shrink: [bool; 2], + contents: impl FnOnce(&mut egui::Ui) -> R, + ) { + // Set a more visible scroll bar color + let mut style = ui.style_mut().clone(); + let previous_active_bg_fill = style.visuals.widgets.active.bg_fill; + let previous_hovered_bg_fill = style.visuals.widgets.hovered.bg_fill; + let previous_inactive_bg_fill = style.visuals.widgets.inactive.bg_fill; + style.visuals.widgets.active.bg_fill = self.design_tokens.gray_400; + style.visuals.widgets.hovered.bg_fill = self.design_tokens.gray_400; + style.visuals.widgets.inactive.bg_fill = self.design_tokens.gray_300; + style.spacing.scroll_bar_inner_margin = 0.0; + ui.set_style(style); + match direction { + ScrollAreaDirection::Vertical => egui::ScrollArea::vertical(), + ScrollAreaDirection::Horizontal => egui::ScrollArea::horizontal(), + ScrollAreaDirection::Both => egui::ScrollArea::both(), + } + .auto_shrink(auto_shrink) + .show(ui, |ui| { + // Reset styling + let mut style = ui.style_mut().clone(); + style.visuals.widgets.active.bg_fill = previous_active_bg_fill; + style.visuals.widgets.hovered.bg_fill = previous_hovered_bg_fill; + style.visuals.widgets.inactive.bg_fill = previous_inactive_bg_fill; + style.spacing.scroll_bar_inner_margin = 0.0; + ui.set_style(style); + contents(ui); + }); + } + pub fn labeled_dragvalue( &self, ui: &mut egui::Ui, label: &str, value: &mut Num, range: RangeInclusive, - ) where + ) -> egui::Response + where Num: egui::emath::Numeric, { ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| { - ui.add_sized( + let response = ui.add_sized( [Self::box_width(), Self::box_height()], egui::DragValue::new(value).clamp_range(range), ); ui.label(egui::RichText::new(label).color(self.design_tokens.gray_900)); - }); + response + }) + .inner } pub fn labeled_toggle_switch(&self, ui: &mut egui::Ui, label: &str, value: &mut bool) { diff --git a/crates/re_viewer/src/app.rs b/crates/re_viewer/src/app.rs index 1702312cc594..07ddc2cbe37b 100644 --- a/crates/re_viewer/src/app.rs +++ b/crates/re_viewer/src/app.rs @@ -21,6 +21,7 @@ use crate::{ misc::{AppOptions, Caches, RecordingConfig, ViewerContext}, ui::{data_ui::ComponentUiRegistry, Blueprint}, viewer_analytics::ViewerAnalytics, + AppEnvironment, }; #[cfg(not(target_arch = "wasm32"))] @@ -101,15 +102,21 @@ pub struct App { icon_status: AppIconStatus, + #[cfg(not(target_arch = "wasm32"))] + python_path: Option, + #[cfg(not(target_arch = "wasm32"))] backend_handle: Option, } impl App { #[cfg(not(target_arch = "wasm32"))] - fn spawn_backend() -> Option { + fn spawn_backend(python_path: &Option) -> Option { // TODO(filip): Is there some way I can know for sure where depthai_viewer_backend is? - let backend_handle = match std::process::Command::new("python") + let Some(py_path) = python_path else { + panic!("Python path is missing, exiting..."); + }; + let backend_handle = match std::process::Command::new(py_path) .args(["-m", "depthai_viewer._backend.main"]) .spawn() { @@ -119,19 +126,7 @@ impl App { } Err(err) => { eprintln!("Failed to start depthai viewer: {err}"); - match std::process::Command::new("python3") - .args(["-m", "depthai_viewer._backend.main"]) - .spawn() - { - Ok(child) => { - println!("Backend started successfully."); - Some(child) - } - Err(err) => { - eprintln!("Failed to start depthai_viewer {err}"); - None - } - } + None } }; // assert!( @@ -170,6 +165,12 @@ impl App { let mut analytics = ViewerAnalytics::new(); analytics.on_viewer_started(&build_info, app_env); + #[cfg(not(target_arch = "wasm32"))] + let python_path = match app_env { + AppEnvironment::PythonSdk(_, py_path) => Some(py_path.clone()), + _ => None, + }; + Self { build_info, startup_options, @@ -196,8 +197,11 @@ impl App { analytics, icon_status: AppIconStatus::NotSetTryAgain, + + #[cfg(not(target_arch = "wasm32"))] + python_path: python_path.clone(), #[cfg(not(target_arch = "wasm32"))] - backend_handle: App::spawn_backend(), + backend_handle: App::spawn_backend(&python_path), } } @@ -483,19 +487,23 @@ impl eframe::App for App { Ok(status) => { if status.is_some() { handle.kill(); + self.state.depthai_state.reset(); re_log::debug!("Backend process has exited, restarting!"); - self.backend_handle = App::spawn_backend(); + self.backend_handle = App::spawn_backend(&self.python_path); } } Err(_) => {} }, - None => self.backend_handle = App::spawn_backend(), + None => self.backend_handle = App::spawn_backend(&self.python_path), }; } - if self.backend_handle.is_none() { - self.backend_handle = App::spawn_backend(); - }; + #[cfg(not(target_arch = "wasm32"))] + { + if self.backend_handle.is_none() { + self.backend_handle = App::spawn_backend(&self.python_path); + }; + } if self.startup_options.memory_limit.limit.is_none() { // we only warn about high memory usage if the user hasn't specified a limit @@ -1339,41 +1347,6 @@ fn top_bar_ui( let extra_margin = (ui.available_height() - 24.0) / 2.0; ui.add_space(extra_margin); } - - let mut selection_panel_expanded = blueprint.selection_panel_expanded; - if app - .re_ui - .medium_icon_toggle_button( - ui, - &re_ui::icons::RIGHT_PANEL_TOGGLE, - &mut selection_panel_expanded, - ) - .on_hover_text(format!( - "Toggle Selection View{}", - Command::ToggleSelectionPanel.format_shortcut_tooltip_suffix(ui.ctx()) - )) - .clicked() - { - app.pending_commands.push(Command::ToggleSelectionPanel); - } - - let mut time_panel_expanded = blueprint.time_panel_expanded; - if app - .re_ui - .medium_icon_toggle_button( - ui, - &re_ui::icons::BOTTOM_PANEL_TOGGLE, - &mut time_panel_expanded, - ) - .on_hover_text(format!( - "Toggle Timeline View{}", - Command::ToggleTimePanel.format_shortcut_tooltip_suffix(ui.ctx()) - )) - .clicked() - { - app.pending_commands.push(Command::ToggleTimePanel); - } - let mut blueprint_panel_expanded = blueprint.blueprint_panel_expanded; if app .re_ui diff --git a/crates/re_viewer/src/depthai/depthai.rs b/crates/re_viewer/src/depthai/depthai.rs index 8323ffc873e1..48c8a8f1de2a 100644 --- a/crates/re_viewer/src/depthai/depthai.rs +++ b/crates/re_viewer/src/depthai/depthai.rs @@ -25,11 +25,21 @@ pub enum ColorCameraResolution { THE_48_MP, } -#[derive(serde::Deserialize, serde::Serialize, fmt::Debug, PartialEq, Clone, Copy, EnumIter)] +#[derive( + serde::Deserialize, + serde::Serialize, + fmt::Debug, + PartialEq, + Eq, + Clone, + Copy, + EnumIter, + PartialOrd, + Ord, +)] #[allow(non_camel_case_types)] pub enum MonoCameraResolution { THE_400_P, - THE_480_P, THE_720_P, THE_800_P, THE_1200_P, @@ -58,7 +68,6 @@ impl fmt::Display for MonoCameraResolution { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::THE_400_P => write!(f, "400p"), - Self::THE_480_P => write!(f, "480p"), Self::THE_720_P => write!(f, "720p"), Self::THE_800_P => write!(f, "800p"), Self::THE_1200_P => write!(f, "1200p"), @@ -137,9 +146,25 @@ impl Default for MonoCameraConfig { fn default() -> Self { Self { fps: 30, - resolution: MonoCameraResolution::THE_800_P, + resolution: MonoCameraResolution::THE_400_P, board_socket: CameraBoardSocket::AUTO, - stream_enabled: false, + stream_enabled: true, + } + } +} + +impl MonoCameraConfig { + fn left() -> Self { + Self { + board_socket: CameraBoardSocket::LEFT, + ..Default::default() + } + } + + fn right() -> Self { + Self { + board_socket: CameraBoardSocket::RIGHT, + ..Default::default() } } } @@ -232,11 +257,11 @@ impl DepthConfig { } } -#[derive(Default, serde::Deserialize, serde::Serialize, Clone)] +#[derive(serde::Deserialize, serde::Serialize, Clone)] pub struct DeviceConfig { pub color_camera: ColorCameraConfig, - pub left_camera: MonoCameraConfig, - pub right_camera: MonoCameraConfig, + pub left_camera: Option, + pub right_camera: Option, #[serde(default = "bool_true")] pub depth_enabled: bool, // Much easier to have an explicit bool for checkbox #[serde(default = "DepthConfig::default_as_option")] @@ -244,6 +269,19 @@ pub struct DeviceConfig { pub ai_model: AiModel, } +impl Default for DeviceConfig { + fn default() -> Self { + Self { + color_camera: ColorCameraConfig::default(), + left_camera: Some(MonoCameraConfig::left()), + right_camera: Some(MonoCameraConfig::right()), + depth_enabled: true, + depth: Some(DepthConfig::default()), + ai_model: AiModel::default(), + } + } +} + #[derive(serde::Serialize, serde::Deserialize)] #[allow(non_camel_case_types)] pub enum CameraSensorType { @@ -313,7 +351,8 @@ const fn bool_true() -> bool { #[derive(Default, serde::Deserialize, serde::Serialize)] pub struct DeviceConfigState { - pub config: DeviceConfig, + #[serde(skip)] + pub config: Option, #[serde(skip)] pub update_in_progress: bool, } @@ -341,7 +380,7 @@ struct PipelineResponse { impl Default for PipelineResponse { fn default() -> Self { Self { - message: "Pipeline not started".to_string(), + message: String::from("Pipeline not started"), } } } @@ -395,6 +434,12 @@ pub struct AiModel { impl Default for AiModel { fn default() -> Self { + default_neural_networks()[1].clone() + } +} + +impl AiModel { + pub fn none() -> Self { Self { path: String::new(), display_name: String::from("No model selected"), @@ -414,8 +459,10 @@ pub struct State { devices_available: Option>, #[serde(skip)] pub selected_device: Device, + #[serde(skip)] pub applied_device_config: DeviceConfigState, - pub modified_device_config: DeviceConfigState, + #[serde(skip)] + pub modified_device_config: DeviceConfig, #[serde(skip, default = "all_subscriptions")] // Want to resubscribe to api when app is reloaded pub subscriptions: Vec, // Shown in ui @@ -437,7 +484,7 @@ fn all_subscriptions() -> Vec { #[inline] fn default_neural_networks() -> Vec { vec![ - AiModel::default(), + AiModel::none(), AiModel { path: String::from("yolov8n_coco_640x352"), display_name: String::from("Yolo V8"), @@ -463,7 +510,7 @@ impl Default for State { devices_available: None, selected_device: Device::default(), applied_device_config: DeviceConfigState::default(), - modified_device_config: DeviceConfigState::default(), + modified_device_config: DeviceConfig::default(), subscriptions: ChannelId::iter().collect(), setting_subscriptions: false, backend_comms: BackendCommChannel::default(), @@ -537,155 +584,68 @@ impl State { && old_config.ai_model == new_config.ai_model } - /// Should the space view be added to the UI based on the new subscriptions (a subscription change occurred) - fn create_entity_paths_from_subscriptions( - &mut self, - new_subscriptions: &Vec, - ) -> Vec { - let mut new_entity_paths = Vec::new(); - for channel in new_subscriptions.iter() { - match channel { - ChannelId::ColorImage => { - new_entity_paths.push(EntityPath::from("color/camera/rgb/Color camera")); - } - ChannelId::LeftMono => { - new_entity_paths.push(EntityPath::from("mono/camera/left_mono/Left mono")); - } - ChannelId::RightMono => { - new_entity_paths.push(EntityPath::from("mono/camera/right_mono/Right mono")); - } - ChannelId::DepthImage => { - new_entity_paths.push(EntityPath::from("color/camera/rgb/Depth")); - new_entity_paths.push(EntityPath::from("mono/camera/right_mono/Depth")); - new_entity_paths.push(EntityPath::from("mono/camera/left_mono/Depth")); - } - _ => {} - } - } - new_entity_paths - } - /// Get the entities that should be removed based on UI (e.g. if depth is disabled, remove the depth image) pub fn get_entities_to_remove(&mut self) -> Vec { let mut remove_entities = Vec::new(); - if self.applied_device_config.config.depth.is_none() { + let Some(applied_device_config) = &self.applied_device_config.config else { + return vec![entity_paths::DEPTH_LEFT_MONO.clone(), + entity_paths::DEPTH_RIGHT_MONO.clone(), + entity_paths::DEPTH_RGB.clone(), + entity_paths::MONO_CAM_3D.clone(), + entity_paths::MONO_CAMERA_TRANSFORM.clone(), + entity_paths::RIGHT_PINHOLE_CAMERA.clone(), + entity_paths::RIGHT_CAMERA_IMAGE.clone(), + entity_paths::LEFT_PINHOLE_CAMERA.clone(), + entity_paths::LEFT_CAMERA_IMAGE.clone(), + entity_paths::RGB_PINHOLE_CAMERA.clone(), + entity_paths::RGB_CAMERA_IMAGE.clone(), + entity_paths::RGB_CAMERA_TRANSFORM.clone(), + entity_paths::COLOR_CAM_3D.clone(), + entity_paths::DETECTIONS.clone(), + entity_paths::DETECTION.clone()] + }; + if applied_device_config.depth.is_none() { remove_entities.push(entity_paths::DEPTH_LEFT_MONO.clone()); remove_entities.push(entity_paths::DEPTH_RIGHT_MONO.clone()); remove_entities.push(entity_paths::DEPTH_RGB.clone()); } - if !self - .applied_device_config - .config - .right_camera - .stream_enabled - { + if let Some(right_cam) = applied_device_config.right_camera { + if !right_cam.stream_enabled { + remove_entities.push(entity_paths::RIGHT_PINHOLE_CAMERA.clone()); + remove_entities.push(entity_paths::RIGHT_CAMERA_IMAGE.clone()); + if let Some(left_cam) = applied_device_config.left_camera { + if !left_cam.stream_enabled { + remove_entities.push(entity_paths::MONO_CAM_3D.clone()); + remove_entities.push(entity_paths::MONO_CAMERA_TRANSFORM.clone()); + } + } else { + remove_entities.push(entity_paths::LEFT_PINHOLE_CAMERA.clone()); + remove_entities.push(entity_paths::LEFT_CAMERA_IMAGE.clone()); + } + } + } else { remove_entities.push(entity_paths::RIGHT_PINHOLE_CAMERA.clone()); remove_entities.push(entity_paths::RIGHT_CAMERA_IMAGE.clone()); - // Both cams disabled -> remove 3D view - if !self.applied_device_config.config.left_camera.stream_enabled { - remove_entities.push(entity_paths::MONO_CAM_3D.clone()); - remove_entities.push(entity_paths::MONO_CAMERA_TRANSFORM.clone()); - } } - if !self.applied_device_config.config.left_camera.stream_enabled { - remove_entities.push(entity_paths::LEFT_PINHOLE_CAMERA.clone()); - remove_entities.push(entity_paths::LEFT_CAMERA_IMAGE.clone()); + if let Some(left_cam) = applied_device_config.left_camera { + if !left_cam.stream_enabled { + remove_entities.push(entity_paths::LEFT_PINHOLE_CAMERA.clone()); + remove_entities.push(entity_paths::LEFT_CAMERA_IMAGE.clone()); + } } - if !self - .applied_device_config - .config - .color_camera - .stream_enabled - { + if !applied_device_config.color_camera.stream_enabled { remove_entities.push(entity_paths::RGB_PINHOLE_CAMERA.clone()); remove_entities.push(entity_paths::RGB_CAMERA_IMAGE.clone()); remove_entities.push(entity_paths::COLOR_CAM_3D.clone()); remove_entities.push(entity_paths::RGB_CAMERA_TRANSFORM.clone()); } - if self.applied_device_config.config.ai_model.path.is_empty() { + if applied_device_config.ai_model.path.is_empty() { remove_entities.push(entity_paths::DETECTIONS.clone()); remove_entities.push(entity_paths::DETECTION.clone()); } remove_entities } - /// DEPRECATED! Just keep it in the code as a reminder of how to do it - /// Probably won't be needed when we make the move away from log_db in the future, will likely be implemented (much) differently. - /// Until then we just loose a bit of performance if a user isn't viewing all of the channels - /// Set subscriptions based on the visible UI - // pub fn set_subscriptions_from_space_views(&mut self, visible_space_views: Vec<&SpaceView>) { - // // If any bool in the vec is true, the channel is currently visible in the ui somewhere - // let mut visibilities = HashMap::>::from([ - // (ChannelId::ColorImage, Vec::new()), - // (ChannelId::LeftMono, Vec::new()), - // (ChannelId::RightMono, Vec::new()), - // (ChannelId::DepthImage, Vec::new()), - // ]); - // // Fill in visibilities - // for space_view in visible_space_views.iter() { - // let property_map = space_view.data_blueprint.data_blueprints_projected(); - // for entity_path in space_view.data_blueprint.entity_paths().iter() { - // if let Some(channel_id) = DEPTHAI_ENTITY_HASHES.get(&entity_path.hash()) { - // if let Some(visibility) = visibilities.get_mut(channel_id) { - // visibility.push(property_map.get(entity_path).visible); - // } - // } - // } - // } - - // // First add subscriptions that don't have explicit enable disable buttons in the ui - // let mut possible_subscriptions = Vec::::from([ChannelId::ImuData]); - // // Now add subscriptions that should be possible in terms of ui - // if self.applied_device_config.config.depth_enabled { - // possible_subscriptions.push(ChannelId::DepthImage); - // } - // if self - // .applied_device_config - // .config - // .color_camera - // .stream_enabled - // { - // possible_subscriptions.push(ChannelId::ColorImage); - // } - - // if self.applied_device_config.config.left_camera.stream_enabled { - // possible_subscriptions.push(ChannelId::LeftMono); - // } - // if self - // .applied_device_config - // .config - // .right_camera - // .stream_enabled - // { - // possible_subscriptions.push(ChannelId::RightMono); - // } - - // // Filter visibilities, include those that are currently visible and also possible (example pointcloud enabled == pointcloud possible) - // let mut subscriptions = visibilities - // .iter() - // .filter_map(|(channel, vis)| { - // if vis.iter().any(|x| *x) { - // if possible_subscriptions.contains(channel) { - // return Some(*channel); - // } - // } - // None - // }) - // .collect_vec(); - - // // Keep subscriptions that should be visible but have not yet been sent by the backend - // for channel in ChannelId::iter() { - // if !subscriptions.contains(&channel) - // && possible_subscriptions.contains(&channel) - // && self.subscriptions.contains(&channel) - // { - // subscriptions.push(channel); - // } - // } - - // self.set_subscriptions(&subscriptions); - // } - pub fn set_subscriptions(&mut self, subscriptions: &Vec) { if self.subscriptions.len() == subscriptions.len() && self @@ -731,18 +691,26 @@ impl State { if config.color_camera.stream_enabled { subs.push(ChannelId::ColorImage); } - if config.left_camera.stream_enabled { - subs.push(ChannelId::LeftMono); + if let Some(left_cam) = config.left_camera { + if left_cam.stream_enabled { + subs.push(ChannelId::LeftMono); + } } - if config.right_camera.stream_enabled { - subs.push(ChannelId::RightMono); + if let Some(right_cam) = config.right_camera { + if right_cam.stream_enabled { + subs.push(ChannelId::RightMono); + } } - self.applied_device_config.config = config.clone(); - self.modified_device_config.config = config; - self.applied_device_config.config.depth_enabled = - self.applied_device_config.config.depth.is_some(); - self.modified_device_config.config.depth_enabled = - self.modified_device_config.config.depth.is_some(); + self.applied_device_config.config = Some(config.clone()); + self.modified_device_config = config.clone(); + let Some(applied_device_config) = self.applied_device_config.config.as_mut() else { + self.reset(); + self.applied_device_config.update_in_progress = false; + return; + }; + applied_device_config.depth_enabled = config.depth.is_some(); + self.modified_device_config.depth_enabled = + self.modified_device_config.depth.is_some(); self.set_subscriptions(&subs); self.applied_device_config.update_in_progress = false; } @@ -750,9 +718,9 @@ impl State { re_log::debug!("Setting device: {device:?}"); self.selected_device = device; self.backend_comms.set_subscriptions(&self.subscriptions); - self.backend_comms - .set_pipeline(&self.applied_device_config.config, false); - self.applied_device_config.update_in_progress = true; + // self.backend_comms + // .set_pipeline(&self.applied_device_config.config, false); + self.applied_device_config.update_in_progress = false; } WsMessageData::Error(error) => { re_log::error!("Error: {:}", error.message); @@ -781,10 +749,8 @@ impl State { } pub fn set_device(&mut self, device_id: DeviceId) { - if self.selected_device.id == device_id { - return; - } re_log::debug!("Setting device: {:?}", device_id); + self.applied_device_config.config = None; self.backend_comms.set_device(device_id); self.applied_device_config.update_in_progress = true; } @@ -799,24 +765,46 @@ impl State { { return; } - config.left_camera.board_socket = CameraBoardSocket::LEFT; - config.right_camera.board_socket = CameraBoardSocket::RIGHT; if !config.depth_enabled { config.depth = None; } + + if self + .selected_device + .supported_left_mono_resolutions + .is_empty() + { + config.left_camera = None; + config.depth = None; + } + if self + .selected_device + .supported_right_mono_resolutions + .is_empty() + { + config.right_camera = None; + config.depth = None; + } if self.selected_device.id.is_empty() { - self.applied_device_config.config = config.clone(); + self.applied_device_config.config = Some(config.clone()); return; } self.backend_comms.set_pipeline(config, runtime_only); if runtime_only { - self.applied_device_config.config = config.clone(); + self.applied_device_config.config = Some(config.clone()); self.applied_device_config.update_in_progress = false; } else { - re_log::info!("Creating pipeline..."); self.applied_device_config.update_in_progress = true; } } + + pub fn reset(&mut self) { + *self = Self::default(); + } + + pub fn is_update_in_progress(&self) -> bool { + self.applied_device_config.update_in_progress + } } pub type DeviceId = String; diff --git a/crates/re_viewer/src/lib.rs b/crates/re_viewer/src/lib.rs index 25618fe852be..3d78fa141c47 100644 --- a/crates/re_viewer/src/lib.rs +++ b/crates/re_viewer/src/lib.rs @@ -70,12 +70,13 @@ macro_rules! profile_scope { } // --------------------------------------------------------------------------- +type SysExePath = String; /// Where is this App running in? #[derive(Clone, Debug, PartialEq, Eq)] pub enum AppEnvironment { /// Created from the Rerun Python SDK. - PythonSdk(PythonVersion), + PythonSdk(PythonVersion, SysExePath), /// Created from the Rerun Rust SDK. RustSdk { @@ -97,7 +98,9 @@ impl AppEnvironment { pub fn from_recording_source(source: &re_log_types::RecordingSource) -> Self { use re_log_types::RecordingSource; match source { - RecordingSource::PythonSdk(python_version) => Self::PythonSdk(python_version.clone()), + RecordingSource::PythonSdk(python_version, sys_exe) => { + Self::PythonSdk(python_version.clone(), sys_exe.clone()) + } RecordingSource::RustSdk { rustc_version: rust_version, llvm_version, diff --git a/crates/re_viewer/src/ui/auto_layout.rs b/crates/re_viewer/src/ui/auto_layout.rs index 7af2a61b8ee1..d158ce0d25ca 100644 --- a/crates/re_viewer/src/ui/auto_layout.rs +++ b/crates/re_viewer/src/ui/auto_layout.rs @@ -197,7 +197,7 @@ pub(crate) fn update_tree( NodeIndex::root(), &LayoutSplit::LeftRight( LayoutSplit::Leaf(Vec::new()).into(), - 0.5, + 0.7, right_panel_split().into(), ), ); @@ -386,6 +386,7 @@ pub(crate) fn default_tree_from_space_views( viewport_size: egui::Vec2, visible: &std::collections::BTreeSet, space_views: &HashMap, + is_maximized: bool, ) -> egui_dock::Tree { // TODO(filip): Implement sensible auto layout when space views changes. // Something like: @@ -434,75 +435,94 @@ pub(crate) fn default_tree_from_space_views( }) .collect_vec(); - if !spaces.is_empty() { - let layout = LayoutSplit::LeftRight( - { - if spaces.len() == 1 { - LayoutSplit::Leaf(spaces) + if !spaces.is_empty() || is_maximized { + let layout = { + if is_maximized { + let space_view_id = visible.first().unwrap(); + if space_views.get(space_view_id).is_none() { + if space_view_id == &STATS_SPACE_VIEW.id { + println!("Space view is stats space view!"); + LayoutSplit::Leaf(vec![SpaceMakeInfo { + id: *space_view_id, + path: None, + category: None, + aspect_ratio: None, + kind: SpaceViewKind::Stats, + }]) + } else { + panic!("Can't maximize this space view"); + } } else { - // Split space views: - // - Color stream available: Split top 3d, bottom 2d - // - if mono available split it right from color streams into 3d top and both 2d in a tab group on the bottom - let mut top_left_spaces = Vec::new(); - let mut top_right_spaces = Vec::new(); - let mut bottom_left_spaces = Vec::new(); - let mut bottom_right_spaces = Vec::new(); - spaces.iter().cloned().for_each(|space| { - let Some(space_path) = &space.path else { + LayoutSplit::Leaf(spaces) + } + } else { + LayoutSplit::LeftRight( + { + // Split space views: + // - Color stream available: Split top 3d, bottom 2d + // - if mono available split it right from color streams into 3d top and both 2d in a tab group on the bottom + let mut top_left_spaces = Vec::new(); + let mut top_right_spaces = Vec::new(); + let mut bottom_left_spaces = Vec::new(); + let mut bottom_right_spaces = Vec::new(); + spaces.iter().cloned().for_each(|space| { + let Some(space_path) = &space.path else { return; }; - if space_path.hash() == depthai::entity_paths::COLOR_CAM_3D.hash() { - top_left_spaces.push(space); - } else if space_path.hash() - == depthai::entity_paths::RGB_PINHOLE_CAMERA.hash() - { - top_right_spaces.push(space); - } else if space_path.hash() == depthai::entity_paths::MONO_CAM_3D.hash() { - bottom_left_spaces.push(space); + if space_path.hash() == depthai::entity_paths::COLOR_CAM_3D.hash() { + top_left_spaces.push(space); + } else if space_path.hash() + == depthai::entity_paths::RGB_PINHOLE_CAMERA.hash() + { + top_right_spaces.push(space); + } else if space_path.hash() == depthai::entity_paths::MONO_CAM_3D.hash() + { + bottom_left_spaces.push(space); + } else { + bottom_right_spaces.push(space); + } + }); + + let color_empty = top_left_spaces.is_empty() && top_right_spaces.is_empty(); + let mono_empty = + bottom_left_spaces.is_empty() && bottom_right_spaces.is_empty(); + let mut color_split = LayoutSplit::TopBottom( + LayoutSplit::Leaf(top_left_spaces.clone()).into(), + 0.5, + LayoutSplit::Leaf(top_right_spaces.clone()).into(), + ); + let mut mono_split = LayoutSplit::TopBottom( + LayoutSplit::Leaf(bottom_left_spaces.clone()).into(), + 0.5, + LayoutSplit::Leaf(bottom_right_spaces.clone()).into(), + ); + + if !color_empty && mono_empty { + color_split + } else if !color_empty && !mono_empty { + if top_left_spaces.is_empty() { + color_split = LayoutSplit::Leaf(top_right_spaces); + } else if top_right_spaces.is_empty() { + color_split = LayoutSplit::Leaf(top_left_spaces); + } + if bottom_left_spaces.is_empty() { + mono_split = LayoutSplit::Leaf(bottom_right_spaces); + } else if bottom_right_spaces.is_empty() { + mono_split = LayoutSplit::Leaf(bottom_left_spaces); + } + LayoutSplit::LeftRight(color_split.into(), 0.5, mono_split.into()) + } else if color_empty && !mono_empty { + mono_split } else { - bottom_right_spaces.push(space); - } - }); - - let color_empty = top_left_spaces.is_empty() && top_right_spaces.is_empty(); - let mono_empty = - bottom_left_spaces.is_empty() && bottom_right_spaces.is_empty(); - let mut color_split = LayoutSplit::TopBottom( - LayoutSplit::Leaf(top_left_spaces.clone()).into(), - 0.5, - LayoutSplit::Leaf(top_right_spaces.clone()).into(), - ); - let mut mono_split = LayoutSplit::TopBottom( - LayoutSplit::Leaf(bottom_left_spaces.clone()).into(), - 0.5, - LayoutSplit::Leaf(bottom_right_spaces.clone()).into(), - ); - - if !color_empty && mono_empty { - color_split - } else if !color_empty && !mono_empty { - if top_left_spaces.is_empty() { - color_split = LayoutSplit::Leaf(top_right_spaces); - } else if top_right_spaces.is_empty() { - color_split = LayoutSplit::Leaf(top_left_spaces); + LayoutSplit::Leaf(spaces) } - if bottom_left_spaces.is_empty() { - mono_split = LayoutSplit::Leaf(bottom_right_spaces); - } else if bottom_right_spaces.is_empty() { - mono_split = LayoutSplit::Leaf(bottom_left_spaces); - } - LayoutSplit::LeftRight(color_split.into(), 0.5, mono_split.into()) - } else if color_empty && !mono_empty { - mono_split - } else { - LayoutSplit::Leaf(spaces) } - } + .into(), + 0.7, + right_panel_split().into(), + ) } - .into(), - 0.7, - right_panel_split().into(), - ); + }; tree_from_split(&mut tree, NodeIndex::root(), &layout); } else { tree_from_split( @@ -515,16 +535,17 @@ pub(crate) fn default_tree_from_space_views( ), ); } - - // Always set the config tab as the active tab - let (config_node, config_tab) = tree - .find_tab( - tree.tabs() - .find(|tab| tab.space_view_id == CONFIG_SPACE_VIEW.id) - .unwrap(), // CONFIG_SPACE_VIEW is always present - ) - .unwrap(); - tree.set_active_tab(config_node, config_tab); + if !is_maximized { + // Always set the config tab as the active tab + let (config_node, config_tab) = tree + .find_tab( + tree.tabs() + .find(|tab| tab.space_view_id == CONFIG_SPACE_VIEW.id) + .unwrap(), // CONFIG_SPACE_VIEW is always present + ) + .unwrap(); + tree.set_active_tab(config_node, config_tab); + } tree } diff --git a/crates/re_viewer/src/ui/device_settings_panel.rs b/crates/re_viewer/src/ui/device_settings_panel.rs index 19cd0b1e953b..a90be8c24894 100644 --- a/crates/re_viewer/src/ui/device_settings_panel.rs +++ b/crates/re_viewer/src/ui/device_settings_panel.rs @@ -14,86 +14,154 @@ impl DeviceSettingsPanel { pub fn show_panel(&mut self, ctx: &mut ViewerContext<'_>, ui: &mut egui::Ui) { let mut available_devices = ctx.depthai_state.get_devices(); let currently_selected_device = ctx.depthai_state.selected_device.clone(); - let mut combo_device: depthai::DeviceId = currently_selected_device.id; + let mut combo_device: depthai::DeviceId = currently_selected_device.clone().id; if !combo_device.is_empty() && available_devices.is_empty() { available_devices.push(combo_device.clone()); } + let mut show_device_config = true; egui::CentralPanel::default() .frame(egui::Frame { - inner_margin: egui::Margin::same(re_ui::ReUi::view_padding()), + inner_margin: egui::Margin::same(0.0), + fill: egui::Color32::WHITE, ..Default::default() }) .show_inside(ui, |ui| { - ui.add_sized( - [ui.available_width(), re_ui::ReUi::box_height()], - |ui: &mut egui::Ui| { - ui.horizontal(|ui| { - ctx.re_ui.labeled_combo_box( - ui, - "Device", - if !combo_device.is_empty() { - combo_device.clone() - } else { - "No device selected".to_owned() - }, - true, - |ui: &mut egui::Ui| { - if ui - .selectable_value( - &mut combo_device, - String::new(), - "No device", - ) - .changed() + egui::Frame { + inner_margin: egui::Margin::same(re_ui::ReUi::view_padding()), + ..Default::default() + } + .show(ui, |ui| { + ui.horizontal(|ui| { + // Use up all the horizontal space (color) + ui.add_sized( + [ui.available_width(), re_ui::ReUi::box_height() + 20.0], + |ui: &mut egui::Ui| { + ui.horizontal(|ui| { + ctx.re_ui.labeled_combo_box( + ui, + "Device", + if !combo_device.is_empty() { + combo_device.clone() + } else { + "No device selected".to_owned() + }, + true, + |ui: &mut egui::Ui| { + if ui + .selectable_value( + &mut combo_device, + String::new(), + "No device", + ) + .changed() + { + ctx.depthai_state.set_device(combo_device.clone()); + } + for device in available_devices { + if ui + .selectable_value( + &mut combo_device, + device.clone(), + device, + ) + .changed() + { + ctx.depthai_state + .set_device(combo_device.clone()); + } + } + }, + ); + if !currently_selected_device.id.is_empty() + && !ctx.depthai_state.is_update_in_progress() { - ctx.depthai_state.set_device(combo_device.clone()); - } - for device in available_devices { - if ui - .selectable_value( - &mut combo_device, - device.clone(), - device, - ) - .changed() - { - ctx.depthai_state.set_device(combo_device.clone()); - } - } - }, - ); - }) - .response - }, - ); + ui.add_sized( + [ + re_ui::ReUi::box_width() / 2.0, + re_ui::ReUi::box_height() + 1.0, + ], + |ui: &mut egui::Ui| { + ui.scope(|ui| { + let mut style = ui.style_mut().clone(); + // TODO(filip): Create a re_ui bound button with this style + let color = + ctx.re_ui.design_tokens.error_bg_color; + let hover_color = ctx + .re_ui + .design_tokens + .error_hover_bg_color; + style.visuals.widgets.hovered.bg_fill = + hover_color; + style.visuals.widgets.hovered.weak_bg_fill = + hover_color; + style.visuals.widgets.inactive.bg_fill = color; + style.visuals.widgets.inactive.weak_bg_fill = + color; + style + .visuals + .widgets + .inactive + .fg_stroke + .color = egui::Color32::WHITE; + style.visuals.widgets.hovered.fg_stroke.color = + egui::Color32::WHITE; - if ctx.depthai_state.applied_device_config.update_in_progress { - ui.add_sized([CONFIG_UI_WIDTH, 10.0], |ui: &mut egui::Ui| { - ui.with_layout(egui::Layout::left_to_right(egui::Align::Center), |ui| { - ui.add(egui::Spinner::new()) - }) - .response + ui.set_style(style); + if ui.button("Disconnect").clicked() { + ctx.depthai_state.set_device(String::new()); + } + }) + .response + }, + ); + } + }) + .response + }, + ); }); - return; + + let device_selected = !ctx.depthai_state.selected_device.id.is_empty(); + let pipeline_update_in_progress = ctx.depthai_state.is_update_in_progress(); + if pipeline_update_in_progress { + ui.add_sized([CONFIG_UI_WIDTH, 10.0], |ui: &mut egui::Ui| { + ui.with_layout(egui::Layout::left_to_right(egui::Align::Center), |ui| { + ui.label(if device_selected { + "Updating Pipeline" + } else { + "Selecting Device" + }); + ui.add(egui::Spinner::new()) + }) + .response + }); + show_device_config = false; + } + if !device_selected && !pipeline_update_in_progress { + ui.label("Select a device to continue..."); + show_device_config = false; + } + }); + + if show_device_config { + Self::device_configuration_ui(ctx, ui); } - Self::device_configuration_ui(ctx, ui); }); } fn device_configuration_ui(ctx: &mut ViewerContext<'_>, ui: &mut egui::Ui) { - let mut device_config = ctx.depthai_state.modified_device_config.config.clone(); + let mut device_config = ctx.depthai_state.modified_device_config.clone(); let primary_700 = ctx.re_ui.design_tokens.primary_700; - egui::ScrollArea::both() - .auto_shrink([false; 2]) - .show(ui, |ui| { - let mut style = ui.style_mut().clone(); - style.spacing.scroll_bar_inner_margin = 0.0; - ui.set_style(style); + + ctx.re_ui.styled_scrollbar( + ui, re_ui::ScrollAreaDirection::Vertical, + [false; 2], + |ui| { egui::Frame { - fill: ctx.re_ui.design_tokens.gray_50, - inner_margin: egui::Margin::symmetric(30.0, 21.0), - ..Default::default() + fill: ctx.re_ui.design_tokens.gray_50, + inner_margin: egui::Margin::symmetric(30.0, 21.0), + ..Default::default() } .show(ui, |ui| { ui.horizontal(|ui| { @@ -114,11 +182,16 @@ impl DeviceSettingsPanel { .selected_device .supported_color_resolutions { - ui.selectable_value( - &mut device_config.color_camera.resolution, - *res, - format!("{res}"), - ); + let disabled = res == &depthai::ColorCameraResolution::THE_4_K || res == &depthai::ColorCameraResolution::THE_12_MP; + ui.add_enabled_ui(!disabled, |ui| { + ui.selectable_value( + &mut device_config.color_camera.resolution, + *res, + format!("{res}"), + ).on_disabled_hover_ui(|ui| { + ui.label(format!("{res} will be available in a future release!")); + }); + }); } }, ); @@ -136,83 +209,129 @@ impl DeviceSettingsPanel { }); }, ); - ui.collapsing( - egui::RichText::new("Left Mono Camera").color(primary_700), - |ui| { + let mut left_mono_config = device_config.left_camera.unwrap_or_else(|| depthai::MonoCameraConfig { + board_socket: depthai::CameraBoardSocket::LEFT, + ..Default::default() + }); + let mut right_mono_config = device_config.right_camera.unwrap_or_else(|| depthai::MonoCameraConfig { + board_socket: depthai::CameraBoardSocket::RIGHT, + ..Default::default() + }); + let has_left_mono = !ctx.depthai_state.selected_device.supported_left_mono_resolutions.is_empty(); + ui.add_enabled_ui(has_left_mono, |ui| { + egui::CollapsingHeader::new(egui::RichText::new("Left Mono Camera").color(primary_700)).default_open(true).open(if !has_left_mono { + Some(false) + } else {None}).show( + ui, |ui| { ui.vertical(|ui| { ui.set_width(CONFIG_UI_WIDTH); ctx.re_ui.labeled_combo_box( ui, "Resolution", - format!("{}", device_config.left_camera.resolution), + format!("{}", left_mono_config.resolution), false, |ui| { - for res in &ctx - .depthai_state - .selected_device - .supported_left_mono_resolutions - { - ui.selectable_value( - &mut device_config.left_camera.resolution, - *res, - format!("{res}"), - ); + let highest_res = ctx.depthai_state.selected_device.supported_left_mono_resolutions.iter().max().unwrap(); + for res in depthai::MonoCameraResolution::iter() { + if &res > highest_res { + continue; + } + if ui + .selectable_value( + &mut left_mono_config + .resolution, + res, + format!("{res}"), + ) + .changed() + { + right_mono_config.resolution = + res; + } } }, ); - ctx.re_ui.labeled_dragvalue( - ui, - "FPS", - &mut device_config.left_camera.fps, - 0..=120, - ); + if ctx + .re_ui + .labeled_dragvalue( + ui, + "FPS", + &mut left_mono_config.fps, + 0..=120, + ) + .changed() + { + right_mono_config.fps = + left_mono_config.fps; + } ctx.re_ui.labeled_checkbox( ui, "Stream", - &mut device_config.left_camera.stream_enabled, + &mut left_mono_config.stream_enabled, ); }) }, - ); - - ui.collapsing( - egui::RichText::new("Right Mono Camera").color(primary_700), - |ui| { + ).header_response.on_disabled_hover_ui(|ui| { + ui.label("Selected device doesn't have a left mono camera."); + }); + }); + let has_right_mono = !ctx.depthai_state.selected_device.supported_right_mono_resolutions.is_empty(); + ui.add_enabled_ui(has_right_mono, |ui| { + egui::CollapsingHeader::new(egui::RichText::new("Right Mono Camera").color(primary_700)).default_open(true).open(if !has_right_mono { + Some(false) + } else {None}).show( + ui, |ui| { ui.vertical(|ui| { ui.set_width(CONFIG_UI_WIDTH); ctx.re_ui.labeled_combo_box( ui, "Resolution", - format!("{}", device_config.right_camera.resolution), + format!("{}", right_mono_config.resolution), false, |ui| { - for res in &ctx - .depthai_state - .selected_device - .supported_right_mono_resolutions - { - ui.selectable_value( - &mut device_config.right_camera.resolution, - *res, - format!("{res}"), - ); + let highest_res = ctx.depthai_state.selected_device.supported_right_mono_resolutions.iter().max().unwrap(); + for res in depthai::MonoCameraResolution::iter() { + if &res > highest_res { + continue; + } + if ui + .selectable_value( + &mut right_mono_config + .resolution, + res, + format!("{res}"), + ) + .changed() + { + left_mono_config.resolution = + res; + } } }, ); - ctx.re_ui.labeled_dragvalue( - ui, - "FPS", - &mut device_config.right_camera.fps, - 0..=120, - ); + if ctx + .re_ui + .labeled_dragvalue( + ui, + "FPS", + &mut right_mono_config.fps, + 0..=120, + ) + .changed() + { + left_mono_config.fps = + right_mono_config.fps; + } ctx.re_ui.labeled_checkbox( ui, "Stream", - &mut device_config.right_camera.stream_enabled, + &mut right_mono_config.stream_enabled, ); }) - }, - ); + }).header_response.on_disabled_hover_ui(|ui| { + ui.label("Selected device doesn't have a right mono camera."); + }); + }); // This is a hack, I wanted AI settings at the bottom, but some depth settings names // are too long and it messes up the width of the ui layout somehow. @@ -246,9 +365,12 @@ impl DeviceSettingsPanel { depth.align = depthai::CameraBoardSocket::AUTO; } - ui.collapsing( - egui::RichText::new("Depth settings").color(primary_700), - |ui| { + + ui.add_enabled_ui(has_right_mono && has_left_mono, |ui| { + egui::CollapsingHeader::new(egui::RichText::new("Depth Settings").color(primary_700)).open(if !(has_right_mono && has_left_mono) { + Some(false) + } else {None}).show( + ui, |ui| { ui.vertical(|ui| { ui.set_width(CONFIG_UI_WIDTH); ctx.re_ui.labeled_checkbox( @@ -329,29 +451,40 @@ impl DeviceSettingsPanel { ); }); }, - ); + ).header_response.on_disabled_hover_ui(|ui| { + ui.label("Selected device doesn't support depth!"); + }); + }); + device_config.left_camera = Some(left_mono_config); + device_config.right_camera = Some(right_mono_config); device_config.depth = Some(depth); - ctx.depthai_state.modified_device_config.config = device_config.clone(); + ctx.depthai_state.modified_device_config = device_config.clone(); ui.vertical(|ui| { ui.horizontal(|ui| { - let only_runtime_configs_changed = - depthai::State::only_runtime_configs_changed( - &ctx.depthai_state.applied_device_config.config, - &device_config, - ); - let apply_enabled = !only_runtime_configs_changed - && device_config - != ctx.depthai_state.applied_device_config.config - && !ctx.depthai_state.selected_device.id.is_empty(); - if !apply_enabled && only_runtime_configs_changed { - ctx.depthai_state - .set_device_config(&mut device_config, true); - } - if ctx.depthai_state.selected_device.id.is_empty() { - ctx.depthai_state - .set_device_config(&mut device_config, false); - } + let apply_enabled = { + if let Some(applied_config) = &ctx.depthai_state.applied_device_config.config { + let only_runtime_configs_changed = + depthai::State::only_runtime_configs_changed( + applied_config, + &device_config, + ); + let apply_enabled = !only_runtime_configs_changed + && ctx.depthai_state.applied_device_config.config.is_some() + && device_config + != applied_config.clone() + && !ctx.depthai_state.selected_device.id.is_empty() && !ctx.depthai_state.is_update_in_progress(); + + if !apply_enabled && only_runtime_configs_changed { + ctx.depthai_state + .set_device_config(&mut device_config, true); + } + apply_enabled + } else { + !ctx.depthai_state.applied_device_config.update_in_progress + } + + }; ui.add_enabled_ui(apply_enabled, |ui| { ui.scope(|ui| { @@ -389,9 +522,11 @@ impl DeviceSettingsPanel { }); }); }); - ui.add_space(ui.available_width()); + ui.allocate_space(ui.available_size()); }); }); - }); + } + ); + // Set a more visible scroll bar color } } diff --git a/crates/re_viewer/src/ui/selection_panel.rs b/crates/re_viewer/src/ui/selection_panel.rs index 1b28d6785db5..da92463e2719 100644 --- a/crates/re_viewer/src/ui/selection_panel.rs +++ b/crates/re_viewer/src/ui/selection_panel.rs @@ -1,6 +1,4 @@ -use egui::{ - NumExt as _, -}; +use egui::NumExt as _; use re_data_store::{ query_latest_single, ColorMapper, Colormap, EditableAutoValue, EntityPath, EntityProperties, }; @@ -9,9 +7,7 @@ use re_log_types::{ TimeType, Transform, }; -use crate::{ - ui::view_spatial::SpatialNavigationMode, Item, UiVerbosity, ViewerContext, -}; +use crate::{ui::view_spatial::SpatialNavigationMode, Item, UiVerbosity, ViewerContext}; use super::{data_ui::DataUi, space_view::ViewState, Viewport}; @@ -24,9 +20,8 @@ pub(crate) struct SelectionPanel {} impl SelectionPanel { pub fn show_panel(ctx: &mut ViewerContext<'_>, ui: &mut egui::Ui, viewport: &mut Viewport) { - egui::ScrollArea::both() - .auto_shrink([true; 2]) - .show(ui, |ui| { + ctx.re_ui + .styled_scrollbar(ui, re_ui::ScrollAreaDirection::Both, [true; 2], |ui| { egui::Frame { inner_margin: egui::Margin::same(re_ui::ReUi::view_padding()), ..Default::default() @@ -34,7 +29,7 @@ impl SelectionPanel { .show(ui, |ui| { Self::contents(ui, ctx, viewport); }); - }); + }) } fn contents(ui: &mut egui::Ui, ctx: &mut ViewerContext<'_>, viewport: &mut Viewport) { @@ -616,4 +611,4 @@ fn backproject_radius_scale_ui(ui: &mut egui::Ui, property: &mut EditableAutoVal *property = EditableAutoValue::UserEdited(value); } ui.end_row(); -} \ No newline at end of file +} diff --git a/crates/re_viewer/src/ui/space_view_entity_picker.rs b/crates/re_viewer/src/ui/space_view_entity_picker.rs index 4129975f887b..e444fcd6ce9f 100644 --- a/crates/re_viewer/src/ui/space_view_entity_picker.rs +++ b/crates/re_viewer/src/ui/space_view_entity_picker.rs @@ -57,9 +57,14 @@ impl SpaceViewEntityPicker { .title_bar(false) .show(ui.ctx(), |ui| { title_bar(ctx.re_ui, ui, title, &mut open); - egui::ScrollArea::vertical().show(ui, |ui| { - add_entities_ui(ctx, ui, space_view); - }); + ctx.re_ui.styled_scrollbar( + ui, + re_ui::ScrollAreaDirection::Vertical, + [false; 2], + |ui| { + add_entities_ui(ctx, ui, space_view); + }, + ); }); // Any click outside causes the window to close. diff --git a/crates/re_viewer/src/ui/stats_panel.rs b/crates/re_viewer/src/ui/stats_panel.rs index e05199f5d8bf..0e1caf6aa80b 100644 --- a/crates/re_viewer/src/ui/stats_panel.rs +++ b/crates/re_viewer/src/ui/stats_panel.rs @@ -204,19 +204,20 @@ struct StatsTabs<'a, 'b> { impl<'a, 'b> StatsTabs<'a, 'b> { fn imu_ui(&mut self, ui: &mut egui::Ui) { - let imu_entity_path = &ImuData::entity_path(); - egui::ScrollArea::both().show(ui, |ui| { - egui::Frame { - inner_margin: egui::Margin::same(re_ui::ReUi::view_padding()), - ..Default::default() - } - .show(ui, |ui| { - let max_width = ui.available_width(); - for kind in ImuTabKind::iter() { - self.xyz_plot_ui(ui, kind, max_width); + self.ctx + .re_ui + .styled_scrollbar(ui, re_ui::ScrollAreaDirection::Both, [false; 2], |ui| { + egui::Frame { + inner_margin: egui::Margin::same(re_ui::ReUi::view_padding()), + ..Default::default() } + .show(ui, |ui| { + let max_width = ui.available_width(); + for kind in ImuTabKind::iter() { + self.xyz_plot_ui(ui, kind, max_width); + } + }); }); - }); } fn xlink_ui(&mut self, ui: &mut egui::Ui) { diff --git a/crates/re_viewer/src/ui/view_text/ui.rs b/crates/re_viewer/src/ui/view_text/ui.rs index 3186edbf35a2..589022e5cb66 100644 --- a/crates/re_viewer/src/ui/view_text/ui.rs +++ b/crates/re_viewer/src/ui/view_text/ui.rs @@ -105,10 +105,15 @@ pub(crate) fn view_text( state.latest_time = time; ui.with_layout(egui::Layout::top_down(egui::Align::Center), |ui| { - egui::ScrollArea::horizontal().show(ui, |ui| { - crate::profile_scope!("render table"); - table_ui(ctx, ui, state, &scene.text_entries, scroll_to_row); - }) + ctx.re_ui.styled_scrollbar( + ui, + re_ui::ScrollAreaDirection::Horizontal, + [false; 2], + |ui| { + crate::profile_scope!("render table"); + table_ui(ctx, ui, state, &scene.text_entries, scroll_to_row); + }, + ); }) .response } diff --git a/crates/re_viewer/src/ui/viewport.rs b/crates/re_viewer/src/ui/viewport.rs index 7f2508169b2c..1c2aa7691a94 100644 --- a/crates/re_viewer/src/ui/viewport.rs +++ b/crates/re_viewer/src/ui/viewport.rs @@ -338,22 +338,23 @@ impl Viewport { .entry(visible_space_views.clone()) .or_insert_with(|| { // TODO(filip): Continue working on this smart layout updater - if let Some(previous_frame_tree) = &self.previous_frame_tree { - let mut tree = previous_frame_tree.clone(); - super::auto_layout::update_tree( - &mut tree, - &visible_space_views, - &self.space_views, - self.maximized.is_some(), - ); - tree - } else { - super::auto_layout::default_tree_from_space_views( - ui.available_size(), - &visible_space_views, - &self.space_views, - ) - } + // if let Some(previous_frame_tree) = &self.previous_frame_tree { + // let mut tree = previous_frame_tree.clone(); + // super::auto_layout::update_tree( + // &mut tree, + // &visible_space_views, + // &self.space_views, + // self.maximized.is_some(), + // ); + // tree + // } else { + super::auto_layout::default_tree_from_space_views( + ui.available_size(), + &visible_space_views, + &self.space_views, + self.maximized.is_some(), + ) + // } }) .clone(); self.previous_frame_tree = Some(tree.clone()); diff --git a/crates/re_viewer/src/viewer_analytics.rs b/crates/re_viewer/src/viewer_analytics.rs index 02727b590127..40a6e40cc73b 100644 --- a/crates/re_viewer/src/viewer_analytics.rs +++ b/crates/re_viewer/src/viewer_analytics.rs @@ -78,7 +78,7 @@ impl ViewerAnalytics { ) { use crate::AppEnvironment; let app_env_str = match app_env { - AppEnvironment::PythonSdk(_) => "python_sdk", + AppEnvironment::PythonSdk(..) => "python_sdk", AppEnvironment::RustSdk { .. } => "rust_sdk", AppEnvironment::RerunCli { .. } => "rerun_cli", AppEnvironment::Web => "web_viewer", @@ -108,7 +108,7 @@ impl ViewerAnalytics { } _ => {} } - if let AppEnvironment::PythonSdk(version) = app_env { + if let AppEnvironment::PythonSdk(version, _) = app_env { event = event.with_prop("python_version", version.to_string()); } @@ -151,7 +151,7 @@ impl ViewerAnalytics { let recording_source = match &rec_info.recording_source { RecordingSource::Unknown => "unknown".to_owned(), - RecordingSource::PythonSdk(_version) => "python_sdk".to_owned(), + RecordingSource::PythonSdk(_version, _) => "python_sdk".to_owned(), RecordingSource::RustSdk { .. } => "rust_sdk".to_owned(), RecordingSource::Other(other) => other.clone(), }; @@ -170,7 +170,7 @@ impl ViewerAnalytics { self.register("llvm_version", llvm_version.to_string()); self.deregister("python_version"); // can't be both! } - if let RecordingSource::PythonSdk(version) = &rec_info.recording_source { + if let RecordingSource::PythonSdk(version, _) = &rec_info.recording_source { self.register("python_version", version.to_string()); self.deregister("rust_version"); // can't be both! self.deregister("llvm_version"); // can't be both! diff --git a/crates/rerun/src/main.rs b/crates/rerun/src/main.rs index ad27f207dd56..0a80e45e384c 100644 --- a/crates/rerun/src/main.rs +++ b/crates/rerun/src/main.rs @@ -6,9 +6,14 @@ static GLOBAL: AccountingAllocator = #[tokio::main] async fn main() -> anyhow::Result { + println!("Running from CLI!!!"); re_log::setup_native_logging(); let build_info = re_build_info::build_info!(); - depthai_viewer::run(build_info, depthai_viewer::CallSource::Cli, std::env::args()) - .await - .map(std::process::ExitCode::from) + depthai_viewer::run( + build_info, + depthai_viewer::CallSource::Cli, + std::env::args(), + ) + .await + .map(std::process::ExitCode::from) } diff --git a/crates/rerun/src/run.rs b/crates/rerun/src/run.rs index a43d371ca96e..4ee3ee57be48 100644 --- a/crates/rerun/src/run.rs +++ b/crates/rerun/src/run.rs @@ -155,6 +155,8 @@ enum AnalyticsCommands { Config, } +type SysExePath = String; + /// Where are we calling [`run`] from? #[derive(Clone, Debug, PartialEq, Eq)] pub enum CallSource { @@ -162,13 +164,13 @@ pub enum CallSource { Cli, /// Called from the Rerun Python SDK. - Python(PythonVersion), + Python(PythonVersion, SysExePath), } #[cfg(feature = "native_viewer")] impl CallSource { fn is_python(&self) -> bool { - matches!(self, Self::Python(_)) + matches!(self, Self::Python(..)) } fn app_env(&self) -> re_viewer::AppEnvironment { @@ -177,8 +179,8 @@ impl CallSource { rustc_version: env!("RE_BUILD_RUSTC_VERSION").into(), llvm_version: env!("RE_BUILD_LLVM_VERSION").into(), }, - CallSource::Python(python_version) => { - re_viewer::AppEnvironment::PythonSdk(python_version.clone()) + CallSource::Python(python_version, sys_exe) => { + re_viewer::AppEnvironment::PythonSdk(python_version.clone(), sys_exe.clone()) } } } diff --git a/justfile b/justfile index c7301a1f1db7..8816178a46c9 100644 --- a/justfile +++ b/justfile @@ -101,8 +101,8 @@ py-test: python -m pytest rerun_py/tests/unit/ # Serve the python docs locally -py-docs-serve: - mkdocs serve -f rerun_py/mkdocs.yml -w rerun_py +# py-docs-serve: +# mkdocs serve -f rerun_py/mkdocs.yml -w rerun_py ### Rust diff --git a/rerun_py/Cargo.toml b/rerun_py/Cargo.toml index 0493512b242f..d6334c3b4f39 100644 --- a/rerun_py/Cargo.toml +++ b/rerun_py/Cargo.toml @@ -8,7 +8,7 @@ version.workspace = true [lib] crate-type = ["cdylib"] -name = "rerun_bindings" # name of the .so library that the Python module will import +name = "depthai_viewer_bindings" # name of the .so library that the Python module will import [features] @@ -79,4 +79,4 @@ pyo3-build-config = "0.18.0" [package.metadata.maturin] -name = "rerun_bindings" +name = "depthai_viewer_bindings" diff --git a/rerun_py/README.md b/rerun_py/README.md index 1a0a09166208..91cdfc96ff53 100644 --- a/rerun_py/README.md +++ b/rerun_py/README.md @@ -1,63 +1,23 @@ -# The Rerun Python Log SDK +# Depthai Viewer -Rerun is an SDK for logging computer vision and robotics data paired with a visualizer for exploring that data over time. -It lets you debug and understand the internal state and data of your systems with minimal code. +![Screenshot from 2023-05-20 00-22-36](https://github.com/luxonis/depthai-viewer/assets/59307111/605bdf38-1bb4-416d-9643-0da1a511d58e) -

- Rerun Viewer -

## Install ```sh -pip3 install depthai-viewer +python3 -m pip install depthai-viewer ``` -ℹ️ Note: -The Python module is called `rerun`, while the package published on PyPI is `depthai-viewer`. - -## Example - -```py -import rerun as rr -import numpy as np - -viewer.spawn() - -positions = np.vstack([xyz.ravel() for xyz in np.mgrid[3 * [slice(-5, 5, 10j)]]]).T -colors = np.vstack([rgb.ravel() for rgb in np.mgrid[3 * [slice(0, 255, 10j)]]]).astype(np.uint8).T - -viewer.log_points("my_points", positions=positions, colors=colors) -``` - -## Resources - -- [Quick start](https://www.rerun.io/docs/getting-started/python) -- [Python API docs](https://ref.rerun.io/docs/python) -- [Tutorial](https://www.rerun.io/docs/getting-started/logging-python) -- [Examples on GitHub](https://github.com/rerun-io/rerun/tree/latest/examples/python) -- [Troubleshooting](https://www.rerun.io/docs/getting-started/troubleshooting) -- [Discord Server](https://discord.com/invite/Gcm8BbTaAj) - -## Logging and viewing in different processes - -You can run the viewer and logger in different processes. - -In one terminal, start up a viewer with a server that the SDK can connect to: - -```sh -python3 -m rerun -``` - -In a second terminal, run the example with the `--connect` option: +## Run ```sh -python3 examples/python/car/main.py --connect +depthai-viewer +# OR +python3 -m depthai_viewer ``` ---- - -# From Source +# Building From Source Setup: @@ -96,7 +56,7 @@ source venv/bin/activate.fish For ease of development you can build and install in "editable" mode. This means you can edit the `rerun` Python code without having to re-build and install to see changes. ```sh -# Build the SDK and install in develop mode into the virtualenv +# Build the depthai-viewer and install in develop mode into the virtualenv # Re-run this if the Rust code has changed! just py-build ``` @@ -110,8 +70,6 @@ just py-test # Run the linting checks just py-lint -# Run an example -python examples/python/car/main.py ``` ## Building an installable Python Wheel @@ -133,33 +91,9 @@ Now you can install `rerun` in any Python3 environment using: pip3 install target/wheels/*.whl ``` -## Viewing the docs locally - -The rerun python docs are generated using `mkdocs` - -Install the doc requirements: - -``` -pip install -r rerun_py/requirements-doc.txt -``` - -Serve the docs: - -```sh -mkdocs serve -f rerun_py/mkdocs.yml -w rerun_py -``` - -or - -```sh -just py-docs-serve -``` - -For information on how the docs system works, see: [docs/docs.md](docs/docs.md) - ## Troubleshooting -You can run with `RUST_LOG=debug` to get more output out of the rerun SDK. +You can run with `RUST_LOG=debug` to get more output out of the depthai-viewer. If you are using an Apple-silicon Mac, make sure `rustc -vV` outputs `host: aarch64-apple-darwin`. If not, this should fix it: diff --git a/rerun_py/depthai_viewer/__init__.py b/rerun_py/depthai_viewer/__init__.py index af379c212ee7..363e19e458ad 100644 --- a/rerun_py/depthai_viewer/__init__.py +++ b/rerun_py/depthai_viewer/__init__.py @@ -1,24 +1,560 @@ -""" -A shim necessary to make maturin dev builds work properly. +"""The Rerun Python SDK, which is a wrapper around the re_sdk crate.""" -Our maturin builds stick our package inside of a "rerun_sdk" folder -to avoid conflicting with the non-rerun "rerun" package. In released -builds, we include a rerun_sdk.pth file that makes things work properly, -but that doesn't work in dev builds where maturin generates its own -.pth file that points 1 level too high. +import atexit +from typing import Optional -When we encounter this file on import, we instead redirect to the -real rerun module by adding it to the path and then, and then -replacing our own module content with it. -""" -import pathlib -import sys +import depthai_viewer_bindings as bindings # type: ignore[attr-defined] -real_path = pathlib.Path(__file__).parent.parent.joinpath("rerun_sdk").resolve() +from depthai_viewer import _backend +from depthai_viewer.log import log_cleared +from depthai_viewer.log.annotation import AnnotationInfo, ClassDescription, log_annotation_context +from depthai_viewer.log.arrow import log_arrow +from depthai_viewer.log.bounding_box import log_obb +from depthai_viewer.log.camera import log_pinhole +from depthai_viewer.log.extension_components import log_extension_components +from depthai_viewer.log.file import ImageFormat, MeshFormat, log_image_file, log_mesh_file +from depthai_viewer.log.image import log_depth_image, log_image, log_segmentation_image +from depthai_viewer.log.imu import log_imu +from depthai_viewer.log.lines import log_line_segments, log_line_strip, log_path +from depthai_viewer.log.mesh import log_mesh, log_meshes +from depthai_viewer.log.pipeline_graph import log_pipeline_graph +from depthai_viewer.log.points import log_point, log_points +from depthai_viewer.log.rects import RectFormat, log_rect, log_rects +from depthai_viewer.log.scalar import log_scalar +from depthai_viewer.log.tensor import log_tensor +from depthai_viewer.log.text import LoggingHandler, LogLevel, log_text_entry +from depthai_viewer.log.transform import log_rigid3, log_unknown_transform, log_view_coordinates +from depthai_viewer.log.xlink_stats import log_xlink_stats +from depthai_viewer.recording import MemoryRecording +from depthai_viewer.script_helpers import script_add_args, script_setup, script_teardown -print(f"DEV ENVIRONMENT DETECTED! Re-importing rerun from: {real_path}", file=sys.stderr) +__all__ = [ + "AnnotationInfo", + "ClassDescription", + "LoggingHandler", + "bindings", + "components", + "inline_show", + "ImageFormat", + "log_annotation_context", + "log_arrow", + "log_cleared", + "log_depth_image", + "log_extension_components", + "log_image_file", + "log_image", + "log_pipeline_graph", + "log_line_segments", + "log_line_strip", + "log_mesh_file", + "log_mesh", + "log_meshes", + "log_obb", + "log_path", + "log_pinhole", + "log_point", + "log_points", + "log_rect", + "log_rects", + "log_rigid3", + "log_scalar", + "log_segmentation_image", + "log_tensor", + "log_text_entry", + "log_unknown_transform", + "log_view_coordinates", + "notebook", + "LogLevel", + "MeshFormat", + "RectFormat", + "script_add_args", + "script_setup", + "script_teardown", + "log_imu", + "log_xlink_stats", + "_backend", +] -sys.path.insert(0, str(real_path)) -del sys.modules["depthai_viewer"] -sys.modules["depthai_viewer"] = __import__("depthai_viewer") +# If `True`, we raise exceptions on use error (wrong parameter types etc). +# If `False` we catch all errors and log a warning instead. +_strict_mode = False + + +def rerun_shutdown() -> None: + bindings.shutdown() + + +atexit.register(rerun_shutdown) + + +def unregister_shutdown() -> None: + atexit.unregister(rerun_shutdown) + + +# ----------------------------------------------------------------------------- + + +def get_recording_id() -> str: + """ + Get the recording ID that this process is logging to, as a UUIDv4. + + The default recording_id is based on `multiprocessing.current_process().authkey` + which means that all processes spawned with `multiprocessing` + will have the same default recording_id. + + If you are not using `multiprocessing` and still want several different Python + processes to log to the same Rerun instance (and be part of the same recording), + you will need to manually assign them all the same recording_id. + Any random UUIDv4 will work, or copy the recording id for the parent process. + + Returns + ------- + str + The recording ID that this process is logging to. + + """ + return str(bindings.get_recording_id()) + + +def set_recording_id(value: str) -> None: + """ + Set the recording ID that this process is logging to, as a UUIDv4. + + The default recording_id is based on `multiprocessing.current_process().authkey` + which means that all processes spawned with `multiprocessing` + will have the same default recording_id. + + If you are not using `multiprocessing` and still want several different Python + processes to log to the same Rerun instance (and be part of the same recording), + you will need to manually assign them all the same recording_id. + Any random UUIDv4 will work, or copy the recording id for the parent process. + + Parameters + ---------- + value : str + The recording ID to use for this process. + + """ + bindings.set_recording_id(value) + + +def init(application_id: str, spawn: bool = False, default_enabled: bool = True, strict: bool = False) -> None: + """ + Initialize the Rerun SDK with a user-chosen application id (name). + + Parameters + ---------- + application_id : str + Your Rerun recordings will be categorized by this application id, so + try to pick a unique one for each application that uses the Rerun SDK. + + For example, if you have one application doing object detection + and another doing camera calibration, you could have + `rerun.init("object_detector")` and `rerun.init("calibrator")`. + spawn : bool + Spawn a Depthai Viewer and stream logging data to it. + Short for calling `spawn` separately. + If you don't call this, log events will be buffered indefinitely until + you call either `connect`, `show`, or `save` + default_enabled + Should Rerun logging be on by default? + Can overridden with the RERUN env-var, e.g. `RERUN=on` or `RERUN=off`. + strict + If `True`, an exceptions is raised on use error (wrong parameter types etc). + If `False`, errors are logged as warnings instead. + + """ + + _strict_mode = strict + application_path = None + + # NOTE: It'd be even nicer to do such thing on the Rust-side so that this little trick would + # only need to be written once and just work for all languages out of the box... unfortunately + # we lose most of the details of the python part of the backtrace once we go over the bridge. + # + # Still, better than nothing! + try: + import inspect + import pathlib + + # We're trying to grab the filesystem path of the example script that called `init()`. + # The tricky part is that we don't know how many layers are between this script and the + # original caller, so we have to walk the stack and look for anything that might look like + # an official Rerun example. + + MAX_FRAMES = 10 # try the first 10 frames, should be more than enough + FRAME_FILENAME_INDEX = 1 # `FrameInfo` tuple has `filename` at index 1 + + stack = inspect.stack() + for frame in stack[:MAX_FRAMES]: + filename = frame[FRAME_FILENAME_INDEX] + # normalize before comparison! + path = pathlib.Path(str(filename)).resolve() + if "rerun/examples" in str(path): + application_path = path + except Exception: + pass + + bindings.init( + application_id=application_id, + application_path=application_path, + default_enabled=default_enabled, + ) + + if spawn: + _spawn() + + +def is_enabled() -> bool: + """ + Is the Rerun SDK enabled. + + If false, all calls to the rerun library are ignored. + + The default can be set in [`rerun.init`][], but is otherwise `True`. + + This can be controlled with the environment variable `RERUN`, + (e.g. `RERUN=on` or `RERUN=off`) and with [`set_enabled`]. + + """ + return bindings.is_enabled() # type: ignore[no-any-return] + + +def set_enabled(enabled: bool) -> None: + """ + Enable or disable logging. + + If false, all calls to the rerun library are ignored. The default is `True`. + + This is a global setting that affects all threads. + + By default logging is enabled, but can be controlled with the environment variable `RERUN`, + (e.g. `RERUN=on` or `RERUN=off`). + + The default can be set in [`rerun.init`][]. + + Parameters + ---------- + enabled : bool + Whether to enable or disable logging. + + """ + bindings.set_enabled(enabled) + + +def strict_mode() -> bool: + """ + Strict mode enabled. + + In strict mode, incorrect use of the Rerun API (wrong parameter types etc.) + will result in exception being raised. + When strict mode is on, such problems are instead logged as warnings. + + The default is OFF. + """ + + return _strict_mode + + +def set_strict_mode(strict_mode: bool) -> None: + """ + Turn strict mode on/off. + + In strict mode, incorrect use of the Rerun API (wrong parameter types etc.) + will result in exception being raised. + When strict mode is off, such problems are instead logged as warnings. + + The default is OFF. + """ + + _strict_mode = strict_mode + + +def connect(addr: Optional[str] = None) -> None: + """ + Connect to a remote Depthai Viewer on the given ip:port. + + Requires that you first start a Depthai Viewer, e.g. with 'python -m rerun' + + This function returns immediately. + + Parameters + ---------- + addr + The ip:port to connect to + + """ + + if not bindings.is_enabled(): + print("Rerun is disabled - connect() call ignored") + return + + bindings.connect(addr) + + +_connect = connect # we need this because Python scoping is horrible + + +def spawn(port: int = 9876, connect: bool = True) -> None: + """ + Spawn a Depthai Viewer, listening on the given port. + + This is often the easiest and best way to use Rerun. + Just call this once at the start of your program. + + You can also call [rerun.init][] with a `spawn=True` argument. + + Parameters + ---------- + port : int + The port to listen on. + connect + also connect to the viewer and stream logging data to it. + + """ + + if not bindings.is_enabled(): + print("Rerun is disabled - spawn() call ignored") + return + + import os + import subprocess + import sys + from time import sleep + + # Let the spawned rerun process know it's just an app + new_env = os.environ.copy() + new_env["RERUN_APP_ONLY"] = "true" + + # sys.executable: the absolute path of the executable binary for the Python interpreter + python_executable = sys.executable + if python_executable is None: + python_executable = "python3" + + # start_new_session=True ensures the spawned process does NOT die when + # we hit ctrl-c in the terminal running the parent Python process. + subprocess.Popen( + [python_executable, "-m", "depthai_viewer", "--port", str(port)], env=new_env, start_new_session=True + ) + + # TODO(emilk): figure out a way to postpone connecting until the rerun viewer is listening. + # For example, wait until it prints "Hosting a SDK server over TCP at …" + sleep(0.5) # almost as good as waiting the correct amount of time + + if connect: + _connect(f"127.0.0.1:{port}") + + +_spawn = spawn # we need this because Python scoping is horrible + + +def serve(open_browser: bool = True, web_port: Optional[int] = None, ws_port: Optional[int] = None) -> None: + """ + Serve log-data over WebSockets and serve a Rerun web viewer over HTTP. + + You can connect to this server using `python -m rerun`. + + WARNING: This is an experimental feature. + + This function returns immediately. + + Parameters + ---------- + open_browser + Open the default browser to the viewer. + web_port: + The port to serve the web viewer on (defaults to 9090). + ws_port: + The port to serve the WebSocket server on (defaults to 9877) + """ + + if not bindings.is_enabled(): + print("Rerun is disabled - serve() call ignored") + return + + bindings.serve(open_browser, web_port, ws_port) + + +def start_web_viewer_server(port: int = 0) -> None: + """ + Start an HTTP server that hosts the rerun web viewer. + + This only provides the web-server that makes the viewer available and + does not otherwise provide a rerun websocket server or facilitate any routing of + data. + + This is generally only necessary for application such as running a jupyter notebook + in a context where app.rerun.io is unavailable, or does not having the matching + resources for your build (such as when running from source.) + + Parameters + ---------- + port + Port to serve assets on. Defaults to 0 (random port). + """ + + if not bindings.is_enabled(): + print("Rerun is disabled - self_host_assets() call ignored") + return + + bindings.start_web_viewer_server(port) + + +def disconnect() -> None: + """ + Closes all TCP connections, servers, and files. + + Closes all TCP connections, servers, and files that have been opened with + [`rerun.connect`], [`rerun.serve`], [`rerun.save`] or [`rerun.spawn`]. + """ + + bindings.disconnect() + + +def save(path: str) -> None: + """ + Stream all log-data to a file. + + Parameters + ---------- + path : str + The path to save the data to. + + """ + + if not bindings.is_enabled(): + print("Rerun is disabled - save() call ignored") + return + + bindings.save(path) + + +def memory_recording() -> MemoryRecording: + """ + Streams all log-data to a memory buffer. + + This can be used to display the RRD to alternative formats such as html. + See: [rerun.MemoryRecording.as_html][]. + + Returns + ------- + MemoryRecording + A memory recording object that can be used to read the data. + """ + + return MemoryRecording(bindings.memory_recording()) + + +def set_time_sequence(timeline: str, sequence: Optional[int]) -> None: + """ + Set the current time for this thread as an integer sequence. + + Used for all subsequent logging on the same thread, + until the next call to `set_time_sequence`. + + For example: `set_time_sequence("frame_nr", frame_nr)`. + + You can remove a timeline again using `set_time_sequence("frame_nr", None)`. + + There is no requirement of monotonicity. You can move the time backwards if you like. + + Parameters + ---------- + timeline : str + The name of the timeline to set the time for. + sequence : int + The current time on the timeline in integer units. + + """ + + if not bindings.is_enabled(): + return + + bindings.set_time_sequence(timeline, sequence) + + +def set_time_seconds(timeline: str, seconds: Optional[float]) -> None: + """ + Set the current time for this thread in seconds. + + Used for all subsequent logging on the same thread, + until the next call to [`rerun.set_time_seconds`][] or [`rerun.set_time_nanos`][]. + + For example: `set_time_seconds("capture_time", seconds_since_unix_epoch)`. + + You can remove a timeline again using `set_time_seconds("capture_time", None)`. + + Very large values will automatically be interpreted as seconds since unix epoch (1970-01-01). + Small values (less than a few years) will be interpreted as relative + some unknown point in time, and will be shown as e.g. `+3.132s`. + + The bindings has a built-in time which is `log_time`, and is logged as seconds + since unix epoch. + + There is no requirement of monotonicity. You can move the time backwards if you like. + + Parameters + ---------- + timeline : str + The name of the timeline to set the time for. + seconds : float + The current time on the timeline in seconds. + + """ + + if not bindings.is_enabled(): + return + + bindings.set_time_seconds(timeline, seconds) + + +def set_time_nanos(timeline: str, nanos: Optional[int]) -> None: + """ + Set the current time for this thread. + + Used for all subsequent logging on the same thread, + until the next call to [`rerun.set_time_nanos`][] or [`rerun.set_time_seconds`][]. + + For example: `set_time_nanos("capture_time", nanos_since_unix_epoch)`. + + You can remove a timeline again using `set_time_nanos("capture_time", None)`. + + Very large values will automatically be interpreted as nanoseconds since unix epoch (1970-01-01). + Small values (less than a few years) will be interpreted as relative + some unknown point in time, and will be shown as e.g. `+3.132s`. + + The bindings has a built-in time which is `log_time`, and is logged as nanos since + unix epoch. + + There is no requirement of monotonicity. You can move the time backwards if you like. + + Parameters + ---------- + timeline : str + The name of the timeline to set the time for. + nanos : int + The current time on the timeline in nanoseconds. + + """ + + if not bindings.is_enabled(): + return + + bindings.set_time_nanos(timeline, nanos) + + +def reset_time() -> None: + """ + Clear all timeline information on this thread. + + This is the same as calling `set_time_*` with `None` for all of the active timelines. + + Used for all subsequent logging on the same thread, + until the next call to [`rerun.set_time_nanos`][] or [`rerun.set_time_seconds`][]. + """ + + if not bindings.is_enabled(): + return + + bindings.reset_time() diff --git a/rerun_py/rerun_sdk/depthai_viewer/__main__.py b/rerun_py/depthai_viewer/__main__.py similarity index 86% rename from rerun_py/rerun_sdk/depthai_viewer/__main__.py rename to rerun_py/depthai_viewer/__main__.py index 32f4965ff05e..c454feb6d507 100644 --- a/rerun_py/rerun_sdk/depthai_viewer/__main__.py +++ b/rerun_py/depthai_viewer/__main__.py @@ -8,7 +8,7 @@ def main() -> None: # We don't need to call shutdown in this case. Rust should be handling everything unregister_shutdown() - exit(bindings.main(sys.argv)) + exit(bindings.main(sys.argv, sys.executable)) if __name__ == "__main__": diff --git a/rerun_py/rerun_sdk/depthai_viewer/_backend/.gitignore b/rerun_py/depthai_viewer/_backend/.gitignore similarity index 100% rename from rerun_py/rerun_sdk/depthai_viewer/_backend/.gitignore rename to rerun_py/depthai_viewer/_backend/.gitignore diff --git a/rerun_py/rerun_sdk/depthai_viewer/_backend/README.md b/rerun_py/depthai_viewer/_backend/README.md similarity index 100% rename from rerun_py/rerun_sdk/depthai_viewer/_backend/README.md rename to rerun_py/depthai_viewer/_backend/README.md diff --git a/rerun_py/rerun_sdk/depthai_viewer/_backend/__init__.py b/rerun_py/depthai_viewer/_backend/__init__.py similarity index 100% rename from rerun_py/rerun_sdk/depthai_viewer/_backend/__init__.py rename to rerun_py/depthai_viewer/_backend/__init__.py diff --git a/rerun_py/rerun_sdk/depthai_viewer/_backend/classification_labels.py b/rerun_py/depthai_viewer/_backend/classification_labels.py similarity index 100% rename from rerun_py/rerun_sdk/depthai_viewer/_backend/classification_labels.py rename to rerun_py/depthai_viewer/_backend/classification_labels.py diff --git a/rerun_py/rerun_sdk/depthai_viewer/_backend/config_api.py b/rerun_py/depthai_viewer/_backend/config_api.py similarity index 98% rename from rerun_py/rerun_sdk/depthai_viewer/_backend/config_api.py rename to rerun_py/depthai_viewer/_backend/config_api.py index 105affef27ba..e19b09fc91c7 100644 --- a/rerun_py/rerun_sdk/depthai_viewer/_backend/config_api.py +++ b/rerun_py/depthai_viewer/_backend/config_api.py @@ -46,13 +46,16 @@ class MessageType: class ErrorAction(Enum): - NONE = None + NONE = "None" FULL_RESET = "FullReset" + def __str__(self) -> str: + return self.value + def error(message: str, action: ErrorAction) -> str: """Create an error message to send via ws.""" - return json.dumps({"type": MessageType.ERROR, "data": {"action": action, "message": message}}) + return json.dumps({"type": MessageType.ERROR, "data": {"action": str(action), "message": message}}) async def ws_api(websocket: WebSocketServerProtocol) -> None: diff --git a/rerun_py/rerun_sdk/depthai_viewer/_backend/depth.py b/rerun_py/depthai_viewer/_backend/depth.py similarity index 100% rename from rerun_py/rerun_sdk/depthai_viewer/_backend/depth.py rename to rerun_py/depthai_viewer/_backend/depth.py diff --git a/rerun_py/rerun_sdk/depthai_viewer/_backend/device_configuration.py b/rerun_py/depthai_viewer/_backend/device_configuration.py similarity index 95% rename from rerun_py/rerun_sdk/depthai_viewer/_backend/device_configuration.py rename to rerun_py/depthai_viewer/_backend/device_configuration.py index 0d2e9614335f..4a757a6a4874 100644 --- a/rerun_py/rerun_sdk/depthai_viewer/_backend/device_configuration.py +++ b/rerun_py/depthai_viewer/_backend/device_configuration.py @@ -153,8 +153,8 @@ class ImuConfiguration(BaseModel): # type: ignore[misc] class PipelineConfiguration(BaseModel): # type: ignore[misc] color_camera: ColorCameraConfiguration = ColorCameraConfiguration() - left_camera: MonoCameraConfiguration = MonoCameraConfiguration.create_left() - right_camera: MonoCameraConfiguration = MonoCameraConfiguration.create_right() + left_camera: Optional[MonoCameraConfiguration] = MonoCameraConfiguration.create_left() + right_camera: Optional[MonoCameraConfiguration] = MonoCameraConfiguration.create_right() depth: Optional[DepthConfiguration] = DepthConfiguration() ai_model: Optional[AiModelConfiguration] = AiModelConfiguration() imu: ImuConfiguration = ImuConfiguration() @@ -165,13 +165,13 @@ def to_json(self) -> Dict[str, Any]: def _fix_depthai_types(self, as_dict: Dict[str, Any]) -> Dict[str, Any]: """ATM Config.json_encoders doesn't work, so we manually fix convert the depthai types to strings here.""" - if as_dict.get("color_camera"): + if as_dict.get("color_camera", None): as_dict["color_camera"] = self._fix_camera(as_dict["color_camera"]) - if as_dict.get("left_camera"): + if as_dict.get("left_camera", None): as_dict["left_camera"] = self._fix_camera(as_dict["left_camera"]) - if as_dict.get("right_camera"): + if as_dict.get("right_camera", None): as_dict["right_camera"] = self._fix_camera(as_dict["right_camera"]) - if as_dict.get("depth"): + if as_dict.get("depth", None): as_dict["depth"] = self._fix_depth(as_dict["depth"]) return as_dict diff --git a/rerun_py/rerun_sdk/depthai_viewer/_backend/main.py b/rerun_py/depthai_viewer/_backend/main.py similarity index 90% rename from rerun_py/rerun_sdk/depthai_viewer/_backend/main.py rename to rerun_py/depthai_viewer/_backend/main.py index 727c4af5b10c..ee4b14639b01 100644 --- a/rerun_py/rerun_sdk/depthai_viewer/_backend/main.py +++ b/rerun_py/depthai_viewer/_backend/main.py @@ -35,7 +35,6 @@ mono_wh_to_enum = { (640, 400): dai.MonoCameraProperties.SensorResolution.THE_400_P, - (640, 480): dai.MonoCameraProperties.SensorResolution.THE_480_P, (1280, 720): dai.MonoCameraProperties.SensorResolution.THE_720_P, (1280, 800): dai.MonoCameraProperties.SensorResolution.THE_800_P, (1920, 1200): dai.MonoCameraProperties.SensorResolution.THE_1200_P, @@ -44,7 +43,7 @@ class SelectedDevice: id: str - intrinsic_matrix: Dict[Tuple[int, int], NDArray[np.float32]] = {} + intrinsic_matrix: Dict[Tuple[dai.CameraBoardSocket, int, int], NDArray[np.float32]] = {} calibration_data: Optional[dai.CalibrationHandler] = None use_encoding: bool = False _time_of_last_xlink_update: int = 0 @@ -63,16 +62,16 @@ def __init__(self, device_id: str): self.oak_cam = OakCamera(self.id) print("Oak cam: ", self.oak_cam) - def get_intrinsic_matrix(self, width: int, height: int) -> NDArray[np.float32]: - if self.intrinsic_matrix.get((width, height)) is not None: - return self.intrinsic_matrix.get((width, height)) # type: ignore[return-value] + def get_intrinsic_matrix(self, board_socket: dai.CameraBoardSocket, width: int, height: int) -> NDArray[np.float32]: + if self.intrinsic_matrix.get((board_socket, width, height)) is not None: + return self.intrinsic_matrix.get((board_socket, width, height)) # type: ignore[return-value] if self.calibration_data is None: raise Exception("Missing calibration data!") M_right = self.calibration_data.getCameraIntrinsics( # type: ignore[union-attr] - dai.CameraBoardSocket.RIGHT, dai.Size2f(width, height) + board_socket, dai.Size2f(width, height) ) - self.intrinsic_matrix[(width, height)] = np.array(M_right).reshape(3, 3) - return self.intrinsic_matrix[(width, height)] + self.intrinsic_matrix[(board_socket, width, height)] = np.array(M_right).reshape(3, 3) + return self.intrinsic_matrix[(board_socket, width, height)] def get_device_properties(self) -> Dict[str, Any]: dai_props = self.oak_cam.device.getConnectedCameraFeatures() @@ -114,18 +113,18 @@ def get_device_properties(self) -> Dict[str, Any]: ) return device_properties - def update_pipeline( - self, config: PipelineConfiguration, runtime_only: bool, callbacks: "SdkCallbacks" - ) -> Tuple[bool, Dict[str, str]]: + def close_oak_cam(self) -> None: if self.oak_cam.running(): - if runtime_only: - if config.depth is not None: - return True, self._stereo.control.send_controls(config.depth.to_runtime_controls()) - return False, {"message": "Depth is not enabled, can't send runtime controls!"} - print("Cam running, closing...") - self.oak_cam.device.close() - self.oak_cam = None - # Check if the device is available, timeout after 10 seconds + self.oak_cam.device.__exit__(0, 0, 0) + + def reconnect_to_oak_cam(self) -> Tuple[bool, Dict[str, str]]: + """ + + Try to reconnect to the device with self.id. + + Timeout after 10 seconds. + """ + if self.oak_cam.device.isClosed(): timeout_start = time.time() while time.time() - timeout_start < 10: available_devices = [ @@ -135,11 +134,27 @@ def update_pipeline( break try: self.oak_cam = OakCamera(self.id) + return True, {"message": "Successfully reconnected to device"} except RuntimeError as e: print("Failed to create oak camera") print(e) self.oak_cam = None - return False, {"message": "Failed to create oak camera"} + return False, {"message": "Failed to create oak camera"} + + def update_pipeline( + self, config: PipelineConfiguration, runtime_only: bool, callbacks: "SdkCallbacks" + ) -> Tuple[bool, Dict[str, str]]: + if self.oak_cam.running(): + if runtime_only: + if config.depth is not None: + return True, self._stereo.control.send_controls(config.depth.to_runtime_controls()) + return False, {"message": "Depth is not enabled, can't send runtime controls!"} + print("Cam running, closing...") + self.close_oak_cam() + # Check if the device is available, timeout after 10 seconds + success, message = self.reconnect_to_oak_cam() + if not success: + return success, message self.use_encoding = self.oak_cam.device.getDeviceInfo().protocol == dai.XLinkProtocol.X_LINK_TCP_IP if self.use_encoding: @@ -190,11 +205,8 @@ def update_pipeline( median=config.depth.median, ) self.oak_cam.callback(self._stereo, callbacks.on_stereo_frame) - # if config.depth.pointcloud and config.depth.pointcloud.enabled: - # self._pc = self.oak_cam.create_pointcloud(stereo=self._stereo, colorize=self._color) - # self.oak_cam.callback(self._pc, callbacks.on_pointcloud) - if config.imu is not None: + if self.oak_cam.device.getConnectedIMU() != "NONE": print("Creating IMU") imu = self.oak_cam.create_imu() sensors = [ @@ -207,6 +219,8 @@ def update_pipeline( sensors, report_rate=config.imu.report_rate, batch_report_threshold=config.imu.batch_report_threshold ) self.oak_cam.callback(imu, callbacks.on_imu) + else: + print("Connected cam doesn't have IMU, skipping IMU creation...") if config.ai_model and config.ai_model.path: if config.ai_model.path == "age-gender-recognition-retail-0013": @@ -252,9 +266,9 @@ class DepthaiViewerBack: _device: Optional[SelectedDevice] = None # Queues for communicating with the API process - action_queue: Queue[Any] - result_queue: Queue[Any] - send_message_queue: Queue[Any] + action_queue: Queue # type: ignore[type-arg] + result_queue: Queue # type: ignore[type-arg] + send_message_queue: Queue # type: ignore[type-arg] # Sdk callbacks for handling data from the device and sending it to the frontend sdk_callbacks: SdkCallbacks @@ -286,9 +300,7 @@ def on_reset(self) -> Tuple[bool, Dict[str, str]]: print("Resetting...") if self._device: print("Closing device...") - self._device.oak_cam.device.close() - self._device.oak_cam.__exit__(None, None, None) - self._device.oak_cam = None + self._device.close_oak_cam() self.set_device(None) print("Done") return True, {"message": "Reset successful"} @@ -345,7 +357,7 @@ def run(self) -> None: except QueueEmptyException: pass - if self._device and self._device.oak_cam: + if self._device: self._device.update() if self._device.oak_cam.device.isClosed(): # TODO(filip): Typehint the messages properly diff --git a/rerun_py/rerun_sdk/depthai_viewer/_backend/py.typed b/rerun_py/depthai_viewer/_backend/py.typed similarity index 100% rename from rerun_py/rerun_sdk/depthai_viewer/_backend/py.typed rename to rerun_py/depthai_viewer/_backend/py.typed diff --git a/rerun_py/rerun_sdk/depthai_viewer/_backend/sdk_callbacks.py b/rerun_py/depthai_viewer/_backend/sdk_callbacks.py similarity index 89% rename from rerun_py/rerun_sdk/depthai_viewer/_backend/sdk_callbacks.py rename to rerun_py/depthai_viewer/_backend/sdk_callbacks.py index d5364301f76a..812de04af44e 100644 --- a/rerun_py/rerun_sdk/depthai_viewer/_backend/sdk_callbacks.py +++ b/rerun_py/depthai_viewer/_backend/sdk_callbacks.py @@ -40,7 +40,7 @@ class EntityPath: class SdkCallbacks: store: Store ahrs: Mahony - _get_camera_intrinsics: Callable[[int, int], NDArray[np.float32]] + _get_camera_intrinsics: Callable[[dai.CameraBoardSocket, int, int], NDArray[np.float32]] def __init__(self, store: Store): viewer.init("Depthai Viewer") @@ -49,7 +49,9 @@ def __init__(self, store: Store): self.ahrs = Mahony(frequency=100) self.ahrs.Q = np.array([1, 0, 0, 0], dtype=np.float64) - def set_camera_intrinsics_getter(self, camera_intrinsics_getter: Callable[[int, int], NDArray[np.float32]]) -> None: + def set_camera_intrinsics_getter( + self, camera_intrinsics_getter: Callable[[dai.CameraBoardSocket, int, int], NDArray[np.float32]] + ) -> None: self._get_camera_intrinsics = camera_intrinsics_getter def on_imu(self, packet: IMUPacket) -> None: @@ -72,7 +74,10 @@ def on_color_frame(self, frame: FramePacket) -> None: viewer.log_rigid3(EntityPath.RGB_CAMERA_TRANSFORM, child_from_parent=([0, 0, 0], self.ahrs.Q), xyz="RDF") h, w, _ = frame.frame.shape viewer.log_pinhole( - EntityPath.RGB_PINHOLE_CAMERA, child_from_parent=self._get_camera_intrinsics(w, h), width=w, height=h + EntityPath.RGB_PINHOLE_CAMERA, + child_from_parent=self._get_camera_intrinsics(dai.CameraBoardSocket.RGB, w, h), + width=w, + height=h, ) viewer.log_image(EntityPath.RGB_CAMERA_IMAGE, cv2.cvtColor(frame.frame, cv2.COLOR_BGR2RGB)) @@ -82,7 +87,10 @@ def on_left_frame(self, frame: FramePacket) -> None: h, w = frame.frame.shape viewer.log_rigid3(EntityPath.MONO_CAMERA_TRANSFORM, child_from_parent=([0, 0, 0], self.ahrs.Q), xyz="RDF") viewer.log_pinhole( - EntityPath.LEFT_PINHOLE_CAMERA, child_from_parent=self._get_camera_intrinsics(w, h), width=w, height=h + EntityPath.LEFT_PINHOLE_CAMERA, + child_from_parent=self._get_camera_intrinsics(dai.CameraBoardSocket.LEFT, w, h), + width=w, + height=h, ) viewer.log_image(EntityPath.LEFT_CAMERA_IMAGE, frame.frame) @@ -92,7 +100,10 @@ def on_right_frame(self, frame: FramePacket) -> None: h, w = frame.frame.shape viewer.log_rigid3(EntityPath.MONO_CAMERA_TRANSFORM, child_from_parent=([0, 0, 0], self.ahrs.Q), xyz="RDF") viewer.log_pinhole( - EntityPath.RIGHT_PINHOLE_CAMERA, child_from_parent=self._get_camera_intrinsics(w, h), width=w, height=h + EntityPath.RIGHT_PINHOLE_CAMERA, + child_from_parent=self._get_camera_intrinsics(dai.CameraBoardSocket.RIGHT, w, h), + width=w, + height=h, ) viewer.log_image(EntityPath.RIGHT_CAMERA_IMAGE, frame.frame) diff --git a/rerun_py/rerun_sdk/depthai_viewer/_backend/store.py b/rerun_py/depthai_viewer/_backend/store.py similarity index 100% rename from rerun_py/rerun_sdk/depthai_viewer/_backend/store.py rename to rerun_py/depthai_viewer/_backend/store.py diff --git a/rerun_py/rerun_sdk/depthai_viewer/_backend/topic.py b/rerun_py/depthai_viewer/_backend/topic.py similarity index 100% rename from rerun_py/rerun_sdk/depthai_viewer/_backend/topic.py rename to rerun_py/depthai_viewer/_backend/topic.py diff --git a/rerun_py/rerun_sdk/depthai_viewer/color_conversion.py b/rerun_py/depthai_viewer/color_conversion.py similarity index 100% rename from rerun_py/rerun_sdk/depthai_viewer/color_conversion.py rename to rerun_py/depthai_viewer/color_conversion.py diff --git a/rerun_py/rerun_sdk/depthai_viewer/components/__init__.py b/rerun_py/depthai_viewer/components/__init__.py similarity index 100% rename from rerun_py/rerun_sdk/depthai_viewer/components/__init__.py rename to rerun_py/depthai_viewer/components/__init__.py diff --git a/rerun_py/rerun_sdk/depthai_viewer/components/annotation.py b/rerun_py/depthai_viewer/components/annotation.py similarity index 100% rename from rerun_py/rerun_sdk/depthai_viewer/components/annotation.py rename to rerun_py/depthai_viewer/components/annotation.py diff --git a/rerun_py/rerun_sdk/depthai_viewer/components/arrow.py b/rerun_py/depthai_viewer/components/arrow.py similarity index 100% rename from rerun_py/rerun_sdk/depthai_viewer/components/arrow.py rename to rerun_py/depthai_viewer/components/arrow.py diff --git a/rerun_py/rerun_sdk/depthai_viewer/components/box.py b/rerun_py/depthai_viewer/components/box.py similarity index 100% rename from rerun_py/rerun_sdk/depthai_viewer/components/box.py rename to rerun_py/depthai_viewer/components/box.py diff --git a/rerun_py/rerun_sdk/depthai_viewer/components/color.py b/rerun_py/depthai_viewer/components/color.py similarity index 100% rename from rerun_py/rerun_sdk/depthai_viewer/components/color.py rename to rerun_py/depthai_viewer/components/color.py diff --git a/rerun_py/rerun_sdk/depthai_viewer/components/imu.py b/rerun_py/depthai_viewer/components/imu.py similarity index 100% rename from rerun_py/rerun_sdk/depthai_viewer/components/imu.py rename to rerun_py/depthai_viewer/components/imu.py diff --git a/rerun_py/rerun_sdk/depthai_viewer/components/instance.py b/rerun_py/depthai_viewer/components/instance.py similarity index 100% rename from rerun_py/rerun_sdk/depthai_viewer/components/instance.py rename to rerun_py/depthai_viewer/components/instance.py diff --git a/rerun_py/rerun_sdk/depthai_viewer/components/label.py b/rerun_py/depthai_viewer/components/label.py similarity index 100% rename from rerun_py/rerun_sdk/depthai_viewer/components/label.py rename to rerun_py/depthai_viewer/components/label.py diff --git a/rerun_py/rerun_sdk/depthai_viewer/components/linestrip.py b/rerun_py/depthai_viewer/components/linestrip.py similarity index 100% rename from rerun_py/rerun_sdk/depthai_viewer/components/linestrip.py rename to rerun_py/depthai_viewer/components/linestrip.py diff --git a/rerun_py/rerun_sdk/depthai_viewer/components/point.py b/rerun_py/depthai_viewer/components/point.py similarity index 100% rename from rerun_py/rerun_sdk/depthai_viewer/components/point.py rename to rerun_py/depthai_viewer/components/point.py diff --git a/rerun_py/rerun_sdk/depthai_viewer/components/quaternion.py b/rerun_py/depthai_viewer/components/quaternion.py similarity index 100% rename from rerun_py/rerun_sdk/depthai_viewer/components/quaternion.py rename to rerun_py/depthai_viewer/components/quaternion.py diff --git a/rerun_py/rerun_sdk/depthai_viewer/components/radius.py b/rerun_py/depthai_viewer/components/radius.py similarity index 100% rename from rerun_py/rerun_sdk/depthai_viewer/components/radius.py rename to rerun_py/depthai_viewer/components/radius.py diff --git a/rerun_py/rerun_sdk/depthai_viewer/components/rect2d.py b/rerun_py/depthai_viewer/components/rect2d.py similarity index 100% rename from rerun_py/rerun_sdk/depthai_viewer/components/rect2d.py rename to rerun_py/depthai_viewer/components/rect2d.py diff --git a/rerun_py/rerun_sdk/depthai_viewer/components/scalar.py b/rerun_py/depthai_viewer/components/scalar.py similarity index 100% rename from rerun_py/rerun_sdk/depthai_viewer/components/scalar.py rename to rerun_py/depthai_viewer/components/scalar.py diff --git a/rerun_py/rerun_sdk/depthai_viewer/components/tensor.py b/rerun_py/depthai_viewer/components/tensor.py similarity index 100% rename from rerun_py/rerun_sdk/depthai_viewer/components/tensor.py rename to rerun_py/depthai_viewer/components/tensor.py diff --git a/rerun_py/rerun_sdk/depthai_viewer/components/text_entry.py b/rerun_py/depthai_viewer/components/text_entry.py similarity index 100% rename from rerun_py/rerun_sdk/depthai_viewer/components/text_entry.py rename to rerun_py/depthai_viewer/components/text_entry.py diff --git a/rerun_py/rerun_sdk/depthai_viewer/components/vec.py b/rerun_py/depthai_viewer/components/vec.py similarity index 100% rename from rerun_py/rerun_sdk/depthai_viewer/components/vec.py rename to rerun_py/depthai_viewer/components/vec.py diff --git a/rerun_py/rerun_sdk/depthai_viewer/components/xlink_stats.py b/rerun_py/depthai_viewer/components/xlink_stats.py similarity index 100% rename from rerun_py/rerun_sdk/depthai_viewer/components/xlink_stats.py rename to rerun_py/depthai_viewer/components/xlink_stats.py diff --git a/rerun_py/rerun_sdk/depthai_viewer/log/__init__.py b/rerun_py/depthai_viewer/log/__init__.py similarity index 100% rename from rerun_py/rerun_sdk/depthai_viewer/log/__init__.py rename to rerun_py/depthai_viewer/log/__init__.py diff --git a/rerun_py/rerun_sdk/depthai_viewer/log/annotation.py b/rerun_py/depthai_viewer/log/annotation.py similarity index 100% rename from rerun_py/rerun_sdk/depthai_viewer/log/annotation.py rename to rerun_py/depthai_viewer/log/annotation.py diff --git a/rerun_py/rerun_sdk/depthai_viewer/log/arrow.py b/rerun_py/depthai_viewer/log/arrow.py similarity index 100% rename from rerun_py/rerun_sdk/depthai_viewer/log/arrow.py rename to rerun_py/depthai_viewer/log/arrow.py diff --git a/rerun_py/rerun_sdk/depthai_viewer/log/bounding_box.py b/rerun_py/depthai_viewer/log/bounding_box.py similarity index 100% rename from rerun_py/rerun_sdk/depthai_viewer/log/bounding_box.py rename to rerun_py/depthai_viewer/log/bounding_box.py diff --git a/rerun_py/rerun_sdk/depthai_viewer/log/camera.py b/rerun_py/depthai_viewer/log/camera.py similarity index 100% rename from rerun_py/rerun_sdk/depthai_viewer/log/camera.py rename to rerun_py/depthai_viewer/log/camera.py diff --git a/rerun_py/rerun_sdk/depthai_viewer/log/error_utils.py b/rerun_py/depthai_viewer/log/error_utils.py similarity index 100% rename from rerun_py/rerun_sdk/depthai_viewer/log/error_utils.py rename to rerun_py/depthai_viewer/log/error_utils.py diff --git a/rerun_py/rerun_sdk/depthai_viewer/log/extension_components.py b/rerun_py/depthai_viewer/log/extension_components.py similarity index 100% rename from rerun_py/rerun_sdk/depthai_viewer/log/extension_components.py rename to rerun_py/depthai_viewer/log/extension_components.py diff --git a/rerun_py/rerun_sdk/depthai_viewer/log/file.py b/rerun_py/depthai_viewer/log/file.py similarity index 100% rename from rerun_py/rerun_sdk/depthai_viewer/log/file.py rename to rerun_py/depthai_viewer/log/file.py diff --git a/rerun_py/rerun_sdk/depthai_viewer/log/image.py b/rerun_py/depthai_viewer/log/image.py similarity index 100% rename from rerun_py/rerun_sdk/depthai_viewer/log/image.py rename to rerun_py/depthai_viewer/log/image.py diff --git a/rerun_py/rerun_sdk/depthai_viewer/log/imu.py b/rerun_py/depthai_viewer/log/imu.py similarity index 100% rename from rerun_py/rerun_sdk/depthai_viewer/log/imu.py rename to rerun_py/depthai_viewer/log/imu.py diff --git a/rerun_py/rerun_sdk/depthai_viewer/log/lines.py b/rerun_py/depthai_viewer/log/lines.py similarity index 100% rename from rerun_py/rerun_sdk/depthai_viewer/log/lines.py rename to rerun_py/depthai_viewer/log/lines.py diff --git a/rerun_py/rerun_sdk/depthai_viewer/log/log_decorator.py b/rerun_py/depthai_viewer/log/log_decorator.py similarity index 100% rename from rerun_py/rerun_sdk/depthai_viewer/log/log_decorator.py rename to rerun_py/depthai_viewer/log/log_decorator.py diff --git a/rerun_py/rerun_sdk/depthai_viewer/log/mesh.py b/rerun_py/depthai_viewer/log/mesh.py similarity index 100% rename from rerun_py/rerun_sdk/depthai_viewer/log/mesh.py rename to rerun_py/depthai_viewer/log/mesh.py diff --git a/rerun_py/rerun_sdk/depthai_viewer/log/pipeline_graph.py b/rerun_py/depthai_viewer/log/pipeline_graph.py similarity index 100% rename from rerun_py/rerun_sdk/depthai_viewer/log/pipeline_graph.py rename to rerun_py/depthai_viewer/log/pipeline_graph.py diff --git a/rerun_py/rerun_sdk/depthai_viewer/log/points.py b/rerun_py/depthai_viewer/log/points.py similarity index 100% rename from rerun_py/rerun_sdk/depthai_viewer/log/points.py rename to rerun_py/depthai_viewer/log/points.py diff --git a/rerun_py/rerun_sdk/depthai_viewer/log/rects.py b/rerun_py/depthai_viewer/log/rects.py similarity index 100% rename from rerun_py/rerun_sdk/depthai_viewer/log/rects.py rename to rerun_py/depthai_viewer/log/rects.py diff --git a/rerun_py/rerun_sdk/depthai_viewer/log/scalar.py b/rerun_py/depthai_viewer/log/scalar.py similarity index 100% rename from rerun_py/rerun_sdk/depthai_viewer/log/scalar.py rename to rerun_py/depthai_viewer/log/scalar.py diff --git a/rerun_py/rerun_sdk/depthai_viewer/log/tensor.py b/rerun_py/depthai_viewer/log/tensor.py similarity index 100% rename from rerun_py/rerun_sdk/depthai_viewer/log/tensor.py rename to rerun_py/depthai_viewer/log/tensor.py diff --git a/rerun_py/rerun_sdk/depthai_viewer/log/text.py b/rerun_py/depthai_viewer/log/text.py similarity index 100% rename from rerun_py/rerun_sdk/depthai_viewer/log/text.py rename to rerun_py/depthai_viewer/log/text.py diff --git a/rerun_py/rerun_sdk/depthai_viewer/log/text_internal.py b/rerun_py/depthai_viewer/log/text_internal.py similarity index 100% rename from rerun_py/rerun_sdk/depthai_viewer/log/text_internal.py rename to rerun_py/depthai_viewer/log/text_internal.py diff --git a/rerun_py/rerun_sdk/depthai_viewer/log/transform.py b/rerun_py/depthai_viewer/log/transform.py similarity index 100% rename from rerun_py/rerun_sdk/depthai_viewer/log/transform.py rename to rerun_py/depthai_viewer/log/transform.py diff --git a/rerun_py/rerun_sdk/depthai_viewer/log/xlink_stats.py b/rerun_py/depthai_viewer/log/xlink_stats.py similarity index 100% rename from rerun_py/rerun_sdk/depthai_viewer/log/xlink_stats.py rename to rerun_py/depthai_viewer/log/xlink_stats.py diff --git a/rerun_py/rerun_sdk/depthai_viewer/py.typed b/rerun_py/depthai_viewer/py.typed similarity index 100% rename from rerun_py/rerun_sdk/depthai_viewer/py.typed rename to rerun_py/depthai_viewer/py.typed diff --git a/rerun_py/rerun_sdk/depthai_viewer/recording.py b/rerun_py/depthai_viewer/recording.py similarity index 100% rename from rerun_py/rerun_sdk/depthai_viewer/recording.py rename to rerun_py/depthai_viewer/recording.py diff --git a/rerun_py/rerun_sdk/depthai_viewer/script_helpers.py b/rerun_py/depthai_viewer/script_helpers.py similarity index 100% rename from rerun_py/rerun_sdk/depthai_viewer/script_helpers.py rename to rerun_py/depthai_viewer/script_helpers.py diff --git a/rerun_py/docs/gen_common_index.py b/rerun_py/docs/gen_common_index.py index 7b9326a9a834..5d14a1509ef7 100644 --- a/rerun_py/docs/gen_common_index.py +++ b/rerun_py/docs/gen_common_index.py @@ -123,7 +123,7 @@ class Section: ] # Virtual folder where we will generate the md files -root = Path(__file__).parent.parent.joinpath("rerun_sdk").resolve() +root = Path(__file__).parent.parent.joinpath("depthai_viewer").resolve() common_dir = Path("common") # We use griffe to access docstrings diff --git a/rerun_py/pyproject.toml b/rerun_py/pyproject.toml index bcf8d7114432..11fcfcfe33ea 100644 --- a/rerun_py/pyproject.toml +++ b/rerun_py/pyproject.toml @@ -10,7 +10,7 @@ dependencies = [ "setuptools", "ahrs", "depthai", # Atm python3 -m pip install --extra-index-url https://artifacts.luxonis.com/artifactory/luxonis-python-snapshot-local/ depthai==2.21.2.0.dev0+5004cc71950e6786feb36147b7919e146f4ef8da --force-reinstall # is required - "depthai-sdk>=1.10.0", + "depthai-sdk>=1.10.1", "websockets", "pydantic", ] @@ -22,8 +22,8 @@ classifiers = [ "Topic :: Scientific/Engineering :: Artificial Intelligence", "Topic :: Scientific/Engineering :: Visualization", ] -description = "The Rerun Logging SDK" -keywords = ["computer-vision", "logging", "rerun"] +description = "Depthai Viewer" +keywords = ["computer-vision", "logging", "depthai-viewer"] name = "depthai-viewer" requires-python = ">=3.8" @@ -97,10 +97,5 @@ select = [ ban-relative-imports = "all" [tool.maturin] -# We use a python package from inside the rerun_sdk folder to avoid conflicting -# with the other `rerun` pypi package. The rerun_sdk.pth adds this to the pythonpath -# which then allows `import rerun` to work as expected. -# See https://github.com/rerun-io/rerun/pull/1085 for more details -include = ["rerun_sdk.pth", "rerun_sdk/rerun_demo/colmap_fiat.rrd"] locked = true -python-packages = ["rerun_sdk/depthai_viewer", "rerun_sdk/rerun_demo"] +python-packages = ["depthai_viewer"] diff --git a/rerun_py/rerun_demo/__init__.py b/rerun_py/rerun_demo/__init__.py deleted file mode 100644 index 0726ded049ac..000000000000 --- a/rerun_py/rerun_demo/__init__.py +++ /dev/null @@ -1,24 +0,0 @@ -""" -A shim necessary to make maturin dev builds work properly. - -Our maturin builds stick our package inside of a "rerun_sdk" folder -to avoid conflicting with the non-rerun "rerun" package. In released -builds, we include a rerun_sdk.pth file that makes things work properly, -but that doesn't work in dev builds where maturin generates its own -.pth file that points 1 level too high. - -When we encounter this file on import, we instead redirect to the -real rerun module by adding it to the path and then, and then -replacing our own module content with it. -""" -import pathlib -import sys - -real_path = pathlib.Path(__file__).parent.parent.joinpath("rerun_sdk").resolve() - -print(f"DEV ENVIRONMENT DETECTED! Re-importing rerun_demo from: {real_path}", file=sys.stderr) - -sys.path.insert(0, str(real_path)) - -del sys.modules["rerun_demo"] -sys.modules["rerun_demo"] = __import__("rerun_demo") diff --git a/rerun_py/rerun_sdk.pth b/rerun_py/rerun_sdk.pth deleted file mode 100644 index bec31610050c..000000000000 --- a/rerun_py/rerun_sdk.pth +++ /dev/null @@ -1 +0,0 @@ -rerun_sdk diff --git a/rerun_py/rerun_sdk/depthai_viewer/__init__.py b/rerun_py/rerun_sdk/depthai_viewer/__init__.py deleted file mode 100644 index 1c1d30efd015..000000000000 --- a/rerun_py/rerun_sdk/depthai_viewer/__init__.py +++ /dev/null @@ -1,560 +0,0 @@ -"""The Rerun Python SDK, which is a wrapper around the re_sdk crate.""" - -import atexit -from typing import Optional - -import rerun_bindings as bindings # type: ignore[attr-defined] - -from depthai_viewer import _backend -from depthai_viewer.log import log_cleared -from depthai_viewer.log.annotation import AnnotationInfo, ClassDescription, log_annotation_context -from depthai_viewer.log.arrow import log_arrow -from depthai_viewer.log.bounding_box import log_obb -from depthai_viewer.log.camera import log_pinhole -from depthai_viewer.log.extension_components import log_extension_components -from depthai_viewer.log.file import ImageFormat, MeshFormat, log_image_file, log_mesh_file -from depthai_viewer.log.image import log_depth_image, log_image, log_segmentation_image -from depthai_viewer.log.imu import log_imu -from depthai_viewer.log.lines import log_line_segments, log_line_strip, log_path -from depthai_viewer.log.mesh import log_mesh, log_meshes -from depthai_viewer.log.pipeline_graph import log_pipeline_graph -from depthai_viewer.log.points import log_point, log_points -from depthai_viewer.log.rects import RectFormat, log_rect, log_rects -from depthai_viewer.log.scalar import log_scalar -from depthai_viewer.log.tensor import log_tensor -from depthai_viewer.log.text import LoggingHandler, LogLevel, log_text_entry -from depthai_viewer.log.transform import log_rigid3, log_unknown_transform, log_view_coordinates -from depthai_viewer.log.xlink_stats import log_xlink_stats -from depthai_viewer.recording import MemoryRecording -from depthai_viewer.script_helpers import script_add_args, script_setup, script_teardown - -__all__ = [ - "AnnotationInfo", - "ClassDescription", - "LoggingHandler", - "bindings", - "components", - "inline_show", - "ImageFormat", - "log_annotation_context", - "log_arrow", - "log_cleared", - "log_depth_image", - "log_extension_components", - "log_image_file", - "log_image", - "log_pipeline_graph", - "log_line_segments", - "log_line_strip", - "log_mesh_file", - "log_mesh", - "log_meshes", - "log_obb", - "log_path", - "log_pinhole", - "log_point", - "log_points", - "log_rect", - "log_rects", - "log_rigid3", - "log_scalar", - "log_segmentation_image", - "log_tensor", - "log_text_entry", - "log_unknown_transform", - "log_view_coordinates", - "notebook", - "LogLevel", - "MeshFormat", - "RectFormat", - "script_add_args", - "script_setup", - "script_teardown", - "log_imu", - "log_xlink_stats", - "_backend", -] - - -# If `True`, we raise exceptions on use error (wrong parameter types etc). -# If `False` we catch all errors and log a warning instead. -_strict_mode = False - - -def rerun_shutdown() -> None: - bindings.shutdown() - - -atexit.register(rerun_shutdown) - - -def unregister_shutdown() -> None: - atexit.unregister(rerun_shutdown) - - -# ----------------------------------------------------------------------------- - - -def get_recording_id() -> str: - """ - Get the recording ID that this process is logging to, as a UUIDv4. - - The default recording_id is based on `multiprocessing.current_process().authkey` - which means that all processes spawned with `multiprocessing` - will have the same default recording_id. - - If you are not using `multiprocessing` and still want several different Python - processes to log to the same Rerun instance (and be part of the same recording), - you will need to manually assign them all the same recording_id. - Any random UUIDv4 will work, or copy the recording id for the parent process. - - Returns - ------- - str - The recording ID that this process is logging to. - - """ - return str(bindings.get_recording_id()) - - -def set_recording_id(value: str) -> None: - """ - Set the recording ID that this process is logging to, as a UUIDv4. - - The default recording_id is based on `multiprocessing.current_process().authkey` - which means that all processes spawned with `multiprocessing` - will have the same default recording_id. - - If you are not using `multiprocessing` and still want several different Python - processes to log to the same Rerun instance (and be part of the same recording), - you will need to manually assign them all the same recording_id. - Any random UUIDv4 will work, or copy the recording id for the parent process. - - Parameters - ---------- - value : str - The recording ID to use for this process. - - """ - bindings.set_recording_id(value) - - -def init(application_id: str, spawn: bool = False, default_enabled: bool = True, strict: bool = False) -> None: - """ - Initialize the Rerun SDK with a user-chosen application id (name). - - Parameters - ---------- - application_id : str - Your Rerun recordings will be categorized by this application id, so - try to pick a unique one for each application that uses the Rerun SDK. - - For example, if you have one application doing object detection - and another doing camera calibration, you could have - `rerun.init("object_detector")` and `rerun.init("calibrator")`. - spawn : bool - Spawn a Depthai Viewer and stream logging data to it. - Short for calling `spawn` separately. - If you don't call this, log events will be buffered indefinitely until - you call either `connect`, `show`, or `save` - default_enabled - Should Rerun logging be on by default? - Can overridden with the RERUN env-var, e.g. `RERUN=on` or `RERUN=off`. - strict - If `True`, an exceptions is raised on use error (wrong parameter types etc). - If `False`, errors are logged as warnings instead. - - """ - - _strict_mode = strict - application_path = None - - # NOTE: It'd be even nicer to do such thing on the Rust-side so that this little trick would - # only need to be written once and just work for all languages out of the box... unfortunately - # we lose most of the details of the python part of the backtrace once we go over the bridge. - # - # Still, better than nothing! - try: - import inspect - import pathlib - - # We're trying to grab the filesystem path of the example script that called `init()`. - # The tricky part is that we don't know how many layers are between this script and the - # original caller, so we have to walk the stack and look for anything that might look like - # an official Rerun example. - - MAX_FRAMES = 10 # try the first 10 frames, should be more than enough - FRAME_FILENAME_INDEX = 1 # `FrameInfo` tuple has `filename` at index 1 - - stack = inspect.stack() - for frame in stack[:MAX_FRAMES]: - filename = frame[FRAME_FILENAME_INDEX] - # normalize before comparison! - path = pathlib.Path(str(filename)).resolve() - if "rerun/examples" in str(path): - application_path = path - except Exception: - pass - - bindings.init( - application_id=application_id, - application_path=application_path, - default_enabled=default_enabled, - ) - - if spawn: - _spawn() - - -def is_enabled() -> bool: - """ - Is the Rerun SDK enabled. - - If false, all calls to the rerun library are ignored. - - The default can be set in [`rerun.init`][], but is otherwise `True`. - - This can be controlled with the environment variable `RERUN`, - (e.g. `RERUN=on` or `RERUN=off`) and with [`set_enabled`]. - - """ - return bindings.is_enabled() # type: ignore[no-any-return] - - -def set_enabled(enabled: bool) -> None: - """ - Enable or disable logging. - - If false, all calls to the rerun library are ignored. The default is `True`. - - This is a global setting that affects all threads. - - By default logging is enabled, but can be controlled with the environment variable `RERUN`, - (e.g. `RERUN=on` or `RERUN=off`). - - The default can be set in [`rerun.init`][]. - - Parameters - ---------- - enabled : bool - Whether to enable or disable logging. - - """ - bindings.set_enabled(enabled) - - -def strict_mode() -> bool: - """ - Strict mode enabled. - - In strict mode, incorrect use of the Rerun API (wrong parameter types etc.) - will result in exception being raised. - When strict mode is on, such problems are instead logged as warnings. - - The default is OFF. - """ - - return _strict_mode - - -def set_strict_mode(strict_mode: bool) -> None: - """ - Turn strict mode on/off. - - In strict mode, incorrect use of the Rerun API (wrong parameter types etc.) - will result in exception being raised. - When strict mode is off, such problems are instead logged as warnings. - - The default is OFF. - """ - - _strict_mode = strict_mode - - -def connect(addr: Optional[str] = None) -> None: - """ - Connect to a remote Depthai Viewer on the given ip:port. - - Requires that you first start a Depthai Viewer, e.g. with 'python -m rerun' - - This function returns immediately. - - Parameters - ---------- - addr - The ip:port to connect to - - """ - - if not bindings.is_enabled(): - print("Rerun is disabled - connect() call ignored") - return - - bindings.connect(addr) - - -_connect = connect # we need this because Python scoping is horrible - - -def spawn(port: int = 9876, connect: bool = True) -> None: - """ - Spawn a Depthai Viewer, listening on the given port. - - This is often the easiest and best way to use Rerun. - Just call this once at the start of your program. - - You can also call [rerun.init][] with a `spawn=True` argument. - - Parameters - ---------- - port : int - The port to listen on. - connect - also connect to the viewer and stream logging data to it. - - """ - - if not bindings.is_enabled(): - print("Rerun is disabled - spawn() call ignored") - return - - import os - import subprocess - import sys - from time import sleep - - # Let the spawned rerun process know it's just an app - new_env = os.environ.copy() - new_env["RERUN_APP_ONLY"] = "true" - - # sys.executable: the absolute path of the executable binary for the Python interpreter - python_executable = sys.executable - if python_executable is None: - python_executable = "python3" - - # start_new_session=True ensures the spawned process does NOT die when - # we hit ctrl-c in the terminal running the parent Python process. - subprocess.Popen( - [python_executable, "-m", "depthai_viewer", "--port", str(port)], env=new_env, start_new_session=True - ) - - # TODO(emilk): figure out a way to postpone connecting until the rerun viewer is listening. - # For example, wait until it prints "Hosting a SDK server over TCP at …" - sleep(0.5) # almost as good as waiting the correct amount of time - - if connect: - _connect(f"127.0.0.1:{port}") - - -_spawn = spawn # we need this because Python scoping is horrible - - -def serve(open_browser: bool = True, web_port: Optional[int] = None, ws_port: Optional[int] = None) -> None: - """ - Serve log-data over WebSockets and serve a Rerun web viewer over HTTP. - - You can connect to this server using `python -m rerun`. - - WARNING: This is an experimental feature. - - This function returns immediately. - - Parameters - ---------- - open_browser - Open the default browser to the viewer. - web_port: - The port to serve the web viewer on (defaults to 9090). - ws_port: - The port to serve the WebSocket server on (defaults to 9877) - """ - - if not bindings.is_enabled(): - print("Rerun is disabled - serve() call ignored") - return - - bindings.serve(open_browser, web_port, ws_port) - - -def start_web_viewer_server(port: int = 0) -> None: - """ - Start an HTTP server that hosts the rerun web viewer. - - This only provides the web-server that makes the viewer available and - does not otherwise provide a rerun websocket server or facilitate any routing of - data. - - This is generally only necessary for application such as running a jupyter notebook - in a context where app.rerun.io is unavailable, or does not having the matching - resources for your build (such as when running from source.) - - Parameters - ---------- - port - Port to serve assets on. Defaults to 0 (random port). - """ - - if not bindings.is_enabled(): - print("Rerun is disabled - self_host_assets() call ignored") - return - - bindings.start_web_viewer_server(port) - - -def disconnect() -> None: - """ - Closes all TCP connections, servers, and files. - - Closes all TCP connections, servers, and files that have been opened with - [`rerun.connect`], [`rerun.serve`], [`rerun.save`] or [`rerun.spawn`]. - """ - - bindings.disconnect() - - -def save(path: str) -> None: - """ - Stream all log-data to a file. - - Parameters - ---------- - path : str - The path to save the data to. - - """ - - if not bindings.is_enabled(): - print("Rerun is disabled - save() call ignored") - return - - bindings.save(path) - - -def memory_recording() -> MemoryRecording: - """ - Streams all log-data to a memory buffer. - - This can be used to display the RRD to alternative formats such as html. - See: [rerun.MemoryRecording.as_html][]. - - Returns - ------- - MemoryRecording - A memory recording object that can be used to read the data. - """ - - return MemoryRecording(bindings.memory_recording()) - - -def set_time_sequence(timeline: str, sequence: Optional[int]) -> None: - """ - Set the current time for this thread as an integer sequence. - - Used for all subsequent logging on the same thread, - until the next call to `set_time_sequence`. - - For example: `set_time_sequence("frame_nr", frame_nr)`. - - You can remove a timeline again using `set_time_sequence("frame_nr", None)`. - - There is no requirement of monotonicity. You can move the time backwards if you like. - - Parameters - ---------- - timeline : str - The name of the timeline to set the time for. - sequence : int - The current time on the timeline in integer units. - - """ - - if not bindings.is_enabled(): - return - - bindings.set_time_sequence(timeline, sequence) - - -def set_time_seconds(timeline: str, seconds: Optional[float]) -> None: - """ - Set the current time for this thread in seconds. - - Used for all subsequent logging on the same thread, - until the next call to [`rerun.set_time_seconds`][] or [`rerun.set_time_nanos`][]. - - For example: `set_time_seconds("capture_time", seconds_since_unix_epoch)`. - - You can remove a timeline again using `set_time_seconds("capture_time", None)`. - - Very large values will automatically be interpreted as seconds since unix epoch (1970-01-01). - Small values (less than a few years) will be interpreted as relative - some unknown point in time, and will be shown as e.g. `+3.132s`. - - The bindings has a built-in time which is `log_time`, and is logged as seconds - since unix epoch. - - There is no requirement of monotonicity. You can move the time backwards if you like. - - Parameters - ---------- - timeline : str - The name of the timeline to set the time for. - seconds : float - The current time on the timeline in seconds. - - """ - - if not bindings.is_enabled(): - return - - bindings.set_time_seconds(timeline, seconds) - - -def set_time_nanos(timeline: str, nanos: Optional[int]) -> None: - """ - Set the current time for this thread. - - Used for all subsequent logging on the same thread, - until the next call to [`rerun.set_time_nanos`][] or [`rerun.set_time_seconds`][]. - - For example: `set_time_nanos("capture_time", nanos_since_unix_epoch)`. - - You can remove a timeline again using `set_time_nanos("capture_time", None)`. - - Very large values will automatically be interpreted as nanoseconds since unix epoch (1970-01-01). - Small values (less than a few years) will be interpreted as relative - some unknown point in time, and will be shown as e.g. `+3.132s`. - - The bindings has a built-in time which is `log_time`, and is logged as nanos since - unix epoch. - - There is no requirement of monotonicity. You can move the time backwards if you like. - - Parameters - ---------- - timeline : str - The name of the timeline to set the time for. - nanos : int - The current time on the timeline in nanoseconds. - - """ - - if not bindings.is_enabled(): - return - - bindings.set_time_nanos(timeline, nanos) - - -def reset_time() -> None: - """ - Clear all timeline information on this thread. - - This is the same as calling `set_time_*` with `None` for all of the active timelines. - - Used for all subsequent logging on the same thread, - until the next call to [`rerun.set_time_nanos`][] or [`rerun.set_time_seconds`][]. - """ - - if not bindings.is_enabled(): - return - - bindings.reset_time() diff --git a/rerun_py/rerun_sdk/depthai_viewer/_backend/pyproject.toml b/rerun_py/rerun_sdk/depthai_viewer/_backend/pyproject.toml deleted file mode 100644 index 49ad3a667c76..000000000000 --- a/rerun_py/rerun_sdk/depthai_viewer/_backend/pyproject.toml +++ /dev/null @@ -1,12 +0,0 @@ -[project] -name = "depthai_viewer_backend" -version = "0.0.1" -authors = [{ name = "Filip Jeretina", email = "filip.jeretina@luxonis.com" }] -description = "DepthAi Viewer Backend" - -[build-system] -requires = ["flit"] -build-backend = "flit.buildapi" - - -dependencies = ["depthai-sdk", "depthai", "numpy==1.24.*", "rerun_sdk", "ahrs", "websockets"] diff --git a/rerun_py/rerun_sdk/rerun_demo/.gitignore b/rerun_py/rerun_sdk/rerun_demo/.gitignore deleted file mode 100644 index 09173cc7af6c..000000000000 --- a/rerun_py/rerun_sdk/rerun_demo/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*.rrd diff --git a/rerun_py/rerun_sdk/rerun_demo/__init__.py b/rerun_py/rerun_sdk/rerun_demo/__init__.py deleted file mode 100644 index a5e3818cb708..000000000000 --- a/rerun_py/rerun_sdk/rerun_demo/__init__.py +++ /dev/null @@ -1,24 +0,0 @@ -""" -The `rerun_demo` package contains utilities for easily demonstrating rerun features. - -__main__.py is a program which tries to load a pre-baked .rrd file into the viewer - -The `data` module contains a collection of reference objects and helpers that can be -easily logged with a few lines of code, but still produce visually interesting -content. - -As an example, consider: -``` python -import depthai_viewer as viewer -from rerun_demo.data import color_grid - -viewer.init("log_points", True) - -viewer.log_points("my_points", color_grid.positions, colors=color_grid.colors) -``` - -Note that because this package is shipped with the depthai-viewer pypi package, it -cannot carry any dependencies beyond those of rerun itself. This generally limits -demos to only using the standard library and numpy for data generation. -""" -__all__ = ["data", "turbo", "util"] diff --git a/rerun_py/rerun_sdk/rerun_demo/__main__.py b/rerun_py/rerun_sdk/rerun_demo/__main__.py deleted file mode 100644 index 6b18f8a5d5d3..000000000000 --- a/rerun_py/rerun_sdk/rerun_demo/__main__.py +++ /dev/null @@ -1,89 +0,0 @@ -"""Demo program which loads an rrd file built into the package.""" - -import argparse -import pathlib -import sys - - -def run_cube(args: argparse.Namespace): - import math - - import depthai_viewer as viewer - import numpy as np - - from rerun_demo.data import build_color_grid - - viewer.script_setup(args, "Cube") - - STEPS = 100 - twists = math.pi * np.sin(np.linspace(0, math.tau, STEPS)) / 4 - for t in range(STEPS): - viewer.set_time_sequence("step", t) - cube = build_color_grid(10, 10, 10, twist=twists[t]) - viewer.log_points("cube", positions=cube.positions, colors=cube.colors, radii=0.5) - - viewer.script_teardown(args) - - -def run_colmap(args): - from depthai_viewer import bindings, unregister_shutdown # type: ignore[attr-defined] - - serve_opts = [] - - # TODO(https://github.com/rerun-io/rerun/issues/1924): The need to special-case - # this flag conversion is a bit awkward. - if args.connect or args.addr: - print("Connecting to external viewer is only supported with the --cube demo.", file=sys.stderr) - exit(1) - if args.save: - print("Saving an RRD file is only supported from the --cube demo.", file=sys.stderr) - exit(1) - if args.serve: - serve_opts.append("--web-viewer") - - # We don't need to call shutdown in this case. Rust should be handling everything - unregister_shutdown() - - rrd_file = pathlib.Path(__file__).parent.joinpath("colmap_fiat.rrd").resolve() - if not rrd_file.exists(): - print("No demo file found at {}. Package was built without demo support".format(rrd_file), file=sys.stderr) - exit(1) - else: - exit(bindings.main([sys.argv[0], str(rrd_file)] + serve_opts)) - - -def main() -> None: - import depthai_viewer as viewer - - parser = argparse.ArgumentParser(description="Run rerun example programs.") - - group = parser.add_mutually_exclusive_group() - - group.add_argument( - "--cube", - action="store_true", - help="Run the color grid cube demo", - ) - - group.add_argument( - "--colmap", - action="store_true", - help="Run the COLMAP data demo", - ) - - viewer.script_add_args(parser) - - args = parser.parse_args() - - if not any([args.cube, args.colmap]): - args.cube = True - - if args.cube: - run_cube(args) - - elif args.colmap: - run_colmap(args) - - -if __name__ == "__main__": - main() diff --git a/rerun_py/rerun_sdk/rerun_demo/data.py b/rerun_py/rerun_sdk/rerun_demo/data.py deleted file mode 100644 index d2c937b5bc19..000000000000 --- a/rerun_py/rerun_sdk/rerun_demo/data.py +++ /dev/null @@ -1,130 +0,0 @@ -"""Simple data to be used for Rerun demos.""" - -from collections import namedtuple -from math import cos, sin, tau - -import numpy as np -from depthai_viewer.log.rects import RectFormat - -from rerun_demo.turbo import turbo_colormap_data - -ColorGrid = namedtuple("ColorGrid", ["positions", "colors"]) - - -def build_color_grid(x_count=10, y_count=10, z_count=10, twist=0): - """ - Create a cube of points with colors. - - The total point cloud will have x_count * y_count * z_count points. - - Parameters - ---------- - x_count, y_count, z_count: - Number of points in each dimension. - twist: - Angle to twist from bottom to top of the cube - - """ - - grid = np.mgrid[ - slice(-10, 10, x_count * 1j), - slice(-10, 10, y_count * 1j), - slice(-10, 10, z_count * 1j), - ] - - angle = np.linspace(-float(twist) / 2, float(twist) / 2, z_count) - for z in range(z_count): - xv, yv, zv = grid[:, :, :, z] - rot_xv = xv * cos(angle[z]) - yv * sin(angle[z]) - rot_yv = xv * sin(angle[z]) + yv * cos(angle[z]) - grid[:, :, :, z] = [rot_xv, rot_yv, zv] - - positions = np.vstack([xyz.ravel() for xyz in grid]) - - colors = np.vstack( - [ - xyz.ravel() - for xyz in np.mgrid[ - slice(0, 255, x_count * 1j), - slice(0, 255, y_count * 1j), - slice(0, 255, z_count * 1j), - ] - ] - ) - - return ColorGrid(positions.T, colors.T.astype(np.uint8)) - - -color_grid = build_color_grid() -"""Default color grid""" - - -RectPyramid = namedtuple("RectPyramid", ["rects", "format", "colors"]) - - -def build_rect_pyramid(count=20, width=100, height=100): - """ - Create a stack of N colored rectangles. - - Parameters - ---------- - count: - Number of rectangles to create. - width: - Width of the base of the pyramid. - height: - Height of the pyramid. - - """ - x = np.zeros(count) - y = np.linspace(0, height, count) - widths = np.linspace(float(width) / count, width, count) - heights = 0.8 * np.ones(count) * height / count - rects = np.array(list(zip(x, y, widths, heights))) - colors = turbo_colormap_data[np.linspace(0, len(turbo_colormap_data) - 1, count, dtype=int)] - - return RectPyramid(rects, RectFormat.XCYCWH, colors) - - -rect_pyramid = build_rect_pyramid() -"""Default rect pyramid data""" - - -ColorSpiral = namedtuple("ColorSpiral", ["positions", "colors"]) - - -def build_color_spiral(num_points=100, radius=2, angular_step=0.02, angular_offset=0, z_step=0.1): - """ - Create a spiral of points with colors along the Z axis. - - Parameters - ---------- - num_points: - Total number of points. - radius: - The radius of the spiral. - angular_step: - The factor applied between each step along the trigonemetric circle. - angular_offset: - Offsets the starting position on the trigonemetric circle. - z_step: - The factor applied between between each step along the Z axis. - - """ - positions = np.array( - [ - [ - sin(i * tau * angular_step + angular_offset) * radius, - cos(i * tau * angular_step + angular_offset) * radius, - i * z_step, - ] - for i in range(num_points) - ] - ) - colors = turbo_colormap_data[np.linspace(0, len(turbo_colormap_data) - 1, num_points, dtype=int)] - - return ColorSpiral(positions, colors) - - -color_spiral = build_color_spiral() -"""Default color spiral""" diff --git a/rerun_py/rerun_sdk/rerun_demo/turbo.py b/rerun_py/rerun_sdk/rerun_demo/turbo.py deleted file mode 100644 index 9e454e8b2194..000000000000 --- a/rerun_py/rerun_sdk/rerun_demo/turbo.py +++ /dev/null @@ -1,262 +0,0 @@ -import numpy as np - -turbo_colormap_data = np.array( - [ - [0.18995, 0.07176, 0.23217], - [0.19483, 0.08339, 0.26149], - [0.19956, 0.09498, 0.29024], - [0.20415, 0.10652, 0.31844], - [0.20860, 0.11802, 0.34607], - [0.21291, 0.12947, 0.37314], - [0.21708, 0.14087, 0.39964], - [0.22111, 0.15223, 0.42558], - [0.22500, 0.16354, 0.45096], - [0.22875, 0.17481, 0.47578], - [0.23236, 0.18603, 0.50004], - [0.23582, 0.19720, 0.52373], - [0.23915, 0.20833, 0.54686], - [0.24234, 0.21941, 0.56942], - [0.24539, 0.23044, 0.59142], - [0.24830, 0.24143, 0.61286], - [0.25107, 0.25237, 0.63374], - [0.25369, 0.26327, 0.65406], - [0.25618, 0.27412, 0.67381], - [0.25853, 0.28492, 0.69300], - [0.26074, 0.29568, 0.71162], - [0.26280, 0.30639, 0.72968], - [0.26473, 0.31706, 0.74718], - [0.26652, 0.32768, 0.76412], - [0.26816, 0.33825, 0.78050], - [0.26967, 0.34878, 0.79631], - [0.27103, 0.35926, 0.81156], - [0.27226, 0.36970, 0.82624], - [0.27334, 0.38008, 0.84037], - [0.27429, 0.39043, 0.85393], - [0.27509, 0.40072, 0.86692], - [0.27576, 0.41097, 0.87936], - [0.27628, 0.42118, 0.89123], - [0.27667, 0.43134, 0.90254], - [0.27691, 0.44145, 0.91328], - [0.27701, 0.45152, 0.92347], - [0.27698, 0.46153, 0.93309], - [0.27680, 0.47151, 0.94214], - [0.27648, 0.48144, 0.95064], - [0.27603, 0.49132, 0.95857], - [0.27543, 0.50115, 0.96594], - [0.27469, 0.51094, 0.97275], - [0.27381, 0.52069, 0.97899], - [0.27273, 0.53040, 0.98461], - [0.27106, 0.54015, 0.98930], - [0.26878, 0.54995, 0.99303], - [0.26592, 0.55979, 0.99583], - [0.26252, 0.56967, 0.99773], - [0.25862, 0.57958, 0.99876], - [0.25425, 0.58950, 0.99896], - [0.24946, 0.59943, 0.99835], - [0.24427, 0.60937, 0.99697], - [0.23874, 0.61931, 0.99485], - [0.23288, 0.62923, 0.99202], - [0.22676, 0.63913, 0.98851], - [0.22039, 0.64901, 0.98436], - [0.21382, 0.65886, 0.97959], - [0.20708, 0.66866, 0.97423], - [0.20021, 0.67842, 0.96833], - [0.19326, 0.68812, 0.96190], - [0.18625, 0.69775, 0.95498], - [0.17923, 0.70732, 0.94761], - [0.17223, 0.71680, 0.93981], - [0.16529, 0.72620, 0.93161], - [0.15844, 0.73551, 0.92305], - [0.15173, 0.74472, 0.91416], - [0.14519, 0.75381, 0.90496], - [0.13886, 0.76279, 0.89550], - [0.13278, 0.77165, 0.88580], - [0.12698, 0.78037, 0.87590], - [0.12151, 0.78896, 0.86581], - [0.11639, 0.79740, 0.85559], - [0.11167, 0.80569, 0.84525], - [0.10738, 0.81381, 0.83484], - [0.10357, 0.82177, 0.82437], - [0.10026, 0.82955, 0.81389], - [0.09750, 0.83714, 0.80342], - [0.09532, 0.84455, 0.79299], - [0.09377, 0.85175, 0.78264], - [0.09287, 0.85875, 0.77240], - [0.09267, 0.86554, 0.76230], - [0.09320, 0.87211, 0.75237], - [0.09451, 0.87844, 0.74265], - [0.09662, 0.88454, 0.73316], - [0.09958, 0.89040, 0.72393], - [0.10342, 0.89600, 0.71500], - [0.10815, 0.90142, 0.70599], - [0.11374, 0.90673, 0.69651], - [0.12014, 0.91193, 0.68660], - [0.12733, 0.91701, 0.67627], - [0.13526, 0.92197, 0.66556], - [0.14391, 0.92680, 0.65448], - [0.15323, 0.93151, 0.64308], - [0.16319, 0.93609, 0.63137], - [0.17377, 0.94053, 0.61938], - [0.18491, 0.94484, 0.60713], - [0.19659, 0.94901, 0.59466], - [0.20877, 0.95304, 0.58199], - [0.22142, 0.95692, 0.56914], - [0.23449, 0.96065, 0.55614], - [0.24797, 0.96423, 0.54303], - [0.26180, 0.96765, 0.52981], - [0.27597, 0.97092, 0.51653], - [0.29042, 0.97403, 0.50321], - [0.30513, 0.97697, 0.48987], - [0.32006, 0.97974, 0.47654], - [0.33517, 0.98234, 0.46325], - [0.35043, 0.98477, 0.45002], - [0.36581, 0.98702, 0.43688], - [0.38127, 0.98909, 0.42386], - [0.39678, 0.99098, 0.41098], - [0.41229, 0.99268, 0.39826], - [0.42778, 0.99419, 0.38575], - [0.44321, 0.99551, 0.37345], - [0.45854, 0.99663, 0.36140], - [0.47375, 0.99755, 0.34963], - [0.48879, 0.99828, 0.33816], - [0.50362, 0.99879, 0.32701], - [0.51822, 0.99910, 0.31622], - [0.53255, 0.99919, 0.30581], - [0.54658, 0.99907, 0.29581], - [0.56026, 0.99873, 0.28623], - [0.57357, 0.99817, 0.27712], - [0.58646, 0.99739, 0.26849], - [0.59891, 0.99638, 0.26038], - [0.61088, 0.99514, 0.25280], - [0.62233, 0.99366, 0.24579], - [0.63323, 0.99195, 0.23937], - [0.64362, 0.98999, 0.23356], - [0.65394, 0.98775, 0.22835], - [0.66428, 0.98524, 0.22370], - [0.67462, 0.98246, 0.21960], - [0.68494, 0.97941, 0.21602], - [0.69525, 0.97610, 0.21294], - [0.70553, 0.97255, 0.21032], - [0.71577, 0.96875, 0.20815], - [0.72596, 0.96470, 0.20640], - [0.73610, 0.96043, 0.20504], - [0.74617, 0.95593, 0.20406], - [0.75617, 0.95121, 0.20343], - [0.76608, 0.94627, 0.20311], - [0.77591, 0.94113, 0.20310], - [0.78563, 0.93579, 0.20336], - [0.79524, 0.93025, 0.20386], - [0.80473, 0.92452, 0.20459], - [0.81410, 0.91861, 0.20552], - [0.82333, 0.91253, 0.20663], - [0.83241, 0.90627, 0.20788], - [0.84133, 0.89986, 0.20926], - [0.85010, 0.89328, 0.21074], - [0.85868, 0.88655, 0.21230], - [0.86709, 0.87968, 0.21391], - [0.87530, 0.87267, 0.21555], - [0.88331, 0.86553, 0.21719], - [0.89112, 0.85826, 0.21880], - [0.89870, 0.85087, 0.22038], - [0.90605, 0.84337, 0.22188], - [0.91317, 0.83576, 0.22328], - [0.92004, 0.82806, 0.22456], - [0.92666, 0.82025, 0.22570], - [0.93301, 0.81236, 0.22667], - [0.93909, 0.80439, 0.22744], - [0.94489, 0.79634, 0.22800], - [0.95039, 0.78823, 0.22831], - [0.95560, 0.78005, 0.22836], - [0.96049, 0.77181, 0.22811], - [0.96507, 0.76352, 0.22754], - [0.96931, 0.75519, 0.22663], - [0.97323, 0.74682, 0.22536], - [0.97679, 0.73842, 0.22369], - [0.98000, 0.73000, 0.22161], - [0.98289, 0.72140, 0.21918], - [0.98549, 0.71250, 0.21650], - [0.98781, 0.70330, 0.21358], - [0.98986, 0.69382, 0.21043], - [0.99163, 0.68408, 0.20706], - [0.99314, 0.67408, 0.20348], - [0.99438, 0.66386, 0.19971], - [0.99535, 0.65341, 0.19577], - [0.99607, 0.64277, 0.19165], - [0.99654, 0.63193, 0.18738], - [0.99675, 0.62093, 0.18297], - [0.99672, 0.60977, 0.17842], - [0.99644, 0.59846, 0.17376], - [0.99593, 0.58703, 0.16899], - [0.99517, 0.57549, 0.16412], - [0.99419, 0.56386, 0.15918], - [0.99297, 0.55214, 0.15417], - [0.99153, 0.54036, 0.14910], - [0.98987, 0.52854, 0.14398], - [0.98799, 0.51667, 0.13883], - [0.98590, 0.50479, 0.13367], - [0.98360, 0.49291, 0.12849], - [0.98108, 0.48104, 0.12332], - [0.97837, 0.46920, 0.11817], - [0.97545, 0.45740, 0.11305], - [0.97234, 0.44565, 0.10797], - [0.96904, 0.43399, 0.10294], - [0.96555, 0.42241, 0.09798], - [0.96187, 0.41093, 0.09310], - [0.95801, 0.39958, 0.08831], - [0.95398, 0.38836, 0.08362], - [0.94977, 0.37729, 0.07905], - [0.94538, 0.36638, 0.07461], - [0.94084, 0.35566, 0.07031], - [0.93612, 0.34513, 0.06616], - [0.93125, 0.33482, 0.06218], - [0.92623, 0.32473, 0.05837], - [0.92105, 0.31489, 0.05475], - [0.91572, 0.30530, 0.05134], - [0.91024, 0.29599, 0.04814], - [0.90463, 0.28696, 0.04516], - [0.89888, 0.27824, 0.04243], - [0.89298, 0.26981, 0.03993], - [0.88691, 0.26152, 0.03753], - [0.88066, 0.25334, 0.03521], - [0.87422, 0.24526, 0.03297], - [0.86760, 0.23730, 0.03082], - [0.86079, 0.22945, 0.02875], - [0.85380, 0.22170, 0.02677], - [0.84662, 0.21407, 0.02487], - [0.83926, 0.20654, 0.02305], - [0.83172, 0.19912, 0.02131], - [0.82399, 0.19182, 0.01966], - [0.81608, 0.18462, 0.01809], - [0.80799, 0.17753, 0.01660], - [0.79971, 0.17055, 0.01520], - [0.79125, 0.16368, 0.01387], - [0.78260, 0.15693, 0.01264], - [0.77377, 0.15028, 0.01148], - [0.76476, 0.14374, 0.01041], - [0.75556, 0.13731, 0.00942], - [0.74617, 0.13098, 0.00851], - [0.73661, 0.12477, 0.00769], - [0.72686, 0.11867, 0.00695], - [0.71692, 0.11268, 0.00629], - [0.70680, 0.10680, 0.00571], - [0.69650, 0.10102, 0.00522], - [0.68602, 0.09536, 0.00481], - [0.67535, 0.08980, 0.00449], - [0.66449, 0.08436, 0.00424], - [0.65345, 0.07902, 0.00408], - [0.64223, 0.07380, 0.00401], - [0.63082, 0.06868, 0.00401], - [0.61923, 0.06367, 0.00410], - [0.60746, 0.05878, 0.00427], - [0.59550, 0.05399, 0.00453], - [0.58336, 0.04931, 0.00486], - [0.57103, 0.04474, 0.00529], - [0.55852, 0.04028, 0.00579], - [0.54583, 0.03593, 0.00638], - [0.53295, 0.03169, 0.00705], - [0.51989, 0.02756, 0.00780], - [0.50664, 0.02354, 0.00863], - [0.49321, 0.01963, 0.00955], - [0.47960, 0.01583, 0.01055], - ] -) diff --git a/rerun_py/rerun_sdk/rerun_demo/util.py b/rerun_py/rerun_sdk/rerun_demo/util.py deleted file mode 100644 index 76496e1a4135..000000000000 --- a/rerun_py/rerun_sdk/rerun_demo/util.py +++ /dev/null @@ -1,44 +0,0 @@ -"""Simpe utilities to be used for Rerun demos.""" - -import numpy as np - - -def bounce_lerp(a, b, t): - """ - A linear interpolator that bounces between `a` and `b` as `t` goes above `1.0`. - - Parameters - ---------- - a: - Start value (t == 0). - b: - End value (t == 1). - t: - Interpolation coefficient. - - """ - tf = t % 1 - if int(t) % 2 == 0: - return (1.0 - tf) * a + tf * b - else: - return tf * a + (1.0 - tf) * b - - -def interleave(arr1, arr2): - """ - Interleaves two numpy arrays. - - Parameters - ---------- - arr1: - A numpy array of arbitrary shape and length. - arr2: - A numpy array with the same shape and length as `arr1`. - - """ - shape = list(arr1.shape) - shape[0] *= 2 - arr = np.empty(shape, dtype=arr1.dtype) - arr[0::2] = arr1 - arr[1::2] = arr2 - return arr diff --git a/rerun_py/src/python_bridge.rs b/rerun_py/src/python_bridge.rs index 179dfc055440..1152e5e5dbbd 100644 --- a/rerun_py/src/python_bridge.rs +++ b/rerun_py/src/python_bridge.rs @@ -114,9 +114,9 @@ fn python_version(py: Python<'_>) -> re_log_types::PythonVersion { } } -/// The python module is called "rerun_bindings". +/// The python module is called "depthai_viewer_bindings". #[pymodule] -fn rerun_bindings(py: Python<'_>, m: &PyModule) -> PyResult<()> { +fn depthai_viewer_bindings(py: Python<'_>, m: &PyModule) -> PyResult<()> { // NOTE: We do this here because some the inner init methods don't respond too kindly to being // called more than once. re_log::setup_native_logging(); @@ -136,8 +136,8 @@ fn rerun_bindings(py: Python<'_>, m: &PyModule) -> PyResult<()> { if matches!(std::env::var("RERUN_APP_ONLY").as_deref(), Ok("true")) { return Ok(()); } - - python_session().set_python_version(python_version(py)); + let sys_exe: String = py.import("sys")?.getattr("executable")?.extract()?; + python_session().set_python_version(python_version(py), sys_exe); // NOTE: We do this here because we want child processes to share the same recording-id, // whether the user has called `init` or not. @@ -249,9 +249,9 @@ fn time(timeless: bool) -> TimePoint { // ---------------------------------------------------------------------------- #[pyfunction] -fn main(py: Python<'_>, argv: Vec) -> PyResult { +fn main(py: Python<'_>, argv: Vec, sys_exe: String) -> PyResult { let build_info = re_build_info::build_info!(); - let call_src = depthai_viewer::CallSource::Python(python_version(py)); + let call_src = depthai_viewer::CallSource::Python(python_version(py), sys_exe); tokio::runtime::Builder::new_multi_thread() .enable_all() .build() diff --git a/rerun_py/src/python_session.rs b/rerun_py/src/python_session.rs index 5d46575e3c9d..2e15b0de6304 100644 --- a/rerun_py/src/python_session.rs +++ b/rerun_py/src/python_session.rs @@ -104,10 +104,16 @@ impl Default for PythonSession { } } +type SysExePath = String; + impl PythonSession { - pub fn set_python_version(&mut self, python_version: re_log_types::PythonVersion) { + pub fn set_python_version( + &mut self, + python_version: re_log_types::PythonVersion, + sys_exe: SysExePath, + ) { self.recording_meta_data.recording_source = - re_log_types::RecordingSource::PythonSdk(python_version); + re_log_types::RecordingSource::PythonSdk(python_version, sys_exe); } /// Check if logging is enabled on this `Session`. diff --git a/scripts/generate_changelog.py b/scripts/generate_changelog.py index d8417b66df84..8dc0a4187d3b 100755 --- a/scripts/generate_changelog.py +++ b/scripts/generate_changelog.py @@ -18,7 +18,7 @@ OWNER = "luxonis" REPO = "depthai-viewer" -COMMIT_RANGE = "latest..HEAD" +COMMIT_RANGE = "develop..HEAD" INCLUDE_LABELS = False # It adds quite a bit of visual noise OFFICIAL_RERUN_DEVS = [ "emilk", diff --git a/scripts/version_util.py b/scripts/version_util.py index e694f24c0b80..0a57bcc071ff 100755 --- a/scripts/version_util.py +++ b/scripts/version_util.py @@ -11,6 +11,7 @@ EXPECTED_VERSION=$(python3 scripts/version_util.py --bare_cargo_version) """ +import os import re import subprocess import sys @@ -21,6 +22,7 @@ # A regex to match the version number in Cargo.toml as SemVer, e.g., 1.2.3-alpha.0 CARGO_VERSION_REGEX: Final = r"^version\s*=\s*\"(.+)\"$" +VERSION_TAG_REGEX: Final = r"^v(?P([0-9]+)\.([0-9]+)\.([0-9]+))$" def get_cargo_version(cargo_toml: str) -> semver.VersionInfo: @@ -39,6 +41,24 @@ def get_git_sha() -> str: return subprocess.check_output(["git", "rev-parse", "--short", "HEAD"]).decode("utf-8").strip() +def get_ref_name_version() -> str: + """Return the parsed tag version from the GITHUB_REF_NAME environment variable.""" + + # This is the branch, or tag name that triggered the workflow. + ref_name = os.environ.get("GITHUB_REF_NAME") + + if ref_name is None: + raise Exception("GITHUB_REF_NAME environment variable not set") + + # Extract the version number from the tag name + match = re.search(VERSION_TAG_REGEX, ref_name) + + if match is None: + raise Exception("Could not find valid version number in GITHUB_REF_NAME") + + return match.group("version") + + def patch_cargo_version(cargo_toml: str, new_version: str) -> str: """Patch the version number in Cargo.toml with `new_version`.""" @@ -81,6 +101,14 @@ def main() -> None: # is expected to be fed into an environment variable. print(f"{cargo_version}") + elif sys.argv[1] == "--check_version": + ref_version = get_ref_name_version() + if cargo_version != ref_version: + raise Exception( + f"Version number in Cargo.toml ({cargo_version}) does not match tag version ({ref_version})" + ) + print(f"Version numbers match: {cargo_version} == {ref_version}") + else: raise Exception("Invalid argument")