oxihuman_export/
camera_path_export_v2.rs1#![allow(dead_code)]
4
5#[allow(dead_code)]
9#[derive(Debug, Clone)]
10pub struct CamPathKeyV2 {
11 pub time: f32,
12 pub position: [f32; 3],
13 pub target: [f32; 3],
14 pub fov_deg: f32,
15}
16
17#[allow(dead_code)]
19#[derive(Debug, Clone)]
20pub struct CameraPathV2 {
21 pub keys: Vec<CamPathKeyV2>,
22 pub name: String,
23}
24
25#[allow(dead_code)]
27pub fn new_camera_path_v2(name: &str) -> CameraPathV2 {
28 CameraPathV2 {
29 keys: Vec::new(),
30 name: name.to_string(),
31 }
32}
33
34#[allow(dead_code)]
36pub fn add_cam_path_key_v2(path: &mut CameraPathV2, key: CamPathKeyV2) {
37 path.keys.push(key);
38 path.keys.sort_by(|a, b| {
39 a.time
40 .partial_cmp(&b.time)
41 .unwrap_or(std::cmp::Ordering::Equal)
42 });
43}
44
45#[allow(dead_code)]
47pub fn cam_path_v2_key_count(path: &CameraPathV2) -> usize {
48 path.keys.len()
49}
50
51#[allow(dead_code)]
53pub fn cam_path_v2_duration(path: &CameraPathV2) -> f32 {
54 if path.keys.is_empty() {
55 return 0.0;
56 }
57 path.keys.last().map_or(0.0, |k| k.time) - path.keys[0].time
58}
59
60#[allow(dead_code)]
62pub fn cam_path_v2_position_at(path: &CameraPathV2, t: f32) -> [f32; 3] {
63 if path.keys.is_empty() {
64 return [0.0; 3];
65 }
66 if path.keys.len() == 1 {
67 return path.keys[0].position;
68 }
69 let idx = path.keys.partition_point(|k| k.time <= t).saturating_sub(1);
70 let i0 = idx.min(path.keys.len() - 1);
71 let i1 = (idx + 1).min(path.keys.len() - 1);
72 if i0 == i1 {
73 return path.keys[i0].position;
74 }
75 let k0 = &path.keys[i0];
76 let k1 = &path.keys[i1];
77 let dt = k1.time - k0.time;
78 let f = if dt > 0.0 {
79 ((t - k0.time) / dt).clamp(0.0, 1.0)
80 } else {
81 0.0
82 };
83 [
84 k0.position[0] + f * (k1.position[0] - k0.position[0]),
85 k0.position[1] + f * (k1.position[1] - k0.position[1]),
86 k0.position[2] + f * (k1.position[2] - k0.position[2]),
87 ]
88}
89
90#[allow(dead_code)]
92pub fn validate_cam_path_v2(path: &CameraPathV2) -> bool {
93 path.keys.windows(2).all(|w| w[1].time > w[0].time) && path.keys.iter().all(|k| k.fov_deg > 0.0)
94}
95
96#[allow(dead_code)]
98pub fn cam_path_v2_to_json(path: &CameraPathV2) -> String {
99 format!(
100 "{{\"name\":\"{}\",\"key_count\":{}}}",
101 path.name,
102 path.keys.len()
103 )
104}
105
106#[cfg(test)]
107mod tests {
108 use super::*;
109
110 fn sample_path() -> CameraPathV2 {
111 let mut p = new_camera_path_v2("main");
112 add_cam_path_key_v2(
113 &mut p,
114 CamPathKeyV2 {
115 time: 0.0,
116 position: [0.0, 0.0, 0.0],
117 target: [0.0, 0.0, 1.0],
118 fov_deg: 60.0,
119 },
120 );
121 add_cam_path_key_v2(
122 &mut p,
123 CamPathKeyV2 {
124 time: 1.0,
125 position: [1.0, 0.0, 0.0],
126 target: [1.0, 0.0, 1.0],
127 fov_deg: 60.0,
128 },
129 );
130 p
131 }
132
133 #[test]
134 fn test_key_count() {
135 let p = sample_path();
136 assert_eq!(cam_path_v2_key_count(&p), 2);
137 }
138
139 #[test]
140 fn test_duration() {
141 let p = sample_path();
142 assert!((cam_path_v2_duration(&p) - 1.0).abs() < 1e-5);
143 }
144
145 #[test]
146 fn test_position_at_start() {
147 let p = sample_path();
148 let pos = cam_path_v2_position_at(&p, 0.0);
149 assert!(pos[0].abs() < 1e-5);
150 }
151
152 #[test]
153 fn test_position_at_end() {
154 let p = sample_path();
155 let pos = cam_path_v2_position_at(&p, 1.0);
156 assert!((pos[0] - 1.0).abs() < 1e-5);
157 }
158
159 #[test]
160 fn test_position_at_mid() {
161 let p = sample_path();
162 let pos = cam_path_v2_position_at(&p, 0.5);
163 assert!((pos[0] - 0.5).abs() < 1e-5);
164 }
165
166 #[test]
167 fn test_validate_valid() {
168 let p = sample_path();
169 assert!(validate_cam_path_v2(&p));
170 }
171
172 #[test]
173 fn test_validate_empty() {
174 let p = new_camera_path_v2("x");
175 assert!(validate_cam_path_v2(&p));
176 }
177
178 #[test]
179 fn test_cam_path_v2_to_json() {
180 let p = sample_path();
181 let j = cam_path_v2_to_json(&p);
182 assert!(j.contains("key_count"));
183 }
184
185 #[test]
186 fn test_empty_path_duration_zero() {
187 let p = new_camera_path_v2("x");
188 assert!(cam_path_v2_duration(&p).abs() < 1e-6);
189 }
190
191 #[test]
192 fn test_empty_path_position_zero() {
193 let p = new_camera_path_v2("x");
194 let pos = cam_path_v2_position_at(&p, 0.5);
195 assert_eq!(pos, [0.0; 3]);
196 }
197}