#![allow(dead_code)]
use std::f32::consts::FRAC_PI_6;
#[derive(Debug, Clone, PartialEq)]
#[allow(dead_code)]
pub struct RibCageConfig {
pub width_min: f32,
pub width_max: f32,
pub depth_min: f32,
pub depth_max: f32,
}
impl Default for RibCageConfig {
fn default() -> Self {
Self {
width_min: -1.0,
width_max: 1.0,
depth_min: -1.0,
depth_max: 1.0,
}
}
}
#[derive(Debug, Clone, PartialEq, Default)]
#[allow(dead_code)]
pub struct RibCageState {
pub width: f32,
pub depth: f32,
pub flare: f32,
pub barrel: f32,
}
#[derive(Debug, Clone, PartialEq, Default)]
#[allow(dead_code)]
pub struct RibCageWeights {
pub wide: f32,
pub narrow: f32,
pub deep: f32,
pub shallow: f32,
pub flare_weight: f32,
pub barrel_weight: f32,
}
#[allow(dead_code)]
pub fn default_rib_cage_config() -> RibCageConfig {
RibCageConfig::default()
}
#[allow(dead_code)]
pub fn new_rib_cage_state() -> RibCageState {
RibCageState::default()
}
#[allow(dead_code)]
pub fn rc_set_width(s: &mut RibCageState, cfg: &RibCageConfig, v: f32) {
s.width = v.clamp(cfg.width_min, cfg.width_max);
}
#[allow(dead_code)]
pub fn rc_set_depth(s: &mut RibCageState, cfg: &RibCageConfig, v: f32) {
s.depth = v.clamp(cfg.depth_min, cfg.depth_max);
}
#[allow(dead_code)]
pub fn rc_set_flare(s: &mut RibCageState, v: f32) {
s.flare = v.clamp(0.0, 1.0);
}
#[allow(dead_code)]
pub fn rc_set_barrel(s: &mut RibCageState, v: f32) {
s.barrel = v.clamp(0.0, 1.0);
}
#[allow(dead_code)]
pub fn rc_reset(s: &mut RibCageState) {
*s = RibCageState::default();
}
#[allow(dead_code)]
pub fn rc_blend(a: &RibCageState, b: &RibCageState, t: f32) -> RibCageState {
let t = t.clamp(0.0, 1.0);
RibCageState {
width: a.width + (b.width - a.width) * t,
depth: a.depth + (b.depth - a.depth) * t,
flare: a.flare + (b.flare - a.flare) * t,
barrel: a.barrel + (b.barrel - a.barrel) * t,
}
}
#[allow(dead_code)]
pub fn rc_to_weights(s: &RibCageState) -> RibCageWeights {
RibCageWeights {
wide: s.width.max(0.0),
narrow: (-s.width).max(0.0),
deep: s.depth.max(0.0),
shallow: (-s.depth).max(0.0),
flare_weight: s.flare,
barrel_weight: s.barrel,
}
}
#[allow(dead_code)]
pub fn rc_flare_angle_rad(s: &RibCageState) -> f32 {
s.flare * FRAC_PI_6
}
#[allow(dead_code)]
pub fn rc_to_json(s: &RibCageState) -> String {
format!(
r#"{{"width":{:.4},"depth":{:.4},"flare":{:.4},"barrel":{:.4}}}"#,
s.width, s.depth, s.flare, s.barrel
)
}
#[allow(dead_code)]
pub fn rc_is_neutral(s: &RibCageState) -> bool {
[s.width, s.depth, s.flare, s.barrel]
.iter()
.all(|v| v.abs() < 1e-6)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn default_is_neutral() {
assert!(rc_is_neutral(&new_rib_cage_state()));
}
#[test]
fn width_clamped_positive() {
let cfg = default_rib_cage_config();
let mut s = new_rib_cage_state();
rc_set_width(&mut s, &cfg, 3.0);
assert!((s.width - 1.0).abs() < 1e-6);
}
#[test]
fn depth_clamped_negative() {
let cfg = default_rib_cage_config();
let mut s = new_rib_cage_state();
rc_set_depth(&mut s, &cfg, -3.0);
assert!((s.depth + 1.0).abs() < 1e-6);
}
#[test]
fn flare_clamped() {
let mut s = new_rib_cage_state();
rc_set_flare(&mut s, 2.0);
assert!((s.flare - 1.0).abs() < 1e-6);
}
#[test]
fn reset_works() {
let cfg = default_rib_cage_config();
let mut s = new_rib_cage_state();
rc_set_width(&mut s, &cfg, 0.5);
rc_reset(&mut s);
assert!(rc_is_neutral(&s));
}
#[test]
fn blend_midpoint() {
let a = RibCageState::default();
let b = RibCageState {
width: 1.0,
depth: 1.0,
flare: 1.0,
barrel: 1.0,
};
let m = rc_blend(&a, &b, 0.5);
assert!((m.width - 0.5).abs() < 1e-5);
}
#[test]
fn weights_wide() {
let s = RibCageState {
width: 0.8,
depth: 0.0,
flare: 0.0,
barrel: 0.0,
};
let w = rc_to_weights(&s);
assert!(w.wide > 0.0 && w.narrow < 1e-6);
}
#[test]
fn flare_angle_full() {
let s = RibCageState {
width: 0.0,
depth: 0.0,
flare: 1.0,
barrel: 0.0,
};
let a = rc_flare_angle_rad(&s);
assert!((a - FRAC_PI_6).abs() < 1e-6);
}
#[test]
fn json_contains_barrel() {
let s = RibCageState {
width: 0.0,
depth: 0.0,
flare: 0.0,
barrel: 0.5,
};
assert!(rc_to_json(&s).contains("barrel"));
}
#[test]
fn slice_not_empty_check() {
let weights = [0.1f32, 0.2, 0.3];
assert!(!weights.is_empty());
}
}