Skip to main content

oxihuman_export/
constraint_export.rs

1// Copyright (C) 2026 COOLJAPAN OU (Team KitaSan)
2// SPDX-License-Identifier: Apache-2.0
3#![allow(dead_code)]
4
5//! Constraint export.
6
7/* ── legacy API (kept for backward compat) ── */
8
9#[derive(Debug, Clone, PartialEq)]
10pub enum ConstraintKind {
11    CopyLocation,
12    CopyRotation,
13    CopyScale,
14    LimitRotation,
15    LimitLocation,
16}
17
18#[derive(Debug, Clone)]
19pub struct ConstraintExport {
20    pub name: String,
21    pub kind: ConstraintKind,
22    pub target: String,
23    pub influence: f32,
24}
25
26pub fn new_constraint_export(name: &str, kind: ConstraintKind, target: &str) -> ConstraintExport {
27    ConstraintExport {
28        name: name.to_string(),
29        kind,
30        target: target.to_string(),
31        influence: 1.0,
32    }
33}
34
35pub fn constraint_is_active(constraint: &ConstraintExport) -> bool {
36    constraint.influence > 0.0
37}
38
39pub fn constraint_to_json_legacy(constraint: &ConstraintExport) -> String {
40    format!(
41        "{{\"name\":\"{}\",\"kind\":\"{}\",\"target\":\"{}\",\"influence\":{}}}",
42        constraint.name,
43        constraint_kind_name(constraint),
44        constraint.target,
45        constraint.influence
46    )
47}
48
49pub fn constraint_kind_name(constraint: &ConstraintExport) -> &'static str {
50    match constraint.kind {
51        ConstraintKind::CopyLocation => "CopyLocation",
52        ConstraintKind::CopyRotation => "CopyRotation",
53        ConstraintKind::CopyScale => "CopyScale",
54        ConstraintKind::LimitRotation => "LimitRotation",
55        ConstraintKind::LimitLocation => "LimitLocation",
56    }
57}
58
59pub fn constraint_set_influence(constraint: &mut ConstraintExport, influence: f32) {
60    constraint.influence = influence.clamp(0.0, 1.0);
61}
62
63pub fn constraint_validate(constraint: &ConstraintExport) -> bool {
64    !constraint.name.is_empty()
65        && !constraint.target.is_empty()
66        && (0.0..=1.0).contains(&constraint.influence)
67}
68
69/* ── spec functions (wave 150B) ── */
70
71/// Spec-style constraint data.
72#[derive(Debug, Clone)]
73pub struct ConstraintData {
74    pub name: String,
75    pub constraint_type: String,
76    pub target: String,
77    pub influence: f32,
78    pub active: bool,
79}
80
81/// Create a new `ConstraintData`.
82pub fn new_constraint_data(name: &str, constraint_type: &str, target: &str) -> ConstraintData {
83    ConstraintData {
84        name: name.to_string(),
85        constraint_type: constraint_type.to_string(),
86        target: target.to_string(),
87        influence: 1.0,
88        active: true,
89    }
90}
91
92/// Serialize a `ConstraintData` to JSON.
93pub fn constraint_to_json(c: &ConstraintData) -> String {
94    format!(
95        "{{\"name\":\"{}\",\"type\":\"{}\",\"target\":\"{}\",\"influence\":{},\"active\":{}}}",
96        c.name, c.constraint_type, c.target, c.influence, c.active
97    )
98}
99
100/// Serialize multiple constraints to a JSON array.
101pub fn constraints_to_json(cs: &[ConstraintData]) -> String {
102    let inner: Vec<String> = cs.iter().map(constraint_to_json).collect();
103    format!("[{}]", inner.join(","))
104}
105
106/// Returns `true` if the constraint is active and influence > 0.
107pub fn constraint_is_active_spec(c: &ConstraintData) -> bool {
108    c.active && c.influence > 0.0
109}
110
111/// Count of constraints in a slice.
112pub fn constraint_count(cs: &[ConstraintData]) -> usize {
113    cs.len()
114}
115
116#[cfg(test)]
117mod tests {
118    use super::*;
119
120    #[test]
121    fn test_new_constraint_data() {
122        let c = new_constraint_data("c", "COPY_LOC", "Head");
123        assert_eq!(c.name, "c");
124        assert!(c.active);
125    }
126
127    #[test]
128    fn test_constraint_to_json() {
129        let c = new_constraint_data("c", "COPY_ROT", "Spine");
130        let j = constraint_to_json(&c);
131        assert!(j.contains("COPY_ROT"));
132    }
133
134    #[test]
135    fn test_constraints_to_json() {
136        let cs = vec![
137            new_constraint_data("a", "COPY_LOC", "X"),
138            new_constraint_data("b", "LIMIT_ROT", "Y"),
139        ];
140        let j = constraints_to_json(&cs);
141        assert!(j.starts_with('['));
142    }
143
144    #[test]
145    fn test_constraint_is_active_spec() {
146        let c = new_constraint_data("c", "T", "t");
147        assert!(constraint_is_active_spec(&c));
148    }
149
150    #[test]
151    fn test_constraint_count() {
152        let cs = vec![new_constraint_data("a", "T", "x")];
153        assert_eq!(constraint_count(&cs), 1);
154    }
155}