use std::sync::LazyLock;
use ahash::HashMap;
use nohash_hasher::IntMap;
use re_log_types::{EntityPath, EntityPathHash};
use re_sdk_types::blueprint::components::VisualizerInstructionId;
use slotmap::SlotMap;
use smallvec::SmallVec;
use crate::{
ActiveStoreContext, AppContext, DataResult, ViewContext, ViewId, ViewState, ViewerContext,
blueprint_timeline,
};
slotmap::new_key_type! {
pub struct DataResultHandle;
}
pub struct QueryContext<'a> {
pub view_ctx: &'a ViewContext<'a>,
pub target_entity_path: &'a re_log_types::EntityPath,
pub instruction_id: Option<VisualizerInstructionId>,
pub archetype_name: Option<re_sdk_types::ArchetypeName>,
pub query: re_chunk_store::LatestAtQuery,
}
impl QueryContext<'_> {
#[inline]
pub fn app_ctx(&self) -> &AppContext<'_> {
&self.view_ctx.viewer_ctx.app_ctx
}
#[inline]
pub fn viewer_ctx(&self) -> &ViewerContext<'_> {
self.view_ctx.viewer_ctx
}
#[inline]
pub fn store_ctx(&self) -> &ActiveStoreContext<'_> {
self.view_ctx.viewer_ctx.store_context
}
#[inline]
pub fn render_ctx(&self) -> &re_renderer::RenderContext {
self.view_ctx.viewer_ctx.app_ctx.render_ctx
}
#[inline]
pub fn egui_ctx(&self) -> &egui::Context {
self.view_ctx.viewer_ctx.app_ctx.egui_ctx
}
#[inline]
pub fn recording(&self) -> &re_entity_db::EntityDb {
self.view_ctx.recording()
}
#[inline]
pub fn view_state(&self) -> &dyn ViewState {
self.view_ctx.view_state
}
}
#[derive(Debug)]
pub struct DataQueryResult {
pub tree: DataResultTree,
pub num_matching_entities: usize,
pub num_visualized_entities: usize,
pub view_defaults: re_query::LatestAtResults,
}
impl Default for DataQueryResult {
fn default() -> Self {
Self {
tree: Default::default(),
num_matching_entities: 0,
num_visualized_entities: 0,
view_defaults: re_query::LatestAtResults::empty(
"<defaults>".into(),
re_chunk_store::LatestAtQuery::latest(blueprint_timeline()),
),
}
}
}
impl DataQueryResult {
#[inline]
pub fn is_empty(&self) -> bool {
self.tree.is_empty()
}
#[inline]
pub fn result_for_entity(&self, path: &EntityPath) -> Option<&DataResult> {
self.tree
.lookup_result_by_path(path.hash())
.filter(|result| !result.tree_prefix_only)
}
}
impl Clone for DataQueryResult {
fn clone(&self) -> Self {
re_tracing::profile_function!();
Self {
tree: self.tree.clone(),
num_matching_entities: self.num_matching_entities,
num_visualized_entities: self.num_visualized_entities,
view_defaults: self.view_defaults.clone(),
}
}
}
#[derive(Clone, Default, Debug)]
pub struct DataResultTree {
pub data_results: SlotMap<DataResultHandle, DataResultNode>,
pub data_results_by_path: IntMap<EntityPathHash, DataResultHandle>,
pub data_results_by_visualizer_instruction: HashMap<VisualizerInstructionId, DataResultHandle>,
root_handle: Option<DataResultHandle>,
}
#[derive(Clone, Debug)]
pub struct DataResultNode {
pub data_result: DataResult,
pub children: SmallVec<[DataResultHandle; 4]>,
}
impl DataResultTree {
pub fn new(
data_results: SlotMap<DataResultHandle, DataResultNode>,
root_handle: Option<DataResultHandle>,
) -> Self {
re_tracing::profile_function!();
let data_results_by_path = data_results
.iter()
.map(|(handle, node)| (node.data_result.entity_path.hash(), handle))
.collect();
Self {
data_results,
data_results_by_path,
root_handle,
data_results_by_visualizer_instruction: Default::default(),
}
}
pub fn root_handle(&self) -> Option<DataResultHandle> {
self.root_handle
}
pub fn root_node(&self) -> Option<&DataResultNode> {
self.data_results.get(self.root_handle?)
}
pub fn visit<'a>(&'a self, visitor: &mut impl FnMut(&'a DataResultNode) -> bool) {
if let Some(root_handle) = self.root_handle {
self.visit_recursive(root_handle, visitor);
}
}
#[inline]
pub fn lookup_result(&self, handle: DataResultHandle) -> Option<&DataResult> {
self.data_results.get(handle).map(|node| &node.data_result)
}
#[inline]
pub fn lookup_node(&self, handle: DataResultHandle) -> Option<&DataResultNode> {
self.data_results.get(handle)
}
#[inline]
pub fn lookup_node_by_path(&self, path: EntityPathHash) -> Option<&DataResultNode> {
self.lookup_node(*self.data_results_by_path.get(&path)?)
}
#[inline]
pub fn lookup_result_by_path(&self, path: EntityPathHash) -> Option<&DataResult> {
self.lookup_result(*self.data_results_by_path.get(&path)?)
}
#[inline]
pub fn lookup_result_by_visualizer_instruction(
&self,
visualizer_instruction: VisualizerInstructionId,
) -> Option<&DataResult> {
self.lookup_result(
*self
.data_results_by_visualizer_instruction
.get(&visualizer_instruction)?,
)
}
#[inline]
pub fn is_empty(&self) -> bool {
self.data_results_by_path.is_empty()
}
fn visit_recursive<'a>(
&'a self,
handle: DataResultHandle,
visitor: &mut impl FnMut(&'a DataResultNode) -> bool,
) {
if let Some(result) = self.data_results.get(handle)
&& visitor(result)
{
for child in &result.children {
self.visit_recursive(*child, visitor);
}
}
}
#[inline]
pub fn iter_data_results(&self) -> impl Iterator<Item = &DataResult> {
self.data_results.values().map(|node| &node.data_result)
}
}
static EMPTY_QUERY: LazyLock<DataQueryResult> = LazyLock::new(Default::default);
impl ViewerContext<'_> {
pub fn lookup_query_result(&self, id: ViewId) -> &DataQueryResult {
self.query_results.get(&id).unwrap_or_else(|| {
re_log::debug_warn!("Tried looking up a query that doesn't exist: {id:?}");
&EMPTY_QUERY
})
}
}