Skip to content

Commit

Permalink
Add basic support for in-app "Quick Start" guides (#3813)
Browse files Browse the repository at this point in the history
### What

This PR lays the basis for in-app "Quick Start" guides.

After a failed attempt to use `re_sdk` from `re_viewer` (can't target
wasm), this PR builds on recent `StoreDb` API improvements to create
"quick start" recordings from scratch.

This involves:
- adding a nice `StoreDb::from_info_and_rows()` helper function
- adding `SystemCommand::LoadStoreDb(StoreDb)`
- adding `StoreSource::Viewer` (for recordings generated from the viewer
itself)
- some helper functions in `welcome_page.rs`

This PR also introduces a draft for the (native-only) Python and Rust
Quick Starts, and placeholder for the other languages.

To address (in follow-up PR):
- markdown formatting issue where H1 appears smaller than H2 (current
work around: skip on using H1)
  - #3612
- specialise Quick Start for non-server Viewers
  - #3871 
- write Quick Start for C++:
  - #3870 
- markdown formatting issue:
  - lampsitter/egui_commonmark#19

<img width="1315" alt="image"
src="https://github.com/rerun-io/rerun/assets/49431240/232b4a79-7917-4978-95d1-94474142da94">

* Fixes #3095


### Checklist
* [x] I have read and agree to [Contributor
Guide](https://github.com/rerun-io/rerun/blob/main/CONTRIBUTING.md) and
the [Code of
Conduct](https://github.com/rerun-io/rerun/blob/main/CODE_OF_CONDUCT.md)
* [x] I've included a screenshot or gif (if applicable)
* [x] I have tested [demo.rerun.io](https://demo.rerun.io/pr/3813) (if
applicable)
* [x] The PR title and labels are set such as to maximize their
usefulness for the next release's CHANGELOG

- [PR Build Summary](https://build.rerun.io/pr/3813)
- [Docs
preview](https://rerun.io/preview/9c7ebbf37a890b5d09941102a4d7c3a35f53f277/docs)
<!--DOCS-PREVIEW-->
- [Examples
preview](https://rerun.io/preview/9c7ebbf37a890b5d09941102a4d7c3a35f53f277/examples)
<!--EXAMPLES-PREVIEW-->
- [Recent benchmark results](https://ref.rerun.io/dev/bench/)
- [Wasm size tracking](https://ref.rerun.io/dev/sizes/)

---------

Co-authored-by: Emil Ernerfeldt <emil.ernerfeldt@gmail.com>
  • Loading branch information
abey79 and emilk authored Oct 17, 2023
1 parent ea7a169 commit 3c912be
Show file tree
Hide file tree
Showing 20 changed files with 296 additions and 14 deletions.
23 changes: 22 additions & 1 deletion crates/re_data_store/src/store_db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ impl EntityDb {

// ----------------------------------------------------------------------------

/// A in-memory database built from a stream of [`LogMsg`]es.
/// An in-memory database built from a stream of [`LogMsg`]es.
///
/// NOTE: all mutation is to be done via public functions!
pub struct StoreDb {
Expand All @@ -238,6 +238,27 @@ impl StoreDb {
}
}

/// Helper function to create a recording from a [`StoreInfo`] and a some [`DataRow`]s.
///
/// This is useful to programmatically create recordings from within the viewer, which cannot
/// use the `re_sdk`, which is not Wasm-compatible.
pub fn from_info_and_rows(
store_info: StoreInfo,
rows: impl IntoIterator<Item = DataRow>,
) -> Result<Self, Error> {
let mut store_db = StoreDb::new(store_info.store_id.clone());

store_db.set_store_info(SetStoreInfo {
row_id: RowId::random(),
info: store_info,
});
for row in rows {
store_db.add_data_row(&row)?;
}

Ok(store_db)
}

#[inline]
pub fn entity_db(&self) -> &EntityDb {
&self.entity_db
Expand Down
4 changes: 4 additions & 0 deletions crates/re_log_types/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,9 @@ pub enum StoreSource {
file_source: FileSource,
},

/// Generated from the viewer itself.
Viewer,

/// Perhaps from some manual data ingestion?
Other(String),
}
Expand All @@ -360,6 +363,7 @@ impl std::fmt::Display for StoreSource {
FileSource::DragAndDrop => write!(f, "File via drag-and-drop"),
FileSource::FileDialog => write!(f, "File via file dialog"),
},
Self::Viewer => write!(f, "Viewer-generated"),
Self::Other(string) => format!("{string:?}").fmt(f), // put it in quotes
}
}
Expand Down
3 changes: 3 additions & 0 deletions crates/re_viewer/data/quick_start_guides/cpp_native.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
## C++ Quick Start

TODO(ab): https://github.com/rerun-io/rerun/issues/3870
9 changes: 9 additions & 0 deletions crates/re_viewer/data/quick_start_guides/how_does_it_work.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
### How does it work?

Rerun's goal is to make handling and visualizing multimodal data streams easy and performant.

Rerun is made of two main building blocks: the SDK and the Viewer. The data provided by the user code is serialised by the SDK and transferred (via a log file, a TCP socket, a WebSocket, etc.) to the Viewer process for visualization. You can learn more about Rerun's operating modes [here](https://www.rerun.io/docs/reference/sdk-operating-modes).

In the example above, the SDK connects via a TCP socket to the present viewer.

The `log()` function logs _entities_ represented by the "entity path" provided as first argument. Entities are a collection of _components_, which hold the actual data such as position, color, or pixel data. _Archetypes_ such as `Points3D` are builder objects which help creating entities with a consistent set of components that are recognized by the Viewer (they can be entirely bypassed when required by advanced use-cases). You can learn more about Rerun's data model [here](https://www.rerun.io/docs/concepts/entity-component).
33 changes: 33 additions & 0 deletions crates/re_viewer/data/quick_start_guides/python_native.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
## Python Quick Start

### Installing the Rerun SDK

The Rerun SDK is available on [PyPI](https://pypi.org/) under the
[`rerun-sdk`](https://pypi.org/project/rerun-sdk/) name. It can be installed like any other
Python package:

```sh
pip install rerun-sdk
```

### Try out the viewer

The Rerun SDK comes with a demo that can be used to try the viewer. You can send a demo recording
to this viewer using the following command:

```sh
python -m rerun_sdk.demo --connect
```

This will open a new recording that looks like this:

![Demo recording](https://static.rerun.io/quickstart2_simple_cube/632a8f1c79f70a2355fad294fe085291fcf3a8ae/768w.png)


### Logging your own data

Instead of a pre-packaged demo, you can log your own data. Copy and paste the following snippet in a new Python file and execute it to create a new recording in this viewer:

```python
${EXAMPLE_CODE}
```
31 changes: 31 additions & 0 deletions crates/re_viewer/data/quick_start_guides/rust_native.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
## Rust Quick Start

### Installing Rerun

To use the Rerun SDK in your project, you need the [rerun crate](https://crates.io/crates/rerun) which you can add with `cargo add rerun`.

Let's try it out in a brand-new Rust project:

```sh
cargo init cube && cd cube && cargo add rerun --features native_viewer
```

Note that the Rerun SDK requires a working installation of Rust 1.72+.

### Logging your own data

Add the following code to your `main.rs` file:

```rust
${EXAMPLE_CODE}
```

You can now run your application:

```shell
cargo run
```

Once everything finishes compiling, you will see the points in this viewer:

![Demo recording](https://static.rerun.io/intro_rust_result/cc780eb9bf014d8b1a68fac174b654931f92e14f/768w.png)
6 changes: 6 additions & 0 deletions crates/re_viewer/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,12 @@ impl App {
}
}

SystemCommand::LoadStoreDb(store_db) => {
let store_id = store_db.store_id().clone();
store_hub.insert_recording(store_db);
store_hub.set_recording_id(store_id);
}

SystemCommand::ResetViewer => self.reset(store_hub, egui_ctx),
SystemCommand::UpdateBlueprint(blueprint_id, updates) => {
let blueprint_db = store_hub.store_db_mut(&blueprint_id);
Expand Down
5 changes: 4 additions & 1 deletion crates/re_viewer/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,10 @@ impl AppEnvironment {
llvm_version: llvm_version.clone(),
},

StoreSource::File { .. } | StoreSource::Unknown | StoreSource::Other(_) => {
StoreSource::File { .. }
| StoreSource::Unknown
| StoreSource::Viewer
| StoreSource::Other(_) => {
// We should not really get here

#[cfg(debug_assertions)]
Expand Down
8 changes: 8 additions & 0 deletions crates/re_viewer/src/store_hub.rs
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,14 @@ impl StoreHub {
}
}

/// Insert a new recording into the [`StoreHub`].
///
/// Note that the recording is not automatically made active. Use [`StoreHub::set_recording_id`]
/// if needed.
pub fn insert_recording(&mut self, store_db: StoreDb) {
self.store_dbs.insert_recording(store_db);
}

/// Mutable access to a [`StoreDb`] by id
pub fn store_db_mut(&mut self, store_id: &StoreId) -> &mut StoreDb {
self.store_dbs.store_db_entry(store_id)
Expand Down
111 changes: 102 additions & 9 deletions crates/re_viewer/src/ui/welcome_screen/welcome_page.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
use super::{large_text_button, status_strings, url_large_text_button, WelcomeScreenResponse};
use egui::{NumExt, Ui};
use re_log_types::LogMsg;
use itertools::Itertools;
use re_data_store::StoreDb;
use re_log_types::{
DataRow, EntityPath, LogMsg, RowId, StoreId, StoreInfo, StoreKind, StoreSource, Time, TimePoint,
};
use re_smart_channel::ReceiveSet;
use re_ui::UICommandSender;
use re_viewer_context::{SystemCommand, SystemCommandSender};

//const CPP_QUICKSTART: &str = "https://www.rerun.io/docs/getting-started/cpp";
const PYTHON_QUICKSTART: &str = "https://www.rerun.io/docs/getting-started/python";
const RUST_QUICKSTART: &str = "https://www.rerun.io/docs/getting-started/rust";
const SPACE_VIEWS_HELP: &str = "https://www.rerun.io/docs/getting-started/viewer-walkthrough";

/// Show the welcome page.
Expand Down Expand Up @@ -58,10 +60,50 @@ fn onboarding_content_ui(
Visualize synchronized data from multiple processes, locally or over a network.",
image: &re_ui::icons::WELCOME_SCREEN_LIVE_DATA,
add_buttons: Box::new(|ui: &mut egui::Ui| {
// TODO(ab): activate when C++ is ready!
// url_large_text_button(ui, "C++", CPP_QUICKSTART);
url_large_text_button(ui, "Python", PYTHON_QUICKSTART);
url_large_text_button(ui, "Rust", RUST_QUICKSTART);
//TODO(#3870): enable with C++ guides are completed
#[allow(clippy::collapsible_if)]
if false {
if large_text_button(ui, "C++").clicked() {
open_quick_start(
command_sender,
[
include_str!("../../../data/quick_start_guides/cpp_native.md"),
include_str!(
"../../../data/quick_start_guides/how_does_it_work.md"
),
],
include_str!(
"../../../data/quick_start_guides/quick_start_connect.cpp"
),
"C++ Quick Start",
"cpp_quick_start",
);
}
}
if large_text_button(ui, "Python").clicked() {
open_quick_start(
command_sender,
[
include_str!("../../../data/quick_start_guides/python_native.md"),
include_str!("../../../data/quick_start_guides/how_does_it_work.md"),
],
include_str!("../../../data/quick_start_guides/quick_start_connect.py"),
"Python Quick Start",
"python_quick_start",
);
}
if large_text_button(ui, "Rust").clicked() {
open_quick_start(
command_sender,
[
include_str!("../../../data/quick_start_guides/rust_native.md"),
include_str!("../../../data/quick_start_guides/how_does_it_work.md"),
],
include_str!("../../../data/quick_start_guides/quick_start_connect.rs"),
"Rust Quick Start",
"rust_quick_start",
);
}

false
}),
Expand Down Expand Up @@ -102,7 +144,7 @@ fn onboarding_content_ui(
},
];

// Shrink images if needed so user can see all of the content buttons
// Shrink images if needed so user can see all the content buttons
let max_image_height = ui.available_height() - 300.0;

let centering_vspace = (ui.available_height() - 650.0) / 2.0;
Expand Down Expand Up @@ -231,3 +273,54 @@ fn image_banner(ui: &mut egui::Ui, icon: &re_ui::Icon, column_width: f32, max_im
);
});
}

/// Open a Quick Start recording
///
/// The `parts` are joined with newlines to form the markdown, and the spacial tag
/// `"${EXAMPLE_CODE}"` is replaced with the content of th `example_code` variable.
fn open_quick_start<'a>(
command_sender: &re_viewer_context::CommandSender,
parts: impl IntoIterator<Item = &'a str>,
example_code: &str,
app_id: &str,
entity_path: &str,
) {
let mut markdown = parts.into_iter().join("\n");
markdown = markdown.replace("${EXAMPLE_CODE}", example_code);

let res = open_markdown_recording(command_sender, markdown.as_str(), app_id, entity_path);
if let Err(err) = res {
re_log::error!("Failed to load quick start: {}", err);
}
}

fn open_markdown_recording(
command_sender: &re_viewer_context::CommandSender,
markdown: &str,
app_id: &str,
entity_path: &str,
) -> anyhow::Result<()> {
let text_doc = re_types::archetypes::TextDocument::new(markdown)
.with_media_type(re_types::components::MediaType::markdown());

let row = DataRow::from_archetype(
RowId::random(),
TimePoint::timeless(),
EntityPath::from(entity_path),
&text_doc,
)?;

let store_info = StoreInfo {
application_id: app_id.into(),
store_id: StoreId::random(StoreKind::Recording),
is_official_example: true,
started: Time::now(),
store_source: StoreSource::Viewer,
store_kind: StoreKind::Recording,
};

let store_db = StoreDb::from_info_and_rows(store_info, [row])?;
command_sender.send_system(SystemCommand::LoadStoreDb(store_db));

Ok(())
}
3 changes: 2 additions & 1 deletion crates/re_viewer/src/viewer_analytics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,7 @@ impl ViewerAnalytics {
re_log_types::FileSource::DragAndDrop => "file_drag_and_drop".to_owned(),
re_log_types::FileSource::FileDialog => "file_dialog".to_owned(),
},
StoreSource::Viewer => "viewer".to_owned(),
StoreSource::Other(other) => other.clone(),
};

Expand Down Expand Up @@ -210,7 +211,7 @@ impl ViewerAnalytics {
self.deregister("llvm_version"); // can't be both!
}
StoreSource::CSdk => {} // TODO(andreas): Send version and set it.
StoreSource::Unknown | StoreSource::Other(_) => {}
StoreSource::Unknown | StoreSource::Viewer | StoreSource::Other(_) => {}
}

self.register("store_source", store_source);
Expand Down
4 changes: 4 additions & 0 deletions crates/re_viewer_context/src/command_sender.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use re_data_source::DataSource;
use re_data_store::StoreDb;
use re_log_types::{DataRow, StoreId};
use re_ui::{UICommand, UICommandSender};

Expand All @@ -10,6 +11,9 @@ pub enum SystemCommand {
/// Load some data.
LoadDataSource(DataSource),

/// Load some log messages.
LoadStoreDb(StoreDb),

/// Reset the `Viewer` to the default state
ResetViewer,

Expand Down
8 changes: 6 additions & 2 deletions docs/code-examples/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ path = "asset3d_simple.rs"
name = "asset3d_out_of_tree"
path = "asset3d_out_of_tree.rs"

[[bin]]
name = "box2d_simple"
path = "box2d_simple.rs"

[[bin]]
name = "box3d_simple"
path = "box3d_simple.rs"
Expand Down Expand Up @@ -132,8 +136,8 @@ name = "point3d_simple"
path = "point3d_simple.rs"

[[bin]]
name = "box2d_simple"
path = "box2d_simple.rs"
name = "quick_start_connect"
path = "quick_start_connect.rs"

[[bin]]
name = "scalar_simple"
Expand Down
5 changes: 5 additions & 0 deletions docs/code-examples/quick_start_connect.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// TODO(ab): https://github.com/rerun-io/rerun/issues/3870

int main() {
return 0;
}
Loading

0 comments on commit 3c912be

Please sign in to comment.