animsmith-core 0.1.0

Engine-agnostic data model, sampling, measurements, and checks for the animsmith animation-clip linter
Documentation
use animsmith_core::diff::{
    AMPLITUDE_THRESHOLD_M, DURATION_THRESHOLD_S, MetricDelta, PHASE_THRESHOLD,
    ROTATION_RANGE_THRESHOLD_DEG, SEAM_THRESHOLD, SPEED_THRESHOLD_MPS, diff_measurements,
};
use animsmith_core::measure::ClipMeasurements;
use serde_json::json;
use std::collections::BTreeMap;

type MeasurementMap = BTreeMap<String, ClipMeasurements>;
type PublicDiffFn = fn(&MeasurementMap, &MeasurementMap) -> Vec<MetricDelta>;

const PUBLIC_DIFF_MEASUREMENTS: PublicDiffFn = diff_measurements;

fn measurements(duration_s: f64) -> MeasurementMap {
    serde_json::from_value(json!({
        "walk": {
            "duration_s": duration_s,
            "frame_count": 31,
            "animated_bones": ["hips"],
            "bone_rotation_range_deg": { "hips": 10.0 },
            "loop_seam_ratio": 0.2,
            "gait": {
                "phase": 0.25,
                "lr_amplitude_m": 0.1
            },
            "speed_mps": 1.0
        }
    }))
    .expect("valid public measurement map")
}

#[test]
fn public_diff_api_accepts_deserialized_measurements() {
    let deltas = PUBLIC_DIFF_MEASUREMENTS(&measurements(1.0), &measurements(1.1));

    assert_eq!(deltas.len(), 1, "{deltas:?}");
    let delta = &deltas[0];
    assert_eq!(delta.clip, "walk");
    assert_eq!(delta.metric, "duration_s");
    assert_eq!(delta.before, Some(1.0));
    assert_eq!(delta.after, Some(1.1));
    assert_eq!(delta.note, "moved");
}

#[test]
fn threshold_constants_are_public_and_unchanged() {
    assert_eq!(DURATION_THRESHOLD_S, 0.017);
    assert_eq!(ROTATION_RANGE_THRESHOLD_DEG, 1.0);
    assert_eq!(SEAM_THRESHOLD, 0.05);
    assert_eq!(PHASE_THRESHOLD, 0.05);
    assert_eq!(AMPLITUDE_THRESHOLD_M, 0.005);
    assert_eq!(SPEED_THRESHOLD_MPS, 0.1);
}