Skip to main content

oxihuman_export/
driver_export.rs

1// Copyright (C) 2026 COOLJAPAN OU (Team KitaSan)
2// SPDX-License-Identifier: Apache-2.0
3#![allow(dead_code)]
4
5//! Driver/expression export (animation drivers).
6
7/* ── legacy API (kept for backward compat) ── */
8
9#[derive(Debug, Clone, PartialEq)]
10pub enum DriverType {
11    Linear,
12    Polynomial,
13    Scripted,
14}
15
16#[derive(Debug, Clone)]
17pub struct DriverExport {
18    pub name: String,
19    pub target_prop: String,
20    pub driver_type: DriverType,
21    pub coefficients: Vec<f32>,
22}
23
24pub fn new_driver_export(name: &str, target_prop: &str, driver_type: DriverType) -> DriverExport {
25    DriverExport {
26        name: name.to_string(),
27        target_prop: target_prop.to_string(),
28        driver_type,
29        coefficients: Vec::new(),
30    }
31}
32
33pub fn driver_evaluate(driver: &DriverExport, input: f32) -> f32 {
34    match driver.driver_type {
35        DriverType::Scripted => 0.0,
36        DriverType::Linear => {
37            let c0 = driver.coefficients.first().copied().unwrap_or(0.0);
38            let c1 = driver.coefficients.get(1).copied().unwrap_or(1.0);
39            c0 + c1 * input
40        }
41        DriverType::Polynomial => driver
42            .coefficients
43            .iter()
44            .enumerate()
45            .fold(0.0f32, |acc, (i, &c)| acc + c * input.powi(i as i32)),
46    }
47}
48
49pub fn driver_add_coefficient(driver: &mut DriverExport, coeff: f32) {
50    driver.coefficients.push(coeff);
51}
52
53pub fn driver_coefficient_count(driver: &DriverExport) -> usize {
54    driver.coefficients.len()
55}
56
57pub fn driver_to_json_legacy(driver: &DriverExport) -> String {
58    format!(
59        "{{\"name\":\"{}\",\"type\":\"{}\",\"coefficients\":{}}}",
60        driver.name,
61        driver_type_name(driver),
62        driver.coefficients.len()
63    )
64}
65
66pub fn driver_validate(driver: &DriverExport) -> bool {
67    !driver.name.is_empty() && !driver.target_prop.is_empty()
68}
69
70pub fn driver_type_name(driver: &DriverExport) -> &'static str {
71    match driver.driver_type {
72        DriverType::Linear => "Linear",
73        DriverType::Polynomial => "Polynomial",
74        DriverType::Scripted => "Scripted",
75    }
76}
77
78/* ── spec functions (wave 150B) ── */
79
80/// Spec-style driver data.
81#[derive(Debug, Clone)]
82pub struct DriverData {
83    pub name: String,
84    pub expression: String,
85    pub target_prop: String,
86    pub variables: Vec<String>,
87}
88
89/// Create a new `DriverData`.
90pub fn new_driver_data(name: &str, expression: &str, target_prop: &str) -> DriverData {
91    DriverData {
92        name: name.to_string(),
93        expression: expression.to_string(),
94        target_prop: target_prop.to_string(),
95        variables: Vec::new(),
96    }
97}
98
99/// Push a variable name.
100pub fn driver_push_variable(d: &mut DriverData, var: &str) {
101    d.variables.push(var.to_string());
102}
103
104/// Serialize to JSON.
105pub fn driver_to_json(d: &DriverData) -> String {
106    format!(
107        "{{\"name\":\"{}\",\"expression\":\"{}\",\"target\":\"{}\",\"vars\":{}}}",
108        d.name,
109        d.expression,
110        d.target_prop,
111        d.variables.len()
112    )
113}
114
115/// Number of variables.
116pub fn driver_variable_count(d: &DriverData) -> usize {
117    d.variables.len()
118}
119
120/// Returns true if the expression is non-empty.
121pub fn driver_has_expression(d: &DriverData) -> bool {
122    !d.expression.is_empty()
123}
124
125#[cfg(test)]
126mod tests {
127    use super::*;
128
129    #[test]
130    fn test_new_driver_data() {
131        let d = new_driver_data("drv", "x*2", "loc.x");
132        assert_eq!(d.name, "drv");
133    }
134
135    #[test]
136    fn test_driver_push_variable() {
137        let mut d = new_driver_data("d", "e", "p");
138        driver_push_variable(&mut d, "var1");
139        assert_eq!(driver_variable_count(&d), 1);
140    }
141
142    #[test]
143    fn test_driver_to_json() {
144        let d = new_driver_data("d", "x+1", "rot.y");
145        let j = driver_to_json(&d);
146        assert!(j.contains("x+1"));
147    }
148
149    #[test]
150    fn test_driver_has_expression() {
151        let d = new_driver_data("d", "x", "p");
152        assert!(driver_has_expression(&d));
153    }
154
155    #[test]
156    fn test_driver_no_expression() {
157        let d = new_driver_data("d", "", "p");
158        assert!(!driver_has_expression(&d));
159    }
160}