diff --git a/Cargo.lock b/Cargo.lock index 35320fea0898..bce11d2cb0b3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1777,7 +1777,7 @@ checksum = "68b0cf012f1230e43cd00ebb729c6bb58707ecfa8ad08b52ef3a4ccd2697fc30" [[package]] name = "ecolor" version = "0.28.1" -source = "git+https://github.com/emilk/egui.git?rev=6b7f4312373a301a4cdf7d99a0d546acd34bcd66#6b7f4312373a301a4cdf7d99a0d546acd34bcd66" +source = "git+https://github.com/emilk/egui.git?rev=66076101e12eee01dec374285521b0bed4ecc40a#66076101e12eee01dec374285521b0bed4ecc40a" dependencies = [ "bytemuck", "emath", @@ -1787,7 +1787,7 @@ dependencies = [ [[package]] name = "eframe" version = "0.28.1" -source = "git+https://github.com/emilk/egui.git?rev=6b7f4312373a301a4cdf7d99a0d546acd34bcd66#6b7f4312373a301a4cdf7d99a0d546acd34bcd66" +source = "git+https://github.com/emilk/egui.git?rev=66076101e12eee01dec374285521b0bed4ecc40a#66076101e12eee01dec374285521b0bed4ecc40a" dependencies = [ "ahash", "bytemuck", @@ -1824,7 +1824,7 @@ dependencies = [ [[package]] name = "egui" version = "0.28.1" -source = "git+https://github.com/emilk/egui.git?rev=6b7f4312373a301a4cdf7d99a0d546acd34bcd66#6b7f4312373a301a4cdf7d99a0d546acd34bcd66" +source = "git+https://github.com/emilk/egui.git?rev=66076101e12eee01dec374285521b0bed4ecc40a#66076101e12eee01dec374285521b0bed4ecc40a" dependencies = [ "accesskit", "ahash", @@ -1841,7 +1841,7 @@ dependencies = [ [[package]] name = "egui-wgpu" version = "0.28.1" -source = "git+https://github.com/emilk/egui.git?rev=6b7f4312373a301a4cdf7d99a0d546acd34bcd66#6b7f4312373a301a4cdf7d99a0d546acd34bcd66" +source = "git+https://github.com/emilk/egui.git?rev=66076101e12eee01dec374285521b0bed4ecc40a#66076101e12eee01dec374285521b0bed4ecc40a" dependencies = [ "ahash", "bytemuck", @@ -1860,7 +1860,7 @@ dependencies = [ [[package]] name = "egui-winit" version = "0.28.1" -source = "git+https://github.com/emilk/egui.git?rev=6b7f4312373a301a4cdf7d99a0d546acd34bcd66#6b7f4312373a301a4cdf7d99a0d546acd34bcd66" +source = "git+https://github.com/emilk/egui.git?rev=66076101e12eee01dec374285521b0bed4ecc40a#66076101e12eee01dec374285521b0bed4ecc40a" dependencies = [ "accesskit_winit", "ahash", @@ -1879,30 +1879,28 @@ dependencies = [ [[package]] name = "egui_commonmark" version = "0.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe88871b75bd43c52a2b44ce5b53160506e7976e239112c56728496d019cc60d" +source = "git+https://github.com/rerun-io/egui_commonmark?rev=7a9dc755bfa351a3796274cb8ca87129b051c084#7a9dc755bfa351a3796274cb8ca87129b051c084" dependencies = [ "egui", "egui_commonmark_backend", "egui_extras", - "pulldown-cmark 0.11.0", + "pulldown-cmark 0.12.1", ] [[package]] name = "egui_commonmark_backend" version = "0.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "148edd9546feba319b16d5a5e551cda46095031ec1e6665e5871eef9ee692967" +source = "git+https://github.com/rerun-io/egui_commonmark?rev=7a9dc755bfa351a3796274cb8ca87129b051c084#7a9dc755bfa351a3796274cb8ca87129b051c084" dependencies = [ "egui", "egui_extras", - "pulldown-cmark 0.11.0", + "pulldown-cmark 0.12.1", ] [[package]] name = "egui_extras" version = "0.28.1" -source = "git+https://github.com/emilk/egui.git?rev=6b7f4312373a301a4cdf7d99a0d546acd34bcd66#6b7f4312373a301a4cdf7d99a0d546acd34bcd66" +source = "git+https://github.com/emilk/egui.git?rev=66076101e12eee01dec374285521b0bed4ecc40a#66076101e12eee01dec374285521b0bed4ecc40a" dependencies = [ "ahash", "egui", @@ -1918,13 +1916,13 @@ dependencies = [ [[package]] name = "egui_glow" version = "0.28.1" -source = "git+https://github.com/emilk/egui.git?rev=6b7f4312373a301a4cdf7d99a0d546acd34bcd66#6b7f4312373a301a4cdf7d99a0d546acd34bcd66" +source = "git+https://github.com/emilk/egui.git?rev=66076101e12eee01dec374285521b0bed4ecc40a#66076101e12eee01dec374285521b0bed4ecc40a" dependencies = [ "ahash", "bytemuck", "egui", "egui-winit", - "glow", + "glow 0.14.0", "log", "memoffset 0.9.0", "puffin", @@ -1947,7 +1945,7 @@ dependencies = [ [[package]] name = "egui_table" version = "0.28.1" -source = "git+https://github.com/rerun-io/egui_table.git?rev=1c5db2cc3c4c8db79e85824f7ca6cca87110111f#1c5db2cc3c4c8db79e85824f7ca6cca87110111f" +source = "git+https://github.com/rerun-io/egui_table.git?rev=c76473b244f03a7c67fbbbff9def6fc86c1ca4ea#c76473b244f03a7c67fbbbff9def6fc86c1ca4ea" dependencies = [ "egui", "serde", @@ -1991,7 +1989,7 @@ checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" [[package]] name = "emath" version = "0.28.1" -source = "git+https://github.com/emilk/egui.git?rev=6b7f4312373a301a4cdf7d99a0d546acd34bcd66#6b7f4312373a301a4cdf7d99a0d546acd34bcd66" +source = "git+https://github.com/emilk/egui.git?rev=66076101e12eee01dec374285521b0bed4ecc40a#66076101e12eee01dec374285521b0bed4ecc40a" dependencies = [ "bytemuck", "serde", @@ -2092,7 +2090,7 @@ dependencies = [ [[package]] name = "epaint" version = "0.28.1" -source = "git+https://github.com/emilk/egui.git?rev=6b7f4312373a301a4cdf7d99a0d546acd34bcd66#6b7f4312373a301a4cdf7d99a0d546acd34bcd66" +source = "git+https://github.com/emilk/egui.git?rev=66076101e12eee01dec374285521b0bed4ecc40a#66076101e12eee01dec374285521b0bed4ecc40a" dependencies = [ "ab_glyph", "ahash", @@ -2111,7 +2109,7 @@ dependencies = [ [[package]] name = "epaint_default_fonts" version = "0.28.1" -source = "git+https://github.com/emilk/egui.git?rev=6b7f4312373a301a4cdf7d99a0d546acd34bcd66#6b7f4312373a301a4cdf7d99a0d546acd34bcd66" +source = "git+https://github.com/emilk/egui.git?rev=66076101e12eee01dec374285521b0bed4ecc40a#66076101e12eee01dec374285521b0bed4ecc40a" [[package]] name = "equivalent" @@ -2550,6 +2548,18 @@ dependencies = [ "web-sys", ] +[[package]] +name = "glow" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f865cbd94bd355b89611211e49508da98a1fce0ad755c1e8448fb96711b24528" +dependencies = [ + "js-sys", + "slotmap", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "gltf" version = "1.4.1" @@ -4515,9 +4525,9 @@ dependencies = [ [[package]] name = "pulldown-cmark" -version = "0.11.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8746739f11d39ce5ad5c2520a9b75285310dbfe78c541ccf832d38615765aec0" +checksum = "666f0f59e259aea2d72e6012290c09877a780935cc3c18b1ceded41f3890d59c" dependencies = [ "bitflags 2.6.0", "memchr", @@ -7981,7 +7991,7 @@ dependencies = [ "block", "cfg_aliases 0.1.1", "core-graphics-types", - "glow", + "glow 0.13.1", "glutin_wgl_sys", "gpu-alloc", "gpu-allocator", diff --git a/Cargo.toml b/Cargo.toml index 8c7411d4e004..66e2f4e619a3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -514,12 +514,12 @@ missing_errors_doc = "allow" # As a last resport, patch with a commit to our own repository. # ALWAYS document what PR the commit hash is part of, or when it was merged into the upstream trunk. -ecolor = { git = "https://github.com/emilk/egui.git", rev = "6b7f4312373a301a4cdf7d99a0d546acd34bcd66" } # egui master 2024-09-06 -eframe = { git = "https://github.com/emilk/egui.git", rev = "6b7f4312373a301a4cdf7d99a0d546acd34bcd66" } # egui master 2024-09-06 -egui = { git = "https://github.com/emilk/egui.git", rev = "6b7f4312373a301a4cdf7d99a0d546acd34bcd66" } # egui master 2024-09-06 -egui_extras = { git = "https://github.com/emilk/egui.git", rev = "6b7f4312373a301a4cdf7d99a0d546acd34bcd66" } # egui master 2024-09-06 -egui-wgpu = { git = "https://github.com/emilk/egui.git", rev = "6b7f4312373a301a4cdf7d99a0d546acd34bcd66" } # egui master 2024-09-06 -emath = { git = "https://github.com/emilk/egui.git", rev = "6b7f4312373a301a4cdf7d99a0d546acd34bcd66" } # egui master 2024-09-06 +ecolor = { git = "https://github.com/emilk/egui.git", rev = "66076101e12eee01dec374285521b0bed4ecc40a" } # egui master 2024-09-06 +eframe = { git = "https://github.com/emilk/egui.git", rev = "66076101e12eee01dec374285521b0bed4ecc40a" } # egui master 2024-09-06 +egui = { git = "https://github.com/emilk/egui.git", rev = "66076101e12eee01dec374285521b0bed4ecc40a" } # egui master 2024-09-06 +egui_extras = { git = "https://github.com/emilk/egui.git", rev = "66076101e12eee01dec374285521b0bed4ecc40a" } # egui master 2024-09-06 +egui-wgpu = { git = "https://github.com/emilk/egui.git", rev = "66076101e12eee01dec374285521b0bed4ecc40a" } # egui master 2024-09-06 +emath = { git = "https://github.com/emilk/egui.git", rev = "66076101e12eee01dec374285521b0bed4ecc40a" } # egui master 2024-09-06 # Useful while developing: # ecolor = { path = "../../egui/crates/ecolor" } @@ -535,7 +535,7 @@ emath = { git = "https://github.com/emilk/egui.git", rev = "6b7f4312373a301a4cdf egui_tiles = { git = "https://github.com/rerun-io/egui_tiles", rev = "b2f5e232524deed983bcad01c05f27d0e8b89d98" } # https://github.com/rerun-io/egui_tiles/pull/78 2024-08-28 # egui_tiles = { path = "../egui_tiles" } -# egui_commonmark = { git = "https://github.com/rerun-io/egui_commonmark", rev = "63d5c8933445b9ea9088c4a50b71f4ede1f3c247" } # https://github.com/lampsitter/egui_commonmark/pull/51 +egui_commonmark = { git = "https://github.com/rerun-io/egui_commonmark", rev = "7a9dc755bfa351a3796274cb8ca87129b051c084" } # https://github.com/lampsitter/egui_commonmark/pull/65 # commit on `rerun-io/mp4` `main` branch # https://github.com/rerun-io/mp4/commit/ef529032547d7f97161e95c58bd76856cb116349 diff --git a/crates/build/re_dev_tools/src/build_examples/snippets.rs b/crates/build/re_dev_tools/src/build_examples/snippets.rs index 7454c556b41c..6b8c8dd9da2f 100644 --- a/crates/build/re_dev_tools/src/build_examples/snippets.rs +++ b/crates/build/re_dev_tools/src/build_examples/snippets.rs @@ -36,8 +36,11 @@ impl Snippets { let snippet_root = snippets_dir.join("all"); let snippets = collect_snippets_recursively(&snippet_root, &config, &snippet_root)?; - println!("Running {} snippets…", snippets.len()); + println!("Download test assets…"); let progress = MultiProgress::new(); + download_test_assets(&progress)?; + + println!("Running {} snippets…", snippets.len()); let results: Vec> = snippets .into_par_iter() .map(|example| example.build(&progress, &self.output_dir)) @@ -190,3 +193,12 @@ struct OptOut { /// example name -> languages run: HashMap>, } + +fn download_test_assets(progress: &MultiProgress) -> anyhow::Result<()> { + let download_script = re_build_tools::cargo_metadata()? + .workspace_root + .join("tests/assets/download_test_assets.py"); + let mut cmd = Command::new("python3"); + cmd.arg(download_script.as_str()); + wait_for_output(cmd, "download test assets", progress) +} diff --git a/crates/store/re_data_loader/src/loader_archetype.rs b/crates/store/re_data_loader/src/loader_archetype.rs index 6f29163ef7b6..e1433b7f54f3 100644 --- a/crates/store/re_data_loader/src/loader_archetype.rs +++ b/crates/store/re_data_loader/src/loader_archetype.rs @@ -263,7 +263,7 @@ fn load_video( .flat_map(|segment| { segment.samples.iter().map(|s| { // TODO(andreas): Use sample indices instead of timestamps once possible. - re_types::components::VideoTimestamp::new_nanoseconds( + re_types::components::VideoTimestamp::from_nanoseconds( s.timestamp.as_nanoseconds(), ) }) diff --git a/crates/store/re_types/definitions/rerun/archetypes/asset_video.fbs b/crates/store/re_types/definitions/rerun/archetypes/asset_video.fbs index ed4a1ad95245..7b61fc0f683a 100644 --- a/crates/store/re_types/definitions/rerun/archetypes/asset_video.fbs +++ b/crates/store/re_types/definitions/rerun/archetypes/asset_video.fbs @@ -7,7 +7,9 @@ namespace rerun.archetypes; /// Follow for updates on the native support. /// /// In order to display a video, you need to log a [archetypes.VideoFrameReference] for each frame. -// TODO(#7368): More docs and examples on how to use this. +/// +/// \example archetypes/video_manual_frames title="Video with explicit frames" image="https://static.rerun.io/video_manual_frames/320a44e1e06b8b3a3161ecbbeae3e04d1ccb9589/1200w.png" +// TODO(#7368): Example and reference to `send_video_frames` API. table AssetVideo ( "attr.rerun.experimental" ) { diff --git a/crates/store/re_types/definitions/rerun/archetypes/video_frame_reference.fbs b/crates/store/re_types/definitions/rerun/archetypes/video_frame_reference.fbs index 94d7d29e7667..f503867b41bc 100644 --- a/crates/store/re_types/definitions/rerun/archetypes/video_frame_reference.fbs +++ b/crates/store/re_types/definitions/rerun/archetypes/video_frame_reference.fbs @@ -2,8 +2,11 @@ namespace rerun.archetypes; /// References a single video frame. /// -/// Used to display video frames from a [archetypes.AssetVideo]. -// TODO(#7368): More docs and examples on how to use this. +/// Used to display individual video frames from a [archetypes.AssetVideo]. +/// To show an entire video, a fideo frame reference for each frame of the video should be logged. +/// +/// \example archetypes/video_manual_frames title="Video with explicit frames" image="https://static.rerun.io/video_manual_frames/320a44e1e06b8b3a3161ecbbeae3e04d1ccb9589/1200w.png" +// TODO(#7368): Example and reference to `send_video_frames` API. table VideoFrameReference ( "attr.rerun.experimental" ){ @@ -22,5 +25,9 @@ table VideoFrameReference ( /// If none is specified, the video is assumed to be at the same entity. /// Note that blueprint overrides on the referenced video will be ignored regardless, /// as this is always interpreted as a reference to the data store. + /// + /// For a series of video frame references, it is recommended to specify this path only once + /// at the beginning of the series and then rely on latest-at query semantics to + /// keep the video reference active. video_reference: rerun.components.EntityPath ("attr.rerun.component_optional", nullable, order: 2000); } diff --git a/crates/store/re_types/definitions/rerun/blueprint/archetypes.fbs b/crates/store/re_types/definitions/rerun/blueprint/archetypes.fbs index f6e6710b96b3..f1a11d176958 100644 --- a/crates/store/re_types/definitions/rerun/blueprint/archetypes.fbs +++ b/crates/store/re_types/definitions/rerun/blueprint/archetypes.fbs @@ -11,7 +11,6 @@ include "./archetypes/space_view_contents.fbs"; include "./archetypes/tensor_scalar_mapping.fbs"; include "./archetypes/tensor_slice_selection.fbs"; include "./archetypes/tensor_view_fit.fbs"; -include "./archetypes/time_range_table_order.fbs"; include "./archetypes/viewport_blueprint.fbs"; include "./archetypes/visible_time_ranges.fbs"; include "./archetypes/visual_bounds2d.fbs"; diff --git a/crates/store/re_types/definitions/rerun/blueprint/archetypes/time_range_table_order.fbs b/crates/store/re_types/definitions/rerun/blueprint/archetypes/time_range_table_order.fbs deleted file mode 100644 index c7c8a6b771e1..000000000000 --- a/crates/store/re_types/definitions/rerun/blueprint/archetypes/time_range_table_order.fbs +++ /dev/null @@ -1,16 +0,0 @@ -namespace rerun.blueprint.archetypes; - - -/// Ordering of the time range table of the dataframe view -table TimeRangeTableOrder ( - "attr.rerun.scope": "blueprint", - "attr.rust.derive": "Copy" -) { - // --- Optional --- - - /// The primary sort key - sort_key: rerun.blueprint.components.SortKey ("attr.rerun.component_optional", nullable, order: 2000); - - /// The sort order - sort_order: rerun.blueprint.components.SortOrder ("attr.rerun.component_optional", nullable, order: 3000); -} diff --git a/crates/store/re_types/definitions/rerun/blueprint/components.fbs b/crates/store/re_types/definitions/rerun/blueprint/components.fbs index 95262a56a7e0..035b81c4a7b6 100644 --- a/crates/store/re_types/definitions/rerun/blueprint/components.fbs +++ b/crates/store/re_types/definitions/rerun/blueprint/components.fbs @@ -18,8 +18,6 @@ include "./components/query_expression.fbs"; include "./components/query_kind.fbs"; include "./components/root_container.fbs"; include "./components/row_share.fbs"; -include "./components/sort_key.fbs"; -include "./components/sort_order.fbs"; include "./components/space_view_class.fbs"; include "./components/space_view_maximized.fbs"; include "./components/space_view_origin.fbs"; diff --git a/crates/store/re_types/definitions/rerun/blueprint/components/sort_key.fbs b/crates/store/re_types/definitions/rerun/blueprint/components/sort_key.fbs deleted file mode 100644 index df3acba67c26..000000000000 --- a/crates/store/re_types/definitions/rerun/blueprint/components/sort_key.fbs +++ /dev/null @@ -1,16 +0,0 @@ -namespace rerun.blueprint.components; - - -/// Primary element by which to group by in a temporal data table. -enum SortKey: ubyte ( - "attr.rerun.scope": "blueprint" -) { - /// Invalid value. Won't show up in generated types. - Invalid = 0, - - /// Group by entity. - Entity (default), - - /// Group by instance. - Time, -} diff --git a/crates/store/re_types/definitions/rerun/blueprint/components/sort_order.fbs b/crates/store/re_types/definitions/rerun/blueprint/components/sort_order.fbs deleted file mode 100644 index 99e08febfe43..000000000000 --- a/crates/store/re_types/definitions/rerun/blueprint/components/sort_order.fbs +++ /dev/null @@ -1,16 +0,0 @@ -namespace rerun.blueprint.components; - - -/// Sort order for data table. -enum SortOrder: ubyte ( - "attr.rerun.scope": "blueprint" -) { - /// Invalid value. Won't show up in generated types. - Invalid = 0, - - /// Ascending - Ascending (default), - - /// Descending - Descending, -} diff --git a/crates/store/re_types/src/archetypes/asset_video.rs b/crates/store/re_types/src/archetypes/asset_video.rs index 1133476159b8..ba29d3d91e6f 100644 --- a/crates/store/re_types/src/archetypes/asset_video.rs +++ b/crates/store/re_types/src/archetypes/asset_video.rs @@ -27,6 +27,58 @@ use ::re_types_core::{DeserializationError, DeserializationResult}; /// In order to display a video, you need to log a [`archetypes::VideoFrameReference`][crate::archetypes::VideoFrameReference] for each frame. /// /// ⚠️ **This type is experimental and may be removed in future versions** +/// +/// ## Example +/// +/// ### Video with explicit frames +/// ```ignore +/// use rerun::{external::anyhow, TimeColumn}; +/// +/// fn main() -> anyhow::Result<()> { +/// let args = _args; +/// let Some(path) = args.get(1) else { +/// // TODO(#7354): Only mp4 is supported for now. +/// anyhow::bail!("Usage: {} ", args[0]); +/// }; +/// +/// let rec = +/// rerun::RecordingStreamBuilder::new("rerun_example_asset_video_manual_frames").spawn()?; +/// +/// // Log video asset which is referred to by frame references. +/// rec.set_time_seconds("video_time", 0.0); // Make sure it's available on the timeline used for the frame references. +/// rec.log("video", &rerun::AssetVideo::from_file_path(path)?)?; +/// +/// // Send frame references for every 0.1 seconds over a total of 10 seconds. +/// // Naturally, this will result in a choppy playback and only makes sense if the video is 10 seconds or longer. +/// // TODO(#7368): Point to example using `send_video_frames`. +/// // +/// // Use `send_columns` to send all frame references in a single call. +/// let times = (0..(10 * 10)).map(|t| t as f64 * 0.1).collect::>(); +/// let time_column = TimeColumn::new_seconds("video_time", times.iter().copied()); +/// let frame_reference_indicators = +/// ::Indicator::new_array(times.len()); +/// let video_timestamps = times +/// .into_iter() +/// .map(rerun::components::VideoTimestamp::from_seconds) +/// .collect::>(); +/// rec.send_columns( +/// "video", +/// [time_column], +/// [&frame_reference_indicators as _, &video_timestamps as _], +/// )?; +/// +/// Ok(()) +/// } +/// ``` +///
+/// +/// +/// +/// +/// +/// +/// +///
#[derive(Clone, Debug)] pub struct AssetVideo { /// The asset's bytes. diff --git a/crates/store/re_types/src/archetypes/video_frame_reference.rs b/crates/store/re_types/src/archetypes/video_frame_reference.rs index a70ca383cb02..aeca145292ea 100644 --- a/crates/store/re_types/src/archetypes/video_frame_reference.rs +++ b/crates/store/re_types/src/archetypes/video_frame_reference.rs @@ -20,9 +20,62 @@ use ::re_types_core::{DeserializationError, DeserializationResult}; /// **Archetype**: References a single video frame. /// -/// Used to display video frames from a [`archetypes::AssetVideo`][crate::archetypes::AssetVideo]. +/// Used to display individual video frames from a [`archetypes::AssetVideo`][crate::archetypes::AssetVideo]. +/// To show an entire video, a fideo frame reference for each frame of the video should be logged. /// /// ⚠️ **This type is experimental and may be removed in future versions** +/// +/// ## Example +/// +/// ### Video with explicit frames +/// ```ignore +/// use rerun::{external::anyhow, TimeColumn}; +/// +/// fn main() -> anyhow::Result<()> { +/// let args = _args; +/// let Some(path) = args.get(1) else { +/// // TODO(#7354): Only mp4 is supported for now. +/// anyhow::bail!("Usage: {} ", args[0]); +/// }; +/// +/// let rec = +/// rerun::RecordingStreamBuilder::new("rerun_example_asset_video_manual_frames").spawn()?; +/// +/// // Log video asset which is referred to by frame references. +/// rec.set_time_seconds("video_time", 0.0); // Make sure it's available on the timeline used for the frame references. +/// rec.log("video", &rerun::AssetVideo::from_file_path(path)?)?; +/// +/// // Send frame references for every 0.1 seconds over a total of 10 seconds. +/// // Naturally, this will result in a choppy playback and only makes sense if the video is 10 seconds or longer. +/// // TODO(#7368): Point to example using `send_video_frames`. +/// // +/// // Use `send_columns` to send all frame references in a single call. +/// let times = (0..(10 * 10)).map(|t| t as f64 * 0.1).collect::>(); +/// let time_column = TimeColumn::new_seconds("video_time", times.iter().copied()); +/// let frame_reference_indicators = +/// ::Indicator::new_array(times.len()); +/// let video_timestamps = times +/// .into_iter() +/// .map(rerun::components::VideoTimestamp::from_seconds) +/// .collect::>(); +/// rec.send_columns( +/// "video", +/// [time_column], +/// [&frame_reference_indicators as _, &video_timestamps as _], +/// )?; +/// +/// Ok(()) +/// } +/// ``` +///
+/// +/// +/// +/// +/// +/// +/// +///
#[derive(Clone, Debug)] pub struct VideoFrameReference { /// References the closest video frame to this timestamp. @@ -36,6 +89,10 @@ pub struct VideoFrameReference { /// If none is specified, the video is assumed to be at the same entity. /// Note that blueprint overrides on the referenced video will be ignored regardless, /// as this is always interpreted as a reference to the data store. + /// + /// For a series of video frame references, it is recommended to specify this path only once + /// at the beginning of the series and then rely on latest-at query semantics to + /// keep the video reference active. pub video_reference: Option, } @@ -192,6 +249,10 @@ impl VideoFrameReference { /// If none is specified, the video is assumed to be at the same entity. /// Note that blueprint overrides on the referenced video will be ignored regardless, /// as this is always interpreted as a reference to the data store. + /// + /// For a series of video frame references, it is recommended to specify this path only once + /// at the beginning of the series and then rely on latest-at query semantics to + /// keep the video reference active. #[inline] pub fn with_video_reference( mut self, diff --git a/crates/store/re_types/src/blueprint/archetypes/.gitattributes b/crates/store/re_types/src/blueprint/archetypes/.gitattributes index 51fc7263ee37..8275cbf12a81 100644 --- a/crates/store/re_types/src/blueprint/archetypes/.gitattributes +++ b/crates/store/re_types/src/blueprint/archetypes/.gitattributes @@ -11,6 +11,5 @@ space_view_contents.rs linguist-generated=true tensor_scalar_mapping.rs linguist-generated=true tensor_slice_selection.rs linguist-generated=true tensor_view_fit.rs linguist-generated=true -time_range_table_order.rs linguist-generated=true visible_time_ranges.rs linguist-generated=true visual_bounds2d.rs linguist-generated=true diff --git a/crates/store/re_types/src/blueprint/archetypes/mod.rs b/crates/store/re_types/src/blueprint/archetypes/mod.rs index e0616353c032..19f7dfedff0c 100644 --- a/crates/store/re_types/src/blueprint/archetypes/mod.rs +++ b/crates/store/re_types/src/blueprint/archetypes/mod.rs @@ -9,7 +9,6 @@ mod space_view_contents; mod tensor_scalar_mapping; mod tensor_slice_selection; mod tensor_view_fit; -mod time_range_table_order; mod visible_time_ranges; mod visible_time_ranges_ext; mod visual_bounds2d; @@ -23,6 +22,5 @@ pub use self::space_view_contents::SpaceViewContents; pub use self::tensor_scalar_mapping::TensorScalarMapping; pub use self::tensor_slice_selection::TensorSliceSelection; pub use self::tensor_view_fit::TensorViewFit; -pub use self::time_range_table_order::TimeRangeTableOrder; pub use self::visible_time_ranges::VisibleTimeRanges; pub use self::visual_bounds2d::VisualBounds2D; diff --git a/crates/store/re_types/src/blueprint/archetypes/time_range_table_order.rs b/crates/store/re_types/src/blueprint/archetypes/time_range_table_order.rs deleted file mode 100644 index 2d3693b18997..000000000000 --- a/crates/store/re_types/src/blueprint/archetypes/time_range_table_order.rs +++ /dev/null @@ -1,204 +0,0 @@ -// DO NOT EDIT! This file was auto-generated by crates/build/re_types_builder/src/codegen/rust/api.rs -// Based on "crates/store/re_types/definitions/rerun/blueprint/archetypes/time_range_table_order.fbs". - -#![allow(unused_imports)] -#![allow(unused_parens)] -#![allow(clippy::clone_on_copy)] -#![allow(clippy::cloned_instead_of_copied)] -#![allow(clippy::map_flatten)] -#![allow(clippy::needless_question_mark)] -#![allow(clippy::new_without_default)] -#![allow(clippy::redundant_closure)] -#![allow(clippy::too_many_arguments)] -#![allow(clippy::too_many_lines)] - -use ::re_types_core::external::arrow2; -use ::re_types_core::ComponentName; -use ::re_types_core::SerializationResult; -use ::re_types_core::{ComponentBatch, MaybeOwnedComponentBatch}; -use ::re_types_core::{DeserializationError, DeserializationResult}; - -/// **Archetype**: Ordering of the time range table of the dataframe view -#[derive(Clone, Debug, Copy)] -pub struct TimeRangeTableOrder { - /// The primary sort key - pub sort_key: Option, - - /// The sort order - pub sort_order: Option, -} - -impl ::re_types_core::SizeBytes for TimeRangeTableOrder { - #[inline] - fn heap_size_bytes(&self) -> u64 { - self.sort_key.heap_size_bytes() + self.sort_order.heap_size_bytes() - } - - #[inline] - fn is_pod() -> bool { - >::is_pod() - && >::is_pod() - } -} - -static REQUIRED_COMPONENTS: once_cell::sync::Lazy<[ComponentName; 0usize]> = - once_cell::sync::Lazy::new(|| []); - -static RECOMMENDED_COMPONENTS: once_cell::sync::Lazy<[ComponentName; 1usize]> = - once_cell::sync::Lazy::new(|| { - ["rerun.blueprint.components.TimeRangeTableOrderIndicator".into()] - }); - -static OPTIONAL_COMPONENTS: once_cell::sync::Lazy<[ComponentName; 2usize]> = - once_cell::sync::Lazy::new(|| { - [ - "rerun.blueprint.components.SortKey".into(), - "rerun.blueprint.components.SortOrder".into(), - ] - }); - -static ALL_COMPONENTS: once_cell::sync::Lazy<[ComponentName; 3usize]> = - once_cell::sync::Lazy::new(|| { - [ - "rerun.blueprint.components.TimeRangeTableOrderIndicator".into(), - "rerun.blueprint.components.SortKey".into(), - "rerun.blueprint.components.SortOrder".into(), - ] - }); - -impl TimeRangeTableOrder { - /// The total number of components in the archetype: 0 required, 1 recommended, 2 optional - pub const NUM_COMPONENTS: usize = 3usize; -} - -/// Indicator component for the [`TimeRangeTableOrder`] [`::re_types_core::Archetype`] -pub type TimeRangeTableOrderIndicator = - ::re_types_core::GenericIndicatorComponent; - -impl ::re_types_core::Archetype for TimeRangeTableOrder { - type Indicator = TimeRangeTableOrderIndicator; - - #[inline] - fn name() -> ::re_types_core::ArchetypeName { - "rerun.blueprint.archetypes.TimeRangeTableOrder".into() - } - - #[inline] - fn display_name() -> &'static str { - "Time range table order" - } - - #[inline] - fn indicator() -> MaybeOwnedComponentBatch<'static> { - static INDICATOR: TimeRangeTableOrderIndicator = TimeRangeTableOrderIndicator::DEFAULT; - MaybeOwnedComponentBatch::Ref(&INDICATOR) - } - - #[inline] - fn required_components() -> ::std::borrow::Cow<'static, [ComponentName]> { - REQUIRED_COMPONENTS.as_slice().into() - } - - #[inline] - fn recommended_components() -> ::std::borrow::Cow<'static, [ComponentName]> { - RECOMMENDED_COMPONENTS.as_slice().into() - } - - #[inline] - fn optional_components() -> ::std::borrow::Cow<'static, [ComponentName]> { - OPTIONAL_COMPONENTS.as_slice().into() - } - - #[inline] - fn all_components() -> ::std::borrow::Cow<'static, [ComponentName]> { - ALL_COMPONENTS.as_slice().into() - } - - #[inline] - fn from_arrow_components( - arrow_data: impl IntoIterator)>, - ) -> DeserializationResult { - re_tracing::profile_function!(); - use ::re_types_core::{Loggable as _, ResultExt as _}; - let arrays_by_name: ::std::collections::HashMap<_, _> = arrow_data - .into_iter() - .map(|(name, array)| (name.full_name(), array)) - .collect(); - let sort_key = if let Some(array) = arrays_by_name.get("rerun.blueprint.components.SortKey") - { - ::from_arrow_opt(&**array) - .with_context("rerun.blueprint.archetypes.TimeRangeTableOrder#sort_key")? - .into_iter() - .next() - .flatten() - } else { - None - }; - let sort_order = - if let Some(array) = arrays_by_name.get("rerun.blueprint.components.SortOrder") { - ::from_arrow_opt(&**array) - .with_context("rerun.blueprint.archetypes.TimeRangeTableOrder#sort_order")? - .into_iter() - .next() - .flatten() - } else { - None - }; - Ok(Self { - sort_key, - sort_order, - }) - } -} - -impl ::re_types_core::AsComponents for TimeRangeTableOrder { - fn as_component_batches(&self) -> Vec> { - re_tracing::profile_function!(); - use ::re_types_core::Archetype as _; - [ - Some(Self::indicator()), - self.sort_key - .as_ref() - .map(|comp| (comp as &dyn ComponentBatch).into()), - self.sort_order - .as_ref() - .map(|comp| (comp as &dyn ComponentBatch).into()), - ] - .into_iter() - .flatten() - .collect() - } -} - -impl ::re_types_core::ArchetypeReflectionMarker for TimeRangeTableOrder {} - -impl TimeRangeTableOrder { - /// Create a new `TimeRangeTableOrder`. - #[inline] - pub fn new() -> Self { - Self { - sort_key: None, - sort_order: None, - } - } - - /// The primary sort key - #[inline] - pub fn with_sort_key( - mut self, - sort_key: impl Into, - ) -> Self { - self.sort_key = Some(sort_key.into()); - self - } - - /// The sort order - #[inline] - pub fn with_sort_order( - mut self, - sort_order: impl Into, - ) -> Self { - self.sort_order = Some(sort_order.into()); - self - } -} diff --git a/crates/store/re_types/src/blueprint/components/.gitattributes b/crates/store/re_types/src/blueprint/components/.gitattributes index 93f783652817..09201cfd9152 100644 --- a/crates/store/re_types/src/blueprint/components/.gitattributes +++ b/crates/store/re_types/src/blueprint/components/.gitattributes @@ -14,8 +14,6 @@ panel_state.rs linguist-generated=true query_expression.rs linguist-generated=true query_kind.rs linguist-generated=true row_share.rs linguist-generated=true -sort_key.rs linguist-generated=true -sort_order.rs linguist-generated=true space_view_class.rs linguist-generated=true space_view_origin.rs linguist-generated=true tensor_dimension_index_slider.rs linguist-generated=true diff --git a/crates/store/re_types/src/blueprint/components/mod.rs b/crates/store/re_types/src/blueprint/components/mod.rs index a54028c22654..e371fca4c9e5 100644 --- a/crates/store/re_types/src/blueprint/components/mod.rs +++ b/crates/store/re_types/src/blueprint/components/mod.rs @@ -16,8 +16,6 @@ mod panel_state_ext; mod query_expression; mod query_kind; mod row_share; -mod sort_key; -mod sort_order; mod space_view_class; mod space_view_class_ext; mod space_view_origin; @@ -49,8 +47,6 @@ pub use self::panel_state::PanelState; pub use self::query_expression::QueryExpression; pub use self::query_kind::QueryKind; pub use self::row_share::RowShare; -pub use self::sort_key::SortKey; -pub use self::sort_order::SortOrder; pub use self::space_view_class::SpaceViewClass; pub use self::space_view_origin::SpaceViewOrigin; pub use self::tensor_dimension_index_slider::TensorDimensionIndexSlider; diff --git a/crates/store/re_types/src/blueprint/components/sort_key.rs b/crates/store/re_types/src/blueprint/components/sort_key.rs deleted file mode 100644 index 99e05a181424..000000000000 --- a/crates/store/re_types/src/blueprint/components/sort_key.rs +++ /dev/null @@ -1,150 +0,0 @@ -// DO NOT EDIT! This file was auto-generated by crates/build/re_types_builder/src/codegen/rust/api.rs -// Based on "crates/store/re_types/definitions/rerun/blueprint/components/sort_key.fbs". - -#![allow(unused_imports)] -#![allow(unused_parens)] -#![allow(clippy::clone_on_copy)] -#![allow(clippy::cloned_instead_of_copied)] -#![allow(clippy::map_flatten)] -#![allow(clippy::needless_question_mark)] -#![allow(clippy::new_without_default)] -#![allow(clippy::redundant_closure)] -#![allow(clippy::too_many_arguments)] -#![allow(clippy::too_many_lines)] - -use ::re_types_core::external::arrow2; -use ::re_types_core::ComponentName; -use ::re_types_core::SerializationResult; -use ::re_types_core::{ComponentBatch, MaybeOwnedComponentBatch}; -use ::re_types_core::{DeserializationError, DeserializationResult}; - -/// **Component**: Primary element by which to group by in a temporal data table. -#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, Default)] -#[repr(u8)] -pub enum SortKey { - /// Group by entity. - #[default] - Entity = 1, - - /// Group by instance. - Time = 2, -} - -impl ::re_types_core::reflection::Enum for SortKey { - #[inline] - fn variants() -> &'static [Self] { - &[Self::Entity, Self::Time] - } - - #[inline] - fn docstring_md(self) -> &'static str { - match self { - Self::Entity => "Group by entity.", - Self::Time => "Group by instance.", - } - } -} - -impl ::re_types_core::SizeBytes for SortKey { - #[inline] - fn heap_size_bytes(&self) -> u64 { - 0 - } - - #[inline] - fn is_pod() -> bool { - true - } -} - -impl std::fmt::Display for SortKey { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Self::Entity => write!(f, "Entity"), - Self::Time => write!(f, "Time"), - } - } -} - -::re_types_core::macros::impl_into_cow!(SortKey); - -impl ::re_types_core::Loggable for SortKey { - type Name = ::re_types_core::ComponentName; - - #[inline] - fn name() -> Self::Name { - "rerun.blueprint.components.SortKey".into() - } - - #[inline] - fn arrow_datatype() -> arrow2::datatypes::DataType { - #![allow(clippy::wildcard_imports)] - use arrow2::datatypes::*; - DataType::UInt8 - } - - fn to_arrow_opt<'a>( - data: impl IntoIterator>>>, - ) -> SerializationResult> - where - Self: Clone + 'a, - { - #![allow(clippy::wildcard_imports)] - use ::re_types_core::{Loggable as _, ResultExt as _}; - use arrow2::{array::*, datatypes::*}; - Ok({ - let (somes, data0): (Vec<_>, Vec<_>) = data - .into_iter() - .map(|datum| { - let datum: Option<::std::borrow::Cow<'a, Self>> = datum.map(Into::into); - let datum = datum.map(|datum| *datum as u8); - (datum.is_some(), datum) - }) - .unzip(); - let data0_bitmap: Option = { - let any_nones = somes.iter().any(|some| !*some); - any_nones.then(|| somes.into()) - }; - PrimitiveArray::new( - Self::arrow_datatype(), - data0.into_iter().map(|v| v.unwrap_or_default()).collect(), - data0_bitmap, - ) - .boxed() - }) - } - - fn from_arrow_opt( - arrow_data: &dyn arrow2::array::Array, - ) -> DeserializationResult>> - where - Self: Sized, - { - #![allow(clippy::wildcard_imports)] - use ::re_types_core::{Loggable as _, ResultExt as _}; - use arrow2::{array::*, buffer::*, datatypes::*}; - Ok(arrow_data - .as_any() - .downcast_ref::() - .ok_or_else(|| { - let expected = Self::arrow_datatype(); - let actual = arrow_data.data_type().clone(); - DeserializationError::datatype_mismatch(expected, actual) - }) - .with_context("rerun.blueprint.components.SortKey#enum")? - .into_iter() - .map(|opt| opt.copied()) - .map(|typ| match typ { - Some(1) => Ok(Some(Self::Entity)), - Some(2) => Ok(Some(Self::Time)), - None => Ok(None), - Some(invalid) => Err(DeserializationError::missing_union_arm( - Self::arrow_datatype(), - "", - invalid as _, - )), - }) - .collect::>>>() - .with_context("rerun.blueprint.components.SortKey")?) - } -} diff --git a/crates/store/re_types/src/blueprint/components/sort_order.rs b/crates/store/re_types/src/blueprint/components/sort_order.rs deleted file mode 100644 index 62f0a44a8e5a..000000000000 --- a/crates/store/re_types/src/blueprint/components/sort_order.rs +++ /dev/null @@ -1,150 +0,0 @@ -// DO NOT EDIT! This file was auto-generated by crates/build/re_types_builder/src/codegen/rust/api.rs -// Based on "crates/store/re_types/definitions/rerun/blueprint/components/sort_order.fbs". - -#![allow(unused_imports)] -#![allow(unused_parens)] -#![allow(clippy::clone_on_copy)] -#![allow(clippy::cloned_instead_of_copied)] -#![allow(clippy::map_flatten)] -#![allow(clippy::needless_question_mark)] -#![allow(clippy::new_without_default)] -#![allow(clippy::redundant_closure)] -#![allow(clippy::too_many_arguments)] -#![allow(clippy::too_many_lines)] - -use ::re_types_core::external::arrow2; -use ::re_types_core::ComponentName; -use ::re_types_core::SerializationResult; -use ::re_types_core::{ComponentBatch, MaybeOwnedComponentBatch}; -use ::re_types_core::{DeserializationError, DeserializationResult}; - -/// **Component**: Sort order for data table. -#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, Default)] -#[repr(u8)] -pub enum SortOrder { - /// Ascending - #[default] - Ascending = 1, - - /// Descending - Descending = 2, -} - -impl ::re_types_core::reflection::Enum for SortOrder { - #[inline] - fn variants() -> &'static [Self] { - &[Self::Ascending, Self::Descending] - } - - #[inline] - fn docstring_md(self) -> &'static str { - match self { - Self::Ascending => "Ascending", - Self::Descending => "Descending", - } - } -} - -impl ::re_types_core::SizeBytes for SortOrder { - #[inline] - fn heap_size_bytes(&self) -> u64 { - 0 - } - - #[inline] - fn is_pod() -> bool { - true - } -} - -impl std::fmt::Display for SortOrder { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Self::Ascending => write!(f, "Ascending"), - Self::Descending => write!(f, "Descending"), - } - } -} - -::re_types_core::macros::impl_into_cow!(SortOrder); - -impl ::re_types_core::Loggable for SortOrder { - type Name = ::re_types_core::ComponentName; - - #[inline] - fn name() -> Self::Name { - "rerun.blueprint.components.SortOrder".into() - } - - #[inline] - fn arrow_datatype() -> arrow2::datatypes::DataType { - #![allow(clippy::wildcard_imports)] - use arrow2::datatypes::*; - DataType::UInt8 - } - - fn to_arrow_opt<'a>( - data: impl IntoIterator>>>, - ) -> SerializationResult> - where - Self: Clone + 'a, - { - #![allow(clippy::wildcard_imports)] - use ::re_types_core::{Loggable as _, ResultExt as _}; - use arrow2::{array::*, datatypes::*}; - Ok({ - let (somes, data0): (Vec<_>, Vec<_>) = data - .into_iter() - .map(|datum| { - let datum: Option<::std::borrow::Cow<'a, Self>> = datum.map(Into::into); - let datum = datum.map(|datum| *datum as u8); - (datum.is_some(), datum) - }) - .unzip(); - let data0_bitmap: Option = { - let any_nones = somes.iter().any(|some| !*some); - any_nones.then(|| somes.into()) - }; - PrimitiveArray::new( - Self::arrow_datatype(), - data0.into_iter().map(|v| v.unwrap_or_default()).collect(), - data0_bitmap, - ) - .boxed() - }) - } - - fn from_arrow_opt( - arrow_data: &dyn arrow2::array::Array, - ) -> DeserializationResult>> - where - Self: Sized, - { - #![allow(clippy::wildcard_imports)] - use ::re_types_core::{Loggable as _, ResultExt as _}; - use arrow2::{array::*, buffer::*, datatypes::*}; - Ok(arrow_data - .as_any() - .downcast_ref::() - .ok_or_else(|| { - let expected = Self::arrow_datatype(); - let actual = arrow_data.data_type().clone(); - DeserializationError::datatype_mismatch(expected, actual) - }) - .with_context("rerun.blueprint.components.SortOrder#enum")? - .into_iter() - .map(|opt| opt.copied()) - .map(|typ| match typ { - Some(1) => Ok(Some(Self::Ascending)), - Some(2) => Ok(Some(Self::Descending)), - None => Ok(None), - Some(invalid) => Err(DeserializationError::missing_union_arm( - Self::arrow_datatype(), - "", - invalid as _, - )), - }) - .collect::>>>() - .with_context("rerun.blueprint.components.SortOrder")?) - } -} diff --git a/crates/store/re_types/src/components/video_timestamp_ext.rs b/crates/store/re_types/src/components/video_timestamp_ext.rs index df7a6f9947bc..48efa5617d5e 100644 --- a/crates/store/re_types/src/components/video_timestamp_ext.rs +++ b/crates/store/re_types/src/components/video_timestamp_ext.rs @@ -1,9 +1,21 @@ use super::VideoTimestamp; impl VideoTimestamp { + /// Create new timestamp from seconds since video start. + #[inline] + pub fn from_seconds(seconds: f64) -> Self { + crate::datatypes::VideoTimestamp::from_nanoseconds((seconds * 1e9) as i64).into() + } + + /// Create new timestamp from milliseconds since video start. + #[inline] + pub fn from_milliseconds(milliseconds: f64) -> Self { + crate::datatypes::VideoTimestamp::from_nanoseconds((milliseconds * 1e6) as i64).into() + } + /// Create new timestamp from nanoseconds since video start. #[inline] - pub fn new_nanoseconds(nanos: i64) -> Self { - crate::datatypes::VideoTimestamp::new_nanoseconds(nanos).into() + pub fn from_nanoseconds(nanos: i64) -> Self { + crate::datatypes::VideoTimestamp::from_nanoseconds(nanos).into() } } diff --git a/crates/store/re_types/src/datatypes/video_timestamp_ext.rs b/crates/store/re_types/src/datatypes/video_timestamp_ext.rs index 342e0d1051db..4e17ee943174 100644 --- a/crates/store/re_types/src/datatypes/video_timestamp_ext.rs +++ b/crates/store/re_types/src/datatypes/video_timestamp_ext.rs @@ -3,7 +3,7 @@ use super::{VideoTimeMode, VideoTimestamp}; impl VideoTimestamp { /// Create new timestamp from nanoseconds since video start. #[inline] - pub fn new_nanoseconds(nanos: i64) -> Self { + pub fn from_nanoseconds(nanos: i64) -> Self { Self { video_time: nanos, time_mode: VideoTimeMode::Nanoseconds, diff --git a/crates/store/re_types_core/src/archetype.rs b/crates/store/re_types_core/src/archetype.rs index d00e2792f0d6..6eef97eb8176 100644 --- a/crates/store/re_types_core/src/archetype.rs +++ b/crates/store/re_types_core/src/archetype.rs @@ -194,6 +194,18 @@ impl GenericIndicatorComponent { pub const DEFAULT: Self = Self { _phantom: std::marker::PhantomData::, }; + + /// Create an array of indicator components of this type with the given length. + /// + /// This can be useful when sending columns of indicators with + /// `rerun::RecordingStream::send_columns`. + #[inline] + pub fn new_array(len: usize) -> GenericIndicatorComponentArray { + GenericIndicatorComponentArray { + len, + _phantom: std::marker::PhantomData::, + } + } } impl Default for GenericIndicatorComponent { @@ -221,6 +233,37 @@ impl crate::LoggableBatch for GenericIndicatorComponent { impl crate::ComponentBatch for GenericIndicatorComponent {} +/// A generic [indicator component] array of a given length. +/// +/// This can be useful when sending columns of indicators with +/// `rerun::RecordingStream::send_columns`. +/// +/// To create this type, call [`GenericIndicatorComponent::new_array`]. +/// +/// [indicator component]: [`Archetype::Indicator`] +#[derive(Debug, Clone, Copy)] +pub struct GenericIndicatorComponentArray { + len: usize, + _phantom: std::marker::PhantomData, +} + +impl crate::LoggableBatch for GenericIndicatorComponentArray { + type Name = ComponentName; + + #[inline] + fn name(&self) -> Self::Name { + GenericIndicatorComponent::::DEFAULT.name() + } + + #[inline] + fn to_arrow(&self) -> SerializationResult> { + let datatype = arrow2::datatypes::DataType::Null; + Ok(arrow2::array::NullArray::new(datatype, self.len).boxed()) + } +} + +impl crate::ComponentBatch for GenericIndicatorComponentArray {} + // --- /// An arbitrary named [indicator component]. diff --git a/crates/store/re_types_core/src/lib.rs b/crates/store/re_types_core/src/lib.rs index 443e0254d0a0..95230e3eb3db 100644 --- a/crates/store/re_types_core/src/lib.rs +++ b/crates/store/re_types_core/src/lib.rs @@ -144,3 +144,50 @@ pub mod external { pub use arrow2; pub use re_tuid; } + +/// Useful macro for staticlly asserting that a `struct` contains some specific fields. +/// +/// In particular, this is useful to statcially check that an archetype +/// has a specific component. +/// +/// ``` +/// # #[macro_use] extern crate re_types_core; +/// struct Data { +/// x: f32, +/// y: String, +/// z: u32, +/// } +/// +/// static_assert_struct_has_fields!(Data, x: f32, y: String); +/// ``` +/// +/// This will fail to compile because the type is wrong: +/// +/// ```compile_fail +/// # #[macro_use] extern crate re_types_core; +/// struct Data { +/// x: f32, +/// } +/// +/// static_assert_struct_has_fields!(Data, x: u32); +/// ``` +/// +/// This will fail to compile because the field is missing: +/// +/// ```compile_fail +/// # #[macro_use] extern crate re_types_core; +/// struct Data { +/// x: f32, +/// } +/// +/// static_assert_struct_has_fields!(Data, nosuch: f32); +/// ``` +/// +#[macro_export] +macro_rules! static_assert_struct_has_fields { + ($strct:ty, $($field:ident: $field_typ:ty),+) => { + const _: fn(&$strct) = |s: &$strct| { + $(let _: &$field_typ = &s.$field;)+ + }; + } +} diff --git a/crates/viewer/re_component_ui/src/datatype_uis/enum_combobox.rs b/crates/viewer/re_component_ui/src/datatype_uis/enum_combobox.rs index 318079ab9d96..e0e634512ec7 100644 --- a/crates/viewer/re_component_ui/src/datatype_uis/enum_combobox.rs +++ b/crates/viewer/re_component_ui/src/datatype_uis/enum_combobox.rs @@ -40,10 +40,7 @@ fn edit_view_enum_impl( }); combobox_response.response = combobox_response.response.on_hover_ui(|ui| { - ui.markdown_ui( - ui.id().with(prev_selected_value), - prev_selected_value.docstring_md(), - ); + ui.markdown_ui(prev_selected_value.docstring_md()); }); response_with_changes_of_inner(combobox_response) @@ -59,6 +56,6 @@ fn variant_ui( ) -> egui::Response { ui.selectable_value(current_value, variant, variant.to_string()) .on_hover_ui(|ui| { - ui.markdown_ui(ui.id().with(variant), variant.docstring_md()); + ui.markdown_ui(variant.docstring_md()); }) } diff --git a/crates/viewer/re_component_ui/src/lib.rs b/crates/viewer/re_component_ui/src/lib.rs index 33c72fbdd9e3..7704c0e9b554 100644 --- a/crates/viewer/re_component_ui/src/lib.rs +++ b/crates/viewer/re_component_ui/src/lib.rs @@ -26,9 +26,7 @@ use datatype_uis::{ }; use re_types::{ - blueprint::components::{ - BackgroundKind, Corner2D, LockRangeDuringZoom, SortKey, SortOrder, ViewFit, Visible, - }, + blueprint::components::{BackgroundKind, Corner2D, LockRangeDuringZoom, ViewFit, Visible}, components::{ AggregationPolicy, AlbedoFactor, AxisLength, Color, DepthMeter, DrawOrder, FillMode, FillRatio, GammaCorrection, ImagePlaneDistance, MagnificationFilter, MarkerSize, Name, @@ -91,8 +89,6 @@ pub fn create_component_ui_registry() -> re_viewer_context::ComponentUiRegistry registry.add_singleline_edit_or_view::(edit_view_enum); registry.add_singleline_edit_or_view::(edit_view_enum); registry.add_singleline_edit_or_view::(edit_view_enum); - registry.add_singleline_edit_or_view::(edit_view_enum); - registry.add_singleline_edit_or_view::(edit_view_enum); registry.add_singleline_edit_or_view::(edit_view_enum); registry.add_singleline_edit_or_view::(edit_view_enum); diff --git a/crates/viewer/re_data_ui/src/component_name.rs b/crates/viewer/re_data_ui/src/component_name.rs index 4bd735cc4330..7e771facb081 100644 --- a/crates/viewer/re_data_ui/src/component_name.rs +++ b/crates/viewer/re_data_ui/src/component_name.rs @@ -31,9 +31,9 @@ impl DataUi for ComponentName { .map(|info| info.docstring_md) { if ui_layout.is_selection_panel() { - ui.markdown_ui(egui::Id::new((self, "full")), markdown); + ui.markdown_ui(markdown); } else if let Some(first_line) = markdown.lines().next() { - ui.markdown_ui(egui::Id::new((self, "first_line")), first_line); + ui.markdown_ui(first_line); } } diff --git a/crates/viewer/re_data_ui/src/instance_path.rs b/crates/viewer/re_data_ui/src/instance_path.rs index 71838d8acba5..6db689510b4e 100644 --- a/crates/viewer/re_data_ui/src/instance_path.rs +++ b/crates/viewer/re_data_ui/src/instance_path.rs @@ -7,7 +7,7 @@ use re_types::{ archetypes, components, datatypes::{ChannelDatatype, ColorModel}, image::ImageKind, - Archetype, ComponentName, Loggable, + static_assert_struct_has_fields, Archetype, ComponentName, Loggable, }; use re_ui::{ContextExt as _, UiExt as _}; use re_viewer_context::{ @@ -251,6 +251,23 @@ fn preview_if_image_ui( entity_path: &re_log_types::EntityPath, component_map: &IntMap, ) -> Option<()> { + // First check assumptions: + static_assert_struct_has_fields!( + archetypes::Image, + buffer: components::ImageBuffer, + format: components::ImageFormat + ); + static_assert_struct_has_fields!( + archetypes::DepthImage, + buffer: components::ImageBuffer, + format: components::ImageFormat + ); + static_assert_struct_has_fields!( + archetypes::SegmentationImage, + buffer: components::ImageBuffer, + format: components::ImageFormat + ); + let image_buffer = component_map.get(&components::ImageBuffer::name())?; let buffer_row_id = image_buffer.row_id()?; let image_buffer = image_buffer diff --git a/crates/viewer/re_renderer/src/video/decoder/web.rs b/crates/viewer/re_renderer/src/video/decoder/web.rs index c72438854d11..c62d8072c090 100644 --- a/crates/viewer/re_renderer/src/video/decoder/web.rs +++ b/crates/viewer/re_renderer/src/video/decoder/web.rs @@ -104,7 +104,7 @@ impl VideoDecoder { data.config.coded_height as u32, ); - let mut this = Self { + Ok(Self { data, queue, texture, @@ -115,13 +115,7 @@ impl VideoDecoder { last_used_frame_timestamp: TimeMs::new(f64::MAX), current_segment_idx: usize::MAX, current_sample_idx: usize::MAX, - }; - - // immediately enqueue some frames, assuming playback at start - this.reset()?; - let _ = this.frame_at(TimeMs::new(0.0)); - - Ok(this) + }) } pub fn duration_ms(&self) -> f64 { @@ -165,13 +159,12 @@ impl VideoDecoder { // one would mean decoding and immediately discarding more frames than we otherwise // need to. if requested_segment_idx != self.current_segment_idx { - let segment_distance = - requested_segment_idx as isize - self.current_segment_idx as isize; - if segment_distance == 1 { + let segment_distance = requested_segment_idx.checked_sub(self.current_segment_idx); + if segment_distance == Some(1) { // forward seek to next segment - queue up the one _after_ requested self.enqueue_segment(requested_segment_idx + 1); } else { - // forward seek by N>1 OR backward seek across segments - reset + // Startup, forward seek by N>1, or backward seek across segments -> reset decoder if let Err(err) = self.reset() { return FrameDecodingResult::Error(err); } diff --git a/crates/viewer/re_space_view/src/view_property_ui.rs b/crates/viewer/re_space_view/src/view_property_ui.rs index 89ee31df2e59..1d976fda61d6 100644 --- a/crates/viewer/re_space_view/src/view_property_ui.rs +++ b/crates/viewer/re_space_view/src/view_property_ui.rs @@ -72,7 +72,6 @@ fn view_property_ui_impl( ui, field.component_name, reflection.display_name, - name, field, &blueprint_path, component_results.component_row_id(&field.component_name), @@ -90,7 +89,6 @@ fn view_property_ui_impl( ui, field.component_name, display_name, - name, field, &blueprint_path, component_results.component_row_id(&field.component_name), @@ -119,7 +117,6 @@ fn view_property_component_ui( ui: &mut egui::Ui, component_name: ComponentName, root_item_display_name: &str, - archetype_name: ArchetypeName, field: &ArchetypeFieldReflection, blueprint_path: &re_log_types::EntityPath, row_id: Option, @@ -173,8 +170,7 @@ fn view_property_component_ui( }; list_item_response.on_hover_ui(|ui| { - let id = egui::Id::new((archetype_name, field.display_name)); - ui.markdown_ui(id, field.docstring_md); + ui.markdown_ui(field.docstring_md); }); } diff --git a/crates/viewer/re_space_view_dataframe/Cargo.toml b/crates/viewer/re_space_view_dataframe/Cargo.toml index 80e23ad49ef5..9394d9ebcaa7 100644 --- a/crates/viewer/re_space_view_dataframe/Cargo.toml +++ b/crates/viewer/re_space_view_dataframe/Cargo.toml @@ -36,7 +36,7 @@ re_viewport_blueprint.workspace = true anyhow.workspace = true egui_extras.workspace = true -egui_table = { git = "https://github.com/rerun-io/egui_table.git", rev = "1c5db2cc3c4c8db79e85824f7ca6cca87110111f" } # main as of 2024-09-12 +egui_table = { git = "https://github.com/rerun-io/egui_table.git", rev = "c76473b244f03a7c67fbbbff9def6fc86c1ca4ea" } # main as of 2024-09-13 egui.workspace = true itertools.workspace = true thiserror.workspace = true diff --git a/crates/viewer/re_space_view_dataframe/src/space_view_class.rs b/crates/viewer/re_space_view_dataframe/src/space_view_class.rs index c2ebf321cca6..5fc62bdafa7d 100644 --- a/crates/viewer/re_space_view_dataframe/src/space_view_class.rs +++ b/crates/viewer/re_space_view_dataframe/src/space_view_class.rs @@ -2,10 +2,7 @@ use egui::Ui; use std::any::Any; use re_log_types::{EntityPath, EntityPathFilter, ResolvedTimeRange}; -use re_space_view::view_property_ui; -use re_types::blueprint::archetypes; use re_types_core::SpaceViewClassIdentifier; -use re_ui::list_item; use re_viewer_context::{ SpaceViewClass, SpaceViewClassRegistryError, SpaceViewId, SpaceViewState, SpaceViewStateExt, SpaceViewSystemExecutionError, SystemExecutionOutput, ViewQuery, ViewerContext, @@ -14,7 +11,7 @@ use re_viewport_blueprint::SpaceViewContents; use crate::dataframe_ui::dataframe_ui; use crate::expanded_rows::ExpandedRowsCache; -use crate::{query_kind::QueryKind, view_query::Query, visualizer_system::EmptySystem}; +use crate::{query_kind::QueryKind, visualizer_system::EmptySystem}; #[derive(Default)] struct DataframeSpaceViewState { @@ -105,26 +102,7 @@ mode sets the default time range to _everything_. You can override this in the s _space_origin: &EntityPath, space_view_id: SpaceViewId, ) -> Result<(), SpaceViewSystemExecutionError> { - crate::view_query::query_ui(ctx, ui, state, space_view_id)?; - - list_item::list_item_scope(ui, "dataframe_view_selection_ui", |ui| { - let view_query = Query::try_from_blueprint(ctx, space_view_id)?; - //TODO(#7070): column order and sorting needs much love - ui.add_enabled_ui( - matches!(view_query.kind(ctx), QueryKind::Range { .. }), - |ui| { - view_property_ui::( - ctx, - ui, - space_view_id, - self, - state, - ); - }, - ); - - Ok(()) - }) + crate::view_query::query_ui(ctx, ui, state, space_view_id) } fn ui( diff --git a/crates/viewer/re_space_view_spatial/src/lib.rs b/crates/viewer/re_space_view_spatial/src/lib.rs index 5519348f8556..238706096bb1 100644 --- a/crates/viewer/re_space_view_spatial/src/lib.rs +++ b/crates/viewer/re_space_view_spatial/src/lib.rs @@ -40,8 +40,12 @@ use re_space_view::DataResultQuery as _; use re_viewer_context::{ImageDecodeCache, ViewContext, ViewerContext}; use re_renderer::RenderContext; -use re_types::components::{Color, MediaType, Resolution}; -use re_types::{blueprint::components::BackgroundKind, components::ImageFormat}; +use re_types::{ + archetypes, + blueprint::components::BackgroundKind, + components::{self, Color, ImageFormat, MediaType, Resolution}, + static_assert_struct_has_fields, +}; use re_viewport_blueprint::{ViewProperty, ViewPropertyQueryError}; mod view_kind { @@ -57,6 +61,10 @@ fn resolution_of_image_at( query: &re_chunk_store::LatestAtQuery, entity_path: &re_log_types::EntityPath, ) -> Option { + // First check assumptions: + static_assert_struct_has_fields!(archetypes::Image, format: components::ImageFormat); + static_assert_struct_has_fields!(archetypes::EncodedImage, blob: components::Blob); + let db = ctx.recording(); if let Some((_, image_format)) = db.latest_at_component::(entity_path, query) { diff --git a/crates/viewer/re_space_view_spatial/src/view_3d.rs b/crates/viewer/re_space_view_spatial/src/view_3d.rs index 699b81714367..3262effa2760 100644 --- a/crates/viewer/re_space_view_spatial/src/view_3d.rs +++ b/crates/viewer/re_space_view_spatial/src/view_3d.rs @@ -388,10 +388,7 @@ impl SpaceViewClass for SpatialSpaceView3D { "Scene up is unspecified".to_owned() }; ui.label(up_description).on_hover_ui(|ui| { - ui.markdown_ui( - egui::Id::new("view_coordinates_tooltip"), - "Set with `rerun.ViewCoordinates`.", - ); + ui.markdown_ui("Set with `rerun.ViewCoordinates`."); }); if let Some(eye) = &state.state_3d.view_eye { diff --git a/crates/viewer/re_space_view_text_document/src/space_view_class.rs b/crates/viewer/re_space_view_text_document/src/space_view_class.rs index f72e1fe03261..082925329ce9 100644 --- a/crates/viewer/re_space_view_text_document/src/space_view_class.rs +++ b/crates/viewer/re_space_view_text_document/src/space_view_class.rs @@ -157,7 +157,7 @@ Displays text from a text component, as raw text or markdown." .or_insert(egui::FontId::proportional(32.0)) .size = 24.0; - egui_commonmark::CommonMarkViewer::new("markdown_viewer") + egui_commonmark::CommonMarkViewer::new() .max_image_width(Some(ui.available_width().floor() as _)) .show(ui, &mut state.commonmark_cache, body); return; diff --git a/crates/viewer/re_time_panel/benches/bench_density_graph.rs b/crates/viewer/re_time_panel/benches/bench_density_graph.rs index a80c5da91ec0..017689dafef7 100644 --- a/crates/viewer/re_time_panel/benches/bench_density_graph.rs +++ b/crates/viewer/re_time_panel/benches/bench_density_graph.rs @@ -18,49 +18,51 @@ use re_time_panel::__bench::{ }; fn run(b: &mut Bencher<'_, WallTime>, config: DensityGraphBuilderConfig, entry: ChunkEntry) { - egui::__run_test_ui(|ui| { - let row_rect = ui.max_rect(); - assert!(row_rect.width() > 100.0 && row_rect.height() > 100.0); - - let mut db = EntityDb::with_store_config( - StoreId::from_string(StoreKind::Recording, "test".into()), - ChunkStoreConfig::COMPACTION_DISABLED, - ); - let entity_path = re_log_types::EntityPath::parse_strict("/data").unwrap(); - let timeline = re_log_types::Timeline::log_time(); - - add_data( - &mut db, - &entity_path, - entry.num_chunks, - entry.num_rows_per_chunk, - entry.sorted, - entry.time_start_ms, - timeline, - ) - .unwrap(); - - let item = TimePanelItem { - entity_path, - component_name: None, - }; - - let time_range = db - .time_range_for(&timeline) - .unwrap_or(ResolvedTimeRange::EMPTY); - let time_ranges_ui = - TimeRangesUi::new(row_rect.x_range(), time_range.into(), &[time_range]); - - b.iter(|| { - black_box(build_density_graph( - ui, - &time_ranges_ui, - row_rect, - &db, - &item, + egui::__run_test_ctx(|ctx| { + egui::CentralPanel::default().show(ctx, |ui| { + let row_rect = ui.max_rect(); + assert!(row_rect.width() > 100.0 && row_rect.height() > 100.0); + + let mut db = EntityDb::with_store_config( + StoreId::from_string(StoreKind::Recording, "test".into()), + ChunkStoreConfig::COMPACTION_DISABLED, + ); + let entity_path = re_log_types::EntityPath::parse_strict("/data").unwrap(); + let timeline = re_log_types::Timeline::log_time(); + + add_data( + &mut db, + &entity_path, + entry.num_chunks, + entry.num_rows_per_chunk, + entry.sorted, + entry.time_start_ms, timeline, - config, - )); + ) + .unwrap(); + + let item = TimePanelItem { + entity_path, + component_name: None, + }; + + let time_range = db + .time_range_for(&timeline) + .unwrap_or(ResolvedTimeRange::EMPTY); + let time_ranges_ui = + TimeRangesUi::new(row_rect.x_range(), time_range.into(), &[time_range]); + + b.iter(|| { + black_box(build_density_graph( + ui, + &time_ranges_ui, + row_rect, + &db, + &item, + timeline, + config, + )); + }); }); }); } diff --git a/crates/viewer/re_time_panel/src/time_control_ui.rs b/crates/viewer/re_time_panel/src/time_control_ui.rs index 69525e5664d5..3729c62852ef 100644 --- a/crates/viewer/re_time_panel/src/time_control_ui.rs +++ b/crates/viewer/re_time_panel/src/time_control_ui.rs @@ -44,7 +44,6 @@ impl TimeControlUi { .on_hover_ui(|ui| { list_item::list_item_scope(ui, "tooltip", |ui| { ui.markdown_ui( - egui::Id::new("timeline_selector_tooltip"), r" Select timeline. diff --git a/crates/viewer/re_ui/src/lib.rs b/crates/viewer/re_ui/src/lib.rs index 53ef07b727d3..efb27a0a1c59 100644 --- a/crates/viewer/re_ui/src/lib.rs +++ b/crates/viewer/re_ui/src/lib.rs @@ -91,7 +91,7 @@ pub fn apply_style_and_install_loaders(egui_ctx: &egui::Context) { ); egui_ctx.options_mut(|o| { - o.follow_system_theme = false; + o.theme_preference = egui::ThemePreference::Dark; o.fallback_theme = egui::Theme::Dark; }); diff --git a/crates/viewer/re_ui/src/section_collapsing_header.rs b/crates/viewer/re_ui/src/section_collapsing_header.rs index 9f59c2342e83..8c23857da8f4 100644 --- a/crates/viewer/re_ui/src/section_collapsing_header.rs +++ b/crates/viewer/re_ui/src/section_collapsing_header.rs @@ -56,7 +56,7 @@ impl<'a> SectionCollapsingHeader<'a> { #[inline] pub fn help_markdown(mut self, help: &'a str) -> Self { self.help = Some(Box::new(move |ui| { - ui.markdown_ui(egui::Id::new(help), help); + ui.markdown_ui(help); })); self } diff --git a/crates/viewer/re_ui/src/ui_ext.rs b/crates/viewer/re_ui/src/ui_ext.rs index 31707859ce19..00de8eacd1a2 100644 --- a/crates/viewer/re_ui/src/ui_ext.rs +++ b/crates/viewer/re_ui/src/ui_ext.rs @@ -934,7 +934,7 @@ pub trait UiExt { } /// Show some markdown - fn markdown_ui(&mut self, id: egui::Id, markdown: &str) { + fn markdown_ui(&mut self, markdown: &str) { use parking_lot::Mutex; use std::sync::Arc; @@ -946,7 +946,7 @@ pub trait UiExt { .clone() }); - egui_commonmark::CommonMarkViewer::new(id).show(ui, &mut commonmark_cache.lock(), markdown); + egui_commonmark::CommonMarkViewer::new().show(ui, &mut commonmark_cache.lock(), markdown); } /// A drop-down menu with a list of options. diff --git a/crates/viewer/re_viewer/src/app.rs b/crates/viewer/re_viewer/src/app.rs index 7cd35e91dd5b..fae36b6cf3d9 100644 --- a/crates/viewer/re_viewer/src/app.rs +++ b/crates/viewer/re_viewer/src/app.rs @@ -897,6 +897,14 @@ impl App { .frame(DesignTokens::top_panel_frame()) .show_animated_inside(ui, self.egui_debug_panel_open, |ui| { egui::ScrollArea::vertical().show(ui, |ui| { + if ui + .button("request_discard") + .on_hover_text("Request a second layout pass. Just for testing.") + .clicked() + { + ui.ctx().request_discard(); + } + egui::CollapsingHeader::new("egui settings") .default_open(false) .show(ui, |ui| { diff --git a/crates/viewer/re_viewer/src/app_state.rs b/crates/viewer/re_viewer/src/app_state.rs index 4028996d82a0..0e95e539b6f1 100644 --- a/crates/viewer/re_viewer/src/app_state.rs +++ b/crates/viewer/re_viewer/src/app_state.rs @@ -272,6 +272,10 @@ impl AppState { focused_item, }; + // We move the time at the very start of the frame, + // so that we always show the latest data when we're in "follow" mode. + move_time(&ctx, recording, rx); + // Update the viewport. May spawn new views and handle queued requests (like screenshots). viewport.on_frame_start(&ctx); @@ -466,41 +470,6 @@ impl AppState { // Process deferred layout operations and apply updates back to blueprint viewport.update_and_sync_tile_tree_to_blueprint(&ctx); - { - // We move the time at the very end of the frame, - // so we have one frame to see the first data before we move the time. - let dt = ui.ctx().input(|i| i.stable_dt); - - // Are we still connected to the data source for the current store? - let more_data_is_coming = if let Some(store_source) = &recording.data_source { - rx.sources().iter().any(|s| s.as_ref() == store_source) - } else { - false - }; - - let recording_needs_repaint = ctx.rec_cfg.time_ctrl.write().update( - recording.times_per_timeline(), - dt, - more_data_is_coming, - ); - - let blueprint_needs_repaint = if ctx.app_options.inspect_blueprint_timeline { - ctx.blueprint_cfg.time_ctrl.write().update( - ctx.store_context.blueprint.times_per_timeline(), - dt, - more_data_is_coming, - ) - } else { - re_viewer_context::NeedsRepaint::No - }; - - if recording_needs_repaint == re_viewer_context::NeedsRepaint::Yes - || blueprint_needs_repaint == re_viewer_context::NeedsRepaint::Yes - { - ui.ctx().request_repaint(); - } - } - if WATERMARK { ui.ctx().paint_watermark(); } @@ -537,6 +506,39 @@ impl AppState { } } +fn move_time(ctx: &ViewerContext<'_>, recording: &EntityDb, rx: &ReceiveSet) { + let dt = ctx.egui_ctx.input(|i| i.stable_dt); + + // Are we still connected to the data source for the current store? + let more_data_is_coming = if let Some(store_source) = &recording.data_source { + rx.sources().iter().any(|s| s.as_ref() == store_source) + } else { + false + }; + + let recording_needs_repaint = ctx.rec_cfg.time_ctrl.write().update( + recording.times_per_timeline(), + dt, + more_data_is_coming, + ); + + let blueprint_needs_repaint = if ctx.app_options.inspect_blueprint_timeline { + ctx.blueprint_cfg.time_ctrl.write().update( + ctx.store_context.blueprint.times_per_timeline(), + dt, + more_data_is_coming, + ) + } else { + re_viewer_context::NeedsRepaint::No + }; + + if recording_needs_repaint == re_viewer_context::NeedsRepaint::Yes + || blueprint_needs_repaint == re_viewer_context::NeedsRepaint::Yes + { + ctx.egui_ctx.request_repaint(); + } +} + fn recording_config_entry<'cfgs>( configs: &'cfgs mut HashMap, id: StoreId, diff --git a/crates/viewer/re_viewer/src/blueprint/validation_gen/mod.rs b/crates/viewer/re_viewer/src/blueprint/validation_gen/mod.rs index 4dd334f2c5a7..1a0d2ece893e 100644 --- a/crates/viewer/re_viewer/src/blueprint/validation_gen/mod.rs +++ b/crates/viewer/re_viewer/src/blueprint/validation_gen/mod.rs @@ -13,8 +13,6 @@ pub use re_types::blueprint::components::PanelState; pub use re_types::blueprint::components::QueryExpression; pub use re_types::blueprint::components::QueryKind; pub use re_types::blueprint::components::RowShare; -pub use re_types::blueprint::components::SortKey; -pub use re_types::blueprint::components::SortOrder; pub use re_types::blueprint::components::SpaceViewClass; pub use re_types::blueprint::components::SpaceViewOrigin; pub use re_types::blueprint::components::TensorDimensionIndexSlider; @@ -56,8 +54,6 @@ pub fn is_valid_blueprint(blueprint: &EntityDb) -> bool { && validate_component::(blueprint) && validate_component::(blueprint) && validate_component::(blueprint) - && validate_component::(blueprint) - && validate_component::(blueprint) && validate_component::(blueprint) && validate_component::(blueprint) && validate_component::(blueprint) diff --git a/crates/viewer/re_viewer/src/reflection/mod.rs b/crates/viewer/re_viewer/src/reflection/mod.rs index 38617ffe1f4b..666d9d36560a 100644 --- a/crates/viewer/re_viewer/src/reflection/mod.rs +++ b/crates/viewer/re_viewer/src/reflection/mod.rs @@ -160,20 +160,6 @@ fn generate_component_reflection() -> Result::name(), - ComponentReflection { - docstring_md: "Primary element by which to group by in a temporal data table.", - placeholder: Some(SortKey::default().to_arrow()?), - }, - ), - ( - ::name(), - ComponentReflection { - docstring_md: "Sort order for data table.", - placeholder: Some(SortOrder::default().to_arrow()?), - }, - ), ( ::name(), ComponentReflection { @@ -1470,7 +1456,7 @@ fn generate_archetype_reflection() -> ArchetypeReflectionMap { is_required : true, }, ArchetypeFieldReflection { component_name : "rerun.components.EntityPath".into(), display_name : "Video reference", docstring_md : - "Optional reference to an entity with a [`archetypes.AssetVideo`](https://rerun.io/docs/reference/types/archetypes/asset_video).\n\nIf none is specified, the video is assumed to be at the same entity.\nNote that blueprint overrides on the referenced video will be ignored regardless,\nas this is always interpreted as a reference to the data store.\n\n⚠\u{fe0f} **This type is experimental and may be removed in future versions**", + "Optional reference to an entity with a [`archetypes.AssetVideo`](https://rerun.io/docs/reference/types/archetypes/asset_video).\n\nIf none is specified, the video is assumed to be at the same entity.\nNote that blueprint overrides on the referenced video will be ignored regardless,\nas this is always interpreted as a reference to the data store.\n\nFor a series of video frame references, it is recommended to specify this path only once\nat the beginning of the series and then rely on latest-at query semantics to\nkeep the video reference active.\n\n⚠\u{fe0f} **This type is experimental and may be removed in future versions**", is_required : false, }, ], }, @@ -1703,21 +1689,6 @@ fn generate_archetype_reflection() -> ArchetypeReflectionMap { ], }, ), - ( - ArchetypeName::new("rerun.blueprint.archetypes.TimeRangeTableOrder"), - ArchetypeReflection { - display_name: "Time range table order", - fields: vec![ - ArchetypeFieldReflection { component_name : - "rerun.blueprint.components.SortKey".into(), display_name : - "Sort key", docstring_md : "The primary sort key", is_required : - false, }, ArchetypeFieldReflection { component_name : - "rerun.blueprint.components.SortOrder".into(), display_name : - "Sort order", docstring_md : "The sort order", is_required : false, - }, - ], - }, - ), ( ArchetypeName::new("rerun.blueprint.archetypes.ViewportBlueprint"), ArchetypeReflection { diff --git a/crates/viewer/re_viewer_context/src/space_view/space_view_class_placeholder.rs b/crates/viewer/re_viewer_context/src/space_view/space_view_class_placeholder.rs index efc9671be03a..3aa10c059ac7 100644 --- a/crates/viewer/re_viewer_context/src/space_view/space_view_class_placeholder.rs +++ b/crates/viewer/re_viewer_context/src/space_view/space_view_class_placeholder.rs @@ -54,10 +54,7 @@ impl SpaceViewClass for SpaceViewClassPlaceholder { _query: &ViewQuery<'_>, _system_output: SystemExecutionOutput, ) -> Result<(), SpaceViewSystemExecutionError> { - ui.markdown_ui( - egui::Id::new(self.display_name()), - &self.help_markdown(ctx.egui_ctx), - ); + ui.markdown_ui(&self.help_markdown(ctx.egui_ctx)); Ok(()) } diff --git a/crates/viewer/re_viewer_context/src/test_context.rs b/crates/viewer/re_viewer_context/src/test_context.rs index e87a4ee04041..5c982a97104d 100644 --- a/crates/viewer/re_viewer_context/src/test_context.rs +++ b/crates/viewer/re_viewer_context/src/test_context.rs @@ -51,48 +51,50 @@ impl TestContext { } pub fn run(&self, mut func: impl FnMut(&ViewerContext<'_>, &mut egui::Ui)) { - egui::__run_test_ui(|ui| { - re_ui::apply_style_and_install_loaders(ui.ctx()); - let blueprint_query = LatestAtQuery::latest(self.active_timeline); - let (command_sender, _) = command_channel(); - let component_ui_registry = ComponentUiRegistry::new(Box::new( - |_ctx, _ui, _ui_layout, _query, _db, _entity_path, _row_id, _component| {}, - )); + egui::__run_test_ctx(|ctx| { + egui::CentralPanel::default().show(ctx, |ui| { + re_ui::apply_style_and_install_loaders(ui.ctx()); + let blueprint_query = LatestAtQuery::latest(self.active_timeline); + let (command_sender, _) = command_channel(); + let component_ui_registry = ComponentUiRegistry::new(Box::new( + |_ctx, _ui, _ui_layout, _query, _db, _entity_path, _row_id, _component| {}, + )); - let store_context = StoreContext { - app_id: "rerun_test".into(), - blueprint: &self.blueprint_store, - default_blueprint: None, - recording: &self.recording_store, - bundle: &Default::default(), - hub: &Default::default(), - }; + let store_context = StoreContext { + app_id: "rerun_test".into(), + blueprint: &self.blueprint_store, + default_blueprint: None, + recording: &self.recording_store, + bundle: &Default::default(), + hub: &Default::default(), + }; - let rec_cfg = RecordingConfig::default(); - rec_cfg.time_ctrl.write().set_timeline(self.active_timeline); + let rec_cfg = RecordingConfig::default(); + rec_cfg.time_ctrl.write().set_timeline(self.active_timeline); - let egui_context = ui.ctx().clone(); - let ctx = ViewerContext { - app_options: &Default::default(), - cache: &Default::default(), - reflection: &Default::default(), - component_ui_registry: &component_ui_registry, - space_view_class_registry: &self.space_view_class_registry, - store_context: &store_context, - applicable_entities_per_visualizer: &Default::default(), - indicated_entities_per_visualizer: &Default::default(), - query_results: &Default::default(), - rec_cfg: &rec_cfg, - blueprint_cfg: &Default::default(), - selection_state: &self.selection_state, - blueprint_query: &blueprint_query, - egui_ctx: &egui_context, - render_ctx: None, - command_sender: &command_sender, - focused_item: &None, - }; + let egui_context = ui.ctx().clone(); + let ctx = ViewerContext { + app_options: &Default::default(), + cache: &Default::default(), + reflection: &Default::default(), + component_ui_registry: &component_ui_registry, + space_view_class_registry: &self.space_view_class_registry, + store_context: &store_context, + applicable_entities_per_visualizer: &Default::default(), + indicated_entities_per_visualizer: &Default::default(), + query_results: &Default::default(), + rec_cfg: &rec_cfg, + blueprint_cfg: &Default::default(), + selection_state: &self.selection_state, + blueprint_query: &blueprint_query, + egui_ctx: &egui_context, + render_ctx: None, + command_sender: &command_sender, + focused_item: &None, + }; - func(&ctx, ui); + func(&ctx, ui); + }); }); } } diff --git a/crates/viewer/re_viewport/src/viewport.rs b/crates/viewer/re_viewport/src/viewport.rs index 1c92b78d3bf5..9df0370492af 100644 --- a/crates/viewer/re_viewport/src/viewport.rs +++ b/crates/viewer/re_viewport/src/viewport.rs @@ -100,7 +100,7 @@ impl<'a> Viewport<'a> { let Viewport { blueprint, .. } = self; let is_zero_sized_viewport = ui.available_size().min_elem() <= 0.0; - if is_zero_sized_viewport { + if is_zero_sized_viewport || !ui.is_visible() { return; } @@ -488,7 +488,7 @@ impl<'a, 'b> egui_tiles::Behavior for TabViewer<'a, 'b> { }; let is_zero_sized_viewport = ui.available_size().min_elem() <= 0.0; - if is_zero_sized_viewport { + if is_zero_sized_viewport || !ui.is_visible() { return Default::default(); } @@ -499,8 +499,7 @@ impl<'a, 'b> egui_tiles::Behavior for TabViewer<'a, 'b> { return Default::default(); }; - let (query, system_output) = - self.executed_systems_per_space_view.remove(view_id).unwrap_or_else(|| { + let (query, system_output) = self.executed_systems_per_space_view.remove(view_id).unwrap_or_else(|| { // The space view's systems haven't been executed. // This may indicate that the egui_tiles tree is not in sync // with the blueprint tree. @@ -509,34 +508,34 @@ impl<'a, 'b> egui_tiles::Behavior for TabViewer<'a, 'b> { if cfg!(debug_assertions) { re_log::warn_once!( - "Visualizers for space view {:?} haven't been executed prior to display. This should never happen, please report a bug.", - space_view_blueprint.display_name_or_default() - ); + "Visualizers for space view {:?} haven't been executed prior to display. This should never happen, please report a bug.", + space_view_blueprint.display_name_or_default() + ); } - { - let ctx: &'a ViewerContext<'_> = self.ctx; - let view = space_view_blueprint; - re_tracing::profile_function!(view.class_identifier().as_str()); - let query_result = ctx.lookup_query_result(view.id); + let ctx: &'a ViewerContext<'_> = self.ctx; + let view = space_view_blueprint; + re_tracing::profile_scope!("late-system-execute", view.class_identifier().as_str()); - let mut per_visualizer_data_results = re_viewer_context::PerSystemDataResults::default(); - { - re_tracing::profile_scope!("per_system_data_results"); - - query_result.tree.visit(&mut |node| { - for system in &node.data_result.visualizers { - per_visualizer_data_results - .entry(*system) - .or_default() - .push(&node.data_result); - } - true - }); - } + let query_result = ctx.lookup_query_result(view.id); + + let mut per_visualizer_data_results = re_viewer_context::PerSystemDataResults::default(); - execute_systems_for_space_view(ctx, view, latest_at, self.view_states) + { + re_tracing::profile_scope!("per_system_data_results"); + + query_result.tree.visit(&mut |node| { + for system in &node.data_result.visualizers { + per_visualizer_data_results + .entry(*system) + .or_default() + .push(&node.data_result); + } + true + }); } + + execute_systems_for_space_view(ctx, view, latest_at, self.view_states) }); let class = space_view_blueprint.class(self.ctx.space_view_class_registry); @@ -659,7 +658,7 @@ impl<'a, 'b> egui_tiles::Behavior for TabViewer<'a, 'b> { &mut self, tiles: &egui_tiles::Tiles, ui: &mut egui::Ui, - tile_id: egui_tiles::TileId, + _tile_id: egui_tiles::TileId, tabs: &egui_tiles::Tabs, _scroll_offset: &mut f32, ) { @@ -703,7 +702,7 @@ impl<'a, 'b> egui_tiles::Behavior for TabViewer<'a, 'b> { .class(self.ctx.space_view_class_registry) .help_markdown(self.ctx.egui_ctx); ui.help_hover_button().on_hover_ui(|ui| { - ui.markdown_ui(ui.id().with(tile_id), &help_markdown); + ui.markdown_ui(&help_markdown); }); } diff --git a/deny.toml b/deny.toml index 31505b137cd2..d99c623c3e5d 100644 --- a/deny.toml +++ b/deny.toml @@ -29,7 +29,7 @@ all-features = true [advisories] version = 2 ignore = [ - "RUSTSEC-2023-0081", # TODO(#5998): unmaintained crate "safemem" pulled in by "cargo-run-wasm" + "RUSTSEC-2023-0086", # TODO(#3741): Waiting for https://github.com/apache/arrow-rs/pull/6401 ] diff --git a/docs/content/reference/types/archetypes/asset_video.md b/docs/content/reference/types/archetypes/asset_video.md index 110d10844a8b..4db7fff313df 100644 --- a/docs/content/reference/types/archetypes/asset_video.md +++ b/docs/content/reference/types/archetypes/asset_video.md @@ -25,3 +25,17 @@ In order to display a video, you need to log a [`archetypes.VideoFrameReference` * 🐍 [Python API docs for `AssetVideo`](https://ref.rerun.io/docs/python/stable/common/archetypes#rerun.archetypes.AssetVideo) * 🦀 [Rust API docs for `AssetVideo`](https://docs.rs/rerun/latest/rerun/archetypes/struct.AssetVideo.html) +## Example + +### Video with explicit frames + +snippet: archetypes/video_manual_frames + + + + + + + + + diff --git a/docs/content/reference/types/archetypes/video_frame_reference.md b/docs/content/reference/types/archetypes/video_frame_reference.md index 641e9cbe7d6d..0fe64d88afef 100644 --- a/docs/content/reference/types/archetypes/video_frame_reference.md +++ b/docs/content/reference/types/archetypes/video_frame_reference.md @@ -8,7 +8,8 @@ title: "VideoFrameReference" References a single video frame. -Used to display video frames from a [`archetypes.AssetVideo`](https://rerun.io/docs/reference/types/archetypes/asset_video). +Used to display individual video frames from a [`archetypes.AssetVideo`](https://rerun.io/docs/reference/types/archetypes/asset_video). +To show an entire video, a fideo frame reference for each frame of the video should be logged. ## Components @@ -21,3 +22,17 @@ Used to display video frames from a [`archetypes.AssetVideo`](https://rerun.io/d * 🐍 [Python API docs for `VideoFrameReference`](https://ref.rerun.io/docs/python/stable/common/archetypes#rerun.archetypes.VideoFrameReference) * 🦀 [Rust API docs for `VideoFrameReference`](https://docs.rs/rerun/latest/rerun/archetypes/struct.VideoFrameReference.html) +## Example + +### Video with explicit frames + +snippet: archetypes/video_manual_frames + + + + + + + + + diff --git a/docs/snippets/all/archetypes/asset3d_simple.cpp b/docs/snippets/all/archetypes/asset3d_simple.cpp index 1689e792ef26..9d2c9893983b 100644 --- a/docs/snippets/all/archetypes/asset3d_simple.cpp +++ b/docs/snippets/all/archetypes/asset3d_simple.cpp @@ -2,9 +2,7 @@ #include -#include #include -#include int main(int argc, char* argv[]) { if (argc < 2) { diff --git a/docs/snippets/all/archetypes/video_manual_frames.cpp b/docs/snippets/all/archetypes/video_manual_frames.cpp new file mode 100644 index 000000000000..b24fd12eef85 --- /dev/null +++ b/docs/snippets/all/archetypes/video_manual_frames.cpp @@ -0,0 +1,50 @@ +// Log a video asset using manually created frame references. +// TODO(#7298): ⚠️ Video is currently only supported in the Rerun web viewer. + +#include + +#include + +using namespace std::chrono_literals; + +int main(int argc, char* argv[]) { + if (argc < 2) { + // TODO(#7354): Only mp4 is supported for now. + std::cerr << "Usage: " << argv[0] << " " << std::endl; + return 1; + } + + const auto path = argv[1]; + + const auto rec = rerun::RecordingStream("rerun_example_asset_video_manual_frames"); + rec.spawn().exit_on_failure(); + + // Log video asset which is referred to by frame references. + // Make sure it's available on the timeline used for the frame references. + rec.set_time_seconds("video_time", 0.0); + rec.log("video", rerun::AssetVideo::from_file(path).value_or_throw()); + + // Send frame references for every 0.1 seconds over a total of 10 seconds. + // Naturally, this will result in a choppy playback and only makes sense if the video is 10 seconds or longer. + // TODO(#7368): Point to example using `send_video_frames`. + // + // Use `send_columns` to send all frame references in a single call. + std::vector times(10 * 10); + std::vector video_timestamps(10 * 10); + for (size_t i = 0; i < times.size(); i++) { + times[i] = 100ms * i; + video_timestamps[i] = rerun::components::VideoTimestamp(times[i]); + } + auto video_frame_reference_indicators = + rerun::ComponentColumn::from_indicators( + static_cast(times.size()) + ); + rec.send_columns( + "video", + rerun::TimeColumn::from_times("video_time", rerun::borrow(times)), + { + video_frame_reference_indicators.value_or_throw(), + rerun::ComponentColumn::from_loggable(rerun::borrow(video_timestamps)).value_or_throw(), + } + ); +} diff --git a/docs/snippets/all/archetypes/video_manual_frames.py b/docs/snippets/all/archetypes/video_manual_frames.py new file mode 100644 index 000000000000..db69b510dd01 --- /dev/null +++ b/docs/snippets/all/archetypes/video_manual_frames.py @@ -0,0 +1,30 @@ +"""Log a video asset using manually created frame references.""" +# TODO(#7298): ⚠️ Video is currently only supported in the Rerun web viewer. + +import sys + +import numpy as np +import rerun as rr + +if len(sys.argv) < 2: + # TODO(#7354): Only mp4 is supported for now. + print(f"Usage: {sys.argv[0]} ") + sys.exit(1) + +rr.init("rerun_example_asset_video_manual_frames", spawn=True) + +# Log video asset which is referred to by frame references. +rr.set_time_seconds("video_time", 0) # Make sure it's available on the timeline used for the frame references. +rr.log("video", rr.AssetVideo(path=sys.argv[1])) + +# Send frame references for every 0.1 seconds over a total of 10 seconds. +# Naturally, this will result in a choppy playback and only makes sense if the video is 10 seconds or longer. +# TODO(#7368): Point to example using `send_video_frames`. +# +# Use `send_columns` to send all frame references in a single call. +times = np.arange(0.0, 10.0, 0.1) +rr.send_columns( + "video", + times=[rr.TimeSecondsColumn("video_time", times)], + components=[rr.VideoFrameReference.indicator(), rr.components.VideoTimestamp.seconds(times)], +) diff --git a/docs/snippets/all/archetypes/video_manual_frames.rs b/docs/snippets/all/archetypes/video_manual_frames.rs new file mode 100644 index 000000000000..24e7f25fe799 --- /dev/null +++ b/docs/snippets/all/archetypes/video_manual_frames.rs @@ -0,0 +1,40 @@ +//! Log a video asset using manually created frame references. +//! TODO(#7298): ⚠️ Video is currently only supported in the Rerun web viewer. + +use rerun::{external::anyhow, TimeColumn}; + +fn main() -> anyhow::Result<()> { + let args = _args; + let Some(path) = args.get(1) else { + // TODO(#7354): Only mp4 is supported for now. + anyhow::bail!("Usage: {} ", args[0]); + }; + + let rec = + rerun::RecordingStreamBuilder::new("rerun_example_asset_video_manual_frames").spawn()?; + + // Log video asset which is referred to by frame references. + rec.set_time_seconds("video_time", 0.0); // Make sure it's available on the timeline used for the frame references. + rec.log("video", &rerun::AssetVideo::from_file_path(path)?)?; + + // Send frame references for every 0.1 seconds over a total of 10 seconds. + // Naturally, this will result in a choppy playback and only makes sense if the video is 10 seconds or longer. + // TODO(#7368): Point to example using `send_video_frames`. + // + // Use `send_columns` to send all frame references in a single call. + let times = (0..(10 * 10)).map(|t| t as f64 * 0.1).collect::>(); + let time_column = TimeColumn::new_seconds("video_time", times.iter().copied()); + let frame_reference_indicators = + ::Indicator::new_array(times.len()); + let video_timestamps = times + .into_iter() + .map(rerun::components::VideoTimestamp::from_seconds) + .collect::>(); + rec.send_columns( + "video", + [time_column], + [&frame_reference_indicators as _, &video_timestamps as _], + )?; + + Ok(()) +} diff --git a/docs/snippets/all/archetypes/video_simple.cpp b/docs/snippets/all/archetypes/video_simple.cpp deleted file mode 100644 index 82907b9ed920..000000000000 --- a/docs/snippets/all/archetypes/video_simple.cpp +++ /dev/null @@ -1,21 +0,0 @@ -// Log a video file. - -#include - -#include -#include -#include - -int main(int argc, char* argv[]) { - if (argc < 2) { - std::cerr << "Usage: " << argv[0] << " " << std::endl; - return 1; - } - - const auto path = argv[1]; - - const auto rec = rerun::RecordingStream("rerun_example_asset_video"); - rec.spawn().exit_on_failure(); - - rec.log("world/video", rerun::AssetVideo::from_file(path).value_or_throw()); -} diff --git a/docs/snippets/all/archetypes/video_simple.py b/docs/snippets/all/archetypes/video_simple.py deleted file mode 100755 index a4ef1ac7fe28..000000000000 --- a/docs/snippets/all/archetypes/video_simple.py +++ /dev/null @@ -1,13 +0,0 @@ -"""Log a video file.""" - -import sys - -import rerun as rr - -if len(sys.argv) < 2: - print(f"Usage: {sys.argv[0]} ") - sys.exit(1) - -rr.init("rerun_example_asset_video", spawn=True) - -rr.log("world/video", rr.AssetVideo(path=sys.argv[1])) diff --git a/docs/snippets/all/archetypes/video_simple.rs b/docs/snippets/all/archetypes/video_simple.rs deleted file mode 100644 index 19abd3f95446..000000000000 --- a/docs/snippets/all/archetypes/video_simple.rs +++ /dev/null @@ -1,16 +0,0 @@ -//! Log a video file. - -use rerun::external::anyhow; - -fn main() -> anyhow::Result<()> { - let args = std::env::args().collect::>(); - let Some(path) = args.get(1) else { - anyhow::bail!("Usage: {} ", args[0]); - }; - - let rec = rerun::RecordingStreamBuilder::new("rerun_example_asset_video").spawn()?; - - rec.log("world/video", &rerun::AssetVideo::from_file_path(path)?)?; - - Ok(()) -} diff --git a/docs/snippets/snippets.toml b/docs/snippets/snippets.toml index 535a82f4ca31..2be554f6a1bd 100644 --- a/docs/snippets/snippets.toml +++ b/docs/snippets/snippets.toml @@ -188,4 +188,6 @@ quick_start = [ # These examples don't have exactly the same implementation. [extra_args] "archetypes/asset3d_simple" = ["$config_dir/../../tests/assets/cube.glb"] "archetypes/asset3d_out_of_tree" = ["$config_dir/../../tests/assets/cube.glb"] -"archetypes/video_simple" = ["$config_dir/../../tests/assets/empty.mp4"] +"archetypes/video_manual_frames" = [ + "$config_dir/../../tests/assets/video/Big_Buck_Bunny_1080_10s_av1.mp4", +] diff --git a/rerun_cpp/src/rerun/archetypes/asset3d.hpp b/rerun_cpp/src/rerun/archetypes/asset3d.hpp index 8ce9db4f95cf..812cf71eacb9 100644 --- a/rerun_cpp/src/rerun/archetypes/asset3d.hpp +++ b/rerun_cpp/src/rerun/archetypes/asset3d.hpp @@ -33,9 +33,7 @@ namespace rerun::archetypes { /// ```cpp /// #include /// - /// #include /// #include - /// #include /// /// int main(int argc, char* argv[]) { /// if (argc <2) { diff --git a/rerun_cpp/src/rerun/archetypes/asset_video.hpp b/rerun_cpp/src/rerun/archetypes/asset_video.hpp index d1510a5c8027..bc25a1247fe7 100644 --- a/rerun_cpp/src/rerun/archetypes/asset_video.hpp +++ b/rerun_cpp/src/rerun/archetypes/asset_video.hpp @@ -26,6 +26,61 @@ namespace rerun::archetypes { /// /// In order to display a video, you need to log a `archetypes::VideoFrameReference` for each frame. /// + /// ## Example + /// + /// ### Video with explicit frames + /// ![image](https://static.rerun.io/video_manual_frames/320a44e1e06b8b3a3161ecbbeae3e04d1ccb9589/full.png) + /// + /// ```cpp + /// #include + /// + /// #include + /// + /// using namespace std::chrono_literals; + /// + /// int main(int argc, char* argv[]) { + /// if (argc <2) { + /// // TODO(#7354): Only mp4 is supported for now. + /// std::cerr <<"Usage: " <" < times(10 * 10); + /// std::vector video_timestamps(10 * 10); + /// for (size_t i = 0; i ( + /// static_cast(times.size()) + /// ); + /// rec.send_columns( + /// "video", + /// rerun::TimeColumn::from_times("video_time", rerun::borrow(times)), + /// { + /// video_frame_reference_indicators.value_or_throw(), + /// rerun::ComponentColumn::from_loggable(rerun::borrow(video_timestamps)).value_or_throw(), + /// } + /// ); + /// } + /// ``` + /// /// ⚠ **This is an experimental API! It is not fully supported, and is likely to change significantly in future versions.** struct AssetVideo { /// The asset's bytes. diff --git a/rerun_cpp/src/rerun/archetypes/video_frame_reference.hpp b/rerun_cpp/src/rerun/archetypes/video_frame_reference.hpp index ef96331f1a1a..814baed5c996 100644 --- a/rerun_cpp/src/rerun/archetypes/video_frame_reference.hpp +++ b/rerun_cpp/src/rerun/archetypes/video_frame_reference.hpp @@ -19,7 +19,63 @@ namespace rerun::archetypes { /// **Archetype**: References a single video frame. /// - /// Used to display video frames from a `archetypes::AssetVideo`. + /// Used to display individual video frames from a `archetypes::AssetVideo`. + /// To show an entire video, a fideo frame reference for each frame of the video should be logged. + /// + /// ## Example + /// + /// ### Video with explicit frames + /// ![image](https://static.rerun.io/video_manual_frames/320a44e1e06b8b3a3161ecbbeae3e04d1ccb9589/full.png) + /// + /// ```cpp + /// #include + /// + /// #include + /// + /// using namespace std::chrono_literals; + /// + /// int main(int argc, char* argv[]) { + /// if (argc <2) { + /// // TODO(#7354): Only mp4 is supported for now. + /// std::cerr <<"Usage: " <" < times(10 * 10); + /// std::vector video_timestamps(10 * 10); + /// for (size_t i = 0; i ( + /// static_cast(times.size()) + /// ); + /// rec.send_columns( + /// "video", + /// rerun::TimeColumn::from_times("video_time", rerun::borrow(times)), + /// { + /// video_frame_reference_indicators.value_or_throw(), + /// rerun::ComponentColumn::from_loggable(rerun::borrow(video_timestamps)).value_or_throw(), + /// } + /// ); + /// } + /// ``` /// /// ⚠ **This is an experimental API! It is not fully supported, and is likely to change significantly in future versions.** struct VideoFrameReference { @@ -34,6 +90,10 @@ namespace rerun::archetypes { /// If none is specified, the video is assumed to be at the same entity. /// Note that blueprint overrides on the referenced video will be ignored regardless, /// as this is always interpreted as a reference to the data store. + /// + /// For a series of video frame references, it is recommended to specify this path only once + /// at the beginning of the series and then rely on latest-at query semantics to + /// keep the video reference active. std::optional video_reference; public: @@ -55,6 +115,10 @@ namespace rerun::archetypes { /// If none is specified, the video is assumed to be at the same entity. /// Note that blueprint overrides on the referenced video will be ignored regardless, /// as this is always interpreted as a reference to the data store. + /// + /// For a series of video frame references, it is recommended to specify this path only once + /// at the beginning of the series and then rely on latest-at query semantics to + /// keep the video reference active. VideoFrameReference with_video_reference(rerun::components::EntityPath _video_reference ) && { video_reference = std::move(_video_reference); diff --git a/rerun_cpp/src/rerun/blueprint/archetypes.hpp b/rerun_cpp/src/rerun/blueprint/archetypes.hpp index 39a6b0f8053b..eae71f6b2164 100644 --- a/rerun_cpp/src/rerun/blueprint/archetypes.hpp +++ b/rerun_cpp/src/rerun/blueprint/archetypes.hpp @@ -13,7 +13,6 @@ #include "blueprint/archetypes/tensor_scalar_mapping.hpp" #include "blueprint/archetypes/tensor_slice_selection.hpp" #include "blueprint/archetypes/tensor_view_fit.hpp" -#include "blueprint/archetypes/time_range_table_order.hpp" #include "blueprint/archetypes/viewport_blueprint.hpp" #include "blueprint/archetypes/visible_time_ranges.hpp" #include "blueprint/archetypes/visual_bounds2d.hpp" diff --git a/rerun_cpp/src/rerun/blueprint/archetypes/.gitattributes b/rerun_cpp/src/rerun/blueprint/archetypes/.gitattributes index eae6a969df5b..40240eeaa29b 100644 --- a/rerun_cpp/src/rerun/blueprint/archetypes/.gitattributes +++ b/rerun_cpp/src/rerun/blueprint/archetypes/.gitattributes @@ -23,8 +23,6 @@ tensor_slice_selection.cpp linguist-generated=true tensor_slice_selection.hpp linguist-generated=true tensor_view_fit.cpp linguist-generated=true tensor_view_fit.hpp linguist-generated=true -time_range_table_order.cpp linguist-generated=true -time_range_table_order.hpp linguist-generated=true viewport_blueprint.cpp linguist-generated=true viewport_blueprint.hpp linguist-generated=true visible_time_ranges.cpp linguist-generated=true diff --git a/rerun_cpp/src/rerun/blueprint/archetypes/time_range_table_order.cpp b/rerun_cpp/src/rerun/blueprint/archetypes/time_range_table_order.cpp deleted file mode 100644 index 09f248ebe073..000000000000 --- a/rerun_cpp/src/rerun/blueprint/archetypes/time_range_table_order.cpp +++ /dev/null @@ -1,39 +0,0 @@ -// DO NOT EDIT! This file was auto-generated by crates/build/re_types_builder/src/codegen/cpp/mod.rs -// Based on "crates/store/re_types/definitions/rerun/blueprint/archetypes/time_range_table_order.fbs". - -#include "time_range_table_order.hpp" - -#include "../../collection_adapter_builtins.hpp" - -namespace rerun::blueprint::archetypes {} - -namespace rerun { - - Result> - AsComponents::serialize( - const blueprint::archetypes::TimeRangeTableOrder& archetype - ) { - using namespace blueprint::archetypes; - std::vector cells; - cells.reserve(3); - - if (archetype.sort_key.has_value()) { - auto result = ComponentBatch::from_loggable(archetype.sort_key.value()); - RR_RETURN_NOT_OK(result.error); - cells.push_back(std::move(result.value)); - } - if (archetype.sort_order.has_value()) { - auto result = ComponentBatch::from_loggable(archetype.sort_order.value()); - RR_RETURN_NOT_OK(result.error); - cells.push_back(std::move(result.value)); - } - { - auto indicator = TimeRangeTableOrder::IndicatorComponent(); - auto result = ComponentBatch::from_loggable(indicator); - RR_RETURN_NOT_OK(result.error); - cells.emplace_back(std::move(result.value)); - } - - return cells; - } -} // namespace rerun diff --git a/rerun_cpp/src/rerun/blueprint/archetypes/time_range_table_order.hpp b/rerun_cpp/src/rerun/blueprint/archetypes/time_range_table_order.hpp deleted file mode 100644 index 99fc95b46a60..000000000000 --- a/rerun_cpp/src/rerun/blueprint/archetypes/time_range_table_order.hpp +++ /dev/null @@ -1,70 +0,0 @@ -// DO NOT EDIT! This file was auto-generated by crates/build/re_types_builder/src/codegen/cpp/mod.rs -// Based on "crates/store/re_types/definitions/rerun/blueprint/archetypes/time_range_table_order.fbs". - -#pragma once - -#include "../../blueprint/components/sort_key.hpp" -#include "../../blueprint/components/sort_order.hpp" -#include "../../collection.hpp" -#include "../../compiler_utils.hpp" -#include "../../component_batch.hpp" -#include "../../indicator_component.hpp" -#include "../../result.hpp" - -#include -#include -#include -#include - -namespace rerun::blueprint::archetypes { - /// **Archetype**: Ordering of the time range table of the dataframe view - struct TimeRangeTableOrder { - /// The primary sort key - std::optional sort_key; - - /// The sort order - std::optional sort_order; - - public: - static constexpr const char IndicatorComponentName[] = - "rerun.blueprint.components.TimeRangeTableOrderIndicator"; - - /// Indicator component, used to identify the archetype when converting to a list of components. - using IndicatorComponent = rerun::components::IndicatorComponent; - - public: - TimeRangeTableOrder() = default; - TimeRangeTableOrder(TimeRangeTableOrder&& other) = default; - - /// The primary sort key - TimeRangeTableOrder with_sort_key(rerun::blueprint::components::SortKey _sort_key) && { - sort_key = std::move(_sort_key); - // See: https://github.com/rerun-io/rerun/issues/4027 - RR_WITH_MAYBE_UNINITIALIZED_DISABLED(return std::move(*this);) - } - - /// The sort order - TimeRangeTableOrder with_sort_order(rerun::blueprint::components::SortOrder _sort_order - ) && { - sort_order = std::move(_sort_order); - // See: https://github.com/rerun-io/rerun/issues/4027 - RR_WITH_MAYBE_UNINITIALIZED_DISABLED(return std::move(*this);) - } - }; - -} // namespace rerun::blueprint::archetypes - -namespace rerun { - /// \private - template - struct AsComponents; - - /// \private - template <> - struct AsComponents { - /// Serialize all set component batches. - static Result> serialize( - const blueprint::archetypes::TimeRangeTableOrder& archetype - ); - }; -} // namespace rerun diff --git a/rerun_cpp/src/rerun/blueprint/components.hpp b/rerun_cpp/src/rerun/blueprint/components.hpp index 19657a843547..648fccbf658c 100644 --- a/rerun_cpp/src/rerun/blueprint/components.hpp +++ b/rerun_cpp/src/rerun/blueprint/components.hpp @@ -20,8 +20,6 @@ #include "blueprint/components/query_kind.hpp" #include "blueprint/components/root_container.hpp" #include "blueprint/components/row_share.hpp" -#include "blueprint/components/sort_key.hpp" -#include "blueprint/components/sort_order.hpp" #include "blueprint/components/space_view_class.hpp" #include "blueprint/components/space_view_maximized.hpp" #include "blueprint/components/space_view_origin.hpp" diff --git a/rerun_cpp/src/rerun/blueprint/components/.gitattributes b/rerun_cpp/src/rerun/blueprint/components/.gitattributes index 96df82b452ea..b35efe878717 100644 --- a/rerun_cpp/src/rerun/blueprint/components/.gitattributes +++ b/rerun_cpp/src/rerun/blueprint/components/.gitattributes @@ -25,10 +25,6 @@ query_kind.cpp linguist-generated=true query_kind.hpp linguist-generated=true root_container.hpp linguist-generated=true row_share.hpp linguist-generated=true -sort_key.cpp linguist-generated=true -sort_key.hpp linguist-generated=true -sort_order.cpp linguist-generated=true -sort_order.hpp linguist-generated=true space_view_class.hpp linguist-generated=true space_view_maximized.hpp linguist-generated=true space_view_origin.hpp linguist-generated=true diff --git a/rerun_cpp/src/rerun/blueprint/components/sort_key.cpp b/rerun_cpp/src/rerun/blueprint/components/sort_key.cpp deleted file mode 100644 index 02229a34e398..000000000000 --- a/rerun_cpp/src/rerun/blueprint/components/sort_key.cpp +++ /dev/null @@ -1,58 +0,0 @@ -// DO NOT EDIT! This file was auto-generated by crates/build/re_types_builder/src/codegen/cpp/mod.rs -// Based on "crates/store/re_types/definitions/rerun/blueprint/components/sort_key.fbs". - -#include "sort_key.hpp" - -#include -#include - -namespace rerun { - const std::shared_ptr& - Loggable::arrow_datatype() { - static const auto datatype = arrow::uint8(); - return datatype; - } - - Result> Loggable::to_arrow( - const blueprint::components::SortKey* instances, size_t num_instances - ) { - // TODO(andreas): Allow configuring the memory pool. - arrow::MemoryPool* pool = arrow::default_memory_pool(); - auto datatype = arrow_datatype(); - - ARROW_ASSIGN_OR_RAISE(auto builder, arrow::MakeBuilder(datatype, pool)) - if (instances && num_instances > 0) { - RR_RETURN_NOT_OK(Loggable::fill_arrow_array_builder( - static_cast(builder.get()), - instances, - num_instances - )); - } - std::shared_ptr array; - ARROW_RETURN_NOT_OK(builder->Finish(&array)); - return array; - } - - rerun::Error Loggable::fill_arrow_array_builder( - arrow::UInt8Builder* builder, const blueprint::components::SortKey* elements, - size_t num_elements - ) { - if (builder == nullptr) { - return rerun::Error(ErrorCode::UnexpectedNullArgument, "Passed array builder is null."); - } - if (elements == nullptr) { - return rerun::Error( - ErrorCode::UnexpectedNullArgument, - "Cannot serialize null pointer to arrow array." - ); - } - - ARROW_RETURN_NOT_OK(builder->Reserve(static_cast(num_elements))); - for (size_t elem_idx = 0; elem_idx < num_elements; elem_idx += 1) { - const auto variant = elements[elem_idx]; - ARROW_RETURN_NOT_OK(builder->Append(static_cast(variant))); - } - - return Error::ok(); - } -} // namespace rerun diff --git a/rerun_cpp/src/rerun/blueprint/components/sort_key.hpp b/rerun_cpp/src/rerun/blueprint/components/sort_key.hpp deleted file mode 100644 index c9d97d46606e..000000000000 --- a/rerun_cpp/src/rerun/blueprint/components/sort_key.hpp +++ /dev/null @@ -1,57 +0,0 @@ -// DO NOT EDIT! This file was auto-generated by crates/build/re_types_builder/src/codegen/cpp/mod.rs -// Based on "crates/store/re_types/definitions/rerun/blueprint/components/sort_key.fbs". - -#pragma once - -#include "../../result.hpp" - -#include -#include - -namespace arrow { - /// \private - template - class NumericBuilder; - - class Array; - class DataType; - class UInt8Type; - using UInt8Builder = NumericBuilder; -} // namespace arrow - -namespace rerun::blueprint::components { - /// **Component**: Primary element by which to group by in a temporal data table. - enum class SortKey : uint8_t { - - /// Group by entity. - Entity = 1, - - /// Group by instance. - Time = 2, - }; -} // namespace rerun::blueprint::components - -namespace rerun { - template - struct Loggable; - - /// \private - template <> - struct Loggable { - static constexpr const char Name[] = "rerun.blueprint.components.SortKey"; - - /// Returns the arrow data type this type corresponds to. - static const std::shared_ptr& arrow_datatype(); - - /// Serializes an array of `rerun::blueprint:: components::SortKey` into an arrow array. - static Result> to_arrow( - const blueprint::components::SortKey* instances, size_t num_instances - ); - - /// Fills an arrow array builder with an array of this type. - static rerun::Error fill_arrow_array_builder( - arrow::UInt8Builder* builder, const blueprint::components::SortKey* elements, - size_t num_elements - ); - }; -} // namespace rerun diff --git a/rerun_cpp/src/rerun/blueprint/components/sort_order.cpp b/rerun_cpp/src/rerun/blueprint/components/sort_order.cpp deleted file mode 100644 index f221abafb0ec..000000000000 --- a/rerun_cpp/src/rerun/blueprint/components/sort_order.cpp +++ /dev/null @@ -1,58 +0,0 @@ -// DO NOT EDIT! This file was auto-generated by crates/build/re_types_builder/src/codegen/cpp/mod.rs -// Based on "crates/store/re_types/definitions/rerun/blueprint/components/sort_order.fbs". - -#include "sort_order.hpp" - -#include -#include - -namespace rerun { - const std::shared_ptr& - Loggable::arrow_datatype() { - static const auto datatype = arrow::uint8(); - return datatype; - } - - Result> Loggable::to_arrow( - const blueprint::components::SortOrder* instances, size_t num_instances - ) { - // TODO(andreas): Allow configuring the memory pool. - arrow::MemoryPool* pool = arrow::default_memory_pool(); - auto datatype = arrow_datatype(); - - ARROW_ASSIGN_OR_RAISE(auto builder, arrow::MakeBuilder(datatype, pool)) - if (instances && num_instances > 0) { - RR_RETURN_NOT_OK(Loggable::fill_arrow_array_builder( - static_cast(builder.get()), - instances, - num_instances - )); - } - std::shared_ptr array; - ARROW_RETURN_NOT_OK(builder->Finish(&array)); - return array; - } - - rerun::Error Loggable::fill_arrow_array_builder( - arrow::UInt8Builder* builder, const blueprint::components::SortOrder* elements, - size_t num_elements - ) { - if (builder == nullptr) { - return rerun::Error(ErrorCode::UnexpectedNullArgument, "Passed array builder is null."); - } - if (elements == nullptr) { - return rerun::Error( - ErrorCode::UnexpectedNullArgument, - "Cannot serialize null pointer to arrow array." - ); - } - - ARROW_RETURN_NOT_OK(builder->Reserve(static_cast(num_elements))); - for (size_t elem_idx = 0; elem_idx < num_elements; elem_idx += 1) { - const auto variant = elements[elem_idx]; - ARROW_RETURN_NOT_OK(builder->Append(static_cast(variant))); - } - - return Error::ok(); - } -} // namespace rerun diff --git a/rerun_cpp/src/rerun/blueprint/components/sort_order.hpp b/rerun_cpp/src/rerun/blueprint/components/sort_order.hpp deleted file mode 100644 index 275093f3c933..000000000000 --- a/rerun_cpp/src/rerun/blueprint/components/sort_order.hpp +++ /dev/null @@ -1,57 +0,0 @@ -// DO NOT EDIT! This file was auto-generated by crates/build/re_types_builder/src/codegen/cpp/mod.rs -// Based on "crates/store/re_types/definitions/rerun/blueprint/components/sort_order.fbs". - -#pragma once - -#include "../../result.hpp" - -#include -#include - -namespace arrow { - /// \private - template - class NumericBuilder; - - class Array; - class DataType; - class UInt8Type; - using UInt8Builder = NumericBuilder; -} // namespace arrow - -namespace rerun::blueprint::components { - /// **Component**: Sort order for data table. - enum class SortOrder : uint8_t { - - /// Ascending - Ascending = 1, - - /// Descending - Descending = 2, - }; -} // namespace rerun::blueprint::components - -namespace rerun { - template - struct Loggable; - - /// \private - template <> - struct Loggable { - static constexpr const char Name[] = "rerun.blueprint.components.SortOrder"; - - /// Returns the arrow data type this type corresponds to. - static const std::shared_ptr& arrow_datatype(); - - /// Serializes an array of `rerun::blueprint:: components::SortOrder` into an arrow array. - static Result> to_arrow( - const blueprint::components::SortOrder* instances, size_t num_instances - ); - - /// Fills an arrow array builder with an array of this type. - static rerun::Error fill_arrow_array_builder( - arrow::UInt8Builder* builder, const blueprint::components::SortOrder* elements, - size_t num_elements - ); - }; -} // namespace rerun diff --git a/rerun_cpp/src/rerun/components/video_timestamp.hpp b/rerun_cpp/src/rerun/components/video_timestamp.hpp index 7a09d14eba53..ae5e840b13be 100644 --- a/rerun_cpp/src/rerun/components/video_timestamp.hpp +++ b/rerun_cpp/src/rerun/components/video_timestamp.hpp @@ -6,6 +6,7 @@ #include "../datatypes/video_timestamp.hpp" #include "../result.hpp" +#include #include #include @@ -16,6 +17,40 @@ namespace rerun::components { struct VideoTimestamp { rerun::datatypes::VideoTimestamp timestamp; + public: // START of extensions from video_timestamp_ext.cpp: + /// Creates a new `VideoTimestamp` component. + /// \param video_time Timestamp value, type defined by `time_mode`. + /// \param time_mode How to interpret `video_time`. + VideoTimestamp(int64_t video_time, rerun::datatypes::VideoTimeMode time_mode) { + timestamp.video_time = video_time; + timestamp.time_mode = time_mode; + } + + /// Creates a new `VideoTimestamp` from time since video start. + template + VideoTimestamp(std::chrono::duration time) + : VideoTimestamp( + std::chrono::duration_cast(time).count(), + datatypes::VideoTimeMode::Nanoseconds + ) {} + + /// Creates a new [`VideoTimestamp`] from seconds since video start. + static VideoTimestamp from_seconds(double seconds) { + return VideoTimestamp(std::chrono::duration(seconds)); + } + + /// Creates a new [`VideoTimestamp`] from milliseconds since video start. + static VideoTimestamp from_milliseconds(double milliseconds) { + return VideoTimestamp(std::chrono::duration(milliseconds)); + } + + /// Creates a new [`VideoTimestamp`] from nanoseconds since video start. + static VideoTimestamp from_nanoseconds(int64_t nanoseconds) { + return VideoTimestamp(std::chrono::nanoseconds(nanoseconds)); + } + + // END of extensions from video_timestamp_ext.cpp, start of generated code: + public: VideoTimestamp() = default; diff --git a/rerun_cpp/src/rerun/components/video_timestamp_ext.cpp b/rerun_cpp/src/rerun/components/video_timestamp_ext.cpp new file mode 100644 index 000000000000..97d5e61521fd --- /dev/null +++ b/rerun_cpp/src/rerun/components/video_timestamp_ext.cpp @@ -0,0 +1,51 @@ +#if 0 + +// +#include +// + +#include "../datatypes/video_time_mode.hpp" +#include "video_timestamp.hpp" + +namespace rerun { + namespace components { + + // + + /// Creates a new `VideoTimestamp` component. + /// \param video_time Timestamp value, type defined by `time_mode`. + /// \param time_mode How to interpret `video_time`. + VideoTimestamp(int64_t video_time, rerun::datatypes::VideoTimeMode time_mode) { + timestamp.video_time = video_time; + timestamp.time_mode = time_mode; + } + + /// Creates a new `VideoTimestamp` from time since video start. + template + VideoTimestamp(std::chrono::duration time) + : VideoTimestamp( + std::chrono::duration_cast(time).count(), + datatypes::VideoTimeMode::Nanoseconds + ) {} + + /// Creates a new [`VideoTimestamp`] from seconds since video start. + static VideoTimestamp from_seconds(double seconds) { + return VideoTimestamp(std::chrono::duration(seconds)); + } + + /// Creates a new [`VideoTimestamp`] from milliseconds since video start. + static VideoTimestamp from_milliseconds(double milliseconds) { + return VideoTimestamp(std::chrono::duration(milliseconds)); + } + + /// Creates a new [`VideoTimestamp`] from nanoseconds since video start. + static VideoTimestamp from_nanoseconds(int64_t nanoseconds) { + return VideoTimestamp(std::chrono::nanoseconds(nanoseconds)); + } + + // + + } // namespace components +} // namespace rerun + +#endif diff --git a/rerun_py/rerun_sdk/rerun/__init__.py b/rerun_py/rerun_sdk/rerun/__init__.py index c40e195a7fae..05856117a542 100644 --- a/rerun_py/rerun_sdk/rerun/__init__.py +++ b/rerun_py/rerun_sdk/rerun/__init__.py @@ -79,6 +79,7 @@ TextDocument as TextDocument, TextLog as TextLog, Transform3D as Transform3D, + VideoFrameReference as VideoFrameReference, ViewCoordinates as ViewCoordinates, ) from .archetypes.boxes2d_ext import ( diff --git a/rerun_py/rerun_sdk/rerun/archetypes/asset_video.py b/rerun_py/rerun_sdk/rerun/archetypes/asset_video.py index ad3c40e8bd22..05a7d45aee47 100644 --- a/rerun_py/rerun_sdk/rerun/archetypes/asset_video.py +++ b/rerun_py/rerun_sdk/rerun/archetypes/asset_video.py @@ -28,6 +28,51 @@ class AssetVideo(AssetVideoExt, Archetype): In order to display a video, you need to log a [`archetypes.VideoFrameReference`][rerun.archetypes.VideoFrameReference] for each frame. ⚠️ **This is an experimental API! It is not fully supported, and is likely to change significantly in future versions.** + + Example + ------- + ### Video with explicit frames: + ```python + # TODO(#7298): ⚠️ Video is currently only supported in the Rerun web viewer. + + import sys + + import numpy as np + import rerun as rr + + if len(sys.argv) < 2: + # TODO(#7354): Only mp4 is supported for now. + print(f"Usage: {sys.argv[0]} ") + sys.exit(1) + + rr.init("rerun_example_asset_video_manual_frames", spawn=True) + + # Log video asset which is referred to by frame references. + rr.set_time_seconds("video_time", 0) # Make sure it's available on the timeline used for the frame references. + rr.log("video", rr.AssetVideo(path=sys.argv[1])) + + # Send frame references for every 0.1 seconds over a total of 10 seconds. + # Naturally, this will result in a choppy playback and only makes sense if the video is 10 seconds or longer. + # TODO(#7368): Point to example using `send_video_frames`. + # + # Use `send_columns` to send all frame references in a single call. + times = np.arange(0.0, 10.0, 0.1) + rr.send_columns( + "video", + times=[rr.TimeSecondsColumn("video_time", times)], + components=[rr.VideoFrameReference.indicator(), rr.components.VideoTimestamp.seconds(times)], + ) + ``` +
+ + + + + + + +
+ """ # __init__ can be found in asset_video_ext.py diff --git a/rerun_py/rerun_sdk/rerun/archetypes/video_frame_reference.py b/rerun_py/rerun_sdk/rerun/archetypes/video_frame_reference.py index d73ff771bdf3..6dba6a947778 100644 --- a/rerun_py/rerun_sdk/rerun/archetypes/video_frame_reference.py +++ b/rerun_py/rerun_sdk/rerun/archetypes/video_frame_reference.py @@ -23,9 +23,55 @@ class VideoFrameReference(Archetype): """ **Archetype**: References a single video frame. - Used to display video frames from a [`archetypes.AssetVideo`][rerun.archetypes.AssetVideo]. + Used to display individual video frames from a [`archetypes.AssetVideo`][rerun.archetypes.AssetVideo]. + To show an entire video, a fideo frame reference for each frame of the video should be logged. ⚠️ **This is an experimental API! It is not fully supported, and is likely to change significantly in future versions.** + + Example + ------- + ### Video with explicit frames: + ```python + # TODO(#7298): ⚠️ Video is currently only supported in the Rerun web viewer. + + import sys + + import numpy as np + import rerun as rr + + if len(sys.argv) < 2: + # TODO(#7354): Only mp4 is supported for now. + print(f"Usage: {sys.argv[0]} ") + sys.exit(1) + + rr.init("rerun_example_asset_video_manual_frames", spawn=True) + + # Log video asset which is referred to by frame references. + rr.set_time_seconds("video_time", 0) # Make sure it's available on the timeline used for the frame references. + rr.log("video", rr.AssetVideo(path=sys.argv[1])) + + # Send frame references for every 0.1 seconds over a total of 10 seconds. + # Naturally, this will result in a choppy playback and only makes sense if the video is 10 seconds or longer. + # TODO(#7368): Point to example using `send_video_frames`. + # + # Use `send_columns` to send all frame references in a single call. + times = np.arange(0.0, 10.0, 0.1) + rr.send_columns( + "video", + times=[rr.TimeSecondsColumn("video_time", times)], + components=[rr.VideoFrameReference.indicator(), rr.components.VideoTimestamp.seconds(times)], + ) + ``` +
+ + + + + + + +
+ """ def __init__( @@ -48,6 +94,10 @@ def __init__( Note that blueprint overrides on the referenced video will be ignored regardless, as this is always interpreted as a reference to the data store. + For a series of video frame references, it is recommended to specify this path only once + at the beginning of the series and then rely on latest-at query semantics to + keep the video reference active. + """ # You can define your own __init__ function as a member of VideoFrameReferenceExt in video_frame_reference_ext.py @@ -92,6 +142,10 @@ def _clear(cls) -> VideoFrameReference: # Note that blueprint overrides on the referenced video will be ignored regardless, # as this is always interpreted as a reference to the data store. # + # For a series of video frame references, it is recommended to specify this path only once + # at the beginning of the series and then rely on latest-at query semantics to + # keep the video reference active. + # # (Docstring intentionally commented out to hide this field from the docs) __str__ = Archetype.__str__ diff --git a/rerun_py/rerun_sdk/rerun/blueprint/archetypes/.gitattributes b/rerun_py/rerun_sdk/rerun/blueprint/archetypes/.gitattributes index 58d01529a89b..1db9a825f2e9 100644 --- a/rerun_py/rerun_sdk/rerun/blueprint/archetypes/.gitattributes +++ b/rerun_py/rerun_sdk/rerun/blueprint/archetypes/.gitattributes @@ -13,7 +13,6 @@ space_view_contents.py linguist-generated=true tensor_scalar_mapping.py linguist-generated=true tensor_slice_selection.py linguist-generated=true tensor_view_fit.py linguist-generated=true -time_range_table_order.py linguist-generated=true viewport_blueprint.py linguist-generated=true visible_time_ranges.py linguist-generated=true visual_bounds2d.py linguist-generated=true diff --git a/rerun_py/rerun_sdk/rerun/blueprint/archetypes/__init__.py b/rerun_py/rerun_sdk/rerun/blueprint/archetypes/__init__.py index c07e1563847d..423f025dc7aa 100644 --- a/rerun_py/rerun_sdk/rerun/blueprint/archetypes/__init__.py +++ b/rerun_py/rerun_sdk/rerun/blueprint/archetypes/__init__.py @@ -13,7 +13,6 @@ from .tensor_scalar_mapping import TensorScalarMapping from .tensor_slice_selection import TensorSliceSelection from .tensor_view_fit import TensorViewFit -from .time_range_table_order import TimeRangeTableOrder from .viewport_blueprint import ViewportBlueprint from .visible_time_ranges import VisibleTimeRanges from .visual_bounds2d import VisualBounds2D @@ -30,7 +29,6 @@ "TensorScalarMapping", "TensorSliceSelection", "TensorViewFit", - "TimeRangeTableOrder", "ViewportBlueprint", "VisibleTimeRanges", "VisualBounds2D", diff --git a/rerun_py/rerun_sdk/rerun/blueprint/archetypes/time_range_table_order.py b/rerun_py/rerun_sdk/rerun/blueprint/archetypes/time_range_table_order.py deleted file mode 100644 index 57ad298e82be..000000000000 --- a/rerun_py/rerun_sdk/rerun/blueprint/archetypes/time_range_table_order.py +++ /dev/null @@ -1,82 +0,0 @@ -# DO NOT EDIT! This file was auto-generated by crates/build/re_types_builder/src/codegen/python/mod.rs -# Based on "crates/store/re_types/definitions/rerun/blueprint/archetypes/time_range_table_order.fbs". - -# You can extend this class by creating a "TimeRangeTableOrderExt" class in "time_range_table_order_ext.py". - -from __future__ import annotations - -from typing import Any - -from attrs import define, field - -from ..._baseclasses import ( - Archetype, -) -from ...blueprint import components as blueprint_components -from ...error_utils import catch_and_log_exceptions - -__all__ = ["TimeRangeTableOrder"] - - -@define(str=False, repr=False, init=False) -class TimeRangeTableOrder(Archetype): - """**Archetype**: Ordering of the time range table of the dataframe view.""" - - def __init__( - self: Any, - *, - sort_key: blueprint_components.SortKeyLike | None = None, - sort_order: blueprint_components.SortOrderLike | None = None, - ): - """ - Create a new instance of the TimeRangeTableOrder archetype. - - Parameters - ---------- - sort_key: - The primary sort key - sort_order: - The sort order - - """ - - # You can define your own __init__ function as a member of TimeRangeTableOrderExt in time_range_table_order_ext.py - with catch_and_log_exceptions(context=self.__class__.__name__): - self.__attrs_init__(sort_key=sort_key, sort_order=sort_order) - return - self.__attrs_clear__() - - def __attrs_clear__(self) -> None: - """Convenience method for calling `__attrs_init__` with all `None`s.""" - self.__attrs_init__( - sort_key=None, # type: ignore[arg-type] - sort_order=None, # type: ignore[arg-type] - ) - - @classmethod - def _clear(cls) -> TimeRangeTableOrder: - """Produce an empty TimeRangeTableOrder, bypassing `__init__`.""" - inst = cls.__new__(cls) - inst.__attrs_clear__() - return inst - - sort_key: blueprint_components.SortKeyBatch | None = field( - metadata={"component": "optional"}, - default=None, - converter=blueprint_components.SortKeyBatch._optional, # type: ignore[misc] - ) - # The primary sort key - # - # (Docstring intentionally commented out to hide this field from the docs) - - sort_order: blueprint_components.SortOrderBatch | None = field( - metadata={"component": "optional"}, - default=None, - converter=blueprint_components.SortOrderBatch._optional, # type: ignore[misc] - ) - # The sort order - # - # (Docstring intentionally commented out to hide this field from the docs) - - __str__ = Archetype.__str__ - __repr__ = Archetype.__repr__ # type: ignore[assignment] diff --git a/rerun_py/rerun_sdk/rerun/blueprint/components/.gitattributes b/rerun_py/rerun_sdk/rerun/blueprint/components/.gitattributes index 201c9647727e..cfd7b1d54f2e 100644 --- a/rerun_py/rerun_sdk/rerun/blueprint/components/.gitattributes +++ b/rerun_py/rerun_sdk/rerun/blueprint/components/.gitattributes @@ -20,8 +20,6 @@ query_expression.py linguist-generated=true query_kind.py linguist-generated=true root_container.py linguist-generated=true row_share.py linguist-generated=true -sort_key.py linguist-generated=true -sort_order.py linguist-generated=true space_view_class.py linguist-generated=true space_view_maximized.py linguist-generated=true space_view_origin.py linguist-generated=true diff --git a/rerun_py/rerun_sdk/rerun/blueprint/components/__init__.py b/rerun_py/rerun_sdk/rerun/blueprint/components/__init__.py index 672894030dcb..b1630686b28d 100644 --- a/rerun_py/rerun_sdk/rerun/blueprint/components/__init__.py +++ b/rerun_py/rerun_sdk/rerun/blueprint/components/__init__.py @@ -38,8 +38,6 @@ from .query_kind import QueryKind, QueryKindArrayLike, QueryKindBatch, QueryKindLike, QueryKindType from .root_container import RootContainer, RootContainerBatch, RootContainerType from .row_share import RowShare, RowShareBatch, RowShareType -from .sort_key import SortKey, SortKeyArrayLike, SortKeyBatch, SortKeyLike, SortKeyType -from .sort_order import SortOrder, SortOrderArrayLike, SortOrderBatch, SortOrderLike, SortOrderType from .space_view_class import SpaceViewClass, SpaceViewClassBatch, SpaceViewClassType from .space_view_maximized import SpaceViewMaximized, SpaceViewMaximizedBatch, SpaceViewMaximizedType from .space_view_origin import SpaceViewOrigin, SpaceViewOriginBatch, SpaceViewOriginType @@ -134,16 +132,6 @@ "RowShare", "RowShareBatch", "RowShareType", - "SortKey", - "SortKeyArrayLike", - "SortKeyBatch", - "SortKeyLike", - "SortKeyType", - "SortOrder", - "SortOrderArrayLike", - "SortOrderBatch", - "SortOrderLike", - "SortOrderType", "SpaceViewClass", "SpaceViewClassBatch", "SpaceViewClassType", diff --git a/rerun_py/rerun_sdk/rerun/blueprint/components/sort_key.py b/rerun_py/rerun_sdk/rerun/blueprint/components/sort_key.py deleted file mode 100644 index d4ae1508d4a7..000000000000 --- a/rerun_py/rerun_sdk/rerun/blueprint/components/sort_key.py +++ /dev/null @@ -1,75 +0,0 @@ -# DO NOT EDIT! This file was auto-generated by crates/build/re_types_builder/src/codegen/python/mod.rs -# Based on "crates/store/re_types/definitions/rerun/blueprint/components/sort_key.fbs". - -# You can extend this class by creating a "SortKeyExt" class in "sort_key_ext.py". - -from __future__ import annotations - -from typing import Literal, Sequence, Union - -import pyarrow as pa - -from ..._baseclasses import ( - BaseBatch, - BaseExtensionType, - ComponentBatchMixin, -) - -__all__ = ["SortKey", "SortKeyArrayLike", "SortKeyBatch", "SortKeyLike", "SortKeyType"] - - -from enum import Enum - - -class SortKey(Enum): - """**Component**: Primary element by which to group by in a temporal data table.""" - - Entity = 1 - """Group by entity.""" - - Time = 2 - """Group by instance.""" - - @classmethod - def auto(cls, val: str | int | SortKey) -> SortKey: - """Best-effort converter, including a case-insensitive string matcher.""" - if isinstance(val, SortKey): - return val - if isinstance(val, int): - return cls(val) - try: - return cls[val] - except KeyError: - val_lower = val.lower() - for variant in cls: - if variant.name.lower() == val_lower: - return variant - raise ValueError(f"Cannot convert {val} to {cls.__name__}") - - def __str__(self) -> str: - """Returns the variant name.""" - return self.name - - -SortKeyLike = Union[SortKey, Literal["Entity", "Time", "entity", "time"], int] -SortKeyArrayLike = Union[SortKeyLike, Sequence[SortKeyLike]] - - -class SortKeyType(BaseExtensionType): - _TYPE_NAME: str = "rerun.blueprint.components.SortKey" - - def __init__(self) -> None: - pa.ExtensionType.__init__(self, pa.uint8(), self._TYPE_NAME) - - -class SortKeyBatch(BaseBatch[SortKeyArrayLike], ComponentBatchMixin): - _ARROW_TYPE = SortKeyType() - - @staticmethod - def _native_to_pa_array(data: SortKeyArrayLike, data_type: pa.DataType) -> pa.Array: - if isinstance(data, (SortKey, int, str)): - data = [data] - - pa_data = [SortKey.auto(v).value if v is not None else None for v in data] # type: ignore[redundant-expr] - - return pa.array(pa_data, type=data_type) diff --git a/rerun_py/rerun_sdk/rerun/blueprint/components/sort_order.py b/rerun_py/rerun_sdk/rerun/blueprint/components/sort_order.py deleted file mode 100644 index e9c31f2468d4..000000000000 --- a/rerun_py/rerun_sdk/rerun/blueprint/components/sort_order.py +++ /dev/null @@ -1,75 +0,0 @@ -# DO NOT EDIT! This file was auto-generated by crates/build/re_types_builder/src/codegen/python/mod.rs -# Based on "crates/store/re_types/definitions/rerun/blueprint/components/sort_order.fbs". - -# You can extend this class by creating a "SortOrderExt" class in "sort_order_ext.py". - -from __future__ import annotations - -from typing import Literal, Sequence, Union - -import pyarrow as pa - -from ..._baseclasses import ( - BaseBatch, - BaseExtensionType, - ComponentBatchMixin, -) - -__all__ = ["SortOrder", "SortOrderArrayLike", "SortOrderBatch", "SortOrderLike", "SortOrderType"] - - -from enum import Enum - - -class SortOrder(Enum): - """**Component**: Sort order for data table.""" - - Ascending = 1 - """Ascending""" - - Descending = 2 - """Descending""" - - @classmethod - def auto(cls, val: str | int | SortOrder) -> SortOrder: - """Best-effort converter, including a case-insensitive string matcher.""" - if isinstance(val, SortOrder): - return val - if isinstance(val, int): - return cls(val) - try: - return cls[val] - except KeyError: - val_lower = val.lower() - for variant in cls: - if variant.name.lower() == val_lower: - return variant - raise ValueError(f"Cannot convert {val} to {cls.__name__}") - - def __str__(self) -> str: - """Returns the variant name.""" - return self.name - - -SortOrderLike = Union[SortOrder, Literal["Ascending", "Descending", "ascending", "descending"], int] -SortOrderArrayLike = Union[SortOrderLike, Sequence[SortOrderLike]] - - -class SortOrderType(BaseExtensionType): - _TYPE_NAME: str = "rerun.blueprint.components.SortOrder" - - def __init__(self) -> None: - pa.ExtensionType.__init__(self, pa.uint8(), self._TYPE_NAME) - - -class SortOrderBatch(BaseBatch[SortOrderArrayLike], ComponentBatchMixin): - _ARROW_TYPE = SortOrderType() - - @staticmethod - def _native_to_pa_array(data: SortOrderArrayLike, data_type: pa.DataType) -> pa.Array: - if isinstance(data, (SortOrder, int, str)): - data = [data] - - pa_data = [SortOrder.auto(v).value if v is not None else None for v in data] # type: ignore[redundant-expr] - - return pa.array(pa_data, type=data_type) diff --git a/rerun_py/rerun_sdk/rerun/components/video_timestamp.py b/rerun_py/rerun_sdk/rerun/components/video_timestamp.py index 433fe013f8cc..23b11eae4ddc 100644 --- a/rerun_py/rerun_sdk/rerun/components/video_timestamp.py +++ b/rerun_py/rerun_sdk/rerun/components/video_timestamp.py @@ -10,11 +10,12 @@ ComponentBatchMixin, ComponentMixin, ) +from .video_timestamp_ext import VideoTimestampExt __all__ = ["VideoTimestamp", "VideoTimestampBatch", "VideoTimestampType"] -class VideoTimestamp(datatypes.VideoTimestamp, ComponentMixin): +class VideoTimestamp(VideoTimestampExt, datatypes.VideoTimestamp, ComponentMixin): """ **Component**: Timestamp inside a [`archetypes.AssetVideo`][rerun.archetypes.AssetVideo]. diff --git a/rerun_py/rerun_sdk/rerun/components/video_timestamp_ext.py b/rerun_py/rerun_sdk/rerun/components/video_timestamp_ext.py new file mode 100644 index 000000000000..98403aa558cc --- /dev/null +++ b/rerun_py/rerun_sdk/rerun/components/video_timestamp_ext.py @@ -0,0 +1,65 @@ +from __future__ import annotations + +import numpy as np +import numpy.typing as npt + +from .. import components, datatypes + + +class VideoTimestampExt: + """Extension for [VideoTimestamp][rerun.components.VideoTimestamp].""" + + # Implementation note: + # We could add an init method that deals with seconds/milliseconds/nanoseconds etc. + # However, this would require _a lot_ of slow parameter validation on a per timestamp basis. + # When in actuallity, this data practically always comes in homogeneous batches. + + @staticmethod + def seconds( + seconds: npt.ArrayLike, + ) -> components.VideoTimestampBatch: + """ + Create a video timestamp batch from seconds since video start. + + Parameters + ---------- + seconds: + Timestamp values in seconds since video start. + + """ + return components.VideoTimestamp.nanoseconds(np.array(seconds) * 1e9) + + @staticmethod + def milliseconds( + milliseconds: npt.ArrayLike, + ) -> components.VideoTimestampBatch: + """ + Create a video timestamp batch from milliseconds since video start. + + Parameters + ---------- + milliseconds: + Timestamp values in milliseconds since video start. + + """ + return components.VideoTimestamp.nanoseconds(np.array(milliseconds) * 1e6) + + @staticmethod + def nanoseconds( + nanoseconds: npt.ArrayLike, + ) -> components.VideoTimestampBatch: + """ + Create a video timestamp batch from nanoseconds since video start. + + Parameters + ---------- + nanoseconds: + Timestamp values in nanoseconds since video start. + + """ + nanoseconds = np.asarray(nanoseconds, dtype=np.int64) + + return components.VideoTimestampBatch([ + components.VideoTimestamp(video_time=ns, time_mode=datatypes.VideoTimeMode.Nanoseconds) + for ns in nanoseconds + ])