#![allow(dead_code)]
#[allow(dead_code)]
#[derive(Debug, Clone, PartialEq)]
pub struct FaceFlatnessConfig {
pub max_flatten_m: f32,
}
impl Default for FaceFlatnessConfig {
fn default() -> Self {
Self {
max_flatten_m: 0.018,
}
}
}
#[allow(dead_code)]
#[derive(Debug, Clone, Default)]
pub struct FaceFlatnessState {
pub flatness: f32,
pub mid_compress: f32,
}
#[allow(dead_code)]
pub fn new_face_flatness_state() -> FaceFlatnessState {
FaceFlatnessState::default()
}
#[allow(dead_code)]
pub fn default_face_flatness_config() -> FaceFlatnessConfig {
FaceFlatnessConfig::default()
}
#[allow(dead_code)]
pub fn ffl_set_flatness(state: &mut FaceFlatnessState, v: f32) {
state.flatness = v.clamp(0.0, 1.0);
}
#[allow(dead_code)]
pub fn ffl_set_mid(state: &mut FaceFlatnessState, v: f32) {
state.mid_compress = v.clamp(0.0, 1.0);
}
#[allow(dead_code)]
pub fn ffl_reset(state: &mut FaceFlatnessState) {
*state = FaceFlatnessState::default();
}
#[allow(dead_code)]
pub fn ffl_is_neutral(state: &FaceFlatnessState) -> bool {
state.flatness < 1e-4 && state.mid_compress < 1e-4
}
#[allow(dead_code)]
pub fn ffl_depth_scale(state: &FaceFlatnessState) -> f32 {
1.0 - state.flatness * 0.6
}
#[allow(dead_code)]
pub fn ffl_to_weights(state: &FaceFlatnessState, cfg: &FaceFlatnessConfig) -> [f32; 2] {
[
state.flatness * cfg.max_flatten_m,
state.mid_compress * cfg.max_flatten_m * 0.6,
]
}
#[allow(dead_code)]
pub fn ffl_blend(a: &FaceFlatnessState, b: &FaceFlatnessState, t: f32) -> FaceFlatnessState {
let t = t.clamp(0.0, 1.0);
let inv = 1.0 - t;
FaceFlatnessState {
flatness: a.flatness * inv + b.flatness * t,
mid_compress: a.mid_compress * inv + b.mid_compress * t,
}
}
#[allow(dead_code)]
pub fn ffl_to_json(state: &FaceFlatnessState) -> String {
format!(
"{{\"flatness\":{:.4},\"mid_compress\":{:.4}}}",
state.flatness, state.mid_compress
)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn default_is_neutral() {
assert!(ffl_is_neutral(&new_face_flatness_state()));
}
#[test]
fn flatness_clamps() {
let mut s = new_face_flatness_state();
ffl_set_flatness(&mut s, 5.0);
assert!((s.flatness - 1.0).abs() < 1e-6);
}
#[test]
fn flatness_clamps_low() {
let mut s = new_face_flatness_state();
ffl_set_flatness(&mut s, -1.0);
assert!(s.flatness < 1e-6);
}
#[test]
fn reset_works() {
let mut s = new_face_flatness_state();
ffl_set_flatness(&mut s, 0.9);
ffl_reset(&mut s);
assert!(ffl_is_neutral(&s));
}
#[test]
fn depth_scale_one_at_neutral() {
let s = new_face_flatness_state();
assert!((ffl_depth_scale(&s) - 1.0).abs() < 1e-6);
}
#[test]
fn depth_scale_less_at_max() {
let mut s = new_face_flatness_state();
ffl_set_flatness(&mut s, 1.0);
assert!(ffl_depth_scale(&s) < 1.0);
}
#[test]
fn weights_positive() {
let cfg = default_face_flatness_config();
let mut s = new_face_flatness_state();
ffl_set_flatness(&mut s, 1.0);
let w = ffl_to_weights(&s, &cfg);
assert!(w[0] > 0.0);
}
#[test]
fn blend_midpoint() {
let b = FaceFlatnessState {
flatness: 1.0,
mid_compress: 0.0,
};
let r = ffl_blend(&new_face_flatness_state(), &b, 0.5);
assert!((r.flatness - 0.5).abs() < 1e-5);
}
#[test]
fn json_keys_present() {
let j = ffl_to_json(&new_face_flatness_state());
assert!(j.contains("flatness") && j.contains("mid_compress"));
}
#[test]
fn mid_clamps() {
let mut s = new_face_flatness_state();
ffl_set_mid(&mut s, -5.0);
assert!(s.mid_compress < 1e-6);
}
}