oxihuman_morph/
dental_morph.rs1#![allow(dead_code)]
4
5#[derive(Debug, Clone, Copy, PartialEq)]
9pub enum DentalAlignment {
10 Perfect,
11 SlightOverjet,
12 Crowded,
13 Gapped,
14 Underbite,
15 Overbite,
16}
17
18#[derive(Debug, Clone)]
20pub struct DentalMorph {
21 pub alignment: DentalAlignment,
22 pub tooth_size: f32,
23 pub gum_exposure: f32,
24 pub whitening: f32,
25 pub morph_count: usize,
26 pub enabled: bool,
27}
28
29impl DentalMorph {
30 pub fn new(morph_count: usize) -> Self {
31 DentalMorph {
32 alignment: DentalAlignment::Perfect,
33 tooth_size: 1.0,
34 gum_exposure: 0.3,
35 whitening: 0.8,
36 morph_count,
37 enabled: true,
38 }
39 }
40}
41
42pub fn new_dental_morph(morph_count: usize) -> DentalMorph {
44 DentalMorph::new(morph_count)
45}
46
47pub fn dm_set_alignment(morph: &mut DentalMorph, alignment: DentalAlignment) {
49 morph.alignment = alignment;
50}
51
52pub fn dm_set_tooth_size(morph: &mut DentalMorph, size: f32) {
54 morph.tooth_size = size.clamp(0.5, 2.0);
55}
56
57pub fn dm_set_gum_exposure(morph: &mut DentalMorph, exposure: f32) {
59 morph.gum_exposure = exposure.clamp(0.0, 1.0);
60}
61
62pub fn dm_set_whitening(morph: &mut DentalMorph, whitening: f32) {
64 morph.whitening = whitening.clamp(0.0, 1.0);
65}
66
67pub fn dm_evaluate(morph: &DentalMorph) -> Vec<f32> {
69 if !morph.enabled || morph.morph_count == 0 {
71 return vec![];
72 }
73 let w = ((morph.tooth_size - 0.5) / 1.5) * (1.0 - morph.gum_exposure);
74 vec![w.clamp(0.0, 1.0); morph.morph_count]
75}
76
77pub fn dm_set_enabled(morph: &mut DentalMorph, enabled: bool) {
79 morph.enabled = enabled;
80}
81
82pub fn dm_to_json(morph: &DentalMorph) -> String {
84 let align = match morph.alignment {
85 DentalAlignment::Perfect => "perfect",
86 DentalAlignment::SlightOverjet => "slight_overjet",
87 DentalAlignment::Crowded => "crowded",
88 DentalAlignment::Gapped => "gapped",
89 DentalAlignment::Underbite => "underbite",
90 DentalAlignment::Overbite => "overbite",
91 };
92 format!(
93 r#"{{"alignment":"{}","tooth_size":{},"gum_exposure":{},"enabled":{}}}"#,
94 align, morph.tooth_size, morph.gum_exposure, morph.enabled
95 )
96}
97
98#[cfg(test)]
99mod tests {
100 use super::*;
101
102 #[test]
103 fn test_default_alignment() {
104 let m = new_dental_morph(4);
105 assert_eq!(
106 m.alignment,
107 DentalAlignment::Perfect );
109 }
110
111 #[test]
112 fn test_set_alignment() {
113 let mut m = new_dental_morph(4);
114 dm_set_alignment(&mut m, DentalAlignment::Crowded);
115 assert_eq!(
116 m.alignment,
117 DentalAlignment::Crowded );
119 }
120
121 #[test]
122 fn test_tooth_size_clamped() {
123 let mut m = new_dental_morph(4);
124 dm_set_tooth_size(&mut m, 5.0);
125 assert!((m.tooth_size - 2.0).abs() < 1e-6 );
126 }
127
128 #[test]
129 fn test_gum_exposure_clamped() {
130 let mut m = new_dental_morph(4);
131 dm_set_gum_exposure(&mut m, -0.5);
132 assert!((m.gum_exposure).abs() < 1e-6 );
133 }
134
135 #[test]
136 fn test_whitening_clamped() {
137 let mut m = new_dental_morph(4);
138 dm_set_whitening(&mut m, 1.5);
139 assert!((m.whitening - 1.0).abs() < 1e-6 );
140 }
141
142 #[test]
143 fn test_evaluate_length() {
144 let m = new_dental_morph(5);
145 assert_eq!(
146 dm_evaluate(&m).len(),
147 5 );
149 }
150
151 #[test]
152 fn test_evaluate_disabled() {
153 let mut m = new_dental_morph(4);
154 dm_set_enabled(&mut m, false);
155 assert!(dm_evaluate(&m).is_empty() );
156 }
157
158 #[test]
159 fn test_to_json_has_alignment() {
160 let m = new_dental_morph(4);
161 let j = dm_to_json(&m);
162 assert!(j.contains("\"alignment\"") );
163 }
164
165 #[test]
166 fn test_enabled_default() {
167 let m = new_dental_morph(4);
168 assert!(m.enabled );
169 }
170}