Skip to main content

oxihuman_export/
bone_pose_export.rs

1#![allow(dead_code)]
2// Copyright (C) 2026 COOLJAPAN OU (Team KitaSan)
3// SPDX-License-Identifier: Apache-2.0
4
5//! Export a single pose (bone transforms).
6
7#[allow(dead_code)]
8#[derive(Debug, Clone)]
9pub struct BoneTransform {
10    pub bone_id: u32,
11    pub name: String,
12    pub position: [f32; 3],
13    pub rotation: [f32; 4],
14    pub scale: [f32; 3],
15}
16
17#[allow(dead_code)]
18#[derive(Debug, Clone)]
19pub struct PoseExport {
20    pub name: String,
21    pub bones: Vec<BoneTransform>,
22}
23
24#[allow(dead_code)]
25pub fn new_pose_export(name: &str) -> PoseExport {
26    PoseExport {
27        name: name.to_string(),
28        bones: Vec::new(),
29    }
30}
31
32#[allow(dead_code)]
33pub fn add_bone_transform(
34    p: &mut PoseExport,
35    id: u32,
36    name: &str,
37    pos: [f32; 3],
38    rot: [f32; 4],
39    scale: [f32; 3],
40) {
41    p.bones.push(BoneTransform {
42        bone_id: id,
43        name: name.to_string(),
44        position: pos,
45        rotation: rot,
46        scale,
47    });
48}
49
50#[allow(dead_code)]
51pub fn export_pose_to_json(p: &PoseExport) -> String {
52    let mut bones_json = String::new();
53    for (i, b) in p.bones.iter().enumerate() {
54        if i > 0 {
55            bones_json.push(',');
56        }
57        bones_json.push_str(&format!(
58            r#"{{"id":{},"name":"{}","position":[{},{},{}],"rotation":[{},{},{},{}],"scale":[{},{},{}]}}"#,
59            b.bone_id, b.name,
60            b.position[0], b.position[1], b.position[2],
61            b.rotation[0], b.rotation[1], b.rotation[2], b.rotation[3],
62            b.scale[0], b.scale[1], b.scale[2],
63        ));
64    }
65    format!(r#"{{"name":"{}","bones":[{}]}}"#, p.name, bones_json)
66}
67
68#[allow(dead_code)]
69pub fn bone_count(p: &PoseExport) -> usize {
70    p.bones.len()
71}
72
73#[cfg(test)]
74mod tests {
75    use super::*;
76
77    #[test]
78    fn new_pose_export_empty() {
79        let p = new_pose_export("idle");
80        assert_eq!(p.name, "idle");
81        assert_eq!(bone_count(&p), 0);
82    }
83
84    #[test]
85    fn add_bone_increases_count() {
86        let mut p = new_pose_export("run");
87        add_bone_transform(&mut p, 0, "root", [0.0; 3], [0.0, 0.0, 0.0, 1.0], [1.0; 3]);
88        assert_eq!(bone_count(&p), 1);
89    }
90
91    #[test]
92    fn bone_transform_fields() {
93        let mut p = new_pose_export("test");
94        add_bone_transform(
95            &mut p,
96            5,
97            "spine",
98            [1.0, 2.0, 3.0],
99            [0.0, 0.0, 0.0, 1.0],
100            [1.0; 3],
101        );
102        assert_eq!(p.bones[0].bone_id, 5);
103        assert_eq!(p.bones[0].name, "spine");
104    }
105
106    #[test]
107    fn export_pose_to_json_contains_name() {
108        let p = new_pose_export("walk");
109        let j = export_pose_to_json(&p);
110        assert!(j.contains("walk"));
111    }
112
113    #[test]
114    fn export_pose_to_json_bones_array() {
115        let mut p = new_pose_export("t");
116        add_bone_transform(&mut p, 0, "b0", [0.0; 3], [0.0, 0.0, 0.0, 1.0], [1.0; 3]);
117        let j = export_pose_to_json(&p);
118        assert!(j.contains("bones"));
119        assert!(j.contains("b0"));
120    }
121
122    #[test]
123    fn multiple_bones() {
124        let mut p = new_pose_export("t");
125        for i in 0..5 {
126            add_bone_transform(&mut p, i, "b", [0.0; 3], [0.0, 0.0, 0.0, 1.0], [1.0; 3]);
127        }
128        assert_eq!(bone_count(&p), 5);
129    }
130
131    #[test]
132    fn export_json_has_position() {
133        let mut p = new_pose_export("t");
134        add_bone_transform(
135            &mut p,
136            0,
137            "b",
138            [1.0, 2.0, 3.0],
139            [0.0, 0.0, 0.0, 1.0],
140            [1.0; 3],
141        );
142        let j = export_pose_to_json(&p);
143        assert!(j.contains("position"));
144    }
145
146    #[test]
147    fn export_json_has_rotation() {
148        let mut p = new_pose_export("t");
149        add_bone_transform(&mut p, 0, "b", [0.0; 3], [0.1, 0.2, 0.3, 0.9], [1.0; 3]);
150        let j = export_pose_to_json(&p);
151        assert!(j.contains("rotation"));
152    }
153
154    #[test]
155    fn export_json_has_scale() {
156        let mut p = new_pose_export("t");
157        add_bone_transform(
158            &mut p,
159            0,
160            "b",
161            [0.0; 3],
162            [0.0, 0.0, 0.0, 1.0],
163            [2.0, 2.0, 2.0],
164        );
165        let j = export_pose_to_json(&p);
166        assert!(j.contains("scale"));
167    }
168}