Skip to main content

oxihuman_morph/
mesh_deform_morph.rs

1// Copyright (C) 2026 COOLJAPAN OU (Team KitaSan)
2// SPDX-License-Identifier: Apache-2.0
3#![allow(dead_code)]
4
5//! Mesh deform cage morph target stub.
6
7/// A mesh deform binding for one driven vertex.
8#[derive(Debug, Clone)]
9pub struct MeshDeformBinding {
10    pub cage_vertex_indices: [usize; 4],
11    pub weights: [f32; 4],
12}
13
14impl Default for MeshDeformBinding {
15    fn default() -> Self {
16        MeshDeformBinding {
17            cage_vertex_indices: [0, 1, 2, 3],
18            weights: [0.25; 4],
19        }
20    }
21}
22
23/// Mesh deform morph state.
24#[derive(Debug, Clone)]
25pub struct MeshDeformMorph {
26    pub bindings: Vec<MeshDeformBinding>,
27    pub blend_weight: f32,
28    pub bound: bool,
29}
30
31impl MeshDeformMorph {
32    pub fn new(vertex_count: usize) -> Self {
33        MeshDeformMorph {
34            bindings: (0..vertex_count)
35                .map(|_| MeshDeformBinding::default())
36                .collect(),
37            blend_weight: 0.0,
38            bound: false,
39        }
40    }
41}
42
43/// Create a new mesh deform morph.
44pub fn new_mesh_deform_morph(vertex_count: usize) -> MeshDeformMorph {
45    MeshDeformMorph::new(vertex_count)
46}
47
48/// Set the blend weight.
49pub fn mdm_set_weight(mdm: &mut MeshDeformMorph, weight: f32) {
50    mdm.blend_weight = weight.clamp(0.0, 1.0);
51}
52
53/// Bind the deformer.
54pub fn mdm_bind(mdm: &mut MeshDeformMorph) {
55    mdm.bound = true;
56}
57
58/// Unbind the deformer.
59pub fn mdm_unbind(mdm: &mut MeshDeformMorph) {
60    mdm.bound = false;
61}
62
63/// Return vertex count.
64pub fn mdm_vertex_count(mdm: &MeshDeformMorph) -> usize {
65    mdm.bindings.len()
66}
67
68/// Return a JSON-like string.
69pub fn mdm_to_json(mdm: &MeshDeformMorph) -> String {
70    format!(
71        r#"{{"vertices":{},"weight":{:.4},"bound":{}}}"#,
72        mdm.bindings.len(),
73        mdm.blend_weight,
74        mdm.bound
75    )
76}
77
78/// Check that all binding weights sum to approximately 1.0.
79pub fn mdm_validate_weights(mdm: &MeshDeformMorph) -> bool {
80    mdm.bindings
81        .iter()
82        .all(|b| (b.weights.iter().sum::<f32>() - 1.0).abs() < 1e-4)
83}
84
85#[cfg(test)]
86mod tests {
87    use super::*;
88
89    #[test]
90    fn test_new_mdm_vertex_count() {
91        let m = new_mesh_deform_morph(8);
92        assert_eq!(mdm_vertex_count(&m), 8 /* vertex count must match */,);
93    }
94
95    #[test]
96    fn test_initial_weight_zero() {
97        let m = new_mesh_deform_morph(3);
98        assert!((m.blend_weight).abs() < 1e-6, /* initial weight should be 0 */);
99    }
100
101    #[test]
102    fn test_set_weight_clamps() {
103        let mut m = new_mesh_deform_morph(2);
104        mdm_set_weight(&mut m, 5.0);
105        assert!((m.blend_weight - 1.0).abs() < 1e-5, /* weight clamped to 1 */);
106    }
107
108    #[test]
109    fn test_bind_sets_bound() {
110        let mut m = new_mesh_deform_morph(2);
111        mdm_bind(&mut m);
112        assert!(m.bound /* bind should set bound flag */,);
113    }
114
115    #[test]
116    fn test_unbind_clears_bound() {
117        let mut m = new_mesh_deform_morph(2);
118        mdm_bind(&mut m);
119        mdm_unbind(&mut m);
120        assert!(!m.bound /* unbind should clear bound flag */,);
121    }
122
123    #[test]
124    fn test_initial_weights_valid() {
125        let m = new_mesh_deform_morph(4);
126        assert!(mdm_validate_weights(&m), /* default weights should sum to 1 */);
127    }
128
129    #[test]
130    fn test_to_json_contains_weight() {
131        let m = new_mesh_deform_morph(3);
132        let j = mdm_to_json(&m);
133        assert!(j.contains("weight") /* JSON must contain weight */,);
134    }
135
136    #[test]
137    fn test_to_json_contains_bound() {
138        let m = new_mesh_deform_morph(3);
139        let j = mdm_to_json(&m);
140        assert!(j.contains("bound") /* JSON must contain bound */,);
141    }
142
143    #[test]
144    fn test_set_weight_negative_clamps() {
145        let mut m = new_mesh_deform_morph(2);
146        mdm_set_weight(&mut m, -1.0);
147        assert!((m.blend_weight).abs() < 1e-6, /* negative weight clamped to 0 */);
148    }
149
150    #[test]
151    fn test_initial_not_bound() {
152        let m = new_mesh_deform_morph(5);
153        assert!(!m.bound /* should not be bound initially */,);
154    }
155}