use glam::{Vec3, Vec4};
use polyscope_core::quantity::{FaceQuantity, Quantity, QuantityKind, VertexQuantity};
use polyscope_render::{ColorMap, VectorRenderData, VectorUniforms};
pub struct MeshVertexScalarQuantity {
name: String,
structure_name: String,
values: Vec<f32>,
enabled: bool,
colormap_name: String,
range_min: f32,
range_max: f32,
}
impl MeshVertexScalarQuantity {
pub fn new(
name: impl Into<String>,
structure_name: impl Into<String>,
values: Vec<f32>,
) -> Self {
let min = values.iter().copied().fold(f32::INFINITY, f32::min);
let max = values.iter().copied().fold(f32::NEG_INFINITY, f32::max);
Self {
name: name.into(),
structure_name: structure_name.into(),
values,
enabled: false,
colormap_name: "viridis".to_string(),
range_min: min,
range_max: max,
}
}
#[must_use]
pub fn values(&self) -> &[f32] {
&self.values
}
#[must_use]
pub fn colormap_name(&self) -> &str {
&self.colormap_name
}
pub fn set_colormap(&mut self, name: impl Into<String>) {
self.colormap_name = name.into();
}
#[must_use]
pub fn range_min(&self) -> f32 {
self.range_min
}
#[must_use]
pub fn range_max(&self) -> f32 {
self.range_max
}
pub fn set_range(&mut self, min: f32, max: f32) {
self.range_min = min;
self.range_max = max;
}
#[must_use]
pub fn compute_colors(&self, colormap: &ColorMap) -> Vec<Vec4> {
let range = self.range_max - self.range_min;
let range = if range.abs() < 1e-10 { 1.0 } else { range };
self.values
.iter()
.map(|&v| {
let t = (v - self.range_min) / range;
colormap.sample(t).extend(1.0)
})
.collect()
}
pub fn build_egui_ui(&mut self, ui: &mut egui::Ui) -> bool {
let colormaps = ["viridis", "blues", "reds", "coolwarm", "rainbow"];
polyscope_ui::build_scalar_quantity_ui(
ui,
&self.name,
&mut self.enabled,
&mut self.colormap_name,
&mut self.range_min,
&mut self.range_max,
&colormaps,
)
}
}
impl Quantity for MeshVertexScalarQuantity {
fn as_any(&self) -> &dyn std::any::Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
self
}
fn name(&self) -> &str {
&self.name
}
fn structure_name(&self) -> &str {
&self.structure_name
}
fn kind(&self) -> QuantityKind {
QuantityKind::Scalar
}
fn is_enabled(&self) -> bool {
self.enabled
}
fn set_enabled(&mut self, enabled: bool) {
self.enabled = enabled;
}
fn build_ui(&mut self, _ui: &dyn std::any::Any) {}
fn refresh(&mut self) {}
fn data_size(&self) -> usize {
self.values.len()
}
}
impl VertexQuantity for MeshVertexScalarQuantity {}
pub struct MeshFaceScalarQuantity {
name: String,
structure_name: String,
values: Vec<f32>,
enabled: bool,
colormap_name: String,
range_min: f32,
range_max: f32,
}
impl MeshFaceScalarQuantity {
pub fn new(
name: impl Into<String>,
structure_name: impl Into<String>,
values: Vec<f32>,
) -> Self {
let min = values.iter().copied().fold(f32::INFINITY, f32::min);
let max = values.iter().copied().fold(f32::NEG_INFINITY, f32::max);
Self {
name: name.into(),
structure_name: structure_name.into(),
values,
enabled: false,
colormap_name: "viridis".to_string(),
range_min: min,
range_max: max,
}
}
#[must_use]
pub fn values(&self) -> &[f32] {
&self.values
}
#[must_use]
pub fn colormap_name(&self) -> &str {
&self.colormap_name
}
pub fn set_colormap(&mut self, name: impl Into<String>) {
self.colormap_name = name.into();
}
#[must_use]
pub fn range_min(&self) -> f32 {
self.range_min
}
#[must_use]
pub fn range_max(&self) -> f32 {
self.range_max
}
pub fn set_range(&mut self, min: f32, max: f32) {
self.range_min = min;
self.range_max = max;
}
#[must_use]
pub fn compute_vertex_colors(
&self,
faces: &[Vec<u32>],
num_vertices: usize,
colormap: &ColorMap,
) -> Vec<Vec4> {
let range = self.range_max - self.range_min;
let range = if range.abs() < 1e-10 { 1.0 } else { range };
let mut colors = vec![Vec4::splat(0.5); num_vertices];
for (face_idx, face) in faces.iter().enumerate() {
let t = (self.values[face_idx] - self.range_min) / range;
let color = colormap.sample(t).extend(1.0);
for &vi in face {
colors[vi as usize] = color;
}
}
colors
}
pub fn build_egui_ui(&mut self, ui: &mut egui::Ui) -> bool {
let colormaps = ["viridis", "blues", "reds", "coolwarm", "rainbow"];
polyscope_ui::build_scalar_quantity_ui(
ui,
&self.name,
&mut self.enabled,
&mut self.colormap_name,
&mut self.range_min,
&mut self.range_max,
&colormaps,
)
}
}
impl Quantity for MeshFaceScalarQuantity {
fn as_any(&self) -> &dyn std::any::Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
self
}
fn name(&self) -> &str {
&self.name
}
fn structure_name(&self) -> &str {
&self.structure_name
}
fn kind(&self) -> QuantityKind {
QuantityKind::Scalar
}
fn is_enabled(&self) -> bool {
self.enabled
}
fn set_enabled(&mut self, enabled: bool) {
self.enabled = enabled;
}
fn build_ui(&mut self, _ui: &dyn std::any::Any) {}
fn refresh(&mut self) {}
fn data_size(&self) -> usize {
self.values.len()
}
}
impl FaceQuantity for MeshFaceScalarQuantity {}
pub struct MeshVertexColorQuantity {
name: String,
structure_name: String,
colors: Vec<Vec4>,
enabled: bool,
has_transparency: bool,
}
impl MeshVertexColorQuantity {
pub fn new(
name: impl Into<String>,
structure_name: impl Into<String>,
colors: Vec<Vec3>,
) -> Self {
Self {
name: name.into(),
structure_name: structure_name.into(),
colors: colors.into_iter().map(|c| c.extend(1.0)).collect(),
enabled: false,
has_transparency: false,
}
}
pub fn new_with_alpha(
name: impl Into<String>,
structure_name: impl Into<String>,
colors: Vec<Vec4>,
) -> Self {
let has_transparency = colors.iter().any(|c| c.w < 0.999);
Self {
name: name.into(),
structure_name: structure_name.into(),
colors,
enabled: false,
has_transparency,
}
}
#[must_use]
pub fn colors(&self) -> &[Vec4] {
&self.colors
}
#[must_use]
pub fn has_transparency(&self) -> bool {
self.has_transparency
}
pub fn build_egui_ui(&mut self, ui: &mut egui::Ui) -> bool {
polyscope_ui::build_color_quantity_ui(ui, &self.name, &mut self.enabled, self.colors.len())
}
}
impl Quantity for MeshVertexColorQuantity {
fn as_any(&self) -> &dyn std::any::Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
self
}
fn name(&self) -> &str {
&self.name
}
fn structure_name(&self) -> &str {
&self.structure_name
}
fn kind(&self) -> QuantityKind {
QuantityKind::Color
}
fn is_enabled(&self) -> bool {
self.enabled
}
fn set_enabled(&mut self, enabled: bool) {
self.enabled = enabled;
}
fn build_ui(&mut self, _ui: &dyn std::any::Any) {}
fn refresh(&mut self) {}
fn data_size(&self) -> usize {
self.colors.len()
}
}
impl VertexQuantity for MeshVertexColorQuantity {}
pub struct MeshFaceColorQuantity {
name: String,
structure_name: String,
colors: Vec<Vec4>,
enabled: bool,
has_transparency: bool,
}
impl MeshFaceColorQuantity {
pub fn new(
name: impl Into<String>,
structure_name: impl Into<String>,
colors: Vec<Vec3>,
) -> Self {
Self {
name: name.into(),
structure_name: structure_name.into(),
colors: colors.into_iter().map(|c| c.extend(1.0)).collect(),
enabled: false,
has_transparency: false,
}
}
pub fn new_with_alpha(
name: impl Into<String>,
structure_name: impl Into<String>,
colors: Vec<Vec4>,
) -> Self {
let has_transparency = colors.iter().any(|c| c.w < 0.999);
Self {
name: name.into(),
structure_name: structure_name.into(),
colors,
enabled: false,
has_transparency,
}
}
#[must_use]
pub fn colors(&self) -> &[Vec4] {
&self.colors
}
#[must_use]
pub fn has_transparency(&self) -> bool {
self.has_transparency
}
#[must_use]
pub fn compute_vertex_colors(&self, faces: &[Vec<u32>], num_vertices: usize) -> Vec<Vec4> {
let mut colors = vec![Vec4::splat(0.5); num_vertices];
for (face_idx, face) in faces.iter().enumerate() {
let color = self.colors[face_idx];
for &vi in face {
colors[vi as usize] = color;
}
}
colors
}
pub fn build_egui_ui(&mut self, ui: &mut egui::Ui) -> bool {
polyscope_ui::build_color_quantity_ui(ui, &self.name, &mut self.enabled, self.colors.len())
}
}
impl Quantity for MeshFaceColorQuantity {
fn as_any(&self) -> &dyn std::any::Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
self
}
fn name(&self) -> &str {
&self.name
}
fn structure_name(&self) -> &str {
&self.structure_name
}
fn kind(&self) -> QuantityKind {
QuantityKind::Color
}
fn is_enabled(&self) -> bool {
self.enabled
}
fn set_enabled(&mut self, enabled: bool) {
self.enabled = enabled;
}
fn build_ui(&mut self, _ui: &dyn std::any::Any) {}
fn refresh(&mut self) {}
fn data_size(&self) -> usize {
self.colors.len()
}
}
impl FaceQuantity for MeshFaceColorQuantity {}
pub struct MeshVertexVectorQuantity {
name: String,
structure_name: String,
vectors: Vec<Vec3>,
enabled: bool,
length_scale: f32,
radius: f32,
color: Vec4,
render_data: Option<VectorRenderData>,
}
impl MeshVertexVectorQuantity {
pub fn new(
name: impl Into<String>,
structure_name: impl Into<String>,
vectors: Vec<Vec3>,
) -> Self {
Self {
name: name.into(),
structure_name: structure_name.into(),
vectors,
enabled: false,
length_scale: 1.0,
radius: 0.005,
color: Vec4::new(0.8, 0.2, 0.2, 1.0),
render_data: None,
}
}
#[must_use]
pub fn vectors(&self) -> &[Vec3] {
&self.vectors
}
#[must_use]
pub fn length_scale(&self) -> f32 {
self.length_scale
}
pub fn set_length_scale(&mut self, scale: f32) {
self.length_scale = scale;
}
#[must_use]
pub fn radius(&self) -> f32 {
self.radius
}
pub fn set_radius(&mut self, r: f32) {
self.radius = r;
}
#[must_use]
pub fn color(&self) -> Vec4 {
self.color
}
pub fn set_color(&mut self, c: Vec3) {
self.color = c.extend(1.0);
}
pub fn auto_scale(&mut self, structure_length_scale: f32) {
let avg_length: f32 = if self.vectors.is_empty() {
1.0
} else {
let sum: f32 = self.vectors.iter().map(|v| v.length()).sum();
sum / self.vectors.len() as f32
};
if avg_length > 1e-8 {
self.length_scale = 0.02 * structure_length_scale / avg_length;
}
self.radius = 0.002 * structure_length_scale;
}
pub fn init_gpu_resources(
&mut self,
device: &wgpu::Device,
bind_group_layout: &wgpu::BindGroupLayout,
camera_buffer: &wgpu::Buffer,
base_positions: &[Vec3],
) {
self.render_data = Some(VectorRenderData::new(
device,
bind_group_layout,
camera_buffer,
base_positions,
&self.vectors,
));
}
#[must_use]
pub fn render_data(&self) -> Option<&VectorRenderData> {
self.render_data.as_ref()
}
pub fn update_uniforms(&self, queue: &wgpu::Queue, model: &glam::Mat4) {
if let Some(render_data) = &self.render_data {
let uniforms = VectorUniforms {
model: model.to_cols_array(),
length_scale: self.length_scale,
radius: self.radius,
_padding: [0.0; 2],
color: self.color.to_array(),
};
render_data.update_uniforms(queue, &uniforms);
}
}
pub fn build_egui_ui(&mut self, ui: &mut egui::Ui) -> bool {
let mut color = [self.color.x, self.color.y, self.color.z];
let changed = polyscope_ui::build_vector_quantity_ui(
ui,
&self.name,
&mut self.enabled,
&mut self.length_scale,
&mut self.radius,
&mut color,
);
if changed {
self.color = Vec4::new(color[0], color[1], color[2], self.color.w);
}
changed
}
}
impl Quantity for MeshVertexVectorQuantity {
fn as_any(&self) -> &dyn std::any::Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
self
}
fn name(&self) -> &str {
&self.name
}
fn structure_name(&self) -> &str {
&self.structure_name
}
fn kind(&self) -> QuantityKind {
QuantityKind::Vector
}
fn is_enabled(&self) -> bool {
self.enabled
}
fn set_enabled(&mut self, enabled: bool) {
self.enabled = enabled;
}
fn build_ui(&mut self, _ui: &dyn std::any::Any) {}
fn refresh(&mut self) {}
fn clear_gpu_resources(&mut self) {
self.render_data = None;
}
fn data_size(&self) -> usize {
self.vectors.len()
}
}
impl VertexQuantity for MeshVertexVectorQuantity {}
pub struct MeshFaceVectorQuantity {
name: String,
structure_name: String,
vectors: Vec<Vec3>,
enabled: bool,
length_scale: f32,
radius: f32,
color: Vec4,
render_data: Option<VectorRenderData>,
}
impl MeshFaceVectorQuantity {
pub fn new(
name: impl Into<String>,
structure_name: impl Into<String>,
vectors: Vec<Vec3>,
) -> Self {
Self {
name: name.into(),
structure_name: structure_name.into(),
vectors,
enabled: false,
length_scale: 1.0,
radius: 0.005,
color: Vec4::new(0.2, 0.2, 0.8, 1.0),
render_data: None,
}
}
#[must_use]
pub fn vectors(&self) -> &[Vec3] {
&self.vectors
}
#[must_use]
pub fn length_scale(&self) -> f32 {
self.length_scale
}
pub fn set_length_scale(&mut self, scale: f32) {
self.length_scale = scale;
}
#[must_use]
pub fn radius(&self) -> f32 {
self.radius
}
pub fn set_radius(&mut self, r: f32) {
self.radius = r;
}
#[must_use]
pub fn color(&self) -> Vec4 {
self.color
}
pub fn set_color(&mut self, c: Vec3) {
self.color = c.extend(1.0);
}
pub fn auto_scale(&mut self, structure_length_scale: f32) {
let avg_length: f32 = if self.vectors.is_empty() {
1.0
} else {
let sum: f32 = self.vectors.iter().map(|v| v.length()).sum();
sum / self.vectors.len() as f32
};
if avg_length > 1e-8 {
self.length_scale = 0.02 * structure_length_scale / avg_length;
}
self.radius = 0.002 * structure_length_scale;
}
pub fn init_gpu_resources(
&mut self,
device: &wgpu::Device,
bind_group_layout: &wgpu::BindGroupLayout,
camera_buffer: &wgpu::Buffer,
base_positions: &[Vec3],
) {
self.render_data = Some(VectorRenderData::new(
device,
bind_group_layout,
camera_buffer,
base_positions,
&self.vectors,
));
}
#[must_use]
pub fn render_data(&self) -> Option<&VectorRenderData> {
self.render_data.as_ref()
}
pub fn update_uniforms(&self, queue: &wgpu::Queue, model: &glam::Mat4) {
if let Some(render_data) = &self.render_data {
let uniforms = VectorUniforms {
model: model.to_cols_array(),
length_scale: self.length_scale,
radius: self.radius,
_padding: [0.0; 2],
color: self.color.to_array(),
};
render_data.update_uniforms(queue, &uniforms);
}
}
pub fn build_egui_ui(&mut self, ui: &mut egui::Ui) -> bool {
let mut color = [self.color.x, self.color.y, self.color.z];
let changed = polyscope_ui::build_vector_quantity_ui(
ui,
&self.name,
&mut self.enabled,
&mut self.length_scale,
&mut self.radius,
&mut color,
);
if changed {
self.color = Vec4::new(color[0], color[1], color[2], self.color.w);
}
changed
}
}
impl Quantity for MeshFaceVectorQuantity {
fn as_any(&self) -> &dyn std::any::Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
self
}
fn name(&self) -> &str {
&self.name
}
fn structure_name(&self) -> &str {
&self.structure_name
}
fn kind(&self) -> QuantityKind {
QuantityKind::Vector
}
fn is_enabled(&self) -> bool {
self.enabled
}
fn set_enabled(&mut self, enabled: bool) {
self.enabled = enabled;
}
fn build_ui(&mut self, _ui: &dyn std::any::Any) {}
fn refresh(&mut self) {}
fn clear_gpu_resources(&mut self) {
self.render_data = None;
}
fn data_size(&self) -> usize {
self.vectors.len()
}
}
impl FaceQuantity for MeshFaceVectorQuantity {}