Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fully migrate to DataQueryBlueprint #4311

Merged
merged 26 commits into from
Nov 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
b56fa83
Adding new EntityPath and Uuid datatypes
jleibs Nov 16, 2023
0c51520
Make the SpaceViewComponent a proper type
jleibs Nov 16, 2023
beb1863
Move SpaceViewComponent -> re_types
jleibs Nov 16, 2023
fd51808
Get the types to actually compile
jleibs Nov 16, 2023
d6543ac
Move EntityPath -> re_types_core
jleibs Nov 16, 2023
1ac70d7
Uuid conversions
jleibs Nov 16, 2023
6cbb760
Populate SpaceViewBlueprint from the updated SpaceViewComponent
jleibs Nov 17, 2023
fe0c33a
Save the queries when syncing
jleibs Nov 17, 2023
6627ea0
Add a hacky query-editor for debugging
jleibs Nov 17, 2023
709015c
Backwards compatible property override resolver
jleibs Nov 22, 2023
aacc272
Switch over to using the new query expressions
jleibs Nov 22, 2023
273a3ef
Fix individual / recursive overrides
jleibs Nov 23, 2023
4ba3313
lint
jleibs Nov 23, 2023
06b5bc5
Starting to tear out SpaceViewContents
jleibs Nov 23, 2023
e7f9b7d
Continue tearing out SpaceViewContents
jleibs Nov 28, 2023
431bb14
Tweaks to expression editor
jleibs Nov 28, 2023
3da8c1c
Assorted lints
jleibs Nov 28, 2023
dfe4a3c
Clean up TODOs to reference #4377
jleibs Nov 28, 2023
2e099cc
Avoid parse error on root entity path
jleibs Nov 28, 2023
2723ea8
Remove context systems from EntitiesPerSystemPerClass to fix heuristics
jleibs Nov 28, 2023
0579ef1
Fix the overrides test now that it's using a real query
jleibs Nov 29, 2023
78fa33b
Fix docs for unreleased components
jleibs Nov 29, 2023
b7dcb3b
Make python api call an actual error
jleibs Nov 29, 2023
f35512a
get rid of unwrap
jleibs Nov 29, 2023
103cd38
One more unreleased component
jleibs Nov 29, 2023
68b22fe
Fix speculative links for used-by types
jleibs Nov 29, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion crates/re_data_ui/src/item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ impl DataUi for Item {
query: &re_arrow_store::LatestAtQuery,
) {
match self {
Item::SpaceView(_) | Item::DataBlueprintGroup(_, _) => {
Item::SpaceView(_) | Item::DataBlueprintGroup(_, _, _) => {
// Shouldn't be reachable since SelectionPanel::contents doesn't show data ui for these.
// If you add something in here make sure to adjust SelectionPanel::contents accordingly.
}
Expand Down
7 changes: 4 additions & 3 deletions crates/re_data_ui/src/item_ui.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use egui::Ui;
use re_data_store::InstancePath;
use re_log_types::{ComponentPath, EntityPath, TimeInt, Timeline};
use re_viewer_context::{
DataBlueprintGroupHandle, HoverHighlight, Item, SpaceViewId, UiVerbosity, ViewerContext,
DataQueryId, HoverHighlight, Item, SpaceViewId, UiVerbosity, ViewerContext,
};

use super::DataUi;
Expand Down Expand Up @@ -197,9 +197,10 @@ pub fn data_blueprint_group_button_to(
ui: &mut egui::Ui,
text: impl Into<egui::WidgetText>,
space_view_id: SpaceViewId,
group_handle: DataBlueprintGroupHandle,
query_id: DataQueryId,
entity_path: EntityPath,
) -> egui::Response {
let item = Item::DataBlueprintGroup(space_view_id, group_handle);
let item = Item::DataBlueprintGroup(space_view_id, query_id, entity_path);
let response = ctx
.re_ui
.selectable_label_with_icon(
Expand Down
14 changes: 14 additions & 0 deletions crates/re_log_types/src/path/entity_path.rs
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,20 @@ impl From<EntityPath> for String {
}
}

impl From<re_types_core::datatypes::EntityPath> for EntityPath {
#[inline]
fn from(value: re_types_core::datatypes::EntityPath) -> Self {
EntityPath::parse_forgiving(&value.0)
}
}

impl From<&EntityPath> for re_types_core::datatypes::EntityPath {
#[inline]
fn from(value: &EntityPath) -> Self {
Self(value.to_string().into())
}
}

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

use re_types_core::Loggable;
Expand Down
23 changes: 22 additions & 1 deletion crates/re_log_types/src/path/entity_path_expr.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::fmt::Display;

use crate::EntityPath;

/// An expression that corresponds to multiple [`EntityPath`]s within a tree.
Expand Down Expand Up @@ -44,11 +46,30 @@ impl EntityPathExpr {
}
}

impl Display for EntityPathExpr {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Exact(path) => path.fmt(f),
Self::Recursive(path) => {
if path.is_root() {
write!(f, "/")
} else {
write!(f, "{path}/")
}
}
}
}
}

impl From<&str> for EntityPathExpr {
#[inline]
fn from(path: &str) -> Self {
if let Some(path) = path.strip_suffix('/') {
Self::Recursive(EntityPath::from(path))
if path.is_empty() {
Self::Recursive(EntityPath::root())
} else {
Self::Recursive(EntityPath::from(path))
}
} else {
Self::Exact(EntityPath::from(path))
}
Expand Down
2 changes: 1 addition & 1 deletion crates/re_space_view/src/blueprint/query_expressions.rs

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

14 changes: 14 additions & 0 deletions crates/re_space_view/src/data_query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,20 @@ pub trait PropertyResolver {
fn resolve_entity_overrides(&self, ctx: &StoreContext<'_>) -> EntityOverrides;
}

pub struct NoopResolver {}

impl PropertyResolver for NoopResolver {
fn resolve_entity_overrides(&self, _ctx: &StoreContext<'_>) -> EntityOverrides {
EntityOverrides {
root: EntityProperties::default(),
individual: EntityPropertyMap::default(),
group: EntityPropertyMap::default(),
}
}
}

pub static NOOP_RESOLVER: NoopResolver = NoopResolver {};

/// The common trait implemented for data queries
///
/// Both interfaces return [`re_viewer_context::DataResult`]s, which are self-contained description of the data
Expand Down
187 changes: 176 additions & 11 deletions crates/re_space_view/src/data_query_blueprint.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
use nohash_hasher::IntSet;
use once_cell::sync::Lazy;
use re_data_store::{EntityProperties, EntityTree};
use re_data_store::{
EntityProperties, EntityPropertiesComponent, EntityPropertyMap, EntityTree, StoreDb,
};
use re_log_types::{EntityPath, EntityPathExpr};
use re_viewer_context::{
DataQueryId, DataQueryResult, DataResult, DataResultHandle, DataResultNode, DataResultTree,
EntitiesPerSystem, EntitiesPerSystemPerClass, SpaceViewClassName,
EntitiesPerSystem, EntitiesPerSystemPerClass, SpaceViewClassName, SpaceViewId, StoreContext,
};
use slotmap::SlotMap;
use smallvec::SmallVec;
Expand All @@ -23,14 +25,75 @@ use crate::{blueprint::QueryExpressions, DataQuery, EntityOverrides, PropertyRes
/// The results of recursive expressions are only included if they are found within the [`EntityTree`]
/// and for which there is a valid `ViewPart` system. This keeps recursive expressions from incorrectly
/// picking up irrelevant data within the tree.
#[derive(Clone, PartialEq, Eq)]
pub struct DataQueryBlueprint {
pub id: DataQueryId,
pub space_view_class_name: SpaceViewClassName,
pub expressions: QueryExpressions,
}

impl DataQueryBlueprint {
pub const OVERRIDES_PREFIX: &str = "overrides";
pub fn is_equivalent(&self, other: &DataQueryBlueprint) -> bool {
self.space_view_class_name.eq(&other.space_view_class_name)
&& self.expressions.eq(&other.expressions)
}
}

impl DataQueryBlueprint {
pub const INDIVIDUAL_OVERRIDES_PREFIX: &str = "individual_overrides";
pub const RECURSIVE_OVERRIDES_PREFIX: &str = "recursive_overrides";

pub fn new<'a>(
space_view_class_name: SpaceViewClassName,
queries_entities: impl Iterator<Item = &'a EntityPathExpr>,
) -> Self {
Self {
id: DataQueryId::random(),
space_view_class_name,
expressions: queries_entities
.map(|exp| exp.to_string().into())
.collect::<Vec<_>>()
.into(),
}
}

pub fn try_from_db(
path: &EntityPath,
blueprint_db: &StoreDb,
space_view_class_name: SpaceViewClassName,
) -> Option<Self> {
let expressions = blueprint_db
.store()
.query_timeless_component::<QueryExpressions>(path)
.map(|c| c.value)?;
jleibs marked this conversation as resolved.
Show resolved Hide resolved

let id = DataQueryId::from_entity_path(path);

Some(Self {
id,
space_view_class_name,
expressions,
})
}

pub fn build_resolver<'a>(
&self,
container: SpaceViewId,
auto_properties: &'a EntityPropertyMap,
) -> DataQueryPropertyResolver<'a> {
DataQueryPropertyResolver {
auto_properties,
default_stack: vec![container.as_entity_path(), self.id.as_entity_path()],
individual_override_root: self
.id
.as_entity_path()
.join(&Self::INDIVIDUAL_OVERRIDES_PREFIX.into()),
recursive_override_root: self
.id
.as_entity_path()
.join(&Self::RECURSIVE_OVERRIDES_PREFIX.into()),
}
}
}

impl DataQuery for DataQueryBlueprint {
Expand Down Expand Up @@ -93,6 +156,7 @@ impl<'a> QueryExpressionEvaluator<'a> {
.expressions
.expressions
.iter()
.filter(|exp| !exp.as_str().is_empty())
.map(|exp| EntityPathExpr::from(exp.as_str()))
.collect();

Expand Down Expand Up @@ -166,18 +230,29 @@ impl<'a> QueryExpressionEvaluator<'a> {
}

let base_entity_path = self.blueprint.id.as_entity_path().clone();
let prefix = EntityPath::from(DataQueryBlueprint::OVERRIDES_PREFIX);
let override_path = base_entity_path.join(&prefix).join(&entity_path);

let individual_override_path = base_entity_path
.join(&DataQueryBlueprint::INDIVIDUAL_OVERRIDES_PREFIX.into())
.join(&entity_path);
let recursive_override_path = base_entity_path
.join(&DataQueryBlueprint::RECURSIVE_OVERRIDES_PREFIX.into())
.join(&entity_path);

let self_leaf = if !view_parts.is_empty() || exact_match {
let individual_props = overrides.individual.get_opt(&entity_path);
let mut leaf_resolved_properties = resolved_properties.clone();

if let Some(props) = individual_props {
leaf_resolved_properties = leaf_resolved_properties.with_child(props);
}
Some(data_results.insert(DataResultNode {
data_result: DataResult {
entity_path: entity_path.clone(),
view_parts,
is_group: false,
individual_properties: overrides.individual.get_opt(&entity_path).cloned(),
resolved_properties: resolved_properties.clone(),
override_path: override_path.clone(),
resolved_properties: leaf_resolved_properties,
override_path: individual_override_path,
},
children: Default::default(),
}))
Expand All @@ -196,7 +271,7 @@ impl<'a> QueryExpressionEvaluator<'a> {
self.add_entity_tree_to_data_results_recursive(
subtree,
overrides,
inherited,
&resolved_properties,
data_results,
recursive_match, // Once we have hit a recursive match, it's always propagated
)
Expand All @@ -207,26 +282,109 @@ impl<'a> QueryExpressionEvaluator<'a> {
if children.is_empty() || children.len() == 1 && self_leaf.is_some() {
self_leaf
} else {
let individual_properties = overrides.individual.get_opt(&entity_path).cloned();
// The 'individual' properties of a group are the group overrides
let individual_properties = overrides.group.get_opt(&entity_path).cloned();
Some(data_results.insert(DataResultNode {
data_result: DataResult {
entity_path,
view_parts: Default::default(),
is_group: true,
individual_properties,
resolved_properties,
override_path,
override_path: recursive_override_path,
},
children,
}))
}
}
}

pub struct DataQueryPropertyResolver<'a> {
auto_properties: &'a EntityPropertyMap,
default_stack: Vec<EntityPath>,
individual_override_root: EntityPath,
recursive_override_root: EntityPath,
}

impl DataQueryPropertyResolver<'_> {
fn resolve_entity_overrides_for_path(
&self,
ctx: &StoreContext<'_>,
props_path: &EntityPath,
) -> EntityPropertyMap {
re_tracing::profile_function!();
let blueprint = ctx.blueprint;

let mut prop_map = self.auto_properties.clone();

if let Some(tree) = blueprint.entity_db().tree.subtree(props_path) {
tree.visit_children_recursively(&mut |path: &EntityPath| {
if let Some(props) = blueprint
.store()
.query_timeless_component_quiet::<EntityPropertiesComponent>(path)
{
let overridden_path =
EntityPath::from(&path.as_slice()[props_path.len()..path.len()]);
prop_map.update(overridden_path, props.value.props);
}
});
}
prop_map
}
}

impl<'a> PropertyResolver for DataQueryPropertyResolver<'a> {
/// Helper function to lookup the properties for a given entity path.
///
/// We start with the auto properties for the `SpaceView` as the base layer and
/// then incrementally override from there.
fn resolve_entity_overrides(&self, ctx: &StoreContext<'_>) -> EntityOverrides {
re_tracing::profile_function!();
let blueprint = ctx.blueprint;

let mut root: EntityProperties = Default::default();
for prefix in &self.default_stack {
if let Some(overrides) = ctx
.blueprint
.store()
.query_timeless_component::<EntityPropertiesComponent>(prefix)
{
root = root.with_child(&overrides.value.props);
}
}

let mut individual = self.auto_properties.clone();

if let Some(tree) = blueprint
.entity_db()
.tree
.subtree(&self.individual_override_root)
{
tree.visit_children_recursively(&mut |path: &EntityPath| {
if let Some(props) = blueprint
.store()
.query_timeless_component::<EntityPropertiesComponent>(path)
{
let overridden_path = EntityPath::from(
&path.as_slice()[self.individual_override_root.len()..path.len()],
);
individual.update(overridden_path, props.value.props);
}
});
}

EntityOverrides {
root,
individual: self.resolve_entity_overrides_for_path(ctx, &self.individual_override_root),
group: self.resolve_entity_overrides_for_path(ctx, &self.recursive_override_root),
}
}
}

#[cfg(feature = "testing")]
#[cfg(test)]
mod tests {
use re_data_store::{EntityPropertyMap, StoreDb};
use re_data_store::StoreDb;
use re_log_types::{example_components::MyPoint, DataRow, RowId, StoreId, TimePoint, Timeline};
use re_viewer_context::StoreContext;

Expand Down Expand Up @@ -334,6 +492,13 @@ mod tests {
"parent/skipped/child2",
],
),
(
vec!["not/found"],
// TODO(jleibs): Making this work requires merging the EntityTree walk with a minimal-coverage ExactMatchTree walk
// not crucial for now until we expose a free-form UI for entering paths.
// vec!["/", "not/", "not/found"]),
vec![],
),
];

for (input, outputs) in scenarios {
Expand Down
2 changes: 1 addition & 1 deletion crates/re_space_view/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ mod space_view_contents;
mod unreachable_transform_reason;

pub use blueprint::QueryExpressions;
pub use data_query::{DataQuery, EntityOverrides, PropertyResolver};
pub use data_query::{DataQuery, EntityOverrides, PropertyResolver, NOOP_RESOLVER};
pub use data_query_blueprint::DataQueryBlueprint;
pub use screenshot::ScreenshotMode;
pub use space_view_contents::{DataBlueprintGroup, SpaceViewContents};
Expand Down
Loading
Loading