Skip to content

Commit

Permalink
Separate 2d & 3d spaceview classes, removal of ViewCategory, `Space…
Browse files Browse the repository at this point in the history
…ViewClass` driven spawn heuristics (#2716)

### What

Fixes  #2649
*  #2649

Separates the 2D & 3D space views into separate classes, leading to
removal of the "navigation mode".
Naturally, this means that heuristics that previously defined the
navigation mode move to space view creation.
Space view creation in turn was previously defined via the outdated
`ViewCategory` concept (which assigned each space view class an enum
value). Since we wanted to remove it for a while, I went through with
that, causing heuristics to spread out further into the Space View Class
framework itself.

Default space view spawning & populating is now goverend by:
* remaining hardcoded heuristics in `space_view_heuristics.rs` (to be
phased out!)
* `ViewPartSystem::queries_any_components_of` determines which entities
a `ViewPartSystem` queries. The default implementation works for
everything except `Tensor` (if this case disappears in the future we may
consider removing this method again)
* `SpaceViewClass::auto_spawn_heuristic` drives whether a Space View
Class is spawned for a given root and set of entities. This is fairly
basic at the moment and should eventually take over almost everything in
`space_view_heuristics`. Interestingly, this already allows to express
relatively complex rules like "if there is any log text, default spawn a
single text view at the root and don't affect spawning of other space
views at all"

Noticed that some other space view class icons were wrong and fixed that
up as well.
<img width="164" alt="image"
src="https://github.com/rerun-io/rerun/assets/1220815/9317bbf8-4397-41d0-aa2e-489f24e448cb">


### 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 [demo.rerun.io](https://demo.rerun.io/pr/2716) (if
applicable)

- [PR Build Summary](https://build.rerun.io/pr/2716)
- [Docs
preview](https://rerun.io/preview/pr%3Aandreas%2Fseparate-2d-3d-spaceviews/docs)
- [Examples
preview](https://rerun.io/preview/pr%3Aandreas%2Fseparate-2d-3d-spaceviews/examples)
  • Loading branch information
Wumpf authored Jul 20, 2023
1 parent edaf2e1 commit 60f47f4
Show file tree
Hide file tree
Showing 54 changed files with 861 additions and 713 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

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

2 changes: 1 addition & 1 deletion crates/re_data_ui/src/item_ui.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use re_viewer_context::{

use super::DataUi;

// TODO(andreas): This is where we want to go, but we need to figure out how get the `SpaceViewCategory` from the `SpaceViewId`.
// TODO(andreas): This is where we want to go, but we need to figure out how get the [`re_viewer_context::SpaceViewClass`] from the `SpaceViewId`.
// Simply pass in optional icons?
//
// Show a button to an [`Item`] with a given text.
Expand Down
2 changes: 1 addition & 1 deletion crates/re_space_view_bar_chart/src/space_view_class.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ impl SpaceViewClass for BarChartSpaceView {
&re_ui::icons::SPACE_VIEW_HISTOGRAM
}

fn help_text(&self, re_ui: &re_ui::ReUi, _state: &Self::State) -> egui::WidgetText {
fn help_text(&self, re_ui: &re_ui::ReUi) -> egui::WidgetText {
let mut layout = re_ui::LayoutJobBuilder::new(re_ui);

layout.add("Pan by dragging, or scroll (+ ");
Expand Down
22 changes: 21 additions & 1 deletion crates/re_space_view_bar_chart/src/view_part_system.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::collections::BTreeMap;
use re_arrow_store::LatestAtQuery;
use re_components::Tensor;
use re_data_store::EntityPath;
use re_log_types::Component as _;
use re_log_types::{Component as _, ComponentName, TimeInt, Timeline};
use re_viewer_context::{
ArchetypeDefinition, SpaceViewSystemExecutionError, ViewContextCollection, ViewPartSystem,
ViewQuery, ViewerContext,
Expand All @@ -20,6 +20,26 @@ impl ViewPartSystem for BarChartViewPartSystem {
vec1::vec1![Tensor::name()]
}

fn queries_any_components_of(
&self,
store: &re_arrow_store::DataStore,
ent_path: &EntityPath,
components: &[ComponentName],
) -> bool {
if !components.contains(&Tensor::name()) {
return false;
}

if let Some(tensor) = store.query_latest_component::<Tensor>(
ent_path,
&LatestAtQuery::new(Timeline::log_time(), TimeInt::MAX),
) {
tensor.is_vector()
} else {
false
}
}

fn execute(
&mut self,
ctx: &mut ViewerContext<'_>,
Expand Down
3 changes: 0 additions & 3 deletions crates/re_space_view_spatial/src/contexts/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,13 @@ pub struct SpatialSceneEntityContext<'a> {
pub depth_offset: DepthOffset,
pub annotations: std::sync::Arc<Annotations>,
pub shared_render_builders: &'a SharedRenderBuilders,
pub counter: &'a PrimitiveCounter,

pub highlight: &'a re_viewer_context::SpaceViewOutlineMasks, // Not part of the context, but convenient to have here.
}

// TODO(andreas): num_3d_primitives is likely not needed in this form once 2D/3D separation is gone. num_primitives should be handled in a more general way?
#[derive(Default)]
pub struct PrimitiveCounter {
pub num_primitives: AtomicUsize,
pub num_3d_primitives: AtomicUsize,
}

impl ViewContextSystem for PrimitiveCounter {
Expand Down
47 changes: 47 additions & 0 deletions crates/re_space_view_spatial/src/heuristics.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
use nohash_hasher::IntSet;
use re_log_types::{EntityPath, Timeline};
use re_viewer_context::{AutoSpawnHeuristic, SpaceViewClassName, ViewerContext};

use crate::{parts::SpatialViewPartData, view_kind::SpatialSpaceViewKind};

pub fn auto_spawn_heuristic(
class: &SpaceViewClassName,
ctx: &ViewerContext<'_>,
ent_paths: &IntSet<EntityPath>,
view_kind: SpatialSpaceViewKind,
) -> AutoSpawnHeuristic {
re_tracing::profile_function!();

let store = ctx.store_db.store();
let timeline = Timeline::log_time();

let mut score = 0.0;

let parts = ctx
.space_view_class_registry
.get_system_registry_or_log_error(class)
.new_part_collection();
let parts_with_view_kind = parts
.iter()
.filter(|part| {
part.data()
.and_then(|d| d.downcast_ref::<SpatialViewPartData>())
.map_or(false, |data| data.preferred_view_kind == Some(view_kind))
})
.collect::<Vec<_>>();

for ent_path in ent_paths {
let Some(components) = store.all_components(&timeline, ent_path) else {
continue;
};

for part in &parts_with_view_kind {
if part.queries_any_components_of(store, ent_path, &components) {
score += 1.0;
break;
}
}
}

AutoSpawnHeuristic::SpawnClassWithHighestScoreForRoot(score)
}
17 changes: 15 additions & 2 deletions crates/re_space_view_spatial/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,28 @@
mod contexts;
mod eye;
mod heuristics;
mod instance_hash_conversions;
mod mesh_cache;
mod mesh_loader;
mod parts;
mod picking;
mod space_camera_3d;
mod space_view_class;
mod space_view_2d;
mod space_view_3d;
mod ui;
mod ui_2d;
mod ui_3d;

pub use space_view_class::SpatialSpaceView;
pub use space_view_2d::SpatialSpaceView2D;
pub use space_view_3d::SpatialSpaceView3D;

// ---

mod view_kind {
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SpatialSpaceViewKind {
TwoD,
ThreeD,
}
}
12 changes: 7 additions & 5 deletions crates/re_space_view_spatial/src/parts/arrows3d.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,17 @@ use super::{picking_id_from_instance_key, SpatialViewPartData};
use crate::{
contexts::{EntityDepthOffsets, SpatialSceneEntityContext},
parts::entity_iterator::process_entity_views,
view_kind::SpatialSpaceViewKind,
};

#[derive(Default)]
pub struct Arrows3DPart(SpatialViewPartData);

impl Default for Arrows3DPart {
fn default() -> Self {
Self(SpatialViewPartData::new(Some(SpatialSpaceViewKind::ThreeD)))
}
}

impl Arrows3DPart {
fn process_entity_view(
&mut self,
Expand Down Expand Up @@ -113,10 +119,6 @@ impl ViewPartSystem for Arrows3DPart {
view_ctx.get::<EntityDepthOffsets>()?.points,
self.archetype(),
|_ctx, ent_path, entity_view, ent_context| {
ent_context
.counter
.num_3d_primitives
.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
self.process_entity_view(query, &entity_view, ent_path, ent_context)
},
)?;
Expand Down
8 changes: 7 additions & 1 deletion crates/re_space_view_spatial/src/parts/boxes2d.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,19 @@ use re_viewer_context::{
use crate::{
contexts::{EntityDepthOffsets, SpatialSceneEntityContext},
parts::{entity_iterator::process_entity_views, UiLabel, UiLabelTarget},
view_kind::SpatialSpaceViewKind,
};

use super::{picking_id_from_instance_key, SpatialViewPartData};

#[derive(Default)]
pub struct Boxes2DPart(SpatialViewPartData);

impl Default for Boxes2DPart {
fn default() -> Self {
Self(SpatialViewPartData::new(Some(SpatialSpaceViewKind::TwoD)))
}
}

impl Boxes2DPart {
fn process_entity_view(
&mut self,
Expand Down
12 changes: 7 additions & 5 deletions crates/re_space_view_spatial/src/parts/boxes3d.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,19 @@ use re_viewer_context::{
use crate::{
contexts::SpatialSceneEntityContext,
parts::{entity_iterator::process_entity_views, UiLabel, UiLabelTarget},
view_kind::SpatialSpaceViewKind,
};

use super::{picking_id_from_instance_key, SpatialViewPartData};

#[derive(Default)]
pub struct Boxes3DPart(SpatialViewPartData);

impl Default for Boxes3DPart {
fn default() -> Self {
Self(SpatialViewPartData::new(Some(SpatialSpaceViewKind::ThreeD)))
}
}

impl Boxes3DPart {
fn process_entity_view(
&mut self,
Expand Down Expand Up @@ -124,10 +130,6 @@ impl ViewPartSystem for Boxes3DPart {
0,
self.archetype(),
|_ctx, ent_path, entity_view, ent_context| {
ent_context
.counter
.num_3d_primitives
.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
self.process_entity_view(query, &entity_view, ent_path, ent_context)
},
)?;
Expand Down
23 changes: 12 additions & 11 deletions crates/re_space_view_spatial/src/parts/cameras.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,28 +9,35 @@ use re_viewer_context::{

use super::SpatialViewPartData;
use crate::{
contexts::{
pinhole_camera_view_coordinates, PrimitiveCounter, SharedRenderBuilders, TransformContext,
},
contexts::{pinhole_camera_view_coordinates, SharedRenderBuilders, TransformContext},
instance_hash_conversions::picking_layer_id_from_instance_path_hash,
space_camera_3d::SpaceCamera3D,
};

const CAMERA_COLOR: re_renderer::Color32 = re_renderer::Color32::from_rgb(150, 150, 150);

#[derive(Default)]
pub struct CamerasPart {
pub data: SpatialViewPartData,
pub space_cameras: Vec<SpaceCamera3D>,
}

impl Default for CamerasPart {
fn default() -> Self {
Self {
// Cameras themselves aren't inherently 2D or 3D since they represent intrinsics.
// (extrinsics, represented by [`transform3d_arrow::Transform3DArrowsPart`] are 3D though)
data: (SpatialViewPartData::new(None)),
space_cameras: Vec::new(),
}
}
}

impl CamerasPart {
#[allow(clippy::too_many_arguments)]
fn visit_instance(
&mut self,
transforms: &TransformContext,
shared_render_builders: &SharedRenderBuilders,
primitive_counter: &PrimitiveCounter,
ent_path: &EntityPath,
props: &EntityProperties,
pinhole: Pinhole,
Expand Down Expand Up @@ -152,10 +159,6 @@ impl CamerasPart {
if let Some(outline_mask_ids) = entity_highlight.instances.get(&instance_key) {
lines.outline_mask_ids(*outline_mask_ids);
}

primitive_counter
.num_3d_primitives
.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
}
}

Expand All @@ -174,7 +177,6 @@ impl ViewPartSystem for CamerasPart {

let transforms = view_ctx.get::<TransformContext>()?;
let shared_render_builders = view_ctx.get::<SharedRenderBuilders>()?;
let primitive_counter = view_ctx.get::<PrimitiveCounter>()?;

let store = ctx.store_db.store();
for (ent_path, props) in query.iter_entities() {
Expand All @@ -191,7 +193,6 @@ impl ViewPartSystem for CamerasPart {
self.visit_instance(
transforms,
shared_render_builders,
primitive_counter,
ent_path,
&props,
pinhole,
Expand Down
1 change: 0 additions & 1 deletion crates/re_space_view_spatial/src/parts/entity_iterator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,6 @@ where
annotations: annotations.0.find(ent_path),
shared_render_builders,
highlight: query.highlights.entity_outline_mask(ent_path.hash()),
counter,
};

match query_primary_with_history::<Primary, N>(
Expand Down
31 changes: 29 additions & 2 deletions crates/re_space_view_spatial/src/parts/images.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,13 @@ use egui::NumExt;

use itertools::Itertools as _;
use nohash_hasher::IntSet;
use re_arrow_store::LatestAtQuery;
use re_components::{
ColorRGBA, Component as _, DecodedTensor, DrawOrder, InstanceKey, Pinhole, Tensor,
TensorDataMeaning,
};
use re_data_store::{EntityPath, EntityProperties};
use re_log_types::EntityPathHash;
use re_log_types::{ComponentName, EntityPathHash, TimeInt, Timeline};
use re_query::{EntityView, QueryError};
use re_renderer::{
renderer::{DepthCloud, DepthClouds, RectangleOptions, TexturedRect},
Expand All @@ -24,6 +25,7 @@ use re_viewer_context::{
use crate::{
contexts::{EntityDepthOffsets, SpatialSceneEntityContext, TransformContext},
parts::{entity_iterator::process_entity_views, SIZE_BOOST_IN_POINTS_FOR_POINT_OUTLINES},
view_kind::SpatialSpaceViewKind,
};

use super::SpatialViewPartData;
Expand Down Expand Up @@ -117,13 +119,22 @@ struct ImageGrouping {
draw_order: DrawOrder,
}

#[derive(Default)]
pub struct ImagesPart {
pub data: SpatialViewPartData,
pub images: Vec<Image>,
pub depth_cloud_entities: IntSet<EntityPathHash>,
}

impl Default for ImagesPart {
fn default() -> Self {
Self {
data: SpatialViewPartData::new(Some(SpatialSpaceViewKind::TwoD)),
images: Vec::new(),
depth_cloud_entities: IntSet::default(),
}
}
}

impl ImagesPart {
fn handle_image_layering(&mut self) {
re_tracing::profile_function!();
Expand Down Expand Up @@ -378,6 +389,22 @@ impl ViewPartSystem for ImagesPart {
]
}

fn queries_any_components_of(
&self,
store: &re_arrow_store::DataStore,
ent_path: &EntityPath,
_components: &[ComponentName],
) -> bool {
if let Some(tensor) = store.query_latest_component::<Tensor>(
ent_path,
&LatestAtQuery::new(Timeline::log_time(), TimeInt::MAX),
) {
tensor.is_shaped_like_an_image() && !tensor.is_vector()
} else {
false
}
}

fn execute(
&mut self,
ctx: &mut ViewerContext<'_>,
Expand Down
Loading

0 comments on commit 60f47f4

Please sign in to comment.