Skip to content

Commit

Permalink
Improve UI for keypoint and class-ids of annotations contexts (#2071)
Browse files Browse the repository at this point in the history
* Refactor annotation context ui, and fix a bug in its display

* Break out annotation query

* Add nice ui for class_id component

* Small cleanup

* Add nice ui for KeypointId

* Clean it up
  • Loading branch information
emilk authored May 9, 2023
1 parent fb02dfa commit 1e0d226
Show file tree
Hide file tree
Showing 5 changed files with 249 additions and 109 deletions.
264 changes: 182 additions & 82 deletions crates/re_data_ui/src/annotation_context.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,90 @@
use egui::{color_picker, Vec2};
use itertools::Itertools;

use re_log_types::{context::AnnotationInfo, AnnotationContext};
use re_viewer_context::{auto_color, UiVerbosity, ViewerContext};

use super::DataUi;

const TABLE_SCROLL_AREA_HEIGHT: f32 = 500.0; // add scroll-bars when we get to this height

impl crate::EntityDataUi for re_log_types::component_types::ClassId {
fn entity_data_ui(
&self,
ctx: &mut re_viewer_context::ViewerContext<'_>,
ui: &mut egui::Ui,
verbosity: re_viewer_context::UiVerbosity,
entity_path: &re_log_types::EntityPath,
query: &re_arrow_store::LatestAtQuery,
) {
let annotations = crate::annotations(ctx, query, entity_path);
let class = annotations.class_description(Some(*self)).class_description;
if let Some(class) = class {
let response = ui.horizontal(|ui| {
// Color first, to keep subsequent rows of the same things aligned
small_color_ui(ui, &class.info);
ui.label(format!("{}", self.0));
if let Some(label) = &class.info.label {
ui.label(label.as_str());
}
});

match verbosity {
UiVerbosity::Small => {
if !class.keypoint_connections.is_empty() || !class.keypoint_map.is_empty() {
response.response.on_hover_ui(|ui| {
class_description_ui(ui, class, *self);
});
}
}
UiVerbosity::Reduced | UiVerbosity::All => {
class_description_ui(ui, class, *self);
}
}
} else {
ui.label(format!("{}", self.0));
}
}
}

impl crate::EntityDataUi for re_log_types::component_types::KeypointId {
fn entity_data_ui(
&self,
ctx: &mut re_viewer_context::ViewerContext<'_>,
ui: &mut egui::Ui,
_verbosity: re_viewer_context::UiVerbosity,
entity_path: &re_log_types::EntityPath,
query: &re_arrow_store::LatestAtQuery,
) {
if let Some(info) = annotation_info(ctx, entity_path, query, self) {
ui.horizontal(|ui| {
// Color first, to keep subsequent rows of the same things aligned
small_color_ui(ui, &info);
ui.label(format!("{}", self.0));
if let Some(label) = &info.label {
ui.label(label.as_str());
}
});
} else {
ui.label(format!("{}", self.0));
}
}
}

fn annotation_info(
ctx: &mut re_viewer_context::ViewerContext<'_>,
entity_path: &re_log_types::EntityPath,
query: &re_arrow_store::LatestAtQuery,
keypoint_id: &re_log_types::component_types::KeypointId,
) -> Option<re_log_types::context::AnnotationInfo> {
let class_id = re_data_store::query_latest_single(&ctx.log_db.entity_db, entity_path, query)?;
let annotations = crate::annotations(ctx, query, entity_path);
let class = annotations
.class_description(Some(class_id))
.class_description?;
class.keypoint_map.get(keypoint_id).cloned()
}

impl DataUi for AnnotationContext {
fn data_ui(
&self,
Expand All @@ -23,7 +101,6 @@ impl DataUi for AnnotationContext {
));
}
UiVerbosity::All | UiVerbosity::Reduced => {
let row_height = re_ui::ReUi::table_line_height();
ui.vertical(|ui| {
annotation_info_table_ui(
ui,
Expand All @@ -34,82 +111,88 @@ impl DataUi for AnnotationContext {
);

for (id, class) in &self.class_map {
if class.keypoint_connections.is_empty() && class.keypoint_map.is_empty() {
continue;
}

ui.separator();
ui.strong(format!("Keypoints for Class {}", id.0));

if !class.keypoint_connections.is_empty() {
ui.add_space(8.0);
ui.strong("Keypoints Annotations");
ui.push_id(format!("keypoint_annotations_{}", id.0), |ui| {
annotation_info_table_ui(
ui,
class
.keypoint_map
.values()
.sorted_by_key(|annotation| annotation.id),
);
});
}

if !class.keypoint_connections.is_empty() {
ui.add_space(8.0);
ui.strong("Keypoint Connections");
ui.push_id(format!("keypoints_connections_{}", id.0), |ui| {
use egui_extras::{Column, TableBuilder};

let table = TableBuilder::new(ui)
.min_scrolled_height(TABLE_SCROLL_AREA_HEIGHT)
.max_scroll_height(TABLE_SCROLL_AREA_HEIGHT)
.cell_layout(egui::Layout::left_to_right(egui::Align::Center))
.column(Column::auto().clip(true).at_least(40.0))
.column(Column::auto().clip(true).at_least(40.0));
table
.header(re_ui::ReUi::table_header_height(), |mut header| {
re_ui::ReUi::setup_table_header(&mut header);
header.col(|ui| {
ui.strong("From");
});
header.col(|ui| {
ui.strong("To");
});
})
.body(|mut body| {
re_ui::ReUi::setup_table_body(&mut body);

for (from, to) in &class.keypoint_connections {
body.row(row_height, |mut row| {
for id in [from, to] {
row.col(|ui| {
ui.label(
class
.keypoint_map
.get(id)
.and_then(|info| {
info.label.as_ref()
})
.map_or_else(
|| format!("id {id:?}"),
|label| label.0.clone(),
),
);
});
}
});
}
});
});
}
class_description_ui(ui, class, *id);
}
});
}
}
}
}

fn class_description_ui(
ui: &mut egui::Ui,
class: &re_log_types::context::ClassDescription,
id: re_log_types::component_types::ClassId,
) {
if class.keypoint_connections.is_empty() && class.keypoint_map.is_empty() {
return;
}

let row_height = re_ui::ReUi::table_line_height();
ui.separator();
ui.strong(format!("Keypoints for Class {}", id.0));
if !class.keypoint_map.is_empty() {
ui.add_space(8.0);
ui.strong("Keypoints Annotations");
ui.push_id(format!("keypoint_annotations_{}", id.0), |ui| {
annotation_info_table_ui(
ui,
class
.keypoint_map
.values()
.sorted_by_key(|annotation| annotation.id),
);
});
}

if !class.keypoint_connections.is_empty() {
ui.add_space(8.0);
ui.strong("Keypoint Connections");
ui.push_id(format!("keypoints_connections_{}", id.0), |ui| {
use egui_extras::{Column, TableBuilder};

let table = TableBuilder::new(ui)
.min_scrolled_height(TABLE_SCROLL_AREA_HEIGHT)
.max_scroll_height(TABLE_SCROLL_AREA_HEIGHT)
.cell_layout(egui::Layout::left_to_right(egui::Align::Center))
.column(Column::auto().clip(true).at_least(40.0))
.column(Column::auto().clip(true).at_least(40.0));
table
.header(re_ui::ReUi::table_header_height(), |mut header| {
re_ui::ReUi::setup_table_header(&mut header);
header.col(|ui| {
ui.strong("From");
});
header.col(|ui| {
ui.strong("To");
});
})
.body(|mut body| {
re_ui::ReUi::setup_table_body(&mut body);

for (from, to) in &class.keypoint_connections {
body.row(row_height, |mut row| {
for id in [from, to] {
row.col(|ui| {
ui.label(
class
.keypoint_map
.get(id)
.and_then(|info| info.label.as_ref())
.map_or_else(
|| format!("id {id:?}"),
|label| label.0.clone(),
),
);
});
}
});
}
});
});
}
}

fn annotation_info_table_ui<'a>(
ui: &mut egui::Ui,
annotation_infos: impl Iterator<Item = &'a AnnotationInfo>,
Expand Down Expand Up @@ -158,20 +241,37 @@ fn annotation_info_table_ui<'a>(
ui.label(label);
});
row.col(|ui| {
ui.horizontal(|ui| {
ui.spacing_mut().item_spacing.x = 8.0;
let color = info
.color
.map_or_else(|| auto_color(info.id), |color| color.into());
color_picker::show_color(ui, color, Vec2::new(64.0, row_height));
if info.color.is_none() {
ui.weak("(auto)").on_hover_text(
"Color chosen automatically, since it was not logged.",
);
}
});
color_ui(ui, info, Vec2::new(64.0, row_height));
});
});
}
});
}

fn color_ui(ui: &mut egui::Ui, info: &AnnotationInfo, size: Vec2) {
ui.horizontal(|ui| {
ui.spacing_mut().item_spacing.x = 8.0;
let color = info
.color
.map_or_else(|| auto_color(info.id), |color| color.into());
color_picker::show_color(ui, color, size);
if info.color.is_none() {
ui.weak("(auto)")
.on_hover_text("Color chosen automatically, since it was not logged.");
}
});
}

fn small_color_ui(ui: &mut egui::Ui, info: &AnnotationInfo) {
let size = egui::Vec2::splat(re_ui::ReUi::table_line_height());

let color = info
.color
.map_or_else(|| auto_color(info.id), |color| color.into());

let response = color_picker::show_color(ui, color, size);

if info.color.is_none() {
response.on_hover_text("Color chosen automatically, since it was not logged.");
}
}
4 changes: 2 additions & 2 deletions crates/re_data_ui/src/component_ui_registry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,10 @@ pub fn create_component_ui_registry() -> ComponentUiRegistry {
add::<re_log_types::component_types::AnnotationContext>(&mut registry);
// add::<re_log_types::component_types::Arrow3D>(&mut registry);
// add::<re_log_types::component_types::Box3D>(&mut registry);
// add::<re_log_types::component_types::ClassId>(&mut registry);
add::<re_log_types::component_types::ClassId>(&mut registry);
add::<re_log_types::component_types::ColorRGBA>(&mut registry);
// add::<re_log_types::component_types::InstanceKey>(&mut registry);
// add::<re_log_types::component_types::KeypointId>(&mut registry);
add::<re_log_types::component_types::KeypointId>(&mut registry);
// add::<re_log_types::component_types::Label>(&mut registry);
add::<re_log_types::component_types::LineStrip2D>(&mut registry);
add::<re_log_types::component_types::LineStrip3D>(&mut registry);
Expand Down
Loading

0 comments on commit 1e0d226

Please sign in to comment.