#![allow(dead_code)]
use std::f32::consts::PI;
#[allow(dead_code)]
#[derive(Debug, Clone, PartialEq)]
pub struct SpineCurveConfig {
pub max_lordosis_rad: f32,
pub max_kyphosis_rad: f32,
pub max_scoliosis_rad: f32,
}
impl Default for SpineCurveConfig {
fn default() -> Self {
Self {
max_lordosis_rad: PI / 8.0,
max_kyphosis_rad: PI / 10.0,
max_scoliosis_rad: PI / 16.0,
}
}
}
#[allow(dead_code)]
#[derive(Debug, Clone, Default)]
pub struct SpineCurveState {
pub lordosis: f32,
pub kyphosis: f32,
pub scoliosis: f32,
}
#[allow(dead_code)]
pub fn new_spine_curve_state() -> SpineCurveState {
SpineCurveState::default()
}
#[allow(dead_code)]
pub fn default_spine_curve_config() -> SpineCurveConfig {
SpineCurveConfig::default()
}
#[allow(dead_code)]
pub fn scc_set_lordosis(state: &mut SpineCurveState, v: f32) {
state.lordosis = v.clamp(-1.0, 1.0);
}
#[allow(dead_code)]
pub fn scc_set_kyphosis(state: &mut SpineCurveState, v: f32) {
state.kyphosis = v.clamp(-1.0, 1.0);
}
#[allow(dead_code)]
pub fn scc_set_scoliosis(state: &mut SpineCurveState, v: f32) {
state.scoliosis = v.clamp(-1.0, 1.0);
}
#[allow(dead_code)]
pub fn scc_reset(state: &mut SpineCurveState) {
*state = SpineCurveState::default();
}
#[allow(dead_code)]
pub fn scc_is_neutral(state: &SpineCurveState) -> bool {
state.lordosis.abs() < 1e-4 && state.kyphosis.abs() < 1e-4 && state.scoliosis.abs() < 1e-4
}
#[allow(dead_code)]
pub fn scc_lordosis_angle_rad(state: &SpineCurveState, cfg: &SpineCurveConfig) -> f32 {
state.lordosis * cfg.max_lordosis_rad
}
#[allow(dead_code)]
pub fn scc_kyphosis_angle_rad(state: &SpineCurveState, cfg: &SpineCurveConfig) -> f32 {
state.kyphosis * cfg.max_kyphosis_rad
}
#[allow(dead_code)]
pub fn scc_scoliosis_angle_rad(state: &SpineCurveState, cfg: &SpineCurveConfig) -> f32 {
state.scoliosis * cfg.max_scoliosis_rad
}
#[allow(dead_code)]
pub fn scc_total_curvature(state: &SpineCurveState) -> f32 {
state.lordosis.abs() + state.kyphosis.abs() + state.scoliosis.abs()
}
#[allow(dead_code)]
pub fn scc_to_weights(state: &SpineCurveState) -> [f32; 6] {
[
state.lordosis.max(0.0),
(-state.lordosis).max(0.0),
state.kyphosis.max(0.0),
(-state.kyphosis).max(0.0),
state.scoliosis.max(0.0),
(-state.scoliosis).max(0.0),
]
}
#[allow(dead_code)]
pub fn scc_blend(a: &SpineCurveState, b: &SpineCurveState, t: f32) -> SpineCurveState {
let t = t.clamp(0.0, 1.0);
let inv = 1.0 - t;
SpineCurveState {
lordosis: a.lordosis * inv + b.lordosis * t,
kyphosis: a.kyphosis * inv + b.kyphosis * t,
scoliosis: a.scoliosis * inv + b.scoliosis * t,
}
}
#[allow(dead_code)]
pub fn scc_to_json(state: &SpineCurveState) -> String {
format!(
"{{\"lordosis\":{:.4},\"kyphosis\":{:.4},\"scoliosis\":{:.4}}}",
state.lordosis, state.kyphosis, state.scoliosis
)
}
#[cfg(test)]
mod tests {
use super::*;
use std::f32::consts::PI;
#[test]
fn default_is_neutral() {
assert!(scc_is_neutral(&new_spine_curve_state()));
}
#[test]
fn lordosis_clamps() {
let mut s = new_spine_curve_state();
scc_set_lordosis(&mut s, 5.0);
assert!((s.lordosis - 1.0).abs() < 1e-6);
}
#[test]
fn kyphosis_clamps_negative() {
let mut s = new_spine_curve_state();
scc_set_kyphosis(&mut s, -5.0);
assert!((s.kyphosis + 1.0).abs() < 1e-6);
}
#[test]
fn scoliosis_clamps() {
let mut s = new_spine_curve_state();
scc_set_scoliosis(&mut s, 2.0);
assert!((s.scoliosis - 1.0).abs() < 1e-6);
}
#[test]
fn reset_clears() {
let mut s = new_spine_curve_state();
scc_set_lordosis(&mut s, 0.8);
scc_reset(&mut s);
assert!(scc_is_neutral(&s));
}
#[test]
fn lordosis_angle_positive() {
let cfg = default_spine_curve_config();
let mut s = new_spine_curve_state();
scc_set_lordosis(&mut s, 1.0);
let a = scc_lordosis_angle_rad(&s, &cfg);
assert!(a > 0.0);
assert!(a <= PI / 8.0 + 1e-5);
}
#[test]
fn total_curvature_sums() {
let mut s = new_spine_curve_state();
scc_set_lordosis(&mut s, 0.5);
scc_set_kyphosis(&mut s, 0.5);
assert!((scc_total_curvature(&s) - 1.0).abs() < 1e-5);
}
#[test]
fn weights_six_elements() {
let w = scc_to_weights(&new_spine_curve_state());
assert_eq!(w.len(), 6);
}
#[test]
fn blend_midpoint() {
let mut b = new_spine_curve_state();
scc_set_lordosis(&mut b, 1.0);
let r = scc_blend(&new_spine_curve_state(), &b, 0.5);
assert!((r.lordosis - 0.5).abs() < 1e-5);
}
#[test]
fn json_has_keys() {
let j = scc_to_json(&new_spine_curve_state());
assert!(j.contains("lordosis") && j.contains("kyphosis"));
}
}