use std::iter;
use ordered_float::NotNan;
use re_chunk_store::external::re_chunk::ChunkComponentIterItem;
use re_sdk_types::archetypes::Capsules3D;
use re_sdk_types::components::{ClassId, Color, FillMode, HalfSize3D, Length, Radius, ShowLabels};
use re_sdk_types::reflection::Enum as _;
use re_sdk_types::{Archetype as _, ArrowString, components};
use re_view::clamped_or_else;
use re_viewer_context::{
IdentifiedViewSystem, QueryContext, ViewContext, ViewContextCollection, ViewQuery,
ViewSystemExecutionError, VisualizerExecutionOutput, VisualizerQueryInfo, VisualizerSystem,
typed_fallback_for,
};
use super::SpatialViewVisualizerData;
use super::utilities::{ProcMeshBatch, ProcMeshDrawableBuilder};
use crate::contexts::SpatialSceneVisualizerInstructionContext;
use crate::proc_mesh;
use crate::view_kind::SpatialViewKind;
pub struct Capsules3DVisualizer(SpatialViewVisualizerData);
impl Default for Capsules3DVisualizer {
fn default() -> Self {
Self(SpatialViewVisualizerData::new(Some(
SpatialViewKind::ThreeD,
)))
}
}
impl Capsules3DVisualizer {
fn process_data<'a>(
builder: &mut ProcMeshDrawableBuilder<'_>,
query_context: &QueryContext<'_>,
ent_context: &SpatialSceneVisualizerInstructionContext<'_>,
batches: impl Iterator<Item = Capsules3DComponentData<'a>>,
) -> Result<(), ViewSystemExecutionError> {
for batch in batches {
let num_instances = batch.radii.len().max(batch.lengths.len());
let lengths_iter = clamped_or_else(batch.lengths, || {
typed_fallback_for(query_context, Capsules3D::descriptor_lengths().component)
})
.take(num_instances);
let radii: Vec<Radius> = clamped_or_else(batch.radii, || {
typed_fallback_for(query_context, Capsules3D::descriptor_radii().component)
})
.take(num_instances)
.collect();
let half_sizes: Vec<HalfSize3D> = radii
.iter()
.map(|&Radius(radius)| HalfSize3D::splat(clean_length(radius.0)))
.collect();
let subdivisions = match batch.fill_mode {
FillMode::DenseWireframe => 3, FillMode::Solid | FillMode::TransparentFillMajorWireframe => 4, FillMode::MajorWireframe => 10,
};
let axes_only = batch.fill_mode.axes_only();
let meshes = lengths_iter
.zip(radii.iter())
.map(|(Length(length), &Radius(radius))| {
let ratio = clean_length(length.0 / radius.0);
let granularity = 2.0f32.powi(-4); let ratio = (ratio / granularity).round() * granularity;
proc_mesh::ProcMeshKey::Capsule {
subdivisions,
length: NotNan::new(ratio)
.expect("Ratio was not properly checked with clean_length"),
axes_only,
}
});
builder.add_batch(
query_context,
ent_context,
Capsules3D::descriptor_colors().component,
Capsules3D::descriptor_line_radii().component,
Capsules3D::descriptor_show_labels().component,
glam::Affine3A::IDENTITY,
ProcMeshBatch {
half_sizes: &half_sizes,
centers: batch.translations,
rotation_axis_angles: batch.rotation_axis_angles.as_slice(),
quaternions: batch.quaternions,
meshes,
fill_modes: iter::repeat(batch.fill_mode),
line_radii: batch.line_radii,
colors: batch.colors,
labels: &batch.labels,
show_labels: batch.show_labels,
class_ids: batch.class_ids,
},
)?;
}
Ok(())
}
}
struct Capsules3DComponentData<'a> {
lengths: &'a [Length],
radii: &'a [Radius],
translations: &'a [components::Translation3D],
rotation_axis_angles: ChunkComponentIterItem<components::RotationAxisAngle>,
quaternions: &'a [components::RotationQuat],
colors: &'a [Color],
labels: Vec<ArrowString>,
line_radii: &'a [Radius],
class_ids: &'a [ClassId],
show_labels: Option<ShowLabels>,
fill_mode: FillMode,
}
impl IdentifiedViewSystem for Capsules3DVisualizer {
fn identifier() -> re_viewer_context::ViewSystemIdentifier {
"Capsules3D".into()
}
}
impl VisualizerSystem for Capsules3DVisualizer {
fn visualizer_query_info(
&self,
_app_options: &re_viewer_context::AppOptions,
) -> VisualizerQueryInfo {
VisualizerQueryInfo::single_required_component::<Length>(
&Capsules3D::descriptor_lengths(),
&Capsules3D::all_components(),
)
}
fn execute(
&mut self,
ctx: &ViewContext<'_>,
view_query: &ViewQuery<'_>,
context_systems: &ViewContextCollection,
) -> Result<VisualizerExecutionOutput, ViewSystemExecutionError> {
let output = VisualizerExecutionOutput::default();
let preferred_view_kind = self.0.preferred_view_kind;
let mut builder = ProcMeshDrawableBuilder::new(
&mut self.0,
ctx.viewer_ctx.render_ctx(),
view_query,
"capsules3d",
);
use super::entity_iterator::process_archetype;
process_archetype::<Self, Capsules3D, _>(
ctx,
view_query,
context_systems,
&output,
preferred_view_kind,
|ctx, spatial_ctx, results| {
let all_lengths = results.iter_required(Capsules3D::descriptor_lengths().component);
let all_radii = results.iter_optional(Capsules3D::descriptor_radii().component);
let all_translations =
results.iter_optional(Capsules3D::descriptor_translations().component);
let all_rotation_axis_angles =
results.iter_optional(Capsules3D::descriptor_rotation_axis_angles().component);
let all_quaternions =
results.iter_optional(Capsules3D::descriptor_quaternions().component);
let all_colors = results.iter_optional(Capsules3D::descriptor_colors().component);
let all_labels = results.iter_optional(Capsules3D::descriptor_labels().component);
let all_show_labels =
results.iter_optional(Capsules3D::descriptor_show_labels().component);
let all_fill_modes =
results.iter_optional(Capsules3D::descriptor_fill_mode().component);
let all_line_radii =
results.iter_optional(Capsules3D::descriptor_line_radii().component);
let all_class_ids =
results.iter_optional(Capsules3D::descriptor_class_ids().component);
let data = re_query::range_zip_1x10(
all_lengths.slice::<f32>(),
all_radii.slice::<f32>(),
all_translations.slice::<[f32; 3]>(),
all_rotation_axis_angles.component_slow::<components::RotationAxisAngle>(),
all_quaternions.slice::<[f32; 4]>(),
all_colors.slice::<u32>(),
all_line_radii.slice::<f32>(),
all_fill_modes.slice::<u8>(),
all_labels.slice::<String>(),
all_show_labels.slice::<bool>(),
all_class_ids.slice::<u16>(),
)
.map(
|(
_index,
lengths,
radii,
translations,
rotation_axis_angles,
quaternions,
colors,
line_radii,
fill_modes,
labels,
show_labels,
class_ids,
)| {
Capsules3DComponentData {
lengths: bytemuck::cast_slice(lengths),
radii: radii.map_or(&[], bytemuck::cast_slice),
translations: translations.map_or(&[], bytemuck::cast_slice),
rotation_axis_angles: rotation_axis_angles.unwrap_or_default(),
quaternions: quaternions.map_or(&[], bytemuck::cast_slice),
colors: colors.map_or(&[], |colors| bytemuck::cast_slice(colors)),
labels: labels.unwrap_or_default(),
line_radii: line_radii
.map_or(&[], |line_radii| bytemuck::cast_slice(line_radii)),
fill_mode: fill_modes
.and_then(|s| FillMode::from_integer_slice(s).next()?)
.unwrap_or_default(),
class_ids: class_ids
.map_or(&[], |class_ids| bytemuck::cast_slice(class_ids)),
show_labels: show_labels
.map(|b| !b.is_empty() && b.value(0))
.map(Into::into),
}
},
);
Self::process_data(&mut builder, ctx, spatial_ctx, data)?;
Ok(())
},
)?;
Ok(output.with_draw_data(builder.into_draw_data()?))
}
fn data(&self) -> Option<&dyn std::any::Any> {
Some(self.0.as_any())
}
}
fn clean_length(suspicious_length: f32) -> f32 {
if suspicious_length.is_finite() && suspicious_length > 0.0 {
suspicious_length
} else {
0.0
}
}