Skip to main content

oxihuman_export/
anim_notify_export.rs

1// Copyright (C) 2026 COOLJAPAN OU (Team KitaSan)
2// SPDX-License-Identifier: Apache-2.0
3#![allow(dead_code)]
4
5//! Animation notification event export.
6
7#[allow(dead_code)]
8#[derive(Debug, Clone)]
9pub struct AnimNotify {
10    pub name: String,
11    pub time: f32,
12    pub payload: String,
13}
14
15#[allow(dead_code)]
16#[derive(Debug, Clone)]
17pub struct AnimNotifyTrack {
18    pub track_name: String,
19    pub notifies: Vec<AnimNotify>,
20}
21
22#[allow(dead_code)]
23pub fn new_notify_track(name: &str) -> AnimNotifyTrack {
24    AnimNotifyTrack {
25        track_name: name.to_string(),
26        notifies: Vec::new(),
27    }
28}
29
30#[allow(dead_code)]
31pub fn add_notify(track: &mut AnimNotifyTrack, name: &str, time: f32, payload: &str) {
32    track.notifies.push(AnimNotify {
33        name: name.to_string(),
34        time,
35        payload: payload.to_string(),
36    });
37}
38
39#[allow(dead_code)]
40pub fn notify_count(track: &AnimNotifyTrack) -> usize {
41    track.notifies.len()
42}
43
44#[allow(dead_code)]
45pub fn find_notify<'a>(track: &'a AnimNotifyTrack, name: &str) -> Option<&'a AnimNotify> {
46    track.notifies.iter().find(|n| n.name == name)
47}
48
49#[allow(dead_code)]
50pub fn notifies_in_range(track: &AnimNotifyTrack, start: f32, end: f32) -> Vec<&AnimNotify> {
51    track
52        .notifies
53        .iter()
54        .filter(|n| n.time >= start && n.time <= end)
55        .collect()
56}
57
58#[allow(dead_code)]
59pub fn track_duration(track: &AnimNotifyTrack) -> f32 {
60    track
61        .notifies
62        .iter()
63        .map(|n| n.time)
64        .fold(0.0_f32, f32::max)
65}
66
67#[allow(dead_code)]
68pub fn sort_notifies(track: &mut AnimNotifyTrack) {
69    track.notifies.sort_by(|a, b| {
70        a.time
71            .partial_cmp(&b.time)
72            .unwrap_or(std::cmp::Ordering::Equal)
73    });
74}
75
76#[allow(dead_code)]
77pub fn notify_track_to_json(track: &AnimNotifyTrack) -> String {
78    format!(
79        "{{\"track\":\"{}\",\"notify_count\":{}}}",
80        track.track_name,
81        track.notifies.len()
82    )
83}
84
85#[allow(dead_code)]
86pub fn clear_notifies(track: &mut AnimNotifyTrack) {
87    track.notifies.clear();
88}
89
90#[cfg(test)]
91mod tests {
92    use super::*;
93
94    #[test]
95    fn test_new_track_empty() {
96        let t = new_notify_track("events");
97        assert_eq!(notify_count(&t), 0);
98    }
99
100    #[test]
101    fn test_add_notify() {
102        let mut t = new_notify_track("events");
103        add_notify(&mut t, "hit", 0.5, "{}");
104        assert_eq!(notify_count(&t), 1);
105    }
106
107    #[test]
108    fn test_find_notify() {
109        let mut t = new_notify_track("events");
110        add_notify(&mut t, "footstep", 0.3, "left");
111        assert!(find_notify(&t, "footstep").is_some());
112    }
113
114    #[test]
115    fn test_find_missing() {
116        let t = new_notify_track("events");
117        assert!(find_notify(&t, "missing").is_none());
118    }
119
120    #[test]
121    fn test_notifies_in_range() {
122        let mut t = new_notify_track("events");
123        add_notify(&mut t, "a", 0.1, "");
124        add_notify(&mut t, "b", 0.5, "");
125        add_notify(&mut t, "c", 0.9, "");
126        let r = notifies_in_range(&t, 0.0, 0.6);
127        assert_eq!(r.len(), 2);
128    }
129
130    #[test]
131    fn test_track_duration() {
132        let mut t = new_notify_track("events");
133        add_notify(&mut t, "end", 2.0, "");
134        assert!((track_duration(&t) - 2.0).abs() < 1e-5);
135    }
136
137    #[test]
138    fn test_sort_notifies() {
139        let mut t = new_notify_track("events");
140        add_notify(&mut t, "b", 0.5, "");
141        add_notify(&mut t, "a", 0.1, "");
142        sort_notifies(&mut t);
143        assert!((t.notifies[0].time - 0.1).abs() < 1e-5);
144    }
145
146    #[test]
147    fn test_json_output() {
148        let t = new_notify_track("events");
149        let j = notify_track_to_json(&t);
150        assert!(j.contains("events"));
151    }
152
153    #[test]
154    fn test_clear_notifies() {
155        let mut t = new_notify_track("events");
156        add_notify(&mut t, "x", 0.0, "");
157        clear_notifies(&mut t);
158        assert_eq!(notify_count(&t), 0);
159    }
160
161    #[test]
162    fn test_payload_stored() {
163        let mut t = new_notify_track("events");
164        add_notify(&mut t, "pickup", 1.0, "gold");
165        let n = find_notify(&t, "pickup").expect("should succeed");
166        assert_eq!(n.payload, "gold".to_string());
167    }
168}