Skip to main content

elara_visual/
state.rs

1//! Visual State - The core representation of visual reality
2//!
3//! This is NOT a video frame. This is the STATE of what is visually happening.
4
5use elara_core::{DegradationLevel, NodeId, StateId, StateTime};
6
7use crate::{FaceState, PoseState, SceneState};
8
9/// Visual state identifier
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
11pub struct VisualStateId(pub u64);
12
13impl VisualStateId {
14    pub fn new(id: u64) -> Self {
15        Self(id)
16    }
17}
18
19pub const STATE_TYPE_VISUAL: u16 = 0x0020;
20pub const STATE_TYPE_LIVESTREAM: u16 = 0x0021;
21
22pub fn visual_state_id(node_id: NodeId) -> StateId {
23    StateId::from_type_instance(STATE_TYPE_VISUAL, node_id.0)
24}
25
26pub fn stream_visual_state_id(stream_id: u64) -> StateId {
27    StateId::from_type_instance(STATE_TYPE_VISUAL, stream_id)
28}
29
30pub fn livestream_state_id(stream_id: u64) -> StateId {
31    StateId::from_type_instance(STATE_TYPE_LIVESTREAM, stream_id)
32}
33
34/// The complete visual state of a node
35///
36/// This captures WHAT is visually happening, not HOW it looks pixel-by-pixel.
37/// The renderer reconstructs visuals from this state.
38#[derive(Debug, Clone)]
39pub struct VisualState {
40    /// Unique identifier for this visual state
41    pub id: VisualStateId,
42
43    /// The node this visual state belongs to
44    pub source: NodeId,
45
46    /// Timestamp of this state
47    pub timestamp: StateTime,
48
49    /// Face state (expression, gaze, landmarks)
50    pub face: Option<FaceState>,
51
52    /// Body pose state (skeleton, gestures)
53    pub pose: Option<PoseState>,
54
55    /// Scene/environment state
56    pub scene: Option<SceneState>,
57
58    /// Current degradation level
59    pub degradation: DegradationLevel,
60
61    /// Is this a keyframe (full state) or delta (changes only)?
62    pub is_keyframe: bool,
63
64    /// Reference to previous keyframe if this is a delta
65    pub keyframe_ref: Option<VisualStateId>,
66
67    /// Sequence number for ordering
68    pub sequence: u64,
69}
70
71impl VisualState {
72    /// Create a new keyframe visual state
73    pub fn keyframe(source: NodeId, timestamp: StateTime, sequence: u64) -> Self {
74        Self {
75            id: VisualStateId::new(sequence),
76            source,
77            timestamp,
78            face: None,
79            pose: None,
80            scene: None,
81            degradation: DegradationLevel::L0_FullPerception,
82            is_keyframe: true,
83            keyframe_ref: None,
84            sequence,
85        }
86    }
87
88    /// Create a delta state referencing a keyframe
89    pub fn delta(
90        source: NodeId,
91        timestamp: StateTime,
92        sequence: u64,
93        keyframe: VisualStateId,
94    ) -> Self {
95        Self {
96            id: VisualStateId::new(sequence),
97            source,
98            timestamp,
99            face: None,
100            pose: None,
101            scene: None,
102            degradation: DegradationLevel::L0_FullPerception,
103            is_keyframe: false,
104            keyframe_ref: Some(keyframe),
105            sequence,
106        }
107    }
108
109    /// Set face state
110    pub fn with_face(mut self, face: FaceState) -> Self {
111        self.face = Some(face);
112        self
113    }
114
115    /// Set pose state
116    pub fn with_pose(mut self, pose: PoseState) -> Self {
117        self.pose = Some(pose);
118        self
119    }
120
121    /// Set scene state
122    pub fn with_scene(mut self, scene: SceneState) -> Self {
123        self.scene = Some(scene);
124        self
125    }
126
127    /// Set degradation level
128    pub fn with_degradation(mut self, level: DegradationLevel) -> Self {
129        self.degradation = level;
130        self
131    }
132
133    /// Get the visual complexity score (0.0 - 1.0)
134    /// Higher = more data to transmit
135    pub fn complexity(&self) -> f32 {
136        let mut score: f32 = 0.0;
137
138        if self.face.is_some() {
139            score += 0.3;
140        }
141        if self.pose.is_some() {
142            score += 0.3;
143        }
144        if self.scene.is_some() {
145            score += 0.4;
146        }
147
148        if self.is_keyframe {
149            score *= 1.5; // Keyframes are larger
150        }
151
152        score.min(1.0)
153    }
154
155    /// Degrade this visual state to a lower level
156    /// Returns a simplified version appropriate for the degradation level
157    pub fn degrade(&self, target: DegradationLevel) -> VisualState {
158        let mut degraded = self.clone();
159        degraded.degradation = target;
160
161        match target {
162            DegradationLevel::L0_FullPerception => {
163                // Keep everything
164            }
165            DegradationLevel::L1_DistortedPerception => {
166                // Reduce scene detail
167                if let Some(ref mut scene) = degraded.scene {
168                    scene.reduce_detail(0.7);
169                }
170            }
171            DegradationLevel::L2_FragmentedPerception => {
172                // Remove scene, keep face and pose
173                degraded.scene = None;
174            }
175            DegradationLevel::L3_SymbolicPresence => {
176                // Keep only face (for avatar)
177                degraded.scene = None;
178                degraded.pose = None;
179            }
180            DegradationLevel::L4_MinimalPresence => {
181                // Minimal face only
182                degraded.scene = None;
183                degraded.pose = None;
184                if let Some(ref mut face) = degraded.face {
185                    face.reduce_to_minimal();
186                }
187            }
188            DegradationLevel::L5_LatentPresence => {
189                // Just presence indicator
190                degraded.scene = None;
191                degraded.pose = None;
192                degraded.face = degraded.face.map(|f| f.to_latent());
193            }
194        }
195
196        degraded
197    }
198}
199
200/// Visual state for livestream (asymmetric authority)
201#[derive(Debug, Clone)]
202pub struct LivestreamState {
203    /// The broadcaster (authority)
204    pub broadcaster: NodeId,
205
206    /// Current visual state from broadcaster
207    pub visual: VisualState,
208
209    /// Number of active viewers
210    pub viewer_count: u32,
211
212    /// Is the stream live?
213    pub is_live: bool,
214
215    /// Stream title/description
216    pub title: String,
217
218    /// Stream start time
219    pub started_at: StateTime,
220}
221
222impl LivestreamState {
223    /// Create a new livestream
224    pub fn new(broadcaster: NodeId, title: String, started_at: StateTime) -> Self {
225        Self {
226            broadcaster,
227            visual: VisualState::keyframe(broadcaster, started_at, 0),
228            viewer_count: 0,
229            is_live: true,
230            title,
231            started_at,
232        }
233    }
234
235    /// Update visual state (only broadcaster can do this)
236    pub fn update_visual(&mut self, visual: VisualState) -> Result<(), &'static str> {
237        if visual.source != self.broadcaster {
238            return Err("Only broadcaster can update visual state");
239        }
240        self.visual = visual;
241        Ok(())
242    }
243
244    /// Add a viewer
245    pub fn add_viewer(&mut self) {
246        self.viewer_count += 1;
247    }
248
249    /// Remove a viewer
250    pub fn remove_viewer(&mut self) {
251        self.viewer_count = self.viewer_count.saturating_sub(1);
252    }
253
254    /// End the stream
255    pub fn end_stream(&mut self) {
256        self.is_live = false;
257    }
258}
259
260#[cfg(test)]
261mod tests {
262    use super::*;
263
264    #[test]
265    fn test_visual_state_keyframe() {
266        let node = NodeId::new(1);
267        let time = StateTime::from_millis(1000);
268        let state = VisualState::keyframe(node, time, 1);
269
270        assert!(state.is_keyframe);
271        assert!(state.keyframe_ref.is_none());
272        assert_eq!(state.sequence, 1);
273    }
274
275    #[test]
276    fn test_visual_state_delta() {
277        let node = NodeId::new(1);
278        let time = StateTime::from_millis(1000);
279        let keyframe_id = VisualStateId::new(1);
280        let state = VisualState::delta(node, time, 2, keyframe_id);
281
282        assert!(!state.is_keyframe);
283        assert_eq!(state.keyframe_ref, Some(keyframe_id));
284        assert_eq!(state.sequence, 2);
285    }
286
287    #[test]
288    fn test_visual_state_degradation() {
289        let node = NodeId::new(1);
290        let time = StateTime::from_millis(1000);
291        let state = VisualState::keyframe(node, time, 1);
292
293        let degraded = state.degrade(DegradationLevel::L3_SymbolicPresence);
294        assert_eq!(degraded.degradation, DegradationLevel::L3_SymbolicPresence);
295        assert!(degraded.scene.is_none());
296        assert!(degraded.pose.is_none());
297    }
298
299    #[test]
300    fn test_livestream_state() {
301        let broadcaster = NodeId::new(1);
302        let time = StateTime::from_millis(0);
303        let mut stream = LivestreamState::new(broadcaster, "Test Stream".to_string(), time);
304
305        assert!(stream.is_live);
306        assert_eq!(stream.viewer_count, 0);
307
308        stream.add_viewer();
309        stream.add_viewer();
310        assert_eq!(stream.viewer_count, 2);
311
312        stream.remove_viewer();
313        assert_eq!(stream.viewer_count, 1);
314
315        stream.end_stream();
316        assert!(!stream.is_live);
317    }
318}