use std::{collections::BTreeMap, sync::Arc};
use ahash::HashMap;
use nohash_hasher::IntSet;
use re_chunk::RowId;
use re_chunk_store::LatestAtQuery;
use re_entity_db::EntityPath;
use re_types::components::AnnotationContext;
use re_types::datatypes::{AnnotationInfo, ClassDescription, ClassId, KeypointId, Utf8};
use super::{auto_color_egui, ViewerContext};
const MISSING_ROW_ID: RowId = RowId::ZERO;
#[derive(Clone, Debug)]
pub struct Annotations {
row_id: RowId,
class_map: HashMap<ClassId, CachedClassDescription>,
}
impl Annotations {
#[inline]
pub fn missing() -> Self {
Self {
row_id: MISSING_ROW_ID,
class_map: Default::default(),
}
}
pub fn missing_arc() -> Arc<Self> {
use std::sync::OnceLock;
static CELL: OnceLock<Arc<Annotations>> = OnceLock::new();
CELL.get_or_init(|| Arc::new(Self::missing())).clone()
}
#[inline]
pub fn resolved_class_description(
&self,
class_id: Option<re_types::components::ClassId>,
) -> ResolvedClassDescription<'_> {
let found = class_id.and_then(|class_id| self.class_map.get(&class_id.0));
ResolvedClassDescription {
class_id: class_id.map(|id| id.0),
class_description: found.map(|f| &f.class_description),
keypoint_map: found.map(|f| &f.keypoint_map),
}
}
#[inline]
pub fn row_id(&self) -> RowId {
self.row_id
}
}
#[derive(Clone, Debug)]
struct CachedClassDescription {
class_description: ClassDescription,
keypoint_map: HashMap<KeypointId, AnnotationInfo>,
}
impl From<ClassDescription> for CachedClassDescription {
fn from(desc: ClassDescription) -> Self {
let keypoint_map = desc
.keypoint_annotations
.iter()
.map(|kp| (kp.id.into(), kp.clone()))
.collect();
Self {
class_description: desc,
keypoint_map,
}
}
}
#[derive(Clone, Copy, Debug)]
pub struct ResolvedClassDescription<'a> {
pub class_id: Option<ClassId>,
pub class_description: Option<&'a ClassDescription>,
pub keypoint_map: Option<&'a HashMap<KeypointId, AnnotationInfo>>,
}
impl<'a> ResolvedClassDescription<'a> {
#[inline]
pub fn annotation_info(&self) -> ResolvedAnnotationInfo {
ResolvedAnnotationInfo {
class_id: self.class_id,
annotation_info: self.class_description.map(|desc| desc.info.clone()),
}
}
pub fn annotation_info_with_keypoint(
&self,
keypoint_id: re_types::datatypes::KeypointId,
) -> ResolvedAnnotationInfo {
if let (Some(desc), Some(keypoint_map)) = (self.class_description, self.keypoint_map) {
if let Some(keypoint_annotation_info) = keypoint_map.get(&keypoint_id) {
ResolvedAnnotationInfo {
class_id: self.class_id,
annotation_info: Some(AnnotationInfo {
id: keypoint_id.0,
label: keypoint_annotation_info
.label
.clone()
.or_else(|| desc.info.label.clone()),
color: keypoint_annotation_info.color.or(desc.info.color),
}),
}
} else {
self.annotation_info()
}
} else {
ResolvedAnnotationInfo {
class_id: self.class_id,
annotation_info: None,
}
}
}
}
#[derive(Clone, Default)]
pub struct ResolvedAnnotationInfo {
pub class_id: Option<ClassId>,
pub annotation_info: Option<AnnotationInfo>,
}
impl ResolvedAnnotationInfo {
pub fn color(&self) -> Option<egui::Color32> {
#![allow(clippy::manual_map)] if let Some(info) = &self.annotation_info {
if let Some(color) = info.color {
Some(color.into())
} else {
Some(auto_color_egui(info.id))
}
} else if let Some(class_id) = self.class_id {
Some(auto_color_egui(class_id.0))
} else {
None
}
}
#[inline]
pub fn label(&self, label: Option<&str>) -> Option<String> {
if let Some(label) = label {
Some(label.to_owned())
} else {
self.annotation_info
.as_ref()
.and_then(|info| info.label.as_ref().map(|label| label.to_string()))
}
}
#[inline]
pub fn label_utf8(&self, label: Option<Utf8>) -> Option<Utf8> {
if let Some(label) = label {
Some(label)
} else {
self.annotation_info
.as_ref()
.and_then(|info| info.label.clone())
}
}
}
pub enum ResolvedAnnotationInfos {
Same(usize, ResolvedAnnotationInfo),
Many(Vec<ResolvedAnnotationInfo>),
}
impl ResolvedAnnotationInfos {
pub fn iter(&self) -> impl Iterator<Item = &ResolvedAnnotationInfo> {
use itertools::Either;
match self {
Self::Same(n, info) => Either::Left(std::iter::repeat(info).take(*n)),
Self::Many(infos) => Either::Right(infos.iter()),
}
}
#[inline]
pub fn len(&self) -> usize {
match self {
Self::Same(n, _) => *n,
Self::Many(infos) => infos.len(),
}
}
#[inline]
#[must_use]
pub fn is_empty(&self) -> bool {
match self {
Self::Same(n, _) => *n == 0,
Self::Many(infos) => infos.is_empty(),
}
}
}
#[derive(Default, Clone, Debug)]
pub struct AnnotationMap(pub BTreeMap<EntityPath, Arc<Annotations>>);
impl AnnotationMap {
pub fn load<'a>(
&mut self,
ctx: &ViewerContext<'_>,
time_query: &LatestAtQuery,
entities: impl Iterator<Item = &'a EntityPath>,
) {
re_tracing::profile_function!();
let mut visited = IntSet::<EntityPath>::default();
for ent_path in entities {
let mut next_parent = Some(ent_path.clone());
while let Some(parent) = next_parent {
if !visited.insert(parent.clone()) {
break;
}
match self.0.entry(parent.clone()) {
std::collections::btree_map::Entry::Occupied(_) => break,
std::collections::btree_map::Entry::Vacant(entry) => {
if let Some(((_time, row_id), ann_ctx)) = ctx
.recording()
.latest_at_component::<AnnotationContext>(&parent, time_query)
{
let annotations = Annotations {
row_id,
class_map: ann_ctx
.0
.into_iter()
.map(|elem| {
(
elem.class_id,
CachedClassDescription::from(elem.class_description),
)
})
.collect(),
};
entry.insert(Arc::new(annotations));
}
}
}
next_parent = parent.parent();
}
}
}
pub fn find(&self, entity_path: &EntityPath) -> Arc<Annotations> {
let mut next_parent = Some(entity_path.clone());
while let Some(parent) = next_parent {
if let Some(legend) = self.0.get(&parent) {
return legend.clone();
}
next_parent = parent.parent();
}
Annotations::missing_arc()
}
}