Skip to content

Instantly share code, notes, and snippets.

@matthewjberger
Last active June 11, 2025 16:16
Show Gist options
  • Select an option

  • Save matthewjberger/09f1a715cb251b004592a7dd33ba57a0 to your computer and use it in GitHub Desktop.

Select an option

Save matthewjberger/09f1a715cb251b004592a7dd33ba57a0 to your computer and use it in GitHub Desktop.

Revisions

  1. matthewjberger revised this gist Jun 11, 2025. 1 changed file with 0 additions and 1 deletion.
    1 change: 0 additions & 1 deletion Cargo.toml
    Original file line number Diff line number Diff line change
    @@ -5,7 +5,6 @@ edition = "2024"

    [dependencies]
    mimalloc = "0.1.43"
    ipc = { path = "../controls/crates/ipc" }
    re_crash_handler = { version = "0.22.1", features = ["analytics"] }
    re_grpc_server = "0.22.1"
    re_sdk_comms = { version = "0.22.1", features = ["server"] }
  2. matthewjberger revised this gist Jun 11, 2025. 1 changed file with 25 additions and 25 deletions.
    50 changes: 25 additions & 25 deletions main.rs
    Original file line number Diff line number Diff line change
    @@ -66,7 +66,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
    cc,
    );
    rerun_app.add_receiver(rx);
    rerun_app.add_view_class::<HyphenIpc>().unwrap();
    rerun_app.add_view_class::<SystemIpc>().unwrap();

    let stream = rerun::RecordingStreamBuilder::new("streamer").spawn()?;
    Ok(Box::new(MyApp { rerun_app, stream }))
    @@ -211,33 +211,33 @@ fn format_arrow(array: &dyn arrow::array::Array) -> String {

    /// A custom view class for rerun
    #[derive(Default)]
    pub struct HyphenIpc;
    pub struct SystemIpc;

    impl ViewClass for HyphenIpc {
    impl ViewClass for SystemIpc {
    fn identifier() -> re_types::ViewClassIdentifier
    where
    Self: Sized,
    {
    "Hyphen IPC".into()
    "System IPC".into()
    }

    fn display_name(&self) -> &'static str {
    "Hyphen IPC"
    "System IPC"
    }

    fn help(&self, egui_ctx: &egui::Context) -> re_viewer::external::re_ui::Help<'_> {
    Help::new("Hyphen IPC").markdown("This is a custom view class for Hyphen IPC.")
    Help::new("System IPC").markdown("This is a custom view class for System IPC.")
    }

    fn on_register(
    &self,
    system_registry: &mut re_viewer::external::re_viewer_context::ViewSystemRegistrator<'_>,
    ) -> Result<(), re_viewer::external::re_viewer_context::ViewClassRegistryError> {
    system_registry.register_visualizer::<HyphenIpcSystem>()
    system_registry.register_visualizer::<SystemIpcSystem>()
    }

    fn new_state(&self) -> Box<dyn re_viewer::external::re_viewer_context::ViewState> {
    Box::<HyphenIpcViewState>::default()
    Box::<SystemIpcViewState>::default()
    }

    fn layout_priority(&self) -> re_viewer::external::re_viewer_context::ViewClassLayoutPriority {
    @@ -259,32 +259,32 @@ impl ViewClass for HyphenIpc {
    query: &re_viewer::external::re_viewer_context::ViewQuery<'_>,
    system_output: re_viewer::external::re_viewer_context::SystemExecutionOutput,
    ) -> Result<(), re_viewer::external::re_viewer_context::ViewSystemExecutionError> {
    // let system_outputs = system_output.view_systems.get::<HyphenIpcSystem>()?;
    let state = state.downcast_mut::<HyphenIpcViewState>()?;
    // let system_outputs = system_output.view_systems.get::<SystemIpcSystem>()?;
    let state = state.downcast_mut::<SystemIpcViewState>()?;

    ui.label("Hyphen IPC Connection");
    ui.label("System IPC Connection");
    if ui.button("Connect").clicked() {
    let _ = state.ipc_client.connect("127.0.0.1:9000");
    }
    Ok(())
    }
    }

    /// Our Hyphen IPC view's contents
    /// Our System IPC view's contents
    #[derive(Default)]
    pub struct HyphenIpcSystem {
    pub struct SystemIpcSystem {
    pub value: u8,
    }

    impl IdentifiedViewSystem for HyphenIpcSystem {
    impl IdentifiedViewSystem for SystemIpcSystem {
    fn identifier() -> ViewSystemIdentifier {
    "InstanceHyphenIpc".into()
    "InstanceSystemIpc".into()
    }
    }

    impl VisualizerSystem for HyphenIpcSystem {
    impl VisualizerSystem for SystemIpcSystem {
    fn visualizer_query_info(&self) -> VisualizerQueryInfo {
    VisualizerQueryInfo::from_archetype::<HyphenIpcArchetype>()
    VisualizerQueryInfo::from_archetype::<SystemIpcArchetype>()
    }

    /// Populates the scene part with data from the store.
    @@ -309,11 +309,11 @@ impl VisualizerSystem for HyphenIpcSystem {

    // Implements a `ComponentFallbackProvider` trait for the `InstanceColorSystem`.
    // It is left empty here but could be used to provides fallback values for optional components in case they're missing.
    re_viewer_context::impl_component_fallback_provider!(HyphenIpcSystem => []);
    re_viewer_context::impl_component_fallback_provider!(SystemIpcSystem => []);

    struct HyphenIpcArchetype;
    struct SystemIpcArchetype;

    impl re_types::Archetype for HyphenIpcArchetype {
    impl re_types::Archetype for SystemIpcArchetype {
    type Indicator = re_types::GenericIndicatorComponent<Self>;

    fn indicator() -> re_types::SerializedComponentBatch {
    @@ -323,11 +323,11 @@ impl re_types::Archetype for HyphenIpcArchetype {
    }

    fn name() -> re_types::ArchetypeName {
    "InstanceHyphenIpc".into()
    "InstanceSystemIpc".into()
    }

    fn display_name() -> &'static str {
    "Hyphen IPC"
    "System IPC"
    }

    fn required_components() -> ::std::borrow::Cow<'static, [ComponentDescriptor]> {
    @@ -338,19 +338,19 @@ impl re_types::Archetype for HyphenIpcArchetype {
    /// View state for the custom view.
    ///
    /// This state is preserved between frames, but not across Viewer sessions.
    pub struct HyphenIpcViewState {
    pub struct SystemIpcViewState {
    ipc_client: ipc::Client,
    }

    impl Default for HyphenIpcViewState {
    impl Default for SystemIpcViewState {
    fn default() -> Self {
    Self {
    ipc_client: ipc::Client::new("explorer", ipc::Settings::default()),
    }
    }
    }

    impl ViewState for HyphenIpcViewState {
    impl ViewState for SystemIpcViewState {
    fn as_any(&self) -> &dyn std::any::Any {
    self
    }
  3. matthewjberger revised this gist Mar 4, 2025. 1 changed file with 17 additions and 22 deletions.
    39 changes: 17 additions & 22 deletions main.rs
    Original file line number Diff line number Diff line change
    @@ -7,8 +7,8 @@ use re_viewer::external::{
    re_ui::Help,
    re_viewer_context::{
    self, IdentifiedViewSystem, ViewClass, ViewContext, ViewContextCollection, ViewQuery,
    ViewSpawnHeuristics, ViewState, ViewSystemExecutionError, ViewSystemIdentifier,
    VisualizerQueryInfo, VisualizerSystem,
    ViewSpawnHeuristics, ViewState, ViewStateExt, ViewSystemExecutionError,
    ViewSystemIdentifier, VisualizerQueryInfo, VisualizerSystem,
    },
    };

    @@ -69,19 +69,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
    rerun_app.add_view_class::<HyphenIpc>().unwrap();

    let stream = rerun::RecordingStreamBuilder::new("streamer").spawn()?;
    // stream.log(
    // "world/xyz/",
    // &rerun::Arrows3D::from_vectors(
    // [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]], //
    // )
    // .with_colors([[255, 0, 0], [0, 255, 0], [0, 0, 255]])
    // .with_labels(["X", "Y", "Z"]),
    // )?;
    Ok(Box::new(MyApp {
    rerun_app,
    ipc_client: ipc::Client::new("explorer", ipc::Settings::default()),
    stream,
    }))
    Ok(Box::new(MyApp { rerun_app, stream }))
    }),
    )?;

    @@ -90,7 +78,6 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {

    struct MyApp {
    rerun_app: re_viewer::App,
    ipc_client: ipc::Client,
    stream: rerun::RecordingStream,
    }

    @@ -119,9 +106,7 @@ impl MyApp {
    ui.add_space(4.0);
    ui.vertical_centered(|ui| {
    ui.strong("Custom Right Panel");
    if ui.button("Connect").clicked() {
    //
    }
    if ui.button("Connect").clicked() {}
    });
    ui.separator();

    @@ -274,9 +259,12 @@ impl ViewClass for HyphenIpc {
    query: &re_viewer::external::re_viewer_context::ViewQuery<'_>,
    system_output: re_viewer::external::re_viewer_context::SystemExecutionOutput,
    ) -> Result<(), re_viewer::external::re_viewer_context::ViewSystemExecutionError> {
    // let system_outputs = system_output.view_systems.get::<HyphenIpcSystem>()?;
    let state = state.downcast_mut::<HyphenIpcViewState>()?;

    ui.label("Hyphen IPC Connection");
    if ui.button("Connect").clicked() {
    // TODO: Connect to Hyphen IPC
    let _ = state.ipc_client.connect("127.0.0.1:9000");
    }
    Ok(())
    }
    @@ -350,9 +338,16 @@ impl re_types::Archetype for HyphenIpcArchetype {
    /// View state for the custom view.
    ///
    /// This state is preserved between frames, but not across Viewer sessions.
    #[derive(Default)]
    pub struct HyphenIpcViewState {
    //
    ipc_client: ipc::Client,
    }

    impl Default for HyphenIpcViewState {
    fn default() -> Self {
    Self {
    ipc_client: ipc::Client::new("explorer", ipc::Settings::default()),
    }
    }
    }

    impl ViewState for HyphenIpcViewState {
  4. matthewjberger revised this gist Mar 4, 2025. 1 changed file with 9 additions and 8 deletions.
    17 changes: 9 additions & 8 deletions main.rs
    Original file line number Diff line number Diff line change
    @@ -44,6 +44,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {

    let startup_options = re_viewer::StartupOptions {
    hide_welcome_screen: true,
    persist_state: false,
    ..Default::default()
    };

    @@ -68,14 +69,14 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
    rerun_app.add_view_class::<HyphenIpc>().unwrap();

    let stream = rerun::RecordingStreamBuilder::new("streamer").spawn()?;
    stream.log(
    "world/xyz/",
    &rerun::Arrows3D::from_vectors(
    [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]], //
    )
    .with_colors([[255, 0, 0], [0, 255, 0], [0, 0, 255]])
    .with_labels(["X", "Y", "Z"]),
    )?;
    // stream.log(
    // "world/xyz/",
    // &rerun::Arrows3D::from_vectors(
    // [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]], //
    // )
    // .with_colors([[255, 0, 0], [0, 255, 0], [0, 0, 255]])
    // .with_labels(["X", "Y", "Z"]),
    // )?;
    Ok(Box::new(MyApp {
    rerun_app,
    ipc_client: ipc::Client::new("explorer", ipc::Settings::default()),
  5. matthewjberger created this gist Mar 4, 2025.
    22 changes: 22 additions & 0 deletions Cargo.toml
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,22 @@
    [package]
    name = "scout"
    version = "0.1.0"
    edition = "2024"

    [dependencies]
    mimalloc = "0.1.43"
    ipc = { path = "../controls/crates/ipc" }
    re_crash_handler = { version = "0.22.1", features = ["analytics"] }
    re_grpc_server = "0.22.1"
    re_sdk_comms = { version = "0.22.1", features = ["server"] }
    re_viewer = { version = "0.22.1", features = ["analytics"] }
    tokio = { version = "1.14.0", features = [
    "macros",
    "rt-multi-thread",
    "time",
    "net",
    "io-util",
    "sync",
    "signal",
    ] }
    rerun = "0.22.1"
    365 changes: 365 additions & 0 deletions main.rs
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,365 @@
    //! This example shows how to wrap the Rerun Viewer in your own GUI.
    use re_viewer::external::{
    arrow, eframe, egui, re_chunk_store, re_entity_db, re_log, re_log_types, re_memory,
    re_renderer,
    re_types::{self, ComponentDescriptor},
    re_ui::Help,
    re_viewer_context::{
    self, IdentifiedViewSystem, ViewClass, ViewContext, ViewContextCollection, ViewQuery,
    ViewSpawnHeuristics, ViewState, ViewSystemExecutionError, ViewSystemIdentifier,
    VisualizerQueryInfo, VisualizerSystem,
    },
    };

    // By using `re_memory::AccountingAllocator` Rerun can keep track of exactly how much memory it is using,
    // and prune the data store when it goes above a certain limit.
    // By using `mimalloc` we get faster allocations.
    #[global_allocator]
    static GLOBAL: re_memory::AccountingAllocator<mimalloc::MiMalloc> =
    re_memory::AccountingAllocator::new(mimalloc::MiMalloc);

    fn main() -> Result<(), Box<dyn std::error::Error>> {
    let main_thread_token = re_viewer::MainThreadToken::i_promise_i_am_on_the_main_thread();

    // Direct calls using the `log` crate to stderr. Control with `RUST_LOG=debug` etc.
    re_log::setup_logging();

    // Install handlers for panics and crashes that prints to stderr and send
    // them to Rerun analytics (if the `analytics` feature is on in `Cargo.toml`).
    re_crash_handler::install_crash_handlers(re_viewer::build_info());

    // Listen for TCP connections from Rerun's logging SDKs.
    // There are other ways of "feeding" the viewer though - all you need is a `re_smart_channel::Receiver`.
    let rx = re_sdk_comms::serve(
    "0.0.0.0",
    re_sdk_comms::DEFAULT_SERVER_PORT,
    Default::default(),
    )?;

    let mut native_options = re_viewer::native::eframe_options(None);
    native_options.viewport = native_options
    .viewport
    .with_app_id("rerun_extend_viewer_ui_example");

    let startup_options = re_viewer::StartupOptions {
    hide_welcome_screen: true,
    ..Default::default()
    };

    // This is used for analytics, if the `analytics` feature is on in `Cargo.toml`
    let app_env = re_viewer::AppEnvironment::Custom("My Wrapper".to_owned());

    let window_title = "My Customized Viewer";
    eframe::run_native(
    window_title,
    native_options,
    Box::new(move |cc| {
    re_viewer::customize_eframe_and_setup_renderer(cc)?;

    let mut rerun_app = re_viewer::App::new(
    main_thread_token,
    re_viewer::build_info(),
    &app_env,
    startup_options,
    cc,
    );
    rerun_app.add_receiver(rx);
    rerun_app.add_view_class::<HyphenIpc>().unwrap();

    let stream = rerun::RecordingStreamBuilder::new("streamer").spawn()?;
    stream.log(
    "world/xyz/",
    &rerun::Arrows3D::from_vectors(
    [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]], //
    )
    .with_colors([[255, 0, 0], [0, 255, 0], [0, 0, 255]])
    .with_labels(["X", "Y", "Z"]),
    )?;
    Ok(Box::new(MyApp {
    rerun_app,
    ipc_client: ipc::Client::new("explorer", ipc::Settings::default()),
    stream,
    }))
    }),
    )?;

    Ok(())
    }

    struct MyApp {
    rerun_app: re_viewer::App,
    ipc_client: ipc::Client,
    stream: rerun::RecordingStream,
    }

    impl eframe::App for MyApp {
    fn save(&mut self, storage: &mut dyn eframe::Storage) {
    // Store viewer state on disk
    self.rerun_app.save(storage);
    }

    /// Called whenever we need repainting, which could be 60 Hz.
    fn update(&mut self, ctx: &egui::Context, frame: &mut eframe::Frame) {
    egui::TopBottomPanel::top("top_panel").show(ctx, |ui| {
    ui.label("Top panel");
    });
    egui::SidePanel::right("my_side_panel")
    .default_width(200.0)
    .show(ctx, |ui| {
    self.ui(ui);
    });
    self.rerun_app.update(ctx, frame);
    }
    }

    impl MyApp {
    fn ui(&mut self, ui: &mut egui::Ui) {
    ui.add_space(4.0);
    ui.vertical_centered(|ui| {
    ui.strong("Custom Right Panel");
    if ui.button("Connect").clicked() {
    //
    }
    });
    ui.separator();

    if let Some(entity_db) = self.rerun_app.recording_db() {
    entity_db_ui(ui, entity_db);
    } else {
    ui.label("No log database loaded yet.");
    }
    }
    }

    /// Show the content of the log database.
    fn entity_db_ui(ui: &mut egui::Ui, entity_db: &re_entity_db::EntityDb) {
    if let Some(store_info) = entity_db.store_info() {
    ui.label(format!("Application ID: {}", store_info.application_id));
    }

    // There can be many timelines, but the `log_time` timeline is always there:
    let timeline = re_log_types::Timeline::log_time();

    ui.separator();

    ui.strong("Entities:");

    egui::ScrollArea::vertical()
    .auto_shrink([false, true])
    .show(ui, |ui| {
    for entity_path in entity_db.entity_paths() {
    ui.collapsing(entity_path.to_string(), |ui| {
    entity_ui(ui, entity_db, timeline, entity_path);
    });
    }
    });
    }

    fn entity_ui(
    ui: &mut egui::Ui,
    entity_db: &re_entity_db::EntityDb,
    timeline: re_log_types::Timeline,
    entity_path: &re_log_types::EntityPath,
    ) {
    // Each entity can have many components (e.g. position, color, radius, …):
    if let Some(components) = entity_db
    .storage_engine()
    .store()
    .all_components_on_timeline_sorted(&timeline, entity_path)
    {
    for component in components {
    ui.collapsing(component.to_string(), |ui| {
    component_ui(ui, entity_db, timeline, entity_path, component);
    });
    }
    }
    }

    fn component_ui(
    ui: &mut egui::Ui,
    entity_db: &re_entity_db::EntityDb,
    timeline: re_log_types::Timeline,
    entity_path: &re_log_types::EntityPath,
    component_name: re_types::ComponentName,
    ) {
    // You can query the data for any time point, but for now
    // just show the last value logged for each component:
    let query = re_chunk_store::LatestAtQuery::latest(timeline);

    let results =
    entity_db
    .storage_engine()
    .cache()
    .latest_at(&query, entity_path, [component_name]);

    if let Some(data) = results.component_batch_raw(&component_name) {
    egui::ScrollArea::vertical()
    .auto_shrink([false, true])
    .show(ui, |ui| {
    // Iterate over all the instances (e.g. all the points in the point cloud):

    let num_instances = data.len();
    for i in 0..num_instances {
    ui.label(format_arrow(&*data.slice(i, 1)));
    }
    });
    };
    }

    fn format_arrow(array: &dyn arrow::array::Array) -> String {
    use arrow::util::display::{ArrayFormatter, FormatOptions};

    let num_bytes = array.get_buffer_memory_size();
    if array.len() == 1 && num_bytes < 256 {
    // Print small items:
    let options = FormatOptions::default();
    if let Ok(formatter) = ArrayFormatter::try_new(array, &options) {
    return formatter.value(0).to_string();
    }
    }

    // Fallback:
    format!("{num_bytes} bytes")
    }

    /// A custom view class for rerun
    #[derive(Default)]
    pub struct HyphenIpc;

    impl ViewClass for HyphenIpc {
    fn identifier() -> re_types::ViewClassIdentifier
    where
    Self: Sized,
    {
    "Hyphen IPC".into()
    }

    fn display_name(&self) -> &'static str {
    "Hyphen IPC"
    }

    fn help(&self, egui_ctx: &egui::Context) -> re_viewer::external::re_ui::Help<'_> {
    Help::new("Hyphen IPC").markdown("This is a custom view class for Hyphen IPC.")
    }

    fn on_register(
    &self,
    system_registry: &mut re_viewer::external::re_viewer_context::ViewSystemRegistrator<'_>,
    ) -> Result<(), re_viewer::external::re_viewer_context::ViewClassRegistryError> {
    system_registry.register_visualizer::<HyphenIpcSystem>()
    }

    fn new_state(&self) -> Box<dyn re_viewer::external::re_viewer_context::ViewState> {
    Box::<HyphenIpcViewState>::default()
    }

    fn layout_priority(&self) -> re_viewer::external::re_viewer_context::ViewClassLayoutPriority {
    Default::default()
    }

    fn spawn_heuristics(
    &self,
    ctx: &re_viewer::external::re_viewer_context::ViewerContext<'_>,
    ) -> re_viewer::external::re_viewer_context::ViewSpawnHeuristics {
    ViewSpawnHeuristics::root()
    }

    fn ui(
    &self,
    ctx: &re_viewer::external::re_viewer_context::ViewerContext<'_>,
    ui: &mut egui::Ui,
    state: &mut dyn re_viewer::external::re_viewer_context::ViewState,
    query: &re_viewer::external::re_viewer_context::ViewQuery<'_>,
    system_output: re_viewer::external::re_viewer_context::SystemExecutionOutput,
    ) -> Result<(), re_viewer::external::re_viewer_context::ViewSystemExecutionError> {
    ui.label("Hyphen IPC Connection");
    if ui.button("Connect").clicked() {
    // TODO: Connect to Hyphen IPC
    }
    Ok(())
    }
    }

    /// Our Hyphen IPC view's contents
    #[derive(Default)]
    pub struct HyphenIpcSystem {
    pub value: u8,
    }

    impl IdentifiedViewSystem for HyphenIpcSystem {
    fn identifier() -> ViewSystemIdentifier {
    "InstanceHyphenIpc".into()
    }
    }

    impl VisualizerSystem for HyphenIpcSystem {
    fn visualizer_query_info(&self) -> VisualizerQueryInfo {
    VisualizerQueryInfo::from_archetype::<HyphenIpcArchetype>()
    }

    /// Populates the scene part with data from the store.
    fn execute(
    &mut self,
    ctx: &ViewContext<'_>,
    query: &ViewQuery<'_>,
    _context_systems: &ViewContextCollection,
    ) -> Result<Vec<re_renderer::QueueableDrawData>, ViewSystemExecutionError> {
    // We're not using `re_renderer` here, so return an empty vector.
    Ok(Vec::new())
    }

    fn as_any(&self) -> &dyn std::any::Any {
    self
    }

    fn fallback_provider(&self) -> &dyn re_viewer_context::ComponentFallbackProvider {
    self
    }
    }

    // Implements a `ComponentFallbackProvider` trait for the `InstanceColorSystem`.
    // It is left empty here but could be used to provides fallback values for optional components in case they're missing.
    re_viewer_context::impl_component_fallback_provider!(HyphenIpcSystem => []);

    struct HyphenIpcArchetype;

    impl re_types::Archetype for HyphenIpcArchetype {
    type Indicator = re_types::GenericIndicatorComponent<Self>;

    fn indicator() -> re_types::SerializedComponentBatch {
    use re_types::ComponentBatch as _;
    #[allow(clippy::unwrap_used)]
    Self::Indicator::default().serialized().unwrap()
    }

    fn name() -> re_types::ArchetypeName {
    "InstanceHyphenIpc".into()
    }

    fn display_name() -> &'static str {
    "Hyphen IPC"
    }

    fn required_components() -> ::std::borrow::Cow<'static, [ComponentDescriptor]> {
    vec![].into()
    }
    }

    /// View state for the custom view.
    ///
    /// This state is preserved between frames, but not across Viewer sessions.
    #[derive(Default)]
    pub struct HyphenIpcViewState {
    //
    }

    impl ViewState for HyphenIpcViewState {
    fn as_any(&self) -> &dyn std::any::Any {
    self
    }

    fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
    self
    }
    }