Skip to content

Commit

Permalink
wip: introduce generic_placeholder_for_datatype and make placeholde…
Browse files Browse the repository at this point in the history
…r _always_ be present
  • Loading branch information
Wumpf committed Sep 18, 2024
1 parent ee50fab commit f90a880
Show file tree
Hide file tree
Showing 11 changed files with 246 additions and 163 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ fn generate_component_reflection(
ComponentReflection {
docstring_md: #docstring_md,

placeholder: Some(#type_name::default().to_arrow()?),
custom_placeholder: Some(#type_name::default().to_arrow()?),
}
};
quoted_pairs.push(quote! { (#quoted_name, #quoted_reflection) });
Expand Down
87 changes: 83 additions & 4 deletions crates/store/re_types_core/src/reflection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,82 @@ impl Reflection {
}
}

/// Computes a placeholder for a given arrow datatype.
///
/// See also [`ComponentReflection::custom_placeholder`].
pub fn generic_placeholder_for_datatype(
datatype: &arrow2::datatypes::DataType,
) -> Box<dyn arrow2::array::Array> {
use arrow2::{
array,
datatypes::{DataType, IntervalUnit},
types,
};

match datatype {
DataType::Null => Box::new(array::NullArray::new(datatype.clone(), 1)),
DataType::Boolean => Box::new(array::BooleanArray::from_slice([false])),
DataType::Int8 => Box::new(array::Int8Array::from_slice([0])),
DataType::Int16 => Box::new(array::Int16Array::from_slice([0])),

DataType::Int32
| DataType::Date32
| DataType::Time32(_)
| DataType::Interval(IntervalUnit::YearMonth) => {
// TODO(andreas): Do we have to further distinguish these types? They do share the physical type.
Box::new(array::Int32Array::from_slice([0]))
}
DataType::Int64
| DataType::Date64
| DataType::Timestamp(_, _)
| DataType::Time64(_)
| DataType::Duration(_) => {
// TODO(andreas): Do we have to further distinguish these types? They do share the physical type.
Box::new(array::Int64Array::from_slice([0]))
}

DataType::UInt8 => Box::new(array::UInt8Array::from_slice([0])),
DataType::UInt16 => Box::new(array::UInt16Array::from_slice([0])),
DataType::UInt32 => Box::new(array::UInt32Array::from_slice([0])),
DataType::UInt64 => Box::new(array::UInt64Array::from_slice([0])),
DataType::Float16 => Box::new(array::Float16Array::from_slice([types::f16::from_f32(0.0)])),
DataType::Float32 => Box::new(array::Float32Array::from_slice([0.0])),
DataType::Float64 => Box::new(array::Float64Array::from_slice([0.0])),

DataType::Interval(IntervalUnit::DayTime) => {
Box::new(array::DaysMsArray::from_slice([types::days_ms::new(0, 0)]))
}
DataType::Interval(IntervalUnit::MonthDayNano) => {
Box::new(array::MonthsDaysNsArray::from_slice([
types::months_days_ns::new(0, 0, 0),
]))
}

DataType::Binary => Box::new(array::BinaryArray::<i32>::from_slice([[]])),
DataType::FixedSizeBinary(size) => Box::new(array::FixedSizeBinaryArray::from_iter(
std::iter::once(Some(vec![0; *size])),
*size,
)),
DataType::LargeBinary => Box::new(array::BinaryArray::<i64>::from_slice([[]])),
DataType::Utf8 => Box::new(array::Utf8Array::<i32>::from_slice([""])),
DataType::LargeUtf8 => Box::new(array::Utf8Array::<i64>::from_slice([""])),
DataType::List(arc) => todo!(),
DataType::FixedSizeList(arc, _) => todo!(),
DataType::LargeList(arc) => todo!(),
DataType::Struct(arc) => todo!(),
DataType::Union(arc, arc1, union_mode) => todo!(),
DataType::Map(arc, _) => todo!(),
DataType::Dictionary(integer_type, arc, _) => todo!(),
DataType::Decimal(_, _) => Box::new(array::Int128Array::from_slice([0])),
DataType::Decimal256(_, _) => {
Box::new(array::Int256Array::from_slice([types::i256::from_words(
0, 0,
)]))
}
DataType::Extension(_, datatype, _) => generic_placeholder_for_datatype(datatype),
}
}

/// Runtime reflection about components.
pub type ComponentReflectionMap = nohash_hasher::IntMap<ComponentName, ComponentReflection>;

Expand All @@ -60,12 +136,15 @@ pub struct ComponentReflection {
/// Markdown docstring for the component.
pub docstring_md: &'static str,

/// Placeholder value, used whenever no fallback was provided explicitly.
/// Custom placeholder value, used when not fallback was provided.
///
/// This is usually the default value of the component, serialized.
/// This is usually the default value of the component (if any), serialized.
///
/// This is useful as a base fallback value when displaying UI.
pub placeholder: Option<Box<dyn arrow2::array::Array>>,
/// Placeholders are useful as a base fallback value when displaying UI,
/// especially when it's necessary to have a starting value for edit ui.
/// Typically, this is only used when `FallbackProvider`s are not available.
/// If there's no custom placeholder, a placeholder can be derived from the arrow datatype.
pub custom_placeholder: Option<Box<dyn arrow2::array::Array>>,
}

/// Runtime reflection about archetypes.
Expand Down
14 changes: 4 additions & 10 deletions crates/viewer/re_selection_panel/src/defaults_ui.rs
Original file line number Diff line number Diff line change
Expand Up @@ -290,18 +290,12 @@ fn add_popup_ui(
// - Finally, fall back on the default value from the component registry.

// TODO(jleibs): Is this the right place for fallbacks to come from?
let Some(initial_data) = ctx
.visualizer_collection
.get_by_identifier(viz)
.ok()
.and_then(|sys| sys.fallback_for(&query_context, component_name).ok())
else {
re_log::warn!(
"Could not identify an initial value for: {}",
component_name
);

let Some(visualizer) = ctx.visualizer_collection.get_by_identifier(viz).ok() else {
re_log::warn_once!("Could not find a visualizer name {viz}");
return;
};
let initial_data = visualizer.fallback_for(&query_context, component_name);

match Chunk::builder(defaults_path.clone())
.with_row(
Expand Down
8 changes: 1 addition & 7 deletions crates/viewer/re_selection_panel/src/visualizer_ui.rs
Original file line number Diff line number Diff line change
Expand Up @@ -205,13 +205,7 @@ fn visualizer_components(
let result_default = query_result.defaults.get(&component_name);
let raw_default = non_empty_component_batch_raw(result_default, &component_name);

let raw_fallback = match visualizer.fallback_for(&query_ctx, component_name) {
Ok(fallback) => fallback,
Err(err) => {
re_log::warn_once!("Failed to get fallback for component {component_name}: {err}");
continue; // TODO(andreas): Don't give up on the entire component because of this. Show an error instead.
}
};
let raw_fallback = visualizer.fallback_for(&query_ctx, component_name);

// Determine where the final value comes from.
// Putting this into an enum makes it easier to reason about the next steps.
Expand Down
22 changes: 13 additions & 9 deletions crates/viewer/re_space_view/src/query.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
use nohash_hasher::IntSet;

use re_chunk_store::{LatestAtQuery, RangeQuery, RowId};
use re_chunk_store::{external::re_chunk::ArrowArray, LatestAtQuery, RangeQuery, RowId};
use re_log_types::TimeInt;
use re_query::LatestAtResults;
use re_types_core::ComponentName;
use re_viewer_context::{DataResult, ViewContext, ViewerContext};
use re_viewer_context::{DataResult, QueryContext, ViewContext, ViewerContext};

use crate::results_ext::{HybridLatestAtResults, HybridRangeResults};

Expand Down Expand Up @@ -196,9 +196,10 @@ pub trait DataResultQuery {

fn best_fallback_for<'a>(
&self,
ctx: &'a ViewContext<'a>,
query_ctx: &'a QueryContext<'a>,
visualizer_collection: &'a re_viewer_context::VisualizerCollection,
component: re_types_core::ComponentName,
) -> Option<&'a dyn re_viewer_context::ComponentFallbackProvider>;
) -> Box<dyn ArrowArray>;
}

impl DataResultQuery for DataResult {
Expand All @@ -220,20 +221,23 @@ impl DataResultQuery for DataResult {

fn best_fallback_for<'a>(
&self,
ctx: &'a ViewContext<'a>,
query_ctx: &'a QueryContext<'a>,
visualizer_collection: &'a re_viewer_context::VisualizerCollection,
component: re_types_core::ComponentName,
) -> Option<&'a dyn re_viewer_context::ComponentFallbackProvider> {
) -> Box<dyn ArrowArray> {
// TODO(jleibs): This should be cached somewhere
for vis in &self.visualizers {
let Ok(vis) = ctx.visualizer_collection.get_by_identifier(*vis) else {
let Ok(vis) = visualizer_collection.get_by_identifier(*vis) else {
continue;
};

if vis.visualizer_query_info().queried.contains(&component) {
return Some(vis.as_fallback_provider());
return vis
.as_fallback_provider()
.fallback_for(query_ctx, component);
}
}

None
query_ctx.viewer_ctx.placeholder_for(component)
}
}
19 changes: 9 additions & 10 deletions crates/viewer/re_space_view/src/results_ext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,7 @@ impl<'a> HybridLatestAtResults<'a> {
.or_else(|| self.defaults.get(&component_name))
}

pub fn try_fallback_raw(&self, component_name: ComponentName) -> Option<Box<dyn ArrowArray>> {
let fallback_provider = self
.data_result
.best_fallback_for(self.ctx, component_name)?;

pub fn fallback_raw(&self, component_name: ComponentName) -> Box<dyn ArrowArray> {
let query_context = QueryContext {
viewer_ctx: self.ctx.viewer_ctx,
target_entity_path: &self.data_result.entity_path,
Expand All @@ -64,9 +60,11 @@ impl<'a> HybridLatestAtResults<'a> {
view_ctx: Some(self.ctx),
};

fallback_provider
.fallback_for(&query_context, component_name)
.ok()
self.data_result.best_fallback_for(
&query_context,
&self.ctx.visualizer_collection,
component_name,
)
}

/// Utility for retrieving the first instance of a component, ignoring defaults.
Expand Down Expand Up @@ -119,8 +117,9 @@ impl<'a> HybridLatestAtResults<'a> {
self.get_instance(index)
.or_else(|| {
// No override, no store, no default -> try fallback instead
self.try_fallback_raw(C::name())
.and_then(|raw| C::from_arrow(raw.as_ref()).ok())
let raw_fallback = self.fallback_raw(C::name());
C::from_arrow(raw_fallback.as_ref())
.ok()
.and_then(|r| r.first().cloned())
})
.unwrap_or_default()
Expand Down
Loading

0 comments on commit f90a880

Please sign in to comment.