use re_chunk_store::LatestAtQuery;
use re_entity_db::{EntityDb, external::re_query::LatestAtResults};
use re_log_types::EntityPath;
use re_types::{
Archetype, ArchetypeName, ComponentBatch, ComponentDescriptor, DeserializationError,
};
use re_viewer_context::{
BlueprintContext as _, ComponentFallbackError, ComponentFallbackProvider, QueryContext,
ViewContext, ViewId, ViewSystemExecutionError, ViewerContext,
external::re_entity_db::EntityTree,
};
#[derive(thiserror::Error, Debug)]
pub enum ViewPropertyQueryError {
#[error(transparent)]
DeserializationError(#[from] re_types::DeserializationError),
#[error(transparent)]
ComponentFallbackError(#[from] ComponentFallbackError),
}
impl From<ViewPropertyQueryError> for ViewSystemExecutionError {
fn from(val: ViewPropertyQueryError) -> Self {
match val {
ViewPropertyQueryError::DeserializationError(err) => err.into(),
ViewPropertyQueryError::ComponentFallbackError(err) => err.into(),
}
}
}
#[derive(Debug)]
pub struct ViewProperty {
pub blueprint_store_path: EntityPath,
pub archetype_name: ArchetypeName,
pub component_descrs: Vec<ComponentDescriptor>,
pub query_results: LatestAtResults,
pub blueprint_query: LatestAtQuery,
}
impl ViewProperty {
pub fn from_archetype<A: Archetype>(
blueprint_db: &EntityDb,
blueprint_query: &LatestAtQuery,
view_id: ViewId,
) -> Self {
Self::from_archetype_impl(
blueprint_db,
blueprint_query.clone(),
view_id,
A::name(),
A::all_components().iter().cloned().collect(),
)
}
fn from_archetype_impl(
blueprint_db: &EntityDb,
blueprint_query: LatestAtQuery,
view_id: ViewId,
archetype_name: ArchetypeName,
component_descrs: Vec<ComponentDescriptor>,
) -> Self {
let blueprint_store_path =
entity_path_for_view_property(view_id, blueprint_db.tree(), archetype_name);
let query_results = blueprint_db.latest_at(
&blueprint_query,
&blueprint_store_path,
component_descrs.iter(),
);
Self {
blueprint_store_path,
archetype_name,
query_results,
component_descrs,
blueprint_query,
}
}
pub fn component_or_fallback<C: re_types::Component>(
&self,
ctx: &ViewContext<'_>,
fallback_provider: &dyn ComponentFallbackProvider,
component_descr: &ComponentDescriptor,
) -> Result<C, ViewPropertyQueryError> {
self.component_array_or_fallback::<C>(ctx, fallback_provider, component_descr)?
.into_iter()
.next()
.ok_or(ComponentFallbackError::UnexpectedEmptyFallback.into())
}
pub fn component_array_or_fallback<C: re_types::Component>(
&self,
ctx: &ViewContext<'_>,
fallback_provider: &dyn ComponentFallbackProvider,
component_descr: &ComponentDescriptor,
) -> Result<Vec<C>, ViewPropertyQueryError> {
C::from_arrow(
self.component_or_fallback_raw(ctx, component_descr, fallback_provider)
.as_ref(),
)
.map_err(|err| err.into())
}
#[inline]
pub fn component_or_empty<C: re_types::Component>(
&self,
component_descr: &ComponentDescriptor,
) -> Result<Option<C>, DeserializationError> {
self.component_array(component_descr)
.map(|v| v?.into_iter().next())
}
pub fn component_array<C: re_types::Component>(
&self,
component_descr: &ComponentDescriptor,
) -> Result<Option<Vec<C>>, DeserializationError> {
self.component_raw(component_descr)
.map(|raw| C::from_arrow(raw.as_ref()))
.transpose()
}
pub fn component_array_or_empty<C: re_types::Component>(
&self,
component_descr: &ComponentDescriptor,
) -> Result<Vec<C>, DeserializationError> {
self.component_array(component_descr)
.map(|value| value.unwrap_or_default())
}
pub fn component_row_id(
&self,
component_descr: &ComponentDescriptor,
) -> Option<re_chunk::RowId> {
self.query_results.get(component_descr)?.row_id()
}
pub fn component_raw(
&self,
component_descr: &ComponentDescriptor,
) -> Option<arrow::array::ArrayRef> {
self.query_results
.get(component_descr)?
.component_batch_raw(component_descr)
}
fn component_or_fallback_raw(
&self,
ctx: &ViewContext<'_>,
component_descr: &ComponentDescriptor,
fallback_provider: &dyn ComponentFallbackProvider,
) -> arrow::array::ArrayRef {
if let Some(value) = self.component_raw(component_descr)
&& !value.is_empty()
{
return value;
}
fallback_provider.fallback_for(&self.query_context(ctx), component_descr)
}
pub fn save_blueprint_component(
&self,
ctx: &ViewerContext<'_>,
component_descr: &ComponentDescriptor,
component_batch: &dyn ComponentBatch,
) {
if !self.component_descrs.contains(component_descr) {
if cfg!(debug_assertions) {
panic!(
"trying to save a blueprint component `{component_descr}` that is not part of the view property for archetype `{}`",
self.archetype_name
);
} else {
re_log::warn_once!(
"trying to save a blueprint component `{component_descr}` that is not part of the view property for archetype `{}`",
self.archetype_name
);
}
}
ctx.save_blueprint_component(
self.blueprint_store_path.clone(),
component_descr,
component_batch,
);
}
pub fn clear_blueprint_component(
&self,
ctx: &ViewerContext<'_>,
component_descr: ComponentDescriptor,
) {
ctx.clear_blueprint_component(self.blueprint_store_path.clone(), component_descr);
}
pub fn reset_blueprint_component(
&self,
ctx: &ViewerContext<'_>,
component_descr: ComponentDescriptor,
) {
ctx.reset_blueprint_component(self.blueprint_store_path.clone(), component_descr);
}
pub fn reset_all_components(&self, ctx: &ViewerContext<'_>) {
for component_descr in self.component_descrs.iter().cloned() {
ctx.reset_blueprint_component(self.blueprint_store_path.clone(), component_descr);
}
}
pub fn reset_all_components_to_empty(&self, ctx: &ViewerContext<'_>) {
for component_descr in self.query_results.components.keys().cloned() {
ctx.clear_blueprint_component(self.blueprint_store_path.clone(), component_descr);
}
}
pub fn any_non_empty(&self) -> bool {
self.query_results
.components
.keys()
.any(|descr| self.component_raw(descr).is_some_and(|raw| !raw.is_empty()))
}
pub fn query_context<'a>(&'a self, view_ctx: &'a ViewContext<'_>) -> QueryContext<'a> {
QueryContext {
view_ctx,
target_entity_path: &self.blueprint_store_path,
archetype_name: Some(self.archetype_name),
query: &self.blueprint_query,
}
}
}
pub fn entity_path_for_view_property(
view_id: ViewId,
_blueprint_entity_tree: &EntityTree,
archetype_name: ArchetypeName,
) -> EntityPath {
let view_blueprint_path = view_id.as_entity_path();
view_blueprint_path.join(&EntityPath::from_single_string(archetype_name.short_name()))
}