#![allow(dead_code)]
use std::f32::consts::PI;
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct BackConfig {
pub upper_back_width: f32,
pub lower_back_curve: f32,
pub lat_spread: f32,
pub spine_curvature: f32,
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct BackState {
pub upper_width: f32,
pub lower_curve: f32,
pub lats: f32,
pub spine: f32,
pub trapezius: f32,
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct BackMorphWeights {
pub wide_upper: f32,
pub narrow_upper: f32,
pub lordosis: f32,
pub kyphosis: f32,
pub lat_flare: f32,
pub trap_bulk: f32,
}
#[allow(dead_code)]
pub fn default_back_config() -> BackConfig {
BackConfig {
upper_back_width: 0.5,
lower_back_curve: 0.5,
lat_spread: 0.5,
spine_curvature: 0.5,
}
}
#[allow(dead_code)]
pub fn new_back_state() -> BackState {
BackState {
upper_width: 0.5,
lower_curve: 0.5,
lats: 0.5,
spine: 0.5,
trapezius: 0.5,
}
}
#[allow(dead_code)]
pub fn set_upper_back_width(state: &mut BackState, value: f32) {
state.upper_width = value.clamp(0.0, 1.0);
}
#[allow(dead_code)]
pub fn set_lower_back_curve(state: &mut BackState, value: f32) {
state.lower_curve = value.clamp(0.0, 1.0);
}
#[allow(dead_code)]
pub fn set_lat_spread(state: &mut BackState, value: f32) {
state.lats = value.clamp(0.0, 1.0);
}
#[allow(dead_code)]
pub fn set_trapezius(state: &mut BackState, value: f32) {
state.trapezius = value.clamp(0.0, 1.0);
}
#[allow(dead_code)]
pub fn compute_back_weights(state: &BackState, cfg: &BackConfig) -> BackMorphWeights {
let uw = state.upper_width * cfg.upper_back_width;
let wide_upper = uw.clamp(0.0, 1.0);
let narrow_upper = (1.0 - uw).clamp(0.0, 1.0);
let lc = state.lower_curve * cfg.lower_back_curve;
let lordosis = (lc * (PI * 0.25).sin()).clamp(0.0, 1.0);
let kyphosis = ((1.0 - lc) * 0.5).clamp(0.0, 1.0);
let lat_flare = (state.lats * cfg.lat_spread).clamp(0.0, 1.0);
let trap_bulk = (state.trapezius * cfg.spine_curvature).clamp(0.0, 1.0);
BackMorphWeights {
wide_upper,
narrow_upper,
lordosis,
kyphosis,
lat_flare,
trap_bulk,
}
}
#[allow(dead_code)]
pub fn back_to_json(state: &BackState) -> String {
format!(
r#"{{"upper_width":{},"lower_curve":{},"lats":{},"spine":{},"trapezius":{}}}"#,
state.upper_width, state.lower_curve, state.lats, state.spine, state.trapezius
)
}
#[allow(dead_code)]
pub fn blend_back_states(a: &BackState, b: &BackState, t: f32) -> BackState {
let t = t.clamp(0.0, 1.0);
BackState {
upper_width: a.upper_width + (b.upper_width - a.upper_width) * t,
lower_curve: a.lower_curve + (b.lower_curve - a.lower_curve) * t,
lats: a.lats + (b.lats - a.lats) * t,
spine: a.spine + (b.spine - a.spine) * t,
trapezius: a.trapezius + (b.trapezius - a.trapezius) * t,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_default_config() {
let cfg = default_back_config();
assert!((0.0..=1.0).contains(&cfg.upper_back_width));
}
#[test]
fn test_new_state() {
let s = new_back_state();
assert!((s.upper_width - 0.5).abs() < 1e-6);
}
#[test]
fn test_set_upper_width_clamp() {
let mut s = new_back_state();
set_upper_back_width(&mut s, 2.0);
assert!((s.upper_width - 1.0).abs() < 1e-6);
}
#[test]
fn test_set_lower_curve() {
let mut s = new_back_state();
set_lower_back_curve(&mut s, 0.3);
assert!((s.lower_curve - 0.3).abs() < 1e-6);
}
#[test]
fn test_set_lat_spread() {
let mut s = new_back_state();
set_lat_spread(&mut s, 0.9);
assert!((s.lats - 0.9).abs() < 1e-6);
}
#[test]
fn test_compute_weights_range() {
let s = new_back_state();
let cfg = default_back_config();
let w = compute_back_weights(&s, &cfg);
assert!((0.0..=1.0).contains(&w.wide_upper));
assert!((0.0..=1.0).contains(&w.lordosis));
}
#[test]
fn test_back_to_json() {
let s = new_back_state();
let j = back_to_json(&s);
assert!(j.contains("upper_width"));
}
#[test]
fn test_blend_back() {
let a = new_back_state();
let mut b = new_back_state();
b.lats = 1.0;
let mid = blend_back_states(&a, &b, 0.5);
assert!((mid.lats - 0.75).abs() < 1e-6);
}
#[test]
fn test_set_trapezius() {
let mut s = new_back_state();
set_trapezius(&mut s, 0.4);
assert!((s.trapezius - 0.4).abs() < 1e-6);
}
#[test]
fn test_blend_identity() {
let a = new_back_state();
let r = blend_back_states(&a, &a, 0.5);
assert!((r.lats - a.lats).abs() < 1e-6);
}
}