Skip to main content

oxihuman_morph/
edema_morph.rs

1// Copyright (C) 2026 COOLJAPAN OU (Team KitaSan)
2// SPDX-License-Identifier: Apache-2.0
3#![allow(dead_code)]
4
5//! Edema/swelling morph stub.
6
7/// Edema type classification.
8#[derive(Debug, Clone, Copy, PartialEq)]
9pub enum EdemaType {
10    Pitting,
11    NonPitting,
12    Lymphedema,
13    Angioedema,
14}
15
16/// Edema region entry.
17#[derive(Debug, Clone)]
18pub struct EdemaRegion {
19    pub edema_type: EdemaType,
20    pub position: [f32; 3],
21    pub radius: f32,
22    pub swelling: f32,
23}
24
25/// Edema morph controller.
26#[derive(Debug, Clone)]
27pub struct EdemaMorph {
28    pub regions: Vec<EdemaRegion>,
29    pub global_intensity: f32,
30    pub morph_count: usize,
31    pub enabled: bool,
32}
33
34impl EdemaMorph {
35    pub fn new(morph_count: usize) -> Self {
36        EdemaMorph {
37            regions: Vec::new(),
38            global_intensity: 0.0,
39            morph_count,
40            enabled: true,
41        }
42    }
43}
44
45/// Create a new edema morph.
46pub fn new_edema_morph(morph_count: usize) -> EdemaMorph {
47    EdemaMorph::new(morph_count)
48}
49
50/// Add an edema region.
51pub fn edm_add_region(morph: &mut EdemaMorph, region: EdemaRegion) {
52    morph.regions.push(region);
53}
54
55/// Set global intensity.
56pub fn edm_set_intensity(morph: &mut EdemaMorph, intensity: f32) {
57    morph.global_intensity = intensity.clamp(0.0, 1.0);
58}
59
60/// Clear all regions.
61pub fn edm_clear(morph: &mut EdemaMorph) {
62    morph.regions.clear();
63}
64
65/// Evaluate morph weights (stub: uniform from global_intensity).
66pub fn edm_evaluate(morph: &EdemaMorph) -> Vec<f32> {
67    /* Stub: uniform weight from global_intensity */
68    if !morph.enabled || morph.morph_count == 0 {
69        return vec![];
70    }
71    vec![morph.global_intensity; morph.morph_count]
72}
73
74/// Enable or disable.
75pub fn edm_set_enabled(morph: &mut EdemaMorph, enabled: bool) {
76    morph.enabled = enabled;
77}
78
79/// Return region count.
80pub fn edm_region_count(morph: &EdemaMorph) -> usize {
81    morph.regions.len()
82}
83
84/// Serialize to JSON-like string.
85pub fn edm_to_json(morph: &EdemaMorph) -> String {
86    format!(
87        r#"{{"region_count":{},"global_intensity":{},"morph_count":{},"enabled":{}}}"#,
88        morph.regions.len(),
89        morph.global_intensity,
90        morph.morph_count,
91        morph.enabled
92    )
93}
94
95#[cfg(test)]
96mod tests {
97    use super::*;
98
99    fn make_region() -> EdemaRegion {
100        EdemaRegion {
101            edema_type: EdemaType::Pitting,
102            position: [0.0, 0.0, 0.0],
103            radius: 0.1,
104            swelling: 0.5,
105        }
106    }
107
108    #[test]
109    fn test_initial_empty() {
110        let m = new_edema_morph(4);
111        assert_eq!(edm_region_count(&m), 0 /* no regions initially */);
112    }
113
114    #[test]
115    fn test_add_region() {
116        let mut m = new_edema_morph(4);
117        edm_add_region(&mut m, make_region());
118        assert_eq!(edm_region_count(&m), 1 /* one region after add */);
119    }
120
121    #[test]
122    fn test_clear() {
123        let mut m = new_edema_morph(4);
124        edm_add_region(&mut m, make_region());
125        edm_clear(&mut m);
126        assert_eq!(edm_region_count(&m), 0 /* cleared */);
127    }
128
129    #[test]
130    fn test_intensity_clamp() {
131        let mut m = new_edema_morph(4);
132        edm_set_intensity(&mut m, 1.5);
133        assert!((m.global_intensity - 1.0).abs() < 1e-6 /* clamped to 1.0 */);
134    }
135
136    #[test]
137    fn test_evaluate_length() {
138        let mut m = new_edema_morph(5);
139        edm_set_intensity(&mut m, 0.8);
140        assert_eq!(
141            edm_evaluate(&m).len(),
142            5 /* output must match morph_count */
143        );
144    }
145
146    #[test]
147    fn test_evaluate_disabled() {
148        let mut m = new_edema_morph(4);
149        edm_set_enabled(&mut m, false);
150        assert!(edm_evaluate(&m).is_empty() /* disabled must return empty */);
151    }
152
153    #[test]
154    fn test_to_json_has_region_count() {
155        let m = new_edema_morph(4);
156        let j = edm_to_json(&m);
157        assert!(j.contains("\"region_count\"") /* JSON must have region_count */);
158    }
159
160    #[test]
161    fn test_enabled_default() {
162        let m = new_edema_morph(4);
163        assert!(m.enabled /* must be enabled by default */);
164    }
165
166    #[test]
167    fn test_evaluate_matches_intensity() {
168        let mut m = new_edema_morph(3);
169        edm_set_intensity(&mut m, 0.3);
170        let out = edm_evaluate(&m);
171        assert!((out[0] - 0.3).abs() < 1e-5 /* evaluate must match global_intensity */);
172    }
173}