Skip to main content

oxihuman_morph/
skin_subsurface_morph.rs

1// Copyright (C) 2026 COOLJAPAN OU (Team KitaSan)
2// SPDX-License-Identifier: Apache-2.0
3#![allow(dead_code)]
4
5//! Subsurface scattering depth morph control.
6
7/// Skin subsurface morph configuration.
8#[derive(Debug, Clone)]
9pub struct SkinSubsurfaceMorph {
10    pub scatter_depth: f32,
11    pub red_depth: f32,
12    pub green_depth: f32,
13    pub blue_depth: f32,
14}
15
16impl SkinSubsurfaceMorph {
17    pub fn new() -> Self {
18        Self {
19            scatter_depth: 0.5,
20            red_depth: 0.8,
21            green_depth: 0.4,
22            blue_depth: 0.2,
23        }
24    }
25}
26
27impl Default for SkinSubsurfaceMorph {
28    fn default() -> Self {
29        Self::new()
30    }
31}
32
33/// Create a new skin subsurface morph.
34pub fn new_skin_subsurface_morph() -> SkinSubsurfaceMorph {
35    SkinSubsurfaceMorph::new()
36}
37
38/// Set global scatter depth.
39pub fn skin_sss_set_depth(morph: &mut SkinSubsurfaceMorph, depth: f32) {
40    morph.scatter_depth = depth.clamp(0.0, 1.0);
41}
42
43/// Set per-channel depths for spectral scattering.
44pub fn skin_sss_set_rgb_depths(morph: &mut SkinSubsurfaceMorph, r: f32, g: f32, b: f32) {
45    morph.red_depth = r.clamp(0.0, 1.0);
46    morph.green_depth = g.clamp(0.0, 1.0);
47    morph.blue_depth = b.clamp(0.0, 1.0);
48}
49
50/// Set red channel depth.
51pub fn skin_sss_set_red_depth(morph: &mut SkinSubsurfaceMorph, depth: f32) {
52    morph.red_depth = depth.clamp(0.0, 1.0);
53}
54
55/// Compute mean chromatic depth across channels.
56pub fn skin_sss_mean_depth(morph: &SkinSubsurfaceMorph) -> f32 {
57    (morph.red_depth + morph.green_depth + morph.blue_depth) / 3.0
58}
59
60/// Serialize to JSON-like string.
61pub fn skin_subsurface_morph_to_json(morph: &SkinSubsurfaceMorph) -> String {
62    format!(
63        r#"{{"scatter_depth":{:.4},"red_depth":{:.4},"green_depth":{:.4},"blue_depth":{:.4}}}"#,
64        morph.scatter_depth, morph.red_depth, morph.green_depth, morph.blue_depth
65    )
66}
67
68#[cfg(test)]
69mod tests {
70    use super::*;
71
72    #[test]
73    fn test_defaults() {
74        let m = new_skin_subsurface_morph();
75        assert!((m.scatter_depth - 0.5).abs() < 1e-6);
76    }
77
78    #[test]
79    fn test_depth_clamp() {
80        let mut m = new_skin_subsurface_morph();
81        skin_sss_set_depth(&mut m, 2.0);
82        assert_eq!(m.scatter_depth, 1.0);
83    }
84
85    #[test]
86    fn test_rgb_depths_set() {
87        let mut m = new_skin_subsurface_morph();
88        skin_sss_set_rgb_depths(&mut m, 0.9, 0.5, 0.1);
89        assert!((m.red_depth - 0.9).abs() < 1e-6);
90        assert!((m.blue_depth - 0.1).abs() < 1e-6);
91    }
92
93    #[test]
94    fn test_red_depth_clamp() {
95        let mut m = new_skin_subsurface_morph();
96        skin_sss_set_red_depth(&mut m, -1.0);
97        assert_eq!(m.red_depth, 0.0);
98    }
99
100    #[test]
101    fn test_mean_depth() {
102        let mut m = new_skin_subsurface_morph();
103        skin_sss_set_rgb_depths(&mut m, 0.9, 0.6, 0.3);
104        let mean = skin_sss_mean_depth(&m);
105        assert!((mean - 0.6).abs() < 1e-5);
106    }
107
108    #[test]
109    fn test_json() {
110        let m = new_skin_subsurface_morph();
111        let s = skin_subsurface_morph_to_json(&m);
112        assert!(s.contains("scatter_depth"));
113        assert!(s.contains("red_depth"));
114    }
115
116    #[test]
117    fn test_clone() {
118        let m = new_skin_subsurface_morph();
119        let m2 = m.clone();
120        assert!((m2.blue_depth - m.blue_depth).abs() < 1e-6);
121    }
122
123    #[test]
124    fn test_default_trait() {
125        let m: SkinSubsurfaceMorph = Default::default();
126        assert!((m.green_depth - 0.4).abs() < 1e-6);
127    }
128
129    #[test]
130    fn test_json_blue() {
131        let m = new_skin_subsurface_morph();
132        let s = skin_subsurface_morph_to_json(&m);
133        assert!(s.contains("blue_depth"));
134    }
135}