Skip to main content

oxihuman_export/
haptic_frame_export.rs

1// Copyright (C) 2026 COOLJAPAN OU (Team KitaSan)
2// SPDX-License-Identifier: Apache-2.0
3#![allow(dead_code)]
4
5/// A single haptic feedback frame.
6#[derive(Clone)]
7pub struct HapticFrame {
8    pub time_s: f32,
9    pub force_n: [f32; 3],
10    pub torque_nm: [f32; 3],
11    pub vibration_hz: f32,
12}
13
14pub fn new_haptic_frame(t: f32, force: [f32; 3], vibration_hz: f32) -> HapticFrame {
15    HapticFrame {
16        time_s: t,
17        force_n: force,
18        torque_nm: [0.0; 3],
19        vibration_hz,
20    }
21}
22
23pub fn haptic_frame_to_bytes(frame: &HapticFrame) -> Vec<u8> {
24    let mut out = Vec::with_capacity(7 * 4);
25    out.extend_from_slice(&frame.time_s.to_le_bytes());
26    for &v in &frame.force_n {
27        out.extend_from_slice(&v.to_le_bytes());
28    }
29    for &v in &frame.torque_nm {
30        out.extend_from_slice(&v.to_le_bytes());
31    }
32    out.extend_from_slice(&frame.vibration_hz.to_le_bytes());
33    out
34}
35
36pub fn haptic_sequence_to_bytes(frames: &[HapticFrame]) -> Vec<u8> {
37    let mut out = Vec::with_capacity(frames.len() * 7 * 4);
38    for frame in frames {
39        out.extend(haptic_frame_to_bytes(frame));
40    }
41    out
42}
43
44pub fn haptic_max_force(frames: &[HapticFrame]) -> f32 {
45    frames
46        .iter()
47        .map(|f| {
48            let v = f.force_n;
49            (v[0] * v[0] + v[1] * v[1] + v[2] * v[2]).sqrt()
50        })
51        .fold(0.0f32, f32::max)
52}
53
54pub fn haptic_frame_duration(frames: &[HapticFrame]) -> f32 {
55    if frames.len() < 2 {
56        return 0.0;
57    }
58    frames.last().map_or(0.0, |f| f.time_s) - frames.first().map_or(0.0, |f| f.time_s)
59}
60
61pub fn haptic_frame_count(frames: &[HapticFrame]) -> usize {
62    frames.len()
63}
64
65#[cfg(test)]
66mod tests {
67    use super::*;
68
69    #[test]
70    fn test_new_haptic_frame() {
71        let f = new_haptic_frame(0.0, [1.0, 0.0, 0.0], 200.0);
72        assert!((f.force_n[0] - 1.0).abs() < 1e-5);
73        assert!((f.vibration_hz - 200.0).abs() < 1e-4);
74    }
75
76    #[test]
77    fn test_haptic_frame_to_bytes_len() {
78        /* time_s(4) + force_n(12) + torque_nm(12) + vibration_hz(4) = 32 */
79        let f = new_haptic_frame(0.0, [0.0; 3], 0.0);
80        assert_eq!(haptic_frame_to_bytes(&f).len(), 32);
81    }
82
83    #[test]
84    fn test_haptic_sequence_to_bytes_len() {
85        /* 2 frames * 32 bytes = 64 */
86        let frames = vec![
87            new_haptic_frame(0.0, [0.0; 3], 100.0),
88            new_haptic_frame(1.0, [0.0; 3], 100.0),
89        ];
90        assert_eq!(haptic_sequence_to_bytes(&frames).len(), 64);
91    }
92
93    #[test]
94    fn test_haptic_max_force() {
95        let frames = vec![
96            new_haptic_frame(0.0, [3.0, 4.0, 0.0], 100.0),
97            new_haptic_frame(1.0, [1.0, 0.0, 0.0], 100.0),
98        ];
99        assert!((haptic_max_force(&frames) - 5.0).abs() < 1e-4);
100    }
101
102    #[test]
103    fn test_haptic_frame_duration() {
104        let frames = vec![
105            new_haptic_frame(0.1, [0.0; 3], 100.0),
106            new_haptic_frame(2.1, [0.0; 3], 100.0),
107        ];
108        assert!((haptic_frame_duration(&frames) - 2.0).abs() < 1e-4);
109    }
110
111    #[test]
112    fn test_haptic_frame_count() {
113        let frames = vec![new_haptic_frame(0.0, [0.0; 3], 100.0); 5];
114        assert_eq!(haptic_frame_count(&frames), 5);
115    }
116
117    #[test]
118    fn test_haptic_frame_duration_single() {
119        let frames = vec![new_haptic_frame(1.0, [0.0; 3], 100.0)];
120        assert!((haptic_frame_duration(&frames) - 0.0).abs() < 1e-6);
121    }
122}