Skip to main content

oxihuman_morph/
procedural_wrinkle.rs

1// Copyright (C) 2026 COOLJAPAN OU (Team KitaSan)
2// SPDX-License-Identifier: Apache-2.0
3#![allow(dead_code)]
4
5//! Procedural wrinkle generator stub.
6
7/// Wrinkle pattern type.
8#[derive(Debug, Clone, Copy, PartialEq)]
9pub enum WrinklePattern {
10    Linear,
11    Radial,
12    Noise,
13}
14
15/// A single procedural wrinkle region.
16#[derive(Debug, Clone)]
17pub struct WrinkleRegion {
18    pub pattern: WrinklePattern,
19    pub center: [f32; 3],
20    pub radius: f32,
21    pub amplitude: f32,
22    pub frequency: f32,
23    pub driver_weight: f32,
24}
25
26/// Procedural wrinkle generator.
27#[derive(Debug, Clone)]
28pub struct ProceduralWrinkle {
29    pub regions: Vec<WrinkleRegion>,
30    pub vertex_count: usize,
31    pub enabled: bool,
32    pub global_scale: f32,
33}
34
35impl ProceduralWrinkle {
36    pub fn new(vertex_count: usize) -> Self {
37        ProceduralWrinkle {
38            regions: Vec::new(),
39            vertex_count,
40            enabled: true,
41            global_scale: 1.0,
42        }
43    }
44}
45
46/// Create a new procedural wrinkle generator.
47pub fn new_procedural_wrinkle(vertex_count: usize) -> ProceduralWrinkle {
48    ProceduralWrinkle::new(vertex_count)
49}
50
51/// Add a wrinkle region.
52pub fn pw_add_region(pw: &mut ProceduralWrinkle, region: WrinkleRegion) {
53    pw.regions.push(region);
54}
55
56/// Evaluate wrinkle normals/offsets (stub: zeroed).
57pub fn pw_evaluate(pw: &ProceduralWrinkle) -> Vec<[f32; 3]> {
58    /* Stub: returns zeroed offset array */
59    vec![[0.0; 3]; pw.vertex_count]
60}
61
62/// Set global scale.
63pub fn pw_set_global_scale(pw: &mut ProceduralWrinkle, scale: f32) {
64    pw.global_scale = scale.max(0.0);
65}
66
67/// Enable or disable.
68pub fn pw_set_enabled(pw: &mut ProceduralWrinkle, enabled: bool) {
69    pw.enabled = enabled;
70}
71
72/// Return region count.
73pub fn pw_region_count(pw: &ProceduralWrinkle) -> usize {
74    pw.regions.len()
75}
76
77/// Serialize to JSON-like string.
78pub fn pw_to_json(pw: &ProceduralWrinkle) -> String {
79    format!(
80        r#"{{"vertex_count":{},"region_count":{},"global_scale":{},"enabled":{}}}"#,
81        pw.vertex_count,
82        pw.regions.len(),
83        pw.global_scale,
84        pw.enabled
85    )
86}
87
88#[cfg(test)]
89mod tests {
90    use super::*;
91
92    #[test]
93    fn test_new_vertex_count() {
94        let pw = new_procedural_wrinkle(200);
95        assert_eq!(pw.vertex_count, 200 /* vertex count must match */,);
96    }
97
98    #[test]
99    fn test_no_regions_initially() {
100        let pw = new_procedural_wrinkle(100);
101        assert_eq!(pw_region_count(&pw), 0 /* no regions initially */,);
102    }
103
104    #[test]
105    fn test_add_region() {
106        let mut pw = new_procedural_wrinkle(100);
107        pw_add_region(
108            &mut pw,
109            WrinkleRegion {
110                pattern: WrinklePattern::Linear,
111                center: [0.0; 3],
112                radius: 1.0,
113                amplitude: 0.5,
114                frequency: 2.0,
115                driver_weight: 1.0,
116            },
117        );
118        assert_eq!(pw_region_count(&pw), 1 /* one region after add */,);
119    }
120
121    #[test]
122    fn test_evaluate_length() {
123        let pw = new_procedural_wrinkle(50);
124        let out = pw_evaluate(&pw);
125        assert_eq!(
126            out.len(),
127            50, /* output length must match vertex count */
128        );
129    }
130
131    #[test]
132    fn test_evaluate_zeroed() {
133        let pw = new_procedural_wrinkle(4);
134        let out = pw_evaluate(&pw);
135        assert!((out[0][0]).abs() < 1e-6 /* stub must return zeros */,);
136    }
137
138    #[test]
139    fn test_set_global_scale() {
140        let mut pw = new_procedural_wrinkle(10);
141        pw_set_global_scale(&mut pw, 2.5);
142        assert!((pw.global_scale - 2.5).abs() < 1e-5, /* scale must be set */);
143    }
144
145    #[test]
146    fn test_global_scale_clamped_negative() {
147        let mut pw = new_procedural_wrinkle(10);
148        pw_set_global_scale(&mut pw, -1.0);
149        assert!((pw.global_scale).abs() < 1e-6, /* negative scale clamped to 0 */);
150    }
151
152    #[test]
153    fn test_set_enabled() {
154        let mut pw = new_procedural_wrinkle(10);
155        pw_set_enabled(&mut pw, false);
156        assert!(!pw.enabled /* must be disabled */,);
157    }
158
159    #[test]
160    fn test_to_json_contains_vertex_count() {
161        let pw = new_procedural_wrinkle(30);
162        let j = pw_to_json(&pw);
163        assert!(j.contains("\"vertex_count\""), /* json must contain vertex_count */);
164    }
165
166    #[test]
167    fn test_enabled_default() {
168        let pw = new_procedural_wrinkle(1);
169        assert!(pw.enabled /* must be enabled by default */,);
170    }
171}