use std::iter;
use itertools::{izip, Either};
use re_entity_db::InstancePathHash;
use re_log_types::{EntityPath, Instance};
use re_types::components::{ShowLabels, Text};
use re_types::{Component, Loggable as _};
use re_viewer_context::ResolvedAnnotationInfos;
#[cfg(doc)]
use re_viewer_context::ComponentFallbackProvider;
use crate::visualizers::entity_iterator::clamped_or;
#[derive(Clone)]
pub enum UiLabelTarget {
Rect(egui::Rect),
Point2D(egui::Pos2),
Position3D(glam::Vec3),
}
#[derive(Clone)]
pub enum UiLabelStyle {
Color(egui::Color32),
Error,
}
impl From<egui::Color32> for UiLabelStyle {
fn from(color: egui::Color32) -> Self {
Self::Color(color)
}
}
#[derive(Clone)]
pub struct UiLabel {
pub text: String,
pub style: UiLabelStyle,
pub target: UiLabelTarget,
pub labeled_instance: InstancePathHash,
}
pub struct LabeledBatch<'a, P: 'a, I: Iterator<Item = P> + 'a> {
pub entity_path: &'a EntityPath,
pub num_instances: usize,
pub overall_position: P,
pub instance_positions: I,
pub labels: &'a [re_types::ArrowString],
pub colors: &'a [egui::Color32],
pub show_labels: re_types::components::ShowLabels,
pub annotation_infos: &'a ResolvedAnnotationInfos,
}
const MAX_NUM_LABELS_PER_ENTITY: usize = 30;
pub fn show_labels_fallback<C: Component>(ctx: &re_viewer_context::QueryContext<'_>) -> ShowLabels {
let results =
ctx.recording()
.latest_at(ctx.query, ctx.target_entity_path, [C::name(), Text::name()]);
let num_instances = results
.component_batch_raw(&C::name())
.map_or(0, |array| array.len());
let num_labels = results
.component_batch_raw(&Text::name())
.map_or(0, |array| array.len());
ShowLabels::from(num_labels == 1 || num_instances < MAX_NUM_LABELS_PER_ENTITY)
}
pub fn process_labels_3d<'a>(
batch: LabeledBatch<'a, glam::Vec3, impl Iterator<Item = glam::Vec3> + 'a>,
world_from_obj: glam::Affine3A,
) -> impl Iterator<Item = UiLabel> + 'a {
process_labels(batch, move |position| {
UiLabelTarget::Position3D(world_from_obj.transform_point3(position))
})
}
pub fn process_labels_2d<'a>(
batch: LabeledBatch<'a, glam::Vec2, impl Iterator<Item = glam::Vec2> + 'a>,
world_from_obj: glam::Affine3A,
) -> impl Iterator<Item = UiLabel> + 'a {
process_labels(batch, move |position| {
let point = world_from_obj.transform_point3(position.extend(0.0));
UiLabelTarget::Point2D(egui::pos2(point.x, point.y))
})
}
pub fn process_labels<'a, P: 'a>(
batch: LabeledBatch<'a, P, impl Iterator<Item = P> + 'a>,
target_from_position: impl Fn(P) -> UiLabelTarget + 'a,
) -> impl Iterator<Item = UiLabel> + 'a {
let LabeledBatch {
entity_path,
num_instances,
overall_position,
instance_positions,
labels,
colors,
show_labels,
annotation_infos,
} = batch;
let show_labels = bool::from(show_labels.0);
if !show_labels {
return Either::Left(iter::empty());
}
let label_positions = if labels.len() == 1 && num_instances > 1 {
Either::Left(std::iter::once(overall_position))
} else {
Either::Right(instance_positions)
};
let labels = izip!(
annotation_infos.iter(),
labels.iter().map(Some).chain(std::iter::repeat(None))
)
.map(|(annotation_info, label)| annotation_info.label(label.map(|l| l.as_str())));
let colors = clamped_or(colors, &egui::Color32::WHITE);
Either::Right(
itertools::izip!(label_positions, labels, colors)
.enumerate()
.filter_map(move |(i, (position, label, color))| {
label.map(|label| UiLabel {
text: label,
style: (*color).into(),
target: target_from_position(position),
labeled_instance: InstancePathHash::instance(
entity_path,
Instance::from(i as u64),
),
})
}),
)
}