Skip to main content

oxihuman_morph/
acne_morph.rs

1// Copyright (C) 2026 COOLJAPAN OU (Team KitaSan)
2// SPDX-License-Identifier: Apache-2.0
3#![allow(dead_code)]
4
5//! Acne lesion surface morph stub.
6
7/// Acne lesion type.
8#[derive(Debug, Clone, Copy, PartialEq)]
9pub enum AcneLesionType {
10    Comedone,
11    Papule,
12    Pustule,
13    Nodule,
14    Cyst,
15}
16
17/// An acne lesion entry.
18#[derive(Debug, Clone)]
19pub struct AcneLesion {
20    pub lesion_type: AcneLesionType,
21    pub position: [f32; 3],
22    pub size: f32,
23    pub inflammation: f32,
24}
25
26/// Acne morph controller.
27#[derive(Debug, Clone)]
28pub struct AcneMorph {
29    pub lesions: Vec<AcneLesion>,
30    pub severity: f32,
31    pub morph_count: usize,
32    pub enabled: bool,
33}
34
35impl AcneMorph {
36    pub fn new(morph_count: usize) -> Self {
37        AcneMorph {
38            lesions: Vec::new(),
39            severity: 0.0,
40            morph_count,
41            enabled: true,
42        }
43    }
44}
45
46/// Create a new acne morph.
47pub fn new_acne_morph(morph_count: usize) -> AcneMorph {
48    AcneMorph::new(morph_count)
49}
50
51/// Add a lesion.
52pub fn acm_add_lesion(morph: &mut AcneMorph, lesion: AcneLesion) {
53    morph.lesions.push(lesion);
54}
55
56/// Set overall severity.
57pub fn acm_set_severity(morph: &mut AcneMorph, severity: f32) {
58    morph.severity = severity.clamp(0.0, 1.0);
59}
60
61/// Clear all lesions.
62pub fn acm_clear(morph: &mut AcneMorph) {
63    morph.lesions.clear();
64}
65
66/// Evaluate morph weights (stub: uniform from severity).
67pub fn acm_evaluate(morph: &AcneMorph) -> Vec<f32> {
68    /* Stub: uniform weight from severity */
69    if !morph.enabled || morph.morph_count == 0 {
70        return vec![];
71    }
72    vec![morph.severity; morph.morph_count]
73}
74
75/// Enable or disable.
76pub fn acm_set_enabled(morph: &mut AcneMorph, enabled: bool) {
77    morph.enabled = enabled;
78}
79
80/// Return lesion count.
81pub fn acm_lesion_count(morph: &AcneMorph) -> usize {
82    morph.lesions.len()
83}
84
85/// Serialize to JSON-like string.
86pub fn acm_to_json(morph: &AcneMorph) -> String {
87    format!(
88        r#"{{"lesion_count":{},"severity":{},"morph_count":{},"enabled":{}}}"#,
89        morph.lesions.len(),
90        morph.severity,
91        morph.morph_count,
92        morph.enabled
93    )
94}
95
96#[cfg(test)]
97mod tests {
98    use super::*;
99
100    fn make_lesion() -> AcneLesion {
101        AcneLesion {
102            lesion_type: AcneLesionType::Papule,
103            position: [0.0, 0.1, 0.0],
104            size: 0.01,
105            inflammation: 0.7,
106        }
107    }
108
109    #[test]
110    fn test_initial_empty() {
111        let m = new_acne_morph(4);
112        assert_eq!(acm_lesion_count(&m), 0 /* no lesions initially */);
113    }
114
115    #[test]
116    fn test_add_lesion() {
117        let mut m = new_acne_morph(4);
118        acm_add_lesion(&mut m, make_lesion());
119        assert_eq!(acm_lesion_count(&m), 1 /* one lesion after add */);
120    }
121
122    #[test]
123    fn test_clear() {
124        let mut m = new_acne_morph(4);
125        acm_add_lesion(&mut m, make_lesion());
126        acm_clear(&mut m);
127        assert_eq!(acm_lesion_count(&m), 0 /* cleared */);
128    }
129
130    #[test]
131    fn test_severity_clamp() {
132        let mut m = new_acne_morph(4);
133        acm_set_severity(&mut m, 1.5);
134        assert!((m.severity - 1.0).abs() < 1e-6 /* clamped to 1.0 */);
135    }
136
137    #[test]
138    fn test_evaluate_length() {
139        let mut m = new_acne_morph(6);
140        acm_set_severity(&mut m, 0.5);
141        assert_eq!(
142            acm_evaluate(&m).len(),
143            6 /* output must match morph_count */
144        );
145    }
146
147    #[test]
148    fn test_evaluate_disabled() {
149        let mut m = new_acne_morph(4);
150        acm_set_enabled(&mut m, false);
151        assert!(acm_evaluate(&m).is_empty() /* disabled must return empty */);
152    }
153
154    #[test]
155    fn test_to_json_has_lesion_count() {
156        let m = new_acne_morph(4);
157        let j = acm_to_json(&m);
158        assert!(j.contains("\"lesion_count\"") /* JSON must have lesion_count */);
159    }
160
161    #[test]
162    fn test_enabled_default() {
163        let m = new_acne_morph(4);
164        assert!(m.enabled /* must be enabled by default */);
165    }
166
167    #[test]
168    fn test_evaluate_matches_severity() {
169        let mut m = new_acne_morph(3);
170        acm_set_severity(&mut m, 0.4);
171        let out = acm_evaluate(&m);
172        assert!((out[0] - 0.4).abs() < 1e-5 /* evaluate must match severity */);
173    }
174
175    #[test]
176    fn test_lesion_type_variant() {
177        let l = make_lesion();
178        assert_eq!(
179            l.lesion_type,
180            AcneLesionType::Papule /* lesion type must be Papule */
181        );
182    }
183}