Skip to main content

oxihuman_export/
constraint_target_export.rs

1// Copyright (C) 2026 COOLJAPAN OU (Team KitaSan)
2// SPDX-License-Identifier: Apache-2.0
3#![allow(dead_code)]
4
5//! Constraint target export for bone/object constraints.
6
7/// Constraint target data.
8#[allow(dead_code)]
9#[derive(Debug, Clone)]
10pub struct ConstraintTargetExport {
11    pub name: String,
12    pub target_bone: String,
13    pub influence: f32,
14    pub offset: [f32; 3],
15}
16
17#[allow(dead_code)]
18pub fn new_constraint_target(name: &str, target: &str) -> ConstraintTargetExport {
19    ConstraintTargetExport {
20        name: name.to_string(),
21        target_bone: target.to_string(),
22        influence: 1.0,
23        offset: [0.0; 3],
24    }
25}
26
27#[allow(dead_code)]
28pub fn ct_set_influence(ct: &mut ConstraintTargetExport, v: f32) {
29    ct.influence = v.clamp(0.0, 1.0);
30}
31
32#[allow(dead_code)]
33pub fn ct_set_offset(ct: &mut ConstraintTargetExport, o: [f32; 3]) {
34    ct.offset = o;
35}
36
37#[allow(dead_code)]
38pub fn ct_influence(ct: &ConstraintTargetExport) -> f32 {
39    ct.influence
40}
41
42#[allow(dead_code)]
43pub fn ct_target_name(ct: &ConstraintTargetExport) -> &str {
44    &ct.target_bone
45}
46
47#[allow(dead_code)]
48pub fn ct_offset_magnitude(ct: &ConstraintTargetExport) -> f32 {
49    (ct.offset[0] * ct.offset[0] + ct.offset[1] * ct.offset[1] + ct.offset[2] * ct.offset[2]).sqrt()
50}
51
52#[allow(dead_code)]
53pub fn ct_validate(ct: &ConstraintTargetExport) -> bool {
54    (0.0..=1.0).contains(&ct.influence) && !ct.target_bone.is_empty()
55}
56
57#[allow(dead_code)]
58pub fn constraint_target_to_json(ct: &ConstraintTargetExport) -> String {
59    format!(
60        "{{\"name\":\"{}\",\"target\":\"{}\",\"influence\":{:.6}}}",
61        ct.name, ct.target_bone, ct.influence
62    )
63}
64
65#[cfg(test)]
66mod tests {
67    use super::*;
68
69    #[test]
70    fn test_new() {
71        let ct = new_constraint_target("ik_hand", "hand_bone");
72        assert_eq!(ct_target_name(&ct), "hand_bone");
73    }
74
75    #[test]
76    fn test_default_influence() {
77        let ct = new_constraint_target("a", "b");
78        assert!((ct_influence(&ct) - 1.0).abs() < 1e-6);
79    }
80
81    #[test]
82    fn test_set_influence() {
83        let mut ct = new_constraint_target("a", "b");
84        ct_set_influence(&mut ct, 0.5);
85        assert!((ct_influence(&ct) - 0.5).abs() < 1e-6);
86    }
87
88    #[test]
89    fn test_clamp_influence() {
90        let mut ct = new_constraint_target("a", "b");
91        ct_set_influence(&mut ct, 2.0);
92        assert!((ct_influence(&ct) - 1.0).abs() < 1e-6);
93    }
94
95    #[test]
96    fn test_set_offset() {
97        let mut ct = new_constraint_target("a", "b");
98        ct_set_offset(&mut ct, [1.0, 0.0, 0.0]);
99        assert!((ct_offset_magnitude(&ct) - 1.0).abs() < 1e-6);
100    }
101
102    #[test]
103    fn test_zero_offset() {
104        let ct = new_constraint_target("a", "b");
105        assert!((ct_offset_magnitude(&ct)).abs() < 1e-6);
106    }
107
108    #[test]
109    fn test_validate_ok() {
110        let ct = new_constraint_target("a", "b");
111        assert!(ct_validate(&ct));
112    }
113
114    #[test]
115    fn test_validate_empty_target() {
116        let ct = new_constraint_target("a", "");
117        assert!(!ct_validate(&ct));
118    }
119
120    #[test]
121    fn test_to_json() {
122        let ct = new_constraint_target("ik", "bone");
123        assert!(constraint_target_to_json(&ct).contains("\"target\":\"bone\""));
124    }
125
126    #[test]
127    fn test_clone() {
128        let ct = new_constraint_target("a", "b");
129        let ct2 = ct.clone();
130        assert_eq!(ct2.name, "a");
131    }
132}