Skip to main content

oxihuman_morph/
surface_deform.rs

1// Copyright (C) 2026 COOLJAPAN OU (Team KitaSan)
2// SPDX-License-Identifier: Apache-2.0
3#![allow(dead_code)]
4
5//! Surface deform binding stub.
6
7/// A surface deform binding entry for one vertex.
8#[derive(Debug, Clone)]
9pub struct SurfaceDeformBinding {
10    pub face_index: usize,
11    pub barycentric: [f32; 3],
12    pub normal_offset: f32,
13}
14
15/// Surface deform deformer state.
16#[derive(Debug, Clone)]
17pub struct SurfaceDeform {
18    pub bindings: Vec<SurfaceDeformBinding>,
19    pub strength: f32,
20    pub is_bound: bool,
21}
22
23impl SurfaceDeform {
24    pub fn new(vertex_count: usize) -> Self {
25        SurfaceDeform {
26            bindings: (0..vertex_count)
27                .map(|_| SurfaceDeformBinding {
28                    face_index: 0,
29                    barycentric: [1.0 / 3.0; 3],
30                    normal_offset: 0.0,
31                })
32                .collect(),
33            strength: 1.0,
34            is_bound: false,
35        }
36    }
37}
38
39/// Create a new surface deform.
40pub fn new_surface_deform(vertex_count: usize) -> SurfaceDeform {
41    SurfaceDeform::new(vertex_count)
42}
43
44/// Bind the deformer (marks it as ready).
45pub fn surface_deform_bind(sd: &mut SurfaceDeform) {
46    sd.is_bound = true;
47}
48
49/// Unbind the deformer.
50pub fn surface_deform_unbind(sd: &mut SurfaceDeform) {
51    sd.is_bound = false;
52}
53
54/// Set the strength.
55pub fn surface_deform_set_strength(sd: &mut SurfaceDeform, strength: f32) {
56    sd.strength = strength.clamp(0.0, 1.0);
57}
58
59/// Return the vertex count.
60pub fn surface_deform_vertex_count(sd: &SurfaceDeform) -> usize {
61    sd.bindings.len()
62}
63
64/// Return a JSON-like string.
65pub fn surface_deform_to_json(sd: &SurfaceDeform) -> String {
66    format!(
67        r#"{{"vertices":{},"strength":{:.4},"bound":{}}}"#,
68        sd.bindings.len(),
69        sd.strength,
70        sd.is_bound
71    )
72}
73
74/// Compute barycentric weights sum (should be ~1.0 for valid binding).
75pub fn surface_deform_bary_sum(sd: &SurfaceDeform, binding_index: usize) -> f32 {
76    if binding_index < sd.bindings.len() {
77        let b = &sd.bindings[binding_index];
78        b.barycentric[0] + b.barycentric[1] + b.barycentric[2]
79    } else {
80        0.0
81    }
82}
83
84#[cfg(test)]
85mod tests {
86    use super::*;
87
88    #[test]
89    fn test_new_surface_deform_vertex_count() {
90        let sd = new_surface_deform(15);
91        assert_eq!(
92            surface_deform_vertex_count(&sd),
93            15, /* vertex count must match */
94        );
95    }
96
97    #[test]
98    fn test_initial_not_bound() {
99        let sd = new_surface_deform(5);
100        assert!(!sd.is_bound /* should not be bound initially */,);
101    }
102
103    #[test]
104    fn test_bind_sets_is_bound() {
105        let mut sd = new_surface_deform(5);
106        surface_deform_bind(&mut sd);
107        assert!(sd.is_bound /* bind should set is_bound */,);
108    }
109
110    #[test]
111    fn test_unbind_clears_is_bound() {
112        let mut sd = new_surface_deform(5);
113        surface_deform_bind(&mut sd);
114        surface_deform_unbind(&mut sd);
115        assert!(!sd.is_bound /* unbind should clear is_bound */,);
116    }
117
118    #[test]
119    fn test_set_strength_clamps() {
120        let mut sd = new_surface_deform(2);
121        surface_deform_set_strength(&mut sd, 3.0);
122        assert!((sd.strength - 1.0).abs() < 1e-5, /* strength clamped to 1 */);
123    }
124
125    #[test]
126    fn test_set_strength_negative_clamps() {
127        let mut sd = new_surface_deform(2);
128        surface_deform_set_strength(&mut sd, -1.0);
129        assert!((sd.strength).abs() < 1e-6, /* negative strength clamped to 0 */);
130    }
131
132    #[test]
133    fn test_bary_sum_near_one() {
134        let sd = new_surface_deform(3);
135        let s = surface_deform_bary_sum(&sd, 0);
136        assert!((s - 1.0).abs() < 1e-5, /* barycentric weights should sum to 1 */);
137    }
138
139    #[test]
140    fn test_bary_sum_out_of_bounds() {
141        let sd = new_surface_deform(2);
142        let s = surface_deform_bary_sum(&sd, 99);
143        assert!((s).abs() < 1e-6 /* out-of-bounds returns 0 */,);
144    }
145
146    #[test]
147    fn test_to_json_contains_bound() {
148        let sd = new_surface_deform(3);
149        let j = surface_deform_to_json(&sd);
150        assert!(j.contains("bound") /* JSON must contain bound key */,);
151    }
152
153    #[test]
154    fn test_default_strength_one() {
155        let sd = new_surface_deform(1);
156        assert!((sd.strength - 1.0).abs() < 1e-5, /* default strength is 1.0 */);
157    }
158}