#[allow(dead_code)]
pub struct BodyShapeConfig {
pub min_height_scale: f32,
pub max_height_scale: f32,
pub min_weight: f32,
pub max_weight: f32,
}
#[allow(dead_code)]
#[derive(Clone)]
pub struct BodyShapeState {
pub height_scale: f32,
pub weight: f32,
pub muscle: f32,
pub fat: f32,
}
#[allow(dead_code)]
pub fn default_body_shape_config() -> BodyShapeConfig {
BodyShapeConfig {
min_height_scale: 0.5,
max_height_scale: 2.0,
min_weight: 0.0,
max_weight: 1.0,
}
}
#[allow(dead_code)]
pub fn new_body_shape_state(cfg: &BodyShapeConfig) -> BodyShapeState {
let height_scale = 1.0f32.clamp(cfg.min_height_scale, cfg.max_height_scale);
BodyShapeState {
height_scale,
weight: 0.5,
muscle: 0.5,
fat: 0.5,
}
}
#[allow(dead_code)]
pub fn set_body_height_scale(state: &mut BodyShapeState, scale: f32) {
state.height_scale = scale.clamp(0.5, 2.0);
}
#[allow(dead_code)]
pub fn set_body_weight(state: &mut BodyShapeState, weight: f32) {
state.weight = weight.clamp(0.0, 1.0);
}
#[allow(dead_code)]
pub fn set_body_muscle(state: &mut BodyShapeState, muscle: f32) {
state.muscle = muscle.clamp(0.0, 1.0);
}
#[allow(dead_code)]
pub fn set_body_fat(state: &mut BodyShapeState, fat: f32) {
state.fat = fat.clamp(0.0, 1.0);
}
#[allow(dead_code)]
pub fn body_shape_morph_weights(state: &BodyShapeState) -> [f32; 4] {
[state.height_scale, state.weight, state.muscle, state.fat]
}
#[allow(dead_code)]
pub fn reset_body_shape(state: &mut BodyShapeState) {
state.height_scale = 1.0;
state.weight = 0.5;
state.muscle = 0.5;
state.fat = 0.5;
}
#[allow(dead_code)]
pub fn blend_body_shapes(a: &BodyShapeState, b: &BodyShapeState, t: f32) -> BodyShapeState {
let t = t.clamp(0.0, 1.0);
BodyShapeState {
height_scale: a.height_scale + (b.height_scale - a.height_scale) * t,
weight: a.weight + (b.weight - a.weight) * t,
muscle: a.muscle + (b.muscle - a.muscle) * t,
fat: a.fat + (b.fat - a.fat) * t,
}
}
#[allow(dead_code)]
pub fn body_bmi_estimate(state: &BodyShapeState) -> f32 {
let numerator = (state.weight + state.fat) * 0.5;
let denom = (state.height_scale * state.height_scale).max(0.01);
(numerator / denom).clamp(0.0, 1.0)
}
#[cfg(test)]
mod tests {
use super::*;
fn make_state() -> BodyShapeState {
let cfg = default_body_shape_config();
new_body_shape_state(&cfg)
}
#[test]
fn test_default_config() {
let cfg = default_body_shape_config();
assert!((cfg.min_height_scale - 0.5).abs() < 1e-6);
assert!((cfg.max_height_scale - 2.0).abs() < 1e-6);
}
#[test]
fn test_new_state_neutral() {
let state = make_state();
assert!((state.height_scale - 1.0).abs() < 1e-6);
assert!((state.weight - 0.5).abs() < 1e-6);
assert!((state.muscle - 0.5).abs() < 1e-6);
assert!((state.fat - 0.5).abs() < 1e-6);
}
#[test]
fn test_set_body_height_scale_clamped() {
let mut state = make_state();
set_body_height_scale(&mut state, 5.0);
assert!((state.height_scale - 2.0).abs() < 1e-6);
set_body_height_scale(&mut state, -1.0);
assert!((state.height_scale - 0.5).abs() < 1e-6);
}
#[test]
fn test_set_body_weight_clamped() {
let mut state = make_state();
set_body_weight(&mut state, 1.5);
assert!((state.weight - 1.0).abs() < 1e-6);
set_body_weight(&mut state, -0.5);
assert!((state.weight - 0.0).abs() < 1e-6);
}
#[test]
fn test_set_body_muscle_and_fat() {
let mut state = make_state();
set_body_muscle(&mut state, 0.8);
set_body_fat(&mut state, 0.2);
assert!((state.muscle - 0.8).abs() < 1e-6);
assert!((state.fat - 0.2).abs() < 1e-6);
}
#[test]
fn test_morph_weights_array() {
let mut state = make_state();
set_body_height_scale(&mut state, 1.2);
set_body_weight(&mut state, 0.7);
set_body_muscle(&mut state, 0.6);
set_body_fat(&mut state, 0.3);
let w = body_shape_morph_weights(&state);
assert!((w[0] - 1.2).abs() < 1e-5);
assert!((w[1] - 0.7).abs() < 1e-5);
assert!((w[2] - 0.6).abs() < 1e-5);
assert!((w[3] - 0.3).abs() < 1e-5);
}
#[test]
fn test_reset_body_shape() {
let mut state = make_state();
set_body_height_scale(&mut state, 1.8);
set_body_weight(&mut state, 0.9);
reset_body_shape(&mut state);
assert!((state.height_scale - 1.0).abs() < 1e-6);
assert!((state.weight - 0.5).abs() < 1e-6);
}
#[test]
fn test_blend_body_shapes_midpoint() {
let mut a = make_state();
let mut b = make_state();
set_body_height_scale(&mut a, 1.0);
set_body_height_scale(&mut b, 2.0);
let mid = blend_body_shapes(&a, &b, 0.5);
assert!((mid.height_scale - 1.5).abs() < 1e-5);
}
#[test]
fn test_blend_body_shapes_t_clamped() {
let a = make_state();
let mut b = make_state();
set_body_weight(&mut b, 1.0);
let blended = blend_body_shapes(&a, &b, 2.0);
assert!((blended.weight - b.weight).abs() < 1e-5);
}
#[test]
fn test_bmi_estimate_in_range() {
let state = make_state();
let bmi = body_bmi_estimate(&state);
assert!(bmi >= 0.0);
assert!(bmi <= 1.0);
}
}