rustial_engine/visualization/
grid_scalar_layer.rs1use std::any::Any;
4
5use super::{ColorRamp, GeoGrid, ScalarField2D};
6use crate::layer::{Layer, LayerId, LayerKind};
7
8#[derive(Debug, Clone)]
18pub struct GridScalarLayer {
19 id: LayerId,
20 name: String,
21 visible: bool,
22 opacity: f32,
23 z_index: i32,
24 pub grid: GeoGrid,
26 pub field: ScalarField2D,
28 pub ramp: ColorRamp,
30}
31
32impl GridScalarLayer {
33 pub fn new(
35 name: impl Into<String>,
36 grid: GeoGrid,
37 field: ScalarField2D,
38 ramp: ColorRamp,
39 ) -> Self {
40 Self {
41 id: LayerId::next(),
42 name: name.into(),
43 visible: true,
44 opacity: 1.0,
45 z_index: 0,
46 grid,
47 field,
48 ramp,
49 }
50 }
51
52 pub fn update_field(&mut self, data: Vec<f32>) {
54 self.field.update_values(data);
55 }
56}
57
58impl Layer for GridScalarLayer {
59 fn id(&self) -> LayerId {
60 self.id
61 }
62
63 fn name(&self) -> &str {
64 &self.name
65 }
66
67 fn kind(&self) -> LayerKind {
68 LayerKind::Visualization
69 }
70
71 fn visible(&self) -> bool {
72 self.visible
73 }
74
75 fn set_visible(&mut self, visible: bool) {
76 self.visible = visible;
77 }
78
79 fn opacity(&self) -> f32 {
80 self.opacity
81 }
82
83 fn set_opacity(&mut self, opacity: f32) {
84 self.opacity = opacity.clamp(0.0, 1.0);
85 }
86
87 fn z_index(&self) -> i32 {
88 self.z_index
89 }
90
91 fn as_any(&self) -> &dyn Any {
92 self
93 }
94
95 fn as_any_mut(&mut self) -> &mut dyn Any {
96 self
97 }
98}
99
100#[cfg(test)]
101mod tests {
102 use super::*;
103 use crate::visualization::ColorStop;
104 use rustial_math::GeoCoord;
105
106 fn test_ramp() -> ColorRamp {
107 ColorRamp::new(vec![
108 ColorStop {
109 value: 0.0,
110 color: [0.0, 0.0, 1.0, 1.0],
111 },
112 ColorStop {
113 value: 1.0,
114 color: [1.0, 0.0, 0.0, 1.0],
115 },
116 ])
117 }
118
119 #[test]
120 fn grid_scalar_layer_basics() {
121 let grid = GeoGrid::new(GeoCoord::from_lat_lon(0.0, 0.0), 4, 4, 100.0, 100.0);
122 let field = ScalarField2D::from_data(4, 4, vec![0.0; 16]);
123 let layer = GridScalarLayer::new("density", grid, field, test_ramp());
124 assert_eq!(layer.name(), "density");
125 assert!(layer.visible());
126 assert!((layer.opacity() - 1.0).abs() < f32::EPSILON);
127 assert_eq!(layer.kind(), LayerKind::Visualization);
128 }
129
130 #[test]
131 fn grid_scalar_layer_id_stable() {
132 let grid = GeoGrid::new(GeoCoord::from_lat_lon(0.0, 0.0), 2, 2, 10.0, 10.0);
133 let field = ScalarField2D::from_data(2, 2, vec![1.0; 4]);
134 let layer = GridScalarLayer::new("test", grid, field, test_ramp());
135 assert_eq!(layer.id(), layer.id());
136 }
137
138 #[test]
139 fn grid_scalar_layer_downcast() {
140 let grid = GeoGrid::new(GeoCoord::from_lat_lon(0.0, 0.0), 2, 2, 10.0, 10.0);
141 let field = ScalarField2D::from_data(2, 2, vec![1.0; 4]);
142 let layer: Box<dyn Layer> =
143 Box::new(GridScalarLayer::new("test", grid, field, test_ramp()));
144 assert!(layer.as_any().downcast_ref::<GridScalarLayer>().is_some());
145 }
146
147 #[test]
148 fn update_field_bumps_value_generation() {
149 let grid = GeoGrid::new(GeoCoord::from_lat_lon(0.0, 0.0), 2, 2, 10.0, 10.0);
150 let field = ScalarField2D::from_data(2, 2, vec![0.0; 4]);
151 let mut layer = GridScalarLayer::new("test", grid, field, test_ramp());
152 assert_eq!(layer.field.value_generation, 0);
153 layer.update_field(vec![1.0; 4]);
154 assert_eq!(layer.field.value_generation, 1);
155 }
156}