Skip to main content

oxihuman_morph/
forehead_crease_control.rs

1// Copyright (C) 2026 COOLJAPAN OU (Team KitaSan) / SPDX-License-Identifier: Apache-2.0
2#![allow(dead_code)]
3
4//! Forehead horizontal crease control — depth and count of forehead lines.
5
6/// Config.
7#[allow(dead_code)]
8#[derive(Debug, Clone, PartialEq)]
9pub struct ForeheadCreaseConfig {
10    pub max_depth_m: f32,
11    pub max_lines: u32,
12}
13
14impl Default for ForeheadCreaseConfig {
15    fn default() -> Self {
16        Self {
17            max_depth_m: 0.002,
18            max_lines: 5,
19        }
20    }
21}
22
23/// State.
24#[allow(dead_code)]
25#[derive(Debug, Clone, Default)]
26pub struct ForeheadCreaseState {
27    /// Overall crease depth, 0..=1.
28    pub depth: f32,
29    /// Lateral spread of creases, 0..=1.
30    pub spread: f32,
31    /// Number of lines active (0..=max_lines).
32    pub line_count: u32,
33}
34
35#[allow(dead_code)]
36pub fn new_forehead_crease_state() -> ForeheadCreaseState {
37    ForeheadCreaseState::default()
38}
39
40#[allow(dead_code)]
41pub fn default_forehead_crease_config() -> ForeheadCreaseConfig {
42    ForeheadCreaseConfig::default()
43}
44
45#[allow(dead_code)]
46pub fn fhc_set_depth(state: &mut ForeheadCreaseState, v: f32) {
47    state.depth = v.clamp(0.0, 1.0);
48}
49
50#[allow(dead_code)]
51pub fn fhc_set_spread(state: &mut ForeheadCreaseState, v: f32) {
52    state.spread = v.clamp(0.0, 1.0);
53}
54
55#[allow(dead_code)]
56pub fn fhc_set_lines(state: &mut ForeheadCreaseState, n: u32, cfg: &ForeheadCreaseConfig) {
57    state.line_count = n.min(cfg.max_lines);
58}
59
60#[allow(dead_code)]
61pub fn fhc_reset(state: &mut ForeheadCreaseState) {
62    *state = ForeheadCreaseState::default();
63}
64
65#[allow(dead_code)]
66pub fn fhc_is_neutral(state: &ForeheadCreaseState) -> bool {
67    state.depth < 1e-4 && state.spread < 1e-4 && state.line_count == 0
68}
69
70/// Effective depth in metres.
71#[allow(dead_code)]
72pub fn fhc_effective_depth(state: &ForeheadCreaseState, cfg: &ForeheadCreaseConfig) -> f32 {
73    state.depth * cfg.max_depth_m
74}
75
76/// Wrinkle intensity (depth × spread).
77#[allow(dead_code)]
78pub fn fhc_intensity(state: &ForeheadCreaseState) -> f32 {
79    state.depth * state.spread
80}
81
82#[allow(dead_code)]
83pub fn fhc_blend(a: &ForeheadCreaseState, b: &ForeheadCreaseState, t: f32) -> ForeheadCreaseState {
84    let t = t.clamp(0.0, 1.0);
85    let inv = 1.0 - t;
86    let lines = if t >= 0.5 { b.line_count } else { a.line_count };
87    ForeheadCreaseState {
88        depth: a.depth * inv + b.depth * t,
89        spread: a.spread * inv + b.spread * t,
90        line_count: lines,
91    }
92}
93
94#[allow(dead_code)]
95pub fn fhc_to_json(state: &ForeheadCreaseState) -> String {
96    format!(
97        "{{\"depth\":{:.4},\"spread\":{:.4},\"line_count\":{}}}",
98        state.depth, state.spread, state.line_count
99    )
100}
101
102#[cfg(test)]
103mod tests {
104    use super::*;
105
106    #[test]
107    fn default_neutral() {
108        assert!(fhc_is_neutral(&new_forehead_crease_state()));
109    }
110
111    #[test]
112    fn depth_clamps_high() {
113        let mut s = new_forehead_crease_state();
114        fhc_set_depth(&mut s, 5.0);
115        assert!((s.depth - 1.0).abs() < 1e-6);
116    }
117
118    #[test]
119    fn depth_clamps_low() {
120        let mut s = new_forehead_crease_state();
121        fhc_set_depth(&mut s, -1.0);
122        assert!(s.depth < 1e-6);
123    }
124
125    #[test]
126    fn spread_clamps() {
127        let mut s = new_forehead_crease_state();
128        fhc_set_spread(&mut s, 3.0);
129        assert!((s.spread - 1.0).abs() < 1e-6);
130    }
131
132    #[test]
133    fn line_count_clamps_to_max() {
134        let cfg = default_forehead_crease_config();
135        let mut s = new_forehead_crease_state();
136        fhc_set_lines(&mut s, 100, &cfg);
137        assert!(s.line_count <= cfg.max_lines);
138    }
139
140    #[test]
141    fn reset_clears() {
142        let cfg = default_forehead_crease_config();
143        let mut s = new_forehead_crease_state();
144        fhc_set_depth(&mut s, 0.8);
145        fhc_set_lines(&mut s, 3, &cfg);
146        fhc_reset(&mut s);
147        assert!(fhc_is_neutral(&s));
148    }
149
150    #[test]
151    fn effective_depth_zero_at_neutral() {
152        let cfg = default_forehead_crease_config();
153        let s = new_forehead_crease_state();
154        assert!(fhc_effective_depth(&s, &cfg) < 1e-8);
155    }
156
157    #[test]
158    fn intensity_product_of_depth_and_spread() {
159        let mut s = new_forehead_crease_state();
160        fhc_set_depth(&mut s, 0.5);
161        fhc_set_spread(&mut s, 0.8);
162        assert!((fhc_intensity(&s) - 0.4).abs() < 1e-5);
163    }
164
165    #[test]
166    fn blend_midpoint() {
167        let b = ForeheadCreaseState {
168            depth: 1.0,
169            spread: 1.0,
170            line_count: 4,
171        };
172        let r = fhc_blend(&new_forehead_crease_state(), &b, 0.5);
173        assert!((r.depth - 0.5).abs() < 1e-5);
174    }
175
176    #[test]
177    fn json_has_depth() {
178        let j = fhc_to_json(&new_forehead_crease_state());
179        assert!(j.contains("depth") && j.contains("line_count"));
180    }
181}