polyscope_structures/surface_mesh/
one_form_quantity.rs1use glam::{Vec3, Vec4};
8use polyscope_core::quantity::{EdgeQuantity, Quantity, QuantityKind};
9use polyscope_render::{VectorRenderData, VectorUniforms};
10
11pub struct MeshOneFormQuantity {
17 name: String,
18 structure_name: String,
19 values: Vec<f32>, orientations: Vec<bool>, enabled: bool,
22 length_scale: f32,
23 radius: f32,
24 color: Vec4,
25 render_data: Option<VectorRenderData>,
26}
27
28impl MeshOneFormQuantity {
29 pub fn new(
31 name: impl Into<String>,
32 structure_name: impl Into<String>,
33 values: Vec<f32>,
34 orientations: Vec<bool>,
35 ) -> Self {
36 Self {
37 name: name.into(),
38 structure_name: structure_name.into(),
39 values,
40 orientations,
41 enabled: false,
42 length_scale: 1.0,
43 radius: 0.005,
44 color: Vec4::new(0.2, 0.7, 0.2, 1.0),
45 render_data: None,
46 }
47 }
48
49 #[must_use]
51 pub fn values(&self) -> &[f32] {
52 &self.values
53 }
54
55 #[must_use]
57 pub fn orientations(&self) -> &[bool] {
58 &self.orientations
59 }
60
61 #[must_use]
63 pub fn length_scale(&self) -> f32 {
64 self.length_scale
65 }
66
67 pub fn set_length_scale(&mut self, scale: f32) -> &mut Self {
69 self.length_scale = scale;
70 self
71 }
72
73 #[must_use]
75 pub fn radius(&self) -> f32 {
76 self.radius
77 }
78
79 pub fn set_radius(&mut self, r: f32) -> &mut Self {
81 self.radius = r;
82 self
83 }
84
85 #[must_use]
87 pub fn color(&self) -> Vec4 {
88 self.color
89 }
90
91 pub fn set_color(&mut self, c: Vec3) -> &mut Self {
93 self.color = c.extend(1.0);
94 self
95 }
96
97 #[must_use]
102 pub fn compute_edge_vectors(
103 &self,
104 vertices: &[Vec3],
105 edges: &[(u32, u32)],
106 ) -> (Vec<Vec3>, Vec<Vec3>) {
107 let mut positions = Vec::with_capacity(self.values.len());
108 let mut vectors = Vec::with_capacity(self.values.len());
109
110 for (i, &(v0_idx, v1_idx)) in edges.iter().enumerate() {
111 if i >= self.values.len() {
112 break;
113 }
114
115 let v0 = vertices[v0_idx as usize];
116 let v1 = vertices[v1_idx as usize];
117
118 let midpoint = (v0 + v1) * 0.5;
120
121 let mut direction = (v1 - v0).normalize_or_zero();
123
124 if !self.orientations[i] {
126 direction = -direction;
127 }
128
129 let vector = direction * self.values[i] * self.length_scale;
130
131 positions.push(midpoint);
132 vectors.push(vector);
133 }
134
135 (positions, vectors)
136 }
137
138 pub fn auto_scale(
140 &mut self,
141 structure_length_scale: f32,
142 vertices: &[Vec3],
143 edges: &[(u32, u32)],
144 ) {
145 let (_positions, vecs) = self.compute_edge_vectors(vertices, edges);
146 let avg_length: f32 = if vecs.is_empty() {
147 1.0
148 } else {
149 let sum: f32 = vecs.iter().map(|v| v.length()).sum();
150 sum / vecs.len() as f32
151 };
152 if avg_length > 1e-8 {
153 self.length_scale = 0.02 * structure_length_scale / avg_length;
154 }
155 self.radius = 0.002 * structure_length_scale;
156 }
157
158 pub fn init_gpu_resources(
163 &mut self,
164 device: &wgpu::Device,
165 bind_group_layout: &wgpu::BindGroupLayout,
166 camera_buffer: &wgpu::Buffer,
167 vertices: &[Vec3],
168 edges: &[(u32, u32)],
169 ) {
170 let (positions, vectors) = self.compute_edge_vectors(vertices, edges);
171 self.render_data = Some(VectorRenderData::new(
172 device,
173 bind_group_layout,
174 camera_buffer,
175 &positions,
176 &vectors,
177 ));
178 }
179
180 #[must_use]
182 pub fn render_data(&self) -> Option<&VectorRenderData> {
183 self.render_data.as_ref()
184 }
185
186 pub fn update_uniforms(&self, queue: &wgpu::Queue, model: &glam::Mat4) {
188 if let Some(render_data) = &self.render_data {
189 let uniforms = VectorUniforms {
190 model: model.to_cols_array(),
191 length_scale: self.length_scale,
192 radius: self.radius,
193 _padding: [0.0; 2],
194 color: self.color.to_array(),
195 };
196 render_data.update_uniforms(queue, &uniforms);
197 }
198 }
199
200 pub fn build_egui_ui(&mut self, ui: &mut egui::Ui) -> bool {
202 let mut color = [self.color.x, self.color.y, self.color.z];
203 let changed = polyscope_ui::build_vector_quantity_ui(
204 ui,
205 &self.name,
206 &mut self.enabled,
207 &mut self.length_scale,
208 &mut self.radius,
209 &mut color,
210 );
211 if changed {
212 self.color = Vec4::new(color[0], color[1], color[2], self.color.w);
213 }
214 changed
215 }
216}
217
218impl Quantity for MeshOneFormQuantity {
219 fn as_any(&self) -> &dyn std::any::Any {
220 self
221 }
222 fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
223 self
224 }
225 fn name(&self) -> &str {
226 &self.name
227 }
228 fn structure_name(&self) -> &str {
229 &self.structure_name
230 }
231 fn kind(&self) -> QuantityKind {
232 QuantityKind::Vector
233 }
234 fn is_enabled(&self) -> bool {
235 self.enabled
236 }
237 fn set_enabled(&mut self, enabled: bool) {
238 self.enabled = enabled;
239 }
240 fn build_ui(&mut self, _ui: &dyn std::any::Any) {}
241 fn refresh(&mut self) {}
242 fn clear_gpu_resources(&mut self) {
243 self.render_data = None;
244 }
245 fn data_size(&self) -> usize {
246 self.values.len()
247 }
248}
249
250impl EdgeQuantity for MeshOneFormQuantity {}
251
252#[cfg(test)]
253mod tests {
254 use super::*;
255
256 #[test]
257 fn test_one_form_creation() {
258 let values = vec![1.0, -0.5, 0.3];
259 let orientations = vec![true, true, false];
260 let q = MeshOneFormQuantity::new("flow", "mesh", values, orientations);
261
262 assert_eq!(q.name(), "flow");
263 assert_eq!(q.structure_name(), "mesh");
264 assert_eq!(q.data_size(), 3);
265 assert_eq!(q.kind(), QuantityKind::Vector);
266 assert!(!q.is_enabled());
267 }
268
269 #[test]
270 fn test_edge_vector_computation() {
271 let vertices = vec![
272 Vec3::new(0.0, 0.0, 0.0), Vec3::new(2.0, 0.0, 0.0), Vec3::new(1.0, 2.0, 0.0), ];
276 let edges: Vec<(u32, u32)> = vec![(0, 1), (0, 2), (1, 2)];
278
279 let values = vec![1.0, 0.5, -0.5];
281 let orientations = vec![true, true, true];
282 let q = MeshOneFormQuantity::new("test", "mesh", values, orientations);
283
284 let (positions, vectors) = q.compute_edge_vectors(&vertices, &edges);
285
286 assert_eq!(positions.len(), 3);
287 assert_eq!(vectors.len(), 3);
288
289 assert!((positions[0] - Vec3::new(1.0, 0.0, 0.0)).length() < 1e-5);
291 assert!((vectors[0] - Vec3::new(1.0, 0.0, 0.0)).length() < 1e-5);
292
293 assert!((positions[1] - Vec3::new(0.5, 1.0, 0.0)).length() < 1e-5);
295 }
296
297 #[test]
298 fn test_edge_vector_orientation_flip() {
299 let vertices = vec![Vec3::new(0.0, 0.0, 0.0), Vec3::new(2.0, 0.0, 0.0)];
300 let edges: Vec<(u32, u32)> = vec![(0, 1)];
301
302 let values = vec![1.0];
304 let orientations = vec![false];
305 let q = MeshOneFormQuantity::new("test", "mesh", values, orientations);
306
307 let (positions, vectors) = q.compute_edge_vectors(&vertices, &edges);
308
309 assert!((positions[0] - Vec3::new(1.0, 0.0, 0.0)).length() < 1e-5);
311 assert!((vectors[0] - Vec3::new(-1.0, 0.0, 0.0)).length() < 1e-5);
312 }
313
314 #[test]
315 fn test_edge_vector_negative_value() {
316 let vertices = vec![Vec3::new(0.0, 0.0, 0.0), Vec3::new(2.0, 0.0, 0.0)];
317 let edges: Vec<(u32, u32)> = vec![(0, 1)];
318
319 let values = vec![-1.0];
321 let orientations = vec![true];
322 let q = MeshOneFormQuantity::new("test", "mesh", values, orientations);
323
324 let (_positions, vectors) = q.compute_edge_vectors(&vertices, &edges);
325
326 assert!((vectors[0] - Vec3::new(-1.0, 0.0, 0.0)).length() < 1e-5);
328 }
329}