Skip to main content

oxihuman_morph/
erythema_morph.rs

1// Copyright (C) 2026 COOLJAPAN OU (Team KitaSan)
2// SPDX-License-Identifier: Apache-2.0
3#![allow(dead_code)]
4
5//! Erythema (redness) morph stub.
6
7/// Erythema pattern type.
8#[derive(Debug, Clone, Copy, PartialEq)]
9pub enum ErythemaPattern {
10    Diffuse,
11    Macular,
12    Papular,
13    Reticular,
14}
15
16/// Erythema morph controller.
17#[derive(Debug, Clone)]
18pub struct ErythemaMorph {
19    pub pattern: ErythemaPattern,
20    pub intensity: f32,
21    pub affected_area: f32,
22    pub morph_count: usize,
23    pub enabled: bool,
24}
25
26impl ErythemaMorph {
27    pub fn new(morph_count: usize) -> Self {
28        ErythemaMorph {
29            pattern: ErythemaPattern::Diffuse,
30            intensity: 0.0,
31            affected_area: 0.5,
32            morph_count,
33            enabled: true,
34        }
35    }
36}
37
38/// Create a new erythema morph.
39pub fn new_erythema_morph(morph_count: usize) -> ErythemaMorph {
40    ErythemaMorph::new(morph_count)
41}
42
43/// Set erythema pattern.
44pub fn erm_set_pattern(morph: &mut ErythemaMorph, pattern: ErythemaPattern) {
45    morph.pattern = pattern;
46}
47
48/// Set redness intensity.
49pub fn erm_set_intensity(morph: &mut ErythemaMorph, intensity: f32) {
50    morph.intensity = intensity.clamp(0.0, 1.0);
51}
52
53/// Set affected area fraction.
54pub fn erm_set_affected_area(morph: &mut ErythemaMorph, area: f32) {
55    morph.affected_area = area.clamp(0.0, 1.0);
56}
57
58/// Evaluate morph weights (stub: uniform from intensity).
59pub fn erm_evaluate(morph: &ErythemaMorph) -> Vec<f32> {
60    /* Stub: uniform weight from intensity */
61    if !morph.enabled || morph.morph_count == 0 {
62        return vec![];
63    }
64    vec![morph.intensity; morph.morph_count]
65}
66
67/// Enable or disable.
68pub fn erm_set_enabled(morph: &mut ErythemaMorph, enabled: bool) {
69    morph.enabled = enabled;
70}
71
72/// Serialize to JSON-like string.
73pub fn erm_to_json(morph: &ErythemaMorph) -> String {
74    let pat = match morph.pattern {
75        ErythemaPattern::Diffuse => "diffuse",
76        ErythemaPattern::Macular => "macular",
77        ErythemaPattern::Papular => "papular",
78        ErythemaPattern::Reticular => "reticular",
79    };
80    format!(
81        r#"{{"pattern":"{}","intensity":{},"affected_area":{},"morph_count":{},"enabled":{}}}"#,
82        pat, morph.intensity, morph.affected_area, morph.morph_count, morph.enabled
83    )
84}
85
86#[cfg(test)]
87mod tests {
88    use super::*;
89
90    #[test]
91    fn test_default_pattern() {
92        let m = new_erythema_morph(4);
93        assert_eq!(
94            m.pattern,
95            ErythemaPattern::Diffuse /* default pattern must be Diffuse */
96        );
97    }
98
99    #[test]
100    fn test_set_pattern() {
101        let mut m = new_erythema_morph(4);
102        erm_set_pattern(&mut m, ErythemaPattern::Macular);
103        assert_eq!(
104            m.pattern,
105            ErythemaPattern::Macular /* pattern must be set */
106        );
107    }
108
109    #[test]
110    fn test_intensity_clamp() {
111        let mut m = new_erythema_morph(4);
112        erm_set_intensity(&mut m, 1.5);
113        assert!((m.intensity - 1.0).abs() < 1e-6 /* clamped to 1.0 */);
114    }
115
116    #[test]
117    fn test_area_clamp() {
118        let mut m = new_erythema_morph(4);
119        erm_set_affected_area(&mut m, -0.2);
120        assert!(m.affected_area.abs() < 1e-6 /* clamped to 0.0 */);
121    }
122
123    #[test]
124    fn test_evaluate_length() {
125        let mut m = new_erythema_morph(7);
126        erm_set_intensity(&mut m, 0.5);
127        assert_eq!(
128            erm_evaluate(&m).len(),
129            7 /* output must match morph_count */
130        );
131    }
132
133    #[test]
134    fn test_evaluate_disabled() {
135        let mut m = new_erythema_morph(4);
136        erm_set_enabled(&mut m, false);
137        assert!(erm_evaluate(&m).is_empty() /* disabled must return empty */);
138    }
139
140    #[test]
141    fn test_to_json_has_pattern() {
142        let m = new_erythema_morph(4);
143        let j = erm_to_json(&m);
144        assert!(j.contains("\"pattern\"") /* JSON must have pattern */);
145    }
146
147    #[test]
148    fn test_enabled_default() {
149        let m = new_erythema_morph(4);
150        assert!(m.enabled /* must be enabled by default */);
151    }
152
153    #[test]
154    fn test_evaluate_matches_intensity() {
155        let mut m = new_erythema_morph(4);
156        erm_set_intensity(&mut m, 0.7);
157        let out = erm_evaluate(&m);
158        assert!((out[0] - 0.7).abs() < 1e-5 /* evaluate must match intensity */);
159    }
160}