#![allow(dead_code)]
use std::f32::consts::PI;
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct BodyTaperConfig {
pub min_taper: f32,
pub max_taper: f32,
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct BodyTaperState {
pub shoulder_width: f32,
pub waist_width: f32,
pub hip_width: f32,
}
#[allow(dead_code)]
pub fn default_body_taper_config() -> BodyTaperConfig {
BodyTaperConfig {
min_taper: 0.0,
max_taper: 1.0,
}
}
#[allow(dead_code)]
pub fn new_body_taper_state() -> BodyTaperState {
BodyTaperState {
shoulder_width: 0.6,
waist_width: 0.4,
hip_width: 0.5,
}
}
#[allow(dead_code)]
pub fn bt_set_shoulder(state: &mut BodyTaperState, cfg: &BodyTaperConfig, v: f32) {
state.shoulder_width = v.clamp(cfg.min_taper, cfg.max_taper);
}
#[allow(dead_code)]
pub fn bt_set_waist(state: &mut BodyTaperState, cfg: &BodyTaperConfig, v: f32) {
state.waist_width = v.clamp(cfg.min_taper, cfg.max_taper);
}
#[allow(dead_code)]
pub fn bt_set_hip(state: &mut BodyTaperState, cfg: &BodyTaperConfig, v: f32) {
state.hip_width = v.clamp(cfg.min_taper, cfg.max_taper);
}
#[allow(dead_code)]
pub fn bt_reset(state: &mut BodyTaperState) {
*state = new_body_taper_state();
}
#[allow(dead_code)]
pub fn bt_taper_ratio(state: &BodyTaperState) -> f32 {
if state.shoulder_width.abs() < 1e-9 {
return 0.0;
}
state.waist_width / state.shoulder_width
}
#[allow(dead_code)]
pub fn bt_to_weights(state: &BodyTaperState) -> Vec<(String, f32)> {
vec![
("body_shoulder_width".to_string(), state.shoulder_width),
("body_waist_width".to_string(), state.waist_width),
("body_hip_width".to_string(), state.hip_width),
]
}
#[allow(dead_code)]
pub fn bt_to_json(state: &BodyTaperState) -> String {
format!(
r#"{{"shoulder_width":{:.4},"waist_width":{:.4},"hip_width":{:.4}}}"#,
state.shoulder_width, state.waist_width, state.hip_width
)
}
#[allow(dead_code)]
pub fn bt_blend(a: &BodyTaperState, b: &BodyTaperState, t: f32) -> BodyTaperState {
let t = t.clamp(0.0, 1.0);
BodyTaperState {
shoulder_width: a.shoulder_width + (b.shoulder_width - a.shoulder_width) * t,
waist_width: a.waist_width + (b.waist_width - a.waist_width) * t,
hip_width: a.hip_width + (b.hip_width - a.hip_width) * t,
}
}
#[allow(dead_code)]
pub fn bt_silhouette_area(state: &BodyTaperState, height: f32) -> f32 {
let _ = PI; 0.5 * (state.shoulder_width + state.hip_width) * height
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_default_config() {
let cfg = default_body_taper_config();
assert!(cfg.min_taper.abs() < 1e-6);
assert!((cfg.max_taper - 1.0).abs() < 1e-6);
}
#[test]
fn test_new_state() {
let s = new_body_taper_state();
assert!((s.shoulder_width - 0.6).abs() < 1e-6);
assert!((s.waist_width - 0.4).abs() < 1e-6);
}
#[test]
fn test_set_shoulder_clamps() {
let cfg = default_body_taper_config();
let mut s = new_body_taper_state();
bt_set_shoulder(&mut s, &cfg, 5.0);
assert!((s.shoulder_width - 1.0).abs() < 1e-6);
}
#[test]
fn test_set_waist() {
let cfg = default_body_taper_config();
let mut s = new_body_taper_state();
bt_set_waist(&mut s, &cfg, 0.3);
assert!((s.waist_width - 0.3).abs() < 1e-6);
}
#[test]
fn test_set_hip() {
let cfg = default_body_taper_config();
let mut s = new_body_taper_state();
bt_set_hip(&mut s, &cfg, 0.7);
assert!((s.hip_width - 0.7).abs() < 1e-6);
}
#[test]
fn test_reset() {
let cfg = default_body_taper_config();
let mut s = new_body_taper_state();
bt_set_shoulder(&mut s, &cfg, 0.9);
bt_reset(&mut s);
assert!((s.shoulder_width - 0.6).abs() < 1e-6);
}
#[test]
fn test_taper_ratio() {
let s = new_body_taper_state();
let r = bt_taper_ratio(&s);
assert!((r - 0.4 / 0.6).abs() < 1e-4);
}
#[test]
fn test_to_weights() {
let s = new_body_taper_state();
assert_eq!(bt_to_weights(&s).len(), 3);
}
#[test]
fn test_blend() {
let a = new_body_taper_state();
let mut b = new_body_taper_state();
b.shoulder_width = 1.0;
let mid = bt_blend(&a, &b, 0.5);
assert!((mid.shoulder_width - 0.8).abs() < 1e-6);
}
#[test]
fn test_silhouette_area() {
let s = new_body_taper_state();
let area = bt_silhouette_area(&s, 1.0);
assert!(area > 0.0);
}
}