Skip to content

Commit

Permalink
Introduce a TimeSeries archetype and use it to control the plot legend (
Browse files Browse the repository at this point in the history
#4867)

### What
This is the basic pattern we expect to see for all of these
view-configuration type tasks:
 - Add a new archetype / component
- Add logic to the TimeSeries space-view-class implementation that both
reads and writes the components directly from the blueprint store.
 
This generally seems to be much easier to think about than dealing with
the EntityProperties struct as this was handled previously as it's
exclusively a contact between the TimeSeries archetype and the
TimeSeriesSpaceView implementation.

Open question:
- Should we suffix all our view archetypes with something like
`TimeSeriesView`?

Needs #4609 to land first since I
developed there before rebasing to main.

Will be nice to come back and clean this up again once
#3384 is done.

### 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 the web demo (if applicable):
* Using newly built examples:
[app.rerun.io](https://app.rerun.io/pr/4867/index.html)
* Using examples from latest `main` build:
[app.rerun.io](https://app.rerun.io/pr/4867/index.html?manifest_url=https://app.rerun.io/version/main/examples_manifest.json)
* Using full set of examples from `nightly` build:
[app.rerun.io](https://app.rerun.io/pr/4867/index.html?manifest_url=https://app.rerun.io/version/nightly/examples_manifest.json)
* [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/4867)
- [Docs
preview](https://rerun.io/preview/d76ed7714f29407bc1d4a62779bfa2894a10fa90/docs)
<!--DOCS-PREVIEW-->
- [Examples
preview](https://rerun.io/preview/d76ed7714f29407bc1d4a62779bfa2894a10fa90/examples)
<!--EXAMPLES-PREVIEW-->
- [Recent benchmark results](https://build.rerun.io/graphs/crates.html)
- [Wasm size tracking](https://build.rerun.io/graphs/sizes.html)
  • Loading branch information
jleibs committed Jan 19, 2024
1 parent 2921f3c commit 965d063
Show file tree
Hide file tree
Showing 42 changed files with 1,353 additions and 36 deletions.
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion crates/re_space_view_time_series/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,12 @@ all-features = true
re_data_store.workspace = true
re_format.workspace = true
re_log_types.workspace = true
re_query.workspace = true
re_query_cache.workspace = true
re_renderer.workspace = true
re_space_view.workspace = true
re_tracing.workspace = true
re_types.workspace = true
re_types = { workspace = true, features = ["egui_plot"] }
re_ui.workspace = true
re_viewer_context.workspace = true

Expand Down
85 changes: 52 additions & 33 deletions crates/re_space_view_time_series/src/space_view_class.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,9 @@ use egui_plot::{Legend, Line, Plot, Points};
use re_data_store::TimeType;
use re_format::next_grid_tick_magnitude_ns;
use re_log_types::{EntityPath, TimeZone};
use re_query::query_archetype;
use re_space_view::controls;
use re_viewer_context::external::re_entity_db::{
EditableAutoValue, EntityProperties, LegendCorner,
};
use re_viewer_context::external::re_entity_db::EntityProperties;
use re_viewer_context::{
SpaceViewClass, SpaceViewClassRegistryError, SpaceViewId, SpaceViewState,
SpaceViewSystemExecutionError, SystemExecutionOutput, ViewQuery, ViewerContext,
Expand Down Expand Up @@ -39,6 +38,8 @@ impl SpaceViewState for TimeSeriesSpaceViewState {
#[derive(Default)]
pub struct TimeSeriesSpaceView;

const DEFAULT_LEGEND_CORNER: egui_plot::Corner = egui_plot::Corner::RightBottom;

impl SpaceViewClass for TimeSeriesSpaceView {
type State = TimeSeriesSpaceViewState;

Expand Down Expand Up @@ -99,54 +100,71 @@ impl SpaceViewClass for TimeSeriesSpaceView {
ui: &mut egui::Ui,
_state: &mut Self::State,
_space_origin: &EntityPath,
_space_view_id: SpaceViewId,
root_entity_properties: &mut EntityProperties,
space_view_id: SpaceViewId,
_root_entity_properties: &mut EntityProperties,
) {
let re_types::blueprint::archetypes::TimeSeries { legend } = query_archetype(
ctx.store_context.blueprint.store(),
ctx.blueprint_query,
&space_view_id.as_entity_path(),
)
.and_then(|arch| arch.to_archetype())
.unwrap_or_default();

ctx.re_ui
.selection_grid(ui, "time_series_selection_ui")
.show(ui, |ui| {
ctx.re_ui.grid_left_hand_label(ui, "Legend");

ui.vertical(|ui| {
let mut selected = *root_entity_properties.show_legend.get();
if ctx.re_ui.checkbox(ui, &mut selected, "Visible").changed() {
root_entity_properties.show_legend =
EditableAutoValue::UserEdited(selected);
}
let mut edit_legend = legend.clone();

let mut corner = root_entity_properties
.legend_location
.unwrap_or(LegendCorner::RightBottom);
ctx.re_ui
.checkbox(ui, &mut edit_legend.0.visible, "Visible");

let mut corner = legend.corner().unwrap_or(DEFAULT_LEGEND_CORNER);

egui::ComboBox::from_id_source("legend_corner")
.selected_text(corner.to_string())
.selected_text(re_types::blueprint::components::Legend::to_str(corner))
.show_ui(ui, |ui| {
ui.style_mut().wrap = Some(false);
ui.set_min_width(64.0);

ui.selectable_value(
&mut corner,
LegendCorner::LeftTop,
LegendCorner::LeftTop.to_string(),
egui_plot::Corner::LeftTop,
re_types::blueprint::components::Legend::to_str(
egui_plot::Corner::LeftTop,
),
);
ui.selectable_value(
&mut corner,
LegendCorner::RightTop,
LegendCorner::RightTop.to_string(),
egui_plot::Corner::RightTop,
re_types::blueprint::components::Legend::to_str(
egui_plot::Corner::RightTop,
),
);
ui.selectable_value(
&mut corner,
LegendCorner::LeftBottom,
LegendCorner::LeftBottom.to_string(),
egui_plot::Corner::LeftBottom,
re_types::blueprint::components::Legend::to_str(
egui_plot::Corner::LeftBottom,
),
);
ui.selectable_value(
&mut corner,
LegendCorner::RightBottom,
LegendCorner::RightBottom.to_string(),
egui_plot::Corner::RightBottom,
re_types::blueprint::components::Legend::to_str(
egui_plot::Corner::RightBottom,
),
);
});

root_entity_properties.legend_location = Some(corner);
edit_legend.set_corner(corner);

if legend != edit_legend {
ctx.save_blueprint_component(&space_view_id.as_entity_path(), edit_legend);
}
});
ui.end_row();
});
Expand All @@ -157,12 +175,20 @@ impl SpaceViewClass for TimeSeriesSpaceView {
ctx: &ViewerContext<'_>,
ui: &mut egui::Ui,
state: &mut Self::State,
root_entity_properties: &EntityProperties,
_root_entity_properties: &EntityProperties,
_query: &ViewQuery<'_>,
system_output: SystemExecutionOutput,
) -> Result<(), SpaceViewSystemExecutionError> {
re_tracing::profile_function!();

let re_types::blueprint::archetypes::TimeSeries { legend } = query_archetype(
ctx.store_context.blueprint.store(),
ctx.blueprint_query,
&_query.space_view_id.as_entity_path(),
)
.and_then(|arch| arch.to_archetype())
.unwrap_or_default();

let (current_time, time_type, timeline) = {
// Avoid holding the lock for long
let time_ctrl = ctx.rec_cfg.time_ctrl.read();
Expand Down Expand Up @@ -219,15 +245,8 @@ impl SpaceViewClass for TimeSeriesSpaceView {
)
});

if *root_entity_properties.show_legend {
plot = plot.legend(
Legend::default().position(
root_entity_properties
.legend_location
.unwrap_or(LegendCorner::RightBottom)
.into(),
),
);
if legend.visible {
plot = plot.legend(Legend::default().position(DEFAULT_LEGEND_CORNER));
}

if timeline.typ() == TimeType::Time {
Expand Down
6 changes: 5 additions & 1 deletion crates/re_types/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ features = ["all"]
default = []

## All features except `testing`.
all = ["ecolor", "glam", "image", "mint", "serde"]
all = ["ecolor", "egui_plot", "glam", "image", "mint", "serde"]

## Enables the `datagen` module, which exposes a number of tools for generating random data for
## tests and benchmarks.
Expand All @@ -32,6 +32,9 @@ datagen = ["dep:rand"]
## Enable color conversions.
ecolor = ["dep:ecolor"]

## Enable conversions to plot primitives
egui_plot = ["dep:egui_plot"]

## Add support for some math operations using [`glam`](https://crates.io/crates/glam/).
glam = ["dep:glam"]

Expand Down Expand Up @@ -76,6 +79,7 @@ uuid = { workspace = true, features = ["serde", "v4", "js"] }

# External (optional)
ecolor = { workspace = true, optional = true }
egui_plot = { workspace = true, optional = true }
glam = { workspace = true, optional = true }
image = { workspace = true, optional = true, default-features = false }
mint = { workspace = true, optional = true }
Expand Down
5 changes: 5 additions & 0 deletions crates/re_types/definitions/rerun/blueprint.fbs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@ include "./blueprint/components/space_view_origin.fbs";
include "./blueprint/components/viewport_layout.fbs";
include "./blueprint/components/visible.fbs";

include "./blueprint/datatypes/legend.fbs";
include "./blueprint/components/legend.fbs";

include "./blueprint/archetypes/container_blueprint.fbs";
include "./blueprint/archetypes/space_view_blueprint.fbs";
include "./blueprint/archetypes/viewport_blueprint.fbs";

include "./blueprint/archetypes/time_series.fbs";
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
include "fbs/attributes.fbs";
include "rust/attributes.fbs";

include "rerun/datatypes.fbs";

namespace rerun.blueprint.archetypes;

// ---

/// The configuration options for a `TimeSeries` `SpaceView`.
table TimeSeries (
"attr.docs.unreleased",
"attr.rerun.scope": "blueprint",
"attr.rust.derive": "Default"
) {
// --- Required ---

/// Configuration information for the legend
// TODO(jleibs): This should be optional, but we need at least one required component
legend: rerun.blueprint.components.Legend ("attr.rerun.component_required", order: 1000);

// --- Optional ---

}
18 changes: 18 additions & 0 deletions crates/re_types/definitions/rerun/blueprint/components/legend.fbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
include "arrow/attributes.fbs";
include "python/attributes.fbs";
include "rust/attributes.fbs";

include "rerun/datatypes.fbs";
include "rerun/attributes.fbs";

namespace rerun.blueprint.components;

// ---

/// Configuration for the legend of a plot.
table Legend (
"attr.rerun.scope": "blueprint",
"attr.rust.derive": "PartialEq, Eq, PartialOrd, Ord"
) {
legend: rerun.blueprint.datatypes.Legend (order: 100);
}
39 changes: 39 additions & 0 deletions crates/re_types/definitions/rerun/blueprint/datatypes/legend.fbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
include "arrow/attributes.fbs";
include "python/attributes.fbs";
include "rust/attributes.fbs";

include "rerun/datatypes.fbs";
include "rerun/attributes.fbs";

namespace rerun.blueprint.datatypes;


// TODO(#3384)
/*
enum LegendPosition: byte {
LeftTop = 1,
RightTop = 2,
LeftBottom = 3,
RightBottom = 4
}
*/

// ---

/// Configuration for the legend of a plot.
table Legend (
"attr.rerun.scope": "blueprint",
"attr.rust.derive": "PartialEq, Eq, PartialOrd, Ord"
) {
/// Whether or not the legend should be displayed.
visible: bool (order: 100);

/// Where should the legend be located.
///
/// Allowed values:
/// - LeftTop = 1,
/// - RightTop = 2,
/// - LeftBottom = 3,
/// - RightBottom = 4
location: ubyte (order: 200, nullable);
}
1 change: 1 addition & 0 deletions crates/re_types/src/blueprint/archetypes/.gitattributes

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions crates/re_types/src/blueprint/archetypes/mod.rs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 965d063

Please sign in to comment.