Skip to main content

polyscope_structures/curve_network/
quantities.rs

1//! Curve network quantity implementations.
2
3use glam::{Vec3, Vec4};
4use polyscope_core::quantity::{EdgeQuantity, Quantity, QuantityKind, VertexQuantity};
5use polyscope_render::{ColorMap, CurveNetworkRenderData};
6
7/// A scalar quantity on curve network nodes.
8pub struct CurveNodeScalarQuantity {
9    name: String,
10    structure_name: String,
11    values: Vec<f32>,
12    enabled: bool,
13    colormap_name: String,
14    range_min: f32,
15    range_max: f32,
16}
17
18impl CurveNodeScalarQuantity {
19    /// Creates a new node scalar quantity.
20    pub fn new(
21        name: impl Into<String>,
22        structure_name: impl Into<String>,
23        values: Vec<f32>,
24    ) -> Self {
25        let min = values.iter().copied().fold(f32::INFINITY, f32::min);
26        let max = values.iter().copied().fold(f32::NEG_INFINITY, f32::max);
27
28        Self {
29            name: name.into(),
30            structure_name: structure_name.into(),
31            values,
32            enabled: false,
33            colormap_name: "viridis".to_string(),
34            range_min: min,
35            range_max: max,
36        }
37    }
38
39    /// Returns the scalar values.
40    #[must_use]
41    pub fn values(&self) -> &[f32] {
42        &self.values
43    }
44
45    /// Maps scalar values to colors using the colormap.
46    #[must_use]
47    pub fn compute_colors(&self, colormap: &ColorMap) -> Vec<Vec4> {
48        let range = self.range_max - self.range_min;
49        let range = if range.abs() < 1e-10 { 1.0 } else { range };
50
51        self.values
52            .iter()
53            .map(|&v| {
54                let t = (v - self.range_min) / range;
55                colormap.sample(t).extend(1.0)
56            })
57            .collect()
58    }
59
60    /// Gets the colormap name.
61    #[must_use]
62    pub fn colormap_name(&self) -> &str {
63        &self.colormap_name
64    }
65
66    /// Sets the colormap name.
67    pub fn set_colormap(&mut self, name: impl Into<String>) {
68        self.colormap_name = name.into();
69    }
70
71    /// Gets the range minimum.
72    #[must_use]
73    pub fn range_min(&self) -> f32 {
74        self.range_min
75    }
76
77    /// Gets the range maximum.
78    #[must_use]
79    pub fn range_max(&self) -> f32 {
80        self.range_max
81    }
82
83    /// Sets the range.
84    pub fn set_range(&mut self, min: f32, max: f32) {
85        self.range_min = min;
86        self.range_max = max;
87    }
88
89    /// Builds the egui UI for this scalar quantity.
90    pub fn build_egui_ui(&mut self, ui: &mut egui::Ui) -> bool {
91        let colormaps = ["viridis", "blues", "reds", "coolwarm", "rainbow"];
92        polyscope_ui::build_scalar_quantity_ui(
93            ui,
94            &self.name,
95            &mut self.enabled,
96            &mut self.colormap_name,
97            &mut self.range_min,
98            &mut self.range_max,
99            &colormaps,
100        )
101    }
102}
103
104impl Quantity for CurveNodeScalarQuantity {
105    fn as_any(&self) -> &dyn std::any::Any {
106        self
107    }
108
109    fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
110        self
111    }
112
113    fn name(&self) -> &str {
114        &self.name
115    }
116
117    fn structure_name(&self) -> &str {
118        &self.structure_name
119    }
120
121    fn kind(&self) -> QuantityKind {
122        QuantityKind::Scalar
123    }
124
125    fn is_enabled(&self) -> bool {
126        self.enabled
127    }
128
129    fn set_enabled(&mut self, enabled: bool) {
130        self.enabled = enabled;
131    }
132
133    fn build_ui(&mut self, _ui: &dyn std::any::Any) {
134        // Implemented via build_egui_ui
135    }
136
137    fn refresh(&mut self) {
138        // GPU resources refreshed externally
139    }
140
141    fn data_size(&self) -> usize {
142        self.values.len()
143    }
144}
145
146impl VertexQuantity for CurveNodeScalarQuantity {}
147
148/// A scalar quantity on curve network edges.
149pub struct CurveEdgeScalarQuantity {
150    name: String,
151    structure_name: String,
152    values: Vec<f32>,
153    enabled: bool,
154    colormap_name: String,
155    range_min: f32,
156    range_max: f32,
157}
158
159impl CurveEdgeScalarQuantity {
160    /// Creates a new edge scalar quantity.
161    pub fn new(
162        name: impl Into<String>,
163        structure_name: impl Into<String>,
164        values: Vec<f32>,
165    ) -> Self {
166        let min = values.iter().copied().fold(f32::INFINITY, f32::min);
167        let max = values.iter().copied().fold(f32::NEG_INFINITY, f32::max);
168
169        Self {
170            name: name.into(),
171            structure_name: structure_name.into(),
172            values,
173            enabled: false,
174            colormap_name: "viridis".to_string(),
175            range_min: min,
176            range_max: max,
177        }
178    }
179
180    /// Returns the scalar values.
181    #[must_use]
182    pub fn values(&self) -> &[f32] {
183        &self.values
184    }
185
186    /// Maps scalar values to colors using the colormap.
187    #[must_use]
188    pub fn compute_colors(&self, colormap: &ColorMap) -> Vec<Vec4> {
189        let range = self.range_max - self.range_min;
190        let range = if range.abs() < 1e-10 { 1.0 } else { range };
191
192        self.values
193            .iter()
194            .map(|&v| {
195                let t = (v - self.range_min) / range;
196                colormap.sample(t).extend(1.0)
197            })
198            .collect()
199    }
200
201    /// Gets the colormap name.
202    #[must_use]
203    pub fn colormap_name(&self) -> &str {
204        &self.colormap_name
205    }
206
207    /// Sets the colormap name.
208    pub fn set_colormap(&mut self, name: impl Into<String>) {
209        self.colormap_name = name.into();
210    }
211
212    /// Gets the range minimum.
213    #[must_use]
214    pub fn range_min(&self) -> f32 {
215        self.range_min
216    }
217
218    /// Gets the range maximum.
219    #[must_use]
220    pub fn range_max(&self) -> f32 {
221        self.range_max
222    }
223
224    /// Sets the range.
225    pub fn set_range(&mut self, min: f32, max: f32) {
226        self.range_min = min;
227        self.range_max = max;
228    }
229
230    /// Builds the egui UI for this scalar quantity.
231    pub fn build_egui_ui(&mut self, ui: &mut egui::Ui) -> bool {
232        let colormaps = ["viridis", "blues", "reds", "coolwarm", "rainbow"];
233        polyscope_ui::build_scalar_quantity_ui(
234            ui,
235            &self.name,
236            &mut self.enabled,
237            &mut self.colormap_name,
238            &mut self.range_min,
239            &mut self.range_max,
240            &colormaps,
241        )
242    }
243}
244
245impl Quantity for CurveEdgeScalarQuantity {
246    fn as_any(&self) -> &dyn std::any::Any {
247        self
248    }
249
250    fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
251        self
252    }
253
254    fn name(&self) -> &str {
255        &self.name
256    }
257
258    fn structure_name(&self) -> &str {
259        &self.structure_name
260    }
261
262    fn kind(&self) -> QuantityKind {
263        QuantityKind::Scalar
264    }
265
266    fn is_enabled(&self) -> bool {
267        self.enabled
268    }
269
270    fn set_enabled(&mut self, enabled: bool) {
271        self.enabled = enabled;
272    }
273
274    fn build_ui(&mut self, _ui: &dyn std::any::Any) {
275        // Implemented via build_egui_ui
276    }
277
278    fn refresh(&mut self) {
279        // GPU resources refreshed externally
280    }
281
282    fn data_size(&self) -> usize {
283        self.values.len()
284    }
285}
286
287impl EdgeQuantity for CurveEdgeScalarQuantity {}
288
289/// A color quantity on curve network nodes.
290pub struct CurveNodeColorQuantity {
291    name: String,
292    structure_name: String,
293    colors: Vec<Vec4>,
294    enabled: bool,
295}
296
297impl CurveNodeColorQuantity {
298    /// Creates a new node color quantity.
299    pub fn new(
300        name: impl Into<String>,
301        structure_name: impl Into<String>,
302        colors: Vec<Vec3>,
303    ) -> Self {
304        Self {
305            name: name.into(),
306            structure_name: structure_name.into(),
307            colors: colors.into_iter().map(|c| c.extend(1.0)).collect(),
308            enabled: false,
309        }
310    }
311
312    /// Returns the colors.
313    #[must_use]
314    pub fn colors(&self) -> &[Vec4] {
315        &self.colors
316    }
317
318    /// Applies this color quantity to the curve network render data.
319    pub fn apply_to_render_data(&self, queue: &wgpu::Queue, render_data: &CurveNetworkRenderData) {
320        render_data.update_node_colors(queue, &self.colors);
321    }
322
323    /// Builds the egui UI for this color quantity.
324    pub fn build_egui_ui(&mut self, ui: &mut egui::Ui) -> bool {
325        polyscope_ui::build_color_quantity_ui(ui, &self.name, &mut self.enabled, self.colors.len())
326    }
327}
328
329impl Quantity for CurveNodeColorQuantity {
330    fn as_any(&self) -> &dyn std::any::Any {
331        self
332    }
333
334    fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
335        self
336    }
337
338    fn name(&self) -> &str {
339        &self.name
340    }
341
342    fn structure_name(&self) -> &str {
343        &self.structure_name
344    }
345
346    fn kind(&self) -> QuantityKind {
347        QuantityKind::Color
348    }
349
350    fn is_enabled(&self) -> bool {
351        self.enabled
352    }
353
354    fn set_enabled(&mut self, enabled: bool) {
355        self.enabled = enabled;
356    }
357
358    fn build_ui(&mut self, _ui: &dyn std::any::Any) {
359        // Implemented via build_egui_ui
360    }
361
362    fn refresh(&mut self) {
363        // GPU resources refreshed externally
364    }
365
366    fn data_size(&self) -> usize {
367        self.colors.len()
368    }
369}
370
371impl VertexQuantity for CurveNodeColorQuantity {}
372
373/// A color quantity on curve network edges.
374pub struct CurveEdgeColorQuantity {
375    name: String,
376    structure_name: String,
377    colors: Vec<Vec4>,
378    enabled: bool,
379}
380
381impl CurveEdgeColorQuantity {
382    /// Creates a new edge color quantity.
383    pub fn new(
384        name: impl Into<String>,
385        structure_name: impl Into<String>,
386        colors: Vec<Vec3>,
387    ) -> Self {
388        Self {
389            name: name.into(),
390            structure_name: structure_name.into(),
391            colors: colors.into_iter().map(|c| c.extend(1.0)).collect(),
392            enabled: false,
393        }
394    }
395
396    /// Returns the colors.
397    #[must_use]
398    pub fn colors(&self) -> &[Vec4] {
399        &self.colors
400    }
401
402    /// Applies this color quantity to the curve network render data.
403    pub fn apply_to_render_data(&self, queue: &wgpu::Queue, render_data: &CurveNetworkRenderData) {
404        render_data.update_edge_colors(queue, &self.colors);
405    }
406
407    /// Builds the egui UI for this color quantity.
408    pub fn build_egui_ui(&mut self, ui: &mut egui::Ui) -> bool {
409        polyscope_ui::build_color_quantity_ui(ui, &self.name, &mut self.enabled, self.colors.len())
410    }
411}
412
413impl Quantity for CurveEdgeColorQuantity {
414    fn as_any(&self) -> &dyn std::any::Any {
415        self
416    }
417
418    fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
419        self
420    }
421
422    fn name(&self) -> &str {
423        &self.name
424    }
425
426    fn structure_name(&self) -> &str {
427        &self.structure_name
428    }
429
430    fn kind(&self) -> QuantityKind {
431        QuantityKind::Color
432    }
433
434    fn is_enabled(&self) -> bool {
435        self.enabled
436    }
437
438    fn set_enabled(&mut self, enabled: bool) {
439        self.enabled = enabled;
440    }
441
442    fn build_ui(&mut self, _ui: &dyn std::any::Any) {
443        // Implemented via build_egui_ui
444    }
445
446    fn refresh(&mut self) {
447        // GPU resources refreshed externally
448    }
449
450    fn data_size(&self) -> usize {
451        self.colors.len()
452    }
453}
454
455impl EdgeQuantity for CurveEdgeColorQuantity {}
456
457/// A vector quantity on curve network nodes.
458pub struct CurveNodeVectorQuantity {
459    name: String,
460    structure_name: String,
461    vectors: Vec<Vec3>,
462    enabled: bool,
463    length_scale: f32,
464    radius: f32,
465    color: Vec4,
466}
467
468impl CurveNodeVectorQuantity {
469    /// Creates a new node vector quantity.
470    pub fn new(
471        name: impl Into<String>,
472        structure_name: impl Into<String>,
473        vectors: Vec<Vec3>,
474    ) -> Self {
475        Self {
476            name: name.into(),
477            structure_name: structure_name.into(),
478            vectors,
479            enabled: false,
480            length_scale: 1.0,
481            radius: 0.005,
482            color: Vec4::new(0.8, 0.2, 0.2, 1.0),
483        }
484    }
485
486    /// Returns the vectors.
487    #[must_use]
488    pub fn vectors(&self) -> &[Vec3] {
489        &self.vectors
490    }
491
492    /// Gets the length scale.
493    #[must_use]
494    pub fn length_scale(&self) -> f32 {
495        self.length_scale
496    }
497
498    /// Sets the length scale.
499    pub fn set_length_scale(&mut self, scale: f32) {
500        self.length_scale = scale;
501    }
502
503    /// Gets the radius.
504    #[must_use]
505    pub fn radius(&self) -> f32 {
506        self.radius
507    }
508
509    /// Sets the radius.
510    pub fn set_radius(&mut self, r: f32) {
511        self.radius = r;
512    }
513
514    /// Gets the color.
515    #[must_use]
516    pub fn color(&self) -> Vec4 {
517        self.color
518    }
519
520    /// Sets the color.
521    pub fn set_color(&mut self, c: Vec3) {
522        self.color = c.extend(1.0);
523    }
524
525    /// Builds the egui UI for this quantity.
526    pub fn build_egui_ui(&mut self, ui: &mut egui::Ui) -> bool {
527        let mut color = [self.color.x, self.color.y, self.color.z];
528        let changed = polyscope_ui::build_vector_quantity_ui(
529            ui,
530            &self.name,
531            &mut self.enabled,
532            &mut self.length_scale,
533            &mut self.radius,
534            &mut color,
535        );
536        if changed {
537            self.color = Vec4::new(color[0], color[1], color[2], self.color.w);
538        }
539        changed
540    }
541}
542
543impl Quantity for CurveNodeVectorQuantity {
544    fn as_any(&self) -> &dyn std::any::Any {
545        self
546    }
547
548    fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
549        self
550    }
551
552    fn name(&self) -> &str {
553        &self.name
554    }
555
556    fn structure_name(&self) -> &str {
557        &self.structure_name
558    }
559
560    fn kind(&self) -> QuantityKind {
561        QuantityKind::Vector
562    }
563
564    fn is_enabled(&self) -> bool {
565        self.enabled
566    }
567
568    fn set_enabled(&mut self, enabled: bool) {
569        self.enabled = enabled;
570    }
571
572    fn build_ui(&mut self, _ui: &dyn std::any::Any) {
573        // Implemented via build_egui_ui
574    }
575
576    fn refresh(&mut self) {
577        // GPU resources refreshed externally
578    }
579
580    fn data_size(&self) -> usize {
581        self.vectors.len()
582    }
583}
584
585impl VertexQuantity for CurveNodeVectorQuantity {}
586
587/// A vector quantity on curve network edges.
588pub struct CurveEdgeVectorQuantity {
589    name: String,
590    structure_name: String,
591    vectors: Vec<Vec3>,
592    enabled: bool,
593    length_scale: f32,
594    radius: f32,
595    color: Vec4,
596}
597
598impl CurveEdgeVectorQuantity {
599    /// Creates a new edge vector quantity.
600    pub fn new(
601        name: impl Into<String>,
602        structure_name: impl Into<String>,
603        vectors: Vec<Vec3>,
604    ) -> Self {
605        Self {
606            name: name.into(),
607            structure_name: structure_name.into(),
608            vectors,
609            enabled: false,
610            length_scale: 1.0,
611            radius: 0.005,
612            color: Vec4::new(0.2, 0.8, 0.2, 1.0),
613        }
614    }
615
616    /// Returns the vectors.
617    #[must_use]
618    pub fn vectors(&self) -> &[Vec3] {
619        &self.vectors
620    }
621
622    /// Gets the length scale.
623    #[must_use]
624    pub fn length_scale(&self) -> f32 {
625        self.length_scale
626    }
627
628    /// Sets the length scale.
629    pub fn set_length_scale(&mut self, scale: f32) {
630        self.length_scale = scale;
631    }
632
633    /// Gets the radius.
634    #[must_use]
635    pub fn radius(&self) -> f32 {
636        self.radius
637    }
638
639    /// Sets the radius.
640    pub fn set_radius(&mut self, r: f32) {
641        self.radius = r;
642    }
643
644    /// Gets the color.
645    #[must_use]
646    pub fn color(&self) -> Vec4 {
647        self.color
648    }
649
650    /// Sets the color.
651    pub fn set_color(&mut self, c: Vec3) {
652        self.color = c.extend(1.0);
653    }
654
655    /// Builds the egui UI for this quantity.
656    pub fn build_egui_ui(&mut self, ui: &mut egui::Ui) -> bool {
657        let mut color = [self.color.x, self.color.y, self.color.z];
658        let changed = polyscope_ui::build_vector_quantity_ui(
659            ui,
660            &self.name,
661            &mut self.enabled,
662            &mut self.length_scale,
663            &mut self.radius,
664            &mut color,
665        );
666        if changed {
667            self.color = Vec4::new(color[0], color[1], color[2], self.color.w);
668        }
669        changed
670    }
671}
672
673impl Quantity for CurveEdgeVectorQuantity {
674    fn as_any(&self) -> &dyn std::any::Any {
675        self
676    }
677
678    fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
679        self
680    }
681
682    fn name(&self) -> &str {
683        &self.name
684    }
685
686    fn structure_name(&self) -> &str {
687        &self.structure_name
688    }
689
690    fn kind(&self) -> QuantityKind {
691        QuantityKind::Vector
692    }
693
694    fn is_enabled(&self) -> bool {
695        self.enabled
696    }
697
698    fn set_enabled(&mut self, enabled: bool) {
699        self.enabled = enabled;
700    }
701
702    fn build_ui(&mut self, _ui: &dyn std::any::Any) {
703        // Implemented via build_egui_ui
704    }
705
706    fn refresh(&mut self) {
707        // GPU resources refreshed externally
708    }
709
710    fn data_size(&self) -> usize {
711        self.vectors.len()
712    }
713}
714
715impl EdgeQuantity for CurveEdgeVectorQuantity {}
716
717#[cfg(test)]
718mod tests {
719    use super::*;
720    use polyscope_core::quantity::QuantityKind;
721
722    #[test]
723    fn test_node_scalar_quantity() {
724        let values = vec![0.0, 0.5, 1.0];
725        let q = CurveNodeScalarQuantity::new("test", "parent", values.clone());
726
727        assert_eq!(q.name(), "test");
728        assert_eq!(q.structure_name(), "parent");
729        assert_eq!(q.values(), &values);
730        assert_eq!(q.range_min(), 0.0);
731        assert_eq!(q.range_max(), 1.0);
732        assert!(!q.is_enabled());
733    }
734
735    #[test]
736    fn test_edge_scalar_quantity() {
737        let values = vec![1.0, 2.0, 3.0];
738        let q = CurveEdgeScalarQuantity::new("edge_scalar", "parent", values.clone());
739
740        assert_eq!(q.name(), "edge_scalar");
741        assert_eq!(q.values(), &values);
742        assert_eq!(q.range_min(), 1.0);
743        assert_eq!(q.range_max(), 3.0);
744    }
745
746    #[test]
747    fn test_node_color_quantity() {
748        let colors = vec![Vec3::X, Vec3::Y, Vec3::Z];
749        let q = CurveNodeColorQuantity::new("colors", "parent", colors);
750
751        assert_eq!(q.name(), "colors");
752        assert_eq!(
753            q.colors(),
754            &[
755                Vec4::new(1.0, 0.0, 0.0, 1.0),
756                Vec4::new(0.0, 1.0, 0.0, 1.0),
757                Vec4::new(0.0, 0.0, 1.0, 1.0)
758            ]
759        );
760        assert_eq!(q.data_size(), 3);
761    }
762
763    #[test]
764    fn test_edge_color_quantity() {
765        let colors = vec![Vec3::ONE, Vec3::ZERO];
766        let q = CurveEdgeColorQuantity::new("edge_colors", "parent", colors);
767
768        assert_eq!(q.name(), "edge_colors");
769        assert_eq!(
770            q.colors(),
771            &[Vec4::new(1.0, 1.0, 1.0, 1.0), Vec4::new(0.0, 0.0, 0.0, 1.0)]
772        );
773        assert_eq!(q.data_size(), 2);
774    }
775
776    #[test]
777    fn test_node_vector_quantity() {
778        let vectors = vec![Vec3::X, Vec3::Y, Vec3::Z];
779        let q = CurveNodeVectorQuantity::new("node_vecs", "parent", vectors.clone());
780
781        assert_eq!(q.name(), "node_vecs");
782        assert_eq!(q.vectors(), &vectors);
783        assert_eq!(q.data_size(), 3);
784        assert_eq!(q.kind(), QuantityKind::Vector);
785        assert!(!q.is_enabled());
786    }
787
788    #[test]
789    fn test_edge_vector_quantity() {
790        let vectors = vec![Vec3::X, Vec3::Y];
791        let q = CurveEdgeVectorQuantity::new("edge_vecs", "parent", vectors.clone());
792
793        assert_eq!(q.name(), "edge_vecs");
794        assert_eq!(q.vectors(), &vectors);
795        assert_eq!(q.data_size(), 2);
796        assert_eq!(q.kind(), QuantityKind::Vector);
797        assert!(!q.is_enabled());
798    }
799}