#![allow(dead_code)]
use std::f32::consts::PI;
#[allow(dead_code)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ThighGirthSide {
Left,
Right,
}
#[allow(dead_code)]
#[derive(Debug, Clone, PartialEq)]
pub struct ThighGirthConfig {
pub max_radius_m: f32,
}
impl Default for ThighGirthConfig {
fn default() -> Self {
Self { max_radius_m: 0.04 }
}
}
#[allow(dead_code)]
#[derive(Debug, Clone, Default)]
pub struct ThighGirthState {
pub left_girth: f32,
pub right_girth: f32,
pub left_medial: f32,
pub right_medial: f32,
}
#[allow(dead_code)]
pub fn new_thigh_girth_state() -> ThighGirthState {
ThighGirthState::default()
}
#[allow(dead_code)]
pub fn default_thigh_girth_config() -> ThighGirthConfig {
ThighGirthConfig::default()
}
#[allow(dead_code)]
pub fn tg_set_girth(state: &mut ThighGirthState, side: ThighGirthSide, v: f32) {
let v = v.clamp(0.0, 1.0);
match side {
ThighGirthSide::Left => state.left_girth = v,
ThighGirthSide::Right => state.right_girth = v,
}
}
#[allow(dead_code)]
pub fn tg_set_medial(state: &mut ThighGirthState, side: ThighGirthSide, v: f32) {
let v = v.clamp(0.0, 1.0);
match side {
ThighGirthSide::Left => state.left_medial = v,
ThighGirthSide::Right => state.right_medial = v,
}
}
#[allow(dead_code)]
pub fn tg_set_both(state: &mut ThighGirthState, v: f32) {
let v = v.clamp(0.0, 1.0);
state.left_girth = v;
state.right_girth = v;
}
#[allow(dead_code)]
pub fn tg_reset(state: &mut ThighGirthState) {
*state = ThighGirthState::default();
}
#[allow(dead_code)]
pub fn tg_is_neutral(state: &ThighGirthState) -> bool {
state.left_girth < 1e-4 && state.right_girth < 1e-4
}
#[allow(dead_code)]
pub fn tg_circumference(
state: &ThighGirthState,
side: ThighGirthSide,
cfg: &ThighGirthConfig,
) -> f32 {
let g = match side {
ThighGirthSide::Left => state.left_girth,
ThighGirthSide::Right => state.right_girth,
};
2.0 * PI * (0.06 + g * cfg.max_radius_m)
}
#[allow(dead_code)]
pub fn tg_symmetry(state: &ThighGirthState) -> f32 {
1.0 - (state.left_girth - state.right_girth).abs()
}
#[allow(dead_code)]
pub fn tg_to_weights(state: &ThighGirthState, cfg: &ThighGirthConfig) -> [f32; 4] {
[
state.left_girth * cfg.max_radius_m,
state.right_girth * cfg.max_radius_m,
state.left_medial * cfg.max_radius_m * 0.6,
state.right_medial * cfg.max_radius_m * 0.6,
]
}
#[allow(dead_code)]
pub fn tg_blend(a: &ThighGirthState, b: &ThighGirthState, t: f32) -> ThighGirthState {
let t = t.clamp(0.0, 1.0);
let inv = 1.0 - t;
ThighGirthState {
left_girth: a.left_girth * inv + b.left_girth * t,
right_girth: a.right_girth * inv + b.right_girth * t,
left_medial: a.left_medial * inv + b.left_medial * t,
right_medial: a.right_medial * inv + b.right_medial * t,
}
}
#[allow(dead_code)]
pub fn tg_to_json(state: &ThighGirthState) -> String {
format!(
"{{\"left_girth\":{:.4},\"right_girth\":{:.4}}}",
state.left_girth, state.right_girth
)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn default_neutral() {
assert!(tg_is_neutral(&new_thigh_girth_state()));
}
#[test]
fn girth_clamps_high() {
let mut s = new_thigh_girth_state();
tg_set_girth(&mut s, ThighGirthSide::Left, 5.0);
assert!((s.left_girth - 1.0).abs() < 1e-6);
}
#[test]
fn girth_clamps_low() {
let mut s = new_thigh_girth_state();
tg_set_girth(&mut s, ThighGirthSide::Right, -1.0);
assert!(s.right_girth < 1e-6);
}
#[test]
fn reset_clears() {
let mut s = new_thigh_girth_state();
tg_set_both(&mut s, 0.9);
tg_reset(&mut s);
assert!(tg_is_neutral(&s));
}
#[test]
fn circumference_at_neutral_positive() {
let cfg = default_thigh_girth_config();
let s = new_thigh_girth_state();
assert!(tg_circumference(&s, ThighGirthSide::Left, &cfg) > 0.0);
}
#[test]
fn circumference_grows_with_girth() {
let cfg = default_thigh_girth_config();
let mut s = new_thigh_girth_state();
let base = tg_circumference(&s, ThighGirthSide::Left, &cfg);
tg_set_girth(&mut s, ThighGirthSide::Left, 1.0);
let grown = tg_circumference(&s, ThighGirthSide::Left, &cfg);
assert!(grown > base);
}
#[test]
fn symmetry_one_when_equal() {
let mut s = new_thigh_girth_state();
tg_set_both(&mut s, 0.5);
assert!((tg_symmetry(&s) - 1.0).abs() < 1e-6);
}
#[test]
fn weights_four_elements() {
let w = tg_to_weights(&new_thigh_girth_state(), &default_thigh_girth_config());
assert_eq!(w.len(), 4);
}
#[test]
fn blend_midpoint() {
let mut b = new_thigh_girth_state();
tg_set_both(&mut b, 1.0);
let r = tg_blend(&new_thigh_girth_state(), &b, 0.5);
assert!((r.left_girth - 0.5).abs() < 1e-5);
}
#[test]
fn json_has_keys() {
let j = tg_to_json(&new_thigh_girth_state());
assert!(j.contains("left_girth") && j.contains("right_girth"));
}
}