pub mod bind_pose;
pub mod constant_nonunit_scale;
pub mod constant_track;
pub mod duplicate_loop_endpoint;
pub mod duration_sanity;
pub mod foot_slide;
pub mod fps;
pub mod frozen_bone;
pub mod gait_group;
pub mod in_place;
pub mod loop_closure;
pub mod loop_seam;
pub mod loop_seam_rot;
pub mod loop_seam_vel;
pub mod missing_bones;
pub mod nan;
pub mod non_uniform_scale;
pub mod quat_flip;
pub mod quat_norm;
pub mod required_bones;
pub mod rest_world_scale;
pub mod root_motion_speed;
pub mod scale_keys;
pub mod sync_group;
pub mod time_complement;
pub mod time_monotonic;
mod vec3_trajectory;
use crate::evaluation::{CoverageGap, CoverageGapCode};
use crate::model::{Document, Track};
use crate::profile::{ResolvedRoles, Role};
pub(crate) fn exceeds_f32_cap(measured: f64, cap: f64) -> bool {
(measured as f32) > (cap as f32)
}
pub(crate) fn root_motion_gap(roles: &ResolvedRoles) -> Option<CoverageGap> {
if roles.get(Role::Root).is_some() || roles.get(Role::Hips).is_some() {
None
} else {
Some(CoverageGap::new(
CoverageGapCode::ROLES_UNRESOLVED,
format!(
"root/hips role not resolved (rig profile '{}')",
roles.profile
),
))
}
}
pub(crate) fn gait_gap(roles: &ResolvedRoles) -> Option<CoverageGap> {
let has_foot = [
Role::LeftFoot,
Role::LeftToe,
Role::RightFoot,
Role::RightToe,
]
.iter()
.any(|&r| roles.get(r).is_some());
if roles.get(Role::Hips).is_some() && has_foot {
None
} else {
Some(CoverageGap::new(
CoverageGapCode::ROLES_UNRESOLVED,
format!(
"hips/foot roles not resolved (rig profile '{}') — needs hips and at least one foot role",
roles.profile
),
))
}
}
pub(crate) fn tracks(doc: &Document) -> impl Iterator<Item = (&str, &str, &Track)> {
doc.clips.iter().flat_map(move |clip| {
clip.tracks.iter().map(move |track| {
let bone = doc
.skeleton
.bones
.get(track.bone)
.map(|b| b.name.as_str())
.unwrap_or("<unknown>");
(clip.name.as_str(), bone, track)
})
})
}