#![allow(dead_code)]
use std::f32::consts::PI;
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct BodyVolumeConfig {
pub min_scale: f32,
pub max_scale: f32,
pub exponent: f32,
}
impl Default for BodyVolumeConfig {
fn default() -> Self {
BodyVolumeConfig {
min_scale: 0.5,
max_scale: 2.0,
exponent: 1.0,
}
}
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct BodyVolumeState {
volume: f32,
chest: f32,
abdomen: f32,
config: BodyVolumeConfig,
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct BodyVolumeWeights {
pub overall: f32,
pub chest: f32,
pub abdomen: f32,
pub limbs: f32,
}
pub fn new_body_volume_state(config: BodyVolumeConfig) -> BodyVolumeState {
BodyVolumeState {
volume: 0.5,
chest: 0.5,
abdomen: 0.5,
config,
}
}
pub fn default_body_volume_config() -> BodyVolumeConfig {
BodyVolumeConfig::default()
}
pub fn bvc_set_volume(state: &mut BodyVolumeState, v: f32) {
state.volume = v.clamp(0.0, 1.0);
}
pub fn bvc_set_chest(state: &mut BodyVolumeState, v: f32) {
state.chest = v.clamp(0.0, 1.0);
}
pub fn bvc_set_abdomen(state: &mut BodyVolumeState, v: f32) {
state.abdomen = v.clamp(0.0, 1.0);
}
pub fn bvc_reset(state: &mut BodyVolumeState) {
state.volume = 0.5;
state.chest = 0.5;
state.abdomen = 0.5;
}
pub fn bvc_is_neutral(state: &BodyVolumeState) -> bool {
(state.volume - 0.5).abs() < 1e-5
&& (state.chest - 0.5).abs() < 1e-5
&& (state.abdomen - 0.5).abs() < 1e-5
}
pub fn bvc_to_weights(state: &BodyVolumeState) -> BodyVolumeWeights {
let scale = |x: f32| x.powf(state.config.exponent);
BodyVolumeWeights {
overall: scale(state.volume),
chest: scale(state.chest),
abdomen: scale(state.abdomen),
limbs: scale((state.chest + state.abdomen) * 0.5),
}
}
pub fn bvc_blend(a: &BodyVolumeState, b: &BodyVolumeState, t: f32) -> BodyVolumeState {
let t = t.clamp(0.0, 1.0);
BodyVolumeState {
volume: a.volume + (b.volume - a.volume) * t,
chest: a.chest + (b.chest - a.chest) * t,
abdomen: a.abdomen + (b.abdomen - a.abdomen) * t,
config: a.config.clone(),
}
}
pub fn bvc_estimated_volume(state: &BodyVolumeState) -> f32 {
let w = bvc_to_weights(state);
let r = w.overall * 0.5 + 0.5; (4.0 / 3.0) * PI * r * r * r
}
pub fn bvc_to_json(state: &BodyVolumeState) -> String {
format!(
r#"{{"volume":{:.4},"chest":{:.4},"abdomen":{:.4}}}"#,
state.volume, state.chest, state.abdomen
)
}
#[cfg(test)]
mod tests {
use super::*;
fn make() -> BodyVolumeState {
new_body_volume_state(default_body_volume_config())
}
#[test]
fn neutral_on_creation() {
let s = make();
assert!(bvc_is_neutral(&s));
}
#[test]
fn set_volume_clamps_high() {
let mut s = make();
bvc_set_volume(&mut s, 5.0);
let w = bvc_to_weights(&s);
assert!((0.0..=1.0).contains(&w.overall));
}
#[test]
fn set_volume_clamps_low() {
let mut s = make();
bvc_set_volume(&mut s, -3.0);
let w = bvc_to_weights(&s);
assert!((0.0..=1.0).contains(&w.overall));
}
#[test]
fn reset_restores_neutral() {
let mut s = make();
bvc_set_volume(&mut s, 0.9);
bvc_set_chest(&mut s, 0.1);
bvc_reset(&mut s);
assert!(bvc_is_neutral(&s));
}
#[test]
fn weights_in_unit_range() {
let mut s = make();
bvc_set_volume(&mut s, 0.8);
bvc_set_chest(&mut s, 0.3);
bvc_set_abdomen(&mut s, 0.6);
let w = bvc_to_weights(&s);
assert!((0.0..=1.0).contains(&w.overall));
assert!((0.0..=1.0).contains(&w.chest));
assert!((0.0..=1.0).contains(&w.abdomen));
assert!((0.0..=1.0).contains(&w.limbs));
}
#[test]
fn blend_midpoint() {
let mut a = make();
let mut b = make();
bvc_set_volume(&mut a, 0.0);
bvc_set_volume(&mut b, 1.0);
let mid = bvc_blend(&a, &b, 0.5);
assert!((mid.volume - 0.5).abs() < 1e-5);
}
#[test]
fn blend_at_zero_is_a() {
let a = make();
let b = make();
let r = bvc_blend(&a, &b, 0.0);
assert!((r.volume - a.volume).abs() < 1e-5);
}
#[test]
fn estimated_volume_positive() {
let s = make();
assert!(bvc_estimated_volume(&s) > 0.0);
}
#[test]
fn json_contains_volume_key() {
let s = make();
assert!(bvc_to_json(&s).contains("volume"));
}
#[test]
fn exponent_changes_output() {
let mut cfg = default_body_volume_config();
cfg.exponent = 2.0;
let s = new_body_volume_state(cfg);
let w = bvc_to_weights(&s);
assert!((w.overall - 0.25).abs() < 1e-5);
}
}