oxihuman_morph/
hip_width_control.rs1#![allow(dead_code)]
7
8use std::f32::consts::PI;
9
10#[allow(dead_code)]
12#[derive(Debug, Clone)]
13pub struct HipWidth {
14 pub width: f32,
15 pub depth: f32,
16 pub height: f32,
17}
18
19#[allow(dead_code)]
21pub fn default_hip_width() -> HipWidth {
22 HipWidth {
23 width: 0.5,
24 depth: 0.5,
25 height: 0.5,
26 }
27}
28
29#[allow(dead_code)]
32pub fn apply_hip_width(weights: &mut [f32], hw: &HipWidth) {
33 if !weights.is_empty() {
34 weights[0] = hw.width;
35 }
36 if weights.len() > 1 {
37 weights[1] = hw.depth;
38 }
39 if weights.len() > 2 {
40 weights[2] = hw.height;
41 }
42}
43
44#[allow(dead_code)]
46pub fn hip_width_blend(a: &HipWidth, b: &HipWidth, t: f32) -> HipWidth {
47 let t = t.clamp(0.0, 1.0);
48 HipWidth {
49 width: a.width + (b.width - a.width) * t,
50 depth: a.depth + (b.depth - a.depth) * t,
51 height: a.height + (b.height - a.height) * t,
52 }
53}
54
55#[allow(dead_code)]
57pub fn hip_ratio(hw: &HipWidth) -> f32 {
58 if hw.depth.abs() < f32::EPSILON {
59 return 1.0;
60 }
61 hw.width / hw.depth
62}
63
64#[allow(dead_code)]
66pub fn hip_circumference_approx(hw: &HipWidth, scale_m: f32) -> f32 {
67 let a = hw.width * scale_m;
68 let b = hw.depth * scale_m;
69 let h = (a - b).powi(2) / ((a + b).powi(2) + f32::EPSILON);
70 PI * (a + b) * (1.0 + (3.0 * h) / (10.0 + (4.0 - 3.0 * h).sqrt()))
71}
72
73#[cfg(test)]
74mod tests {
75 use super::*;
76
77 #[test]
78 fn test_default_hip_width() {
79 let hw = default_hip_width();
80 assert!((hw.width - 0.5).abs() < 1e-6);
81 assert!((hw.depth - 0.5).abs() < 1e-6);
82 assert!((hw.height - 0.5).abs() < 1e-6);
83 }
84
85 #[test]
86 fn test_apply_hip_width_full() {
87 let hw = HipWidth {
88 width: 0.6,
89 depth: 0.4,
90 height: 0.7,
91 };
92 let mut w = [0.0f32; 3];
93 apply_hip_width(&mut w, &hw);
94 assert!((w[0] - 0.6).abs() < 1e-6);
95 assert!((w[1] - 0.4).abs() < 1e-6);
96 assert!((w[2] - 0.7).abs() < 1e-6);
97 }
98
99 #[test]
100 fn test_apply_hip_width_short_slice() {
101 let hw = default_hip_width();
102 let mut w = [0.0f32; 1];
103 apply_hip_width(&mut w, &hw);
104 assert!((w[0] - 0.5).abs() < 1e-6);
105 }
106
107 #[test]
108 fn test_apply_hip_width_empty() {
109 let hw = default_hip_width();
110 let mut w: [f32; 0] = [];
111 apply_hip_width(&mut w, &hw); }
113
114 #[test]
115 fn test_blend_at_zero() {
116 let a = default_hip_width();
117 let b = HipWidth {
118 width: 1.0,
119 depth: 1.0,
120 height: 1.0,
121 };
122 let r = hip_width_blend(&a, &b, 0.0);
123 assert!((r.width - 0.5).abs() < 1e-6);
124 }
125
126 #[test]
127 fn test_blend_at_one() {
128 let a = default_hip_width();
129 let b = HipWidth {
130 width: 1.0,
131 depth: 1.0,
132 height: 1.0,
133 };
134 let r = hip_width_blend(&a, &b, 1.0);
135 assert!((r.width - 1.0).abs() < 1e-6);
136 }
137
138 #[test]
139 fn test_blend_clamps_t() {
140 let a = default_hip_width();
141 let b = HipWidth {
142 width: 1.0,
143 depth: 0.8,
144 height: 0.9,
145 };
146 let r = hip_width_blend(&a, &b, -1.0);
147 assert!((r.width - a.width).abs() < 1e-6);
148 }
149
150 #[test]
151 fn test_hip_ratio_equal_axes() {
152 let hw = HipWidth {
153 width: 0.5,
154 depth: 0.5,
155 height: 0.5,
156 };
157 assert!((hip_ratio(&hw) - 1.0).abs() < 1e-6);
158 }
159
160 #[test]
161 fn test_hip_ratio_wide() {
162 let hw = HipWidth {
163 width: 1.0,
164 depth: 0.5,
165 height: 0.5,
166 };
167 assert!((hip_ratio(&hw) - 2.0).abs() < 1e-6);
168 }
169
170 #[test]
171 fn test_hip_circumference_positive() {
172 let hw = default_hip_width();
173 let c = hip_circumference_approx(&hw, 0.5);
174 assert!(c > 0.0);
175 }
176}