Skip to main content

rustial_engine/visualization/
point_cloud_layer.rs

1//! Point-cloud / scatter overlay layer.
2
3use std::any::Any;
4
5use super::{ColorRamp, PointInstanceSet};
6use crate::layer::{Layer, LayerId, LayerKind};
7
8/// A point-cloud / scatter visualization layer.
9#[derive(Debug, Clone)]
10pub struct PointCloudLayer {
11    id: LayerId,
12    name: String,
13    visible: bool,
14    opacity: f32,
15    z_index: i32,
16    /// Point instances.
17    pub points: PointInstanceSet,
18    /// Fallback colour ramp for points without per-instance colour.
19    pub ramp: ColorRamp,
20}
21
22impl PointCloudLayer {
23    /// Create a new point-cloud layer.
24    pub fn new(name: impl Into<String>, points: PointInstanceSet, ramp: ColorRamp) -> Self {
25        Self {
26            id: LayerId::next(),
27            name: name.into(),
28            visible: true,
29            opacity: 1.0,
30            z_index: 0,
31            points,
32            ramp,
33        }
34    }
35
36    /// Replace the point set.
37    pub fn set_points(&mut self, points: PointInstanceSet) {
38        self.points = points;
39    }
40}
41
42impl Layer for PointCloudLayer {
43    fn id(&self) -> LayerId {
44        self.id
45    }
46
47    fn name(&self) -> &str {
48        &self.name
49    }
50
51    fn kind(&self) -> LayerKind {
52        LayerKind::Visualization
53    }
54
55    fn visible(&self) -> bool {
56        self.visible
57    }
58
59    fn set_visible(&mut self, visible: bool) {
60        self.visible = visible;
61    }
62
63    fn opacity(&self) -> f32 {
64        self.opacity
65    }
66
67    fn set_opacity(&mut self, opacity: f32) {
68        self.opacity = opacity.clamp(0.0, 1.0);
69    }
70
71    fn z_index(&self) -> i32 {
72        self.z_index
73    }
74
75    fn as_any(&self) -> &dyn Any {
76        self
77    }
78
79    fn as_any_mut(&mut self) -> &mut dyn Any {
80        self
81    }
82}
83
84#[cfg(test)]
85mod tests {
86    use super::*;
87    use crate::visualization::{ColorStop, PointInstance};
88    use rustial_math::GeoCoord;
89
90    fn test_ramp() -> ColorRamp {
91        ColorRamp::new(vec![
92            ColorStop {
93                value: 0.0,
94                color: [0.0, 0.0, 1.0, 1.0],
95            },
96            ColorStop {
97                value: 1.0,
98                color: [1.0, 0.0, 0.0, 1.0],
99            },
100        ])
101    }
102
103    #[test]
104    fn point_cloud_layer_basics() {
105        let points = PointInstanceSet::new(vec![PointInstance::new(
106            GeoCoord::from_lat_lon(0.0, 0.0),
107            5.0,
108        )
109        .with_pick_id(1)]);
110        let layer = PointCloudLayer::new("points", points, test_ramp());
111        assert_eq!(layer.name(), "points");
112        assert_eq!(layer.points.len(), 1);
113        assert_eq!(layer.points.points[0].pick_id, 1);
114    }
115
116    #[test]
117    fn point_cloud_layer_downcast() {
118        let layer: Box<dyn Layer> = Box::new(PointCloudLayer::new(
119            "test",
120            PointInstanceSet::default(),
121            test_ramp(),
122        ));
123        assert!(layer.as_any().downcast_ref::<PointCloudLayer>().is_some());
124    }
125}