use crate::config::{ClipExpectations, Config};
use crate::finding::{Finding, Severity};
use crate::metrics::MetricGrids;
use crate::model::Document;
use crate::profile::ResolvedRoles;
use crate::sample::PoseGrid;
use std::rc::Rc;
#[derive(Debug)]
pub struct CheckCtx<'a> {
pub doc: &'a Document,
pub roles: &'a ResolvedRoles,
pub config: &'a Config,
grids: &'a MetricGrids<'a>,
expectations: Vec<ClipExpectations>,
}
impl<'a> CheckCtx<'a> {
pub fn new(grids: &'a MetricGrids<'a>, roles: &'a ResolvedRoles, config: &'a Config) -> Self {
let doc = grids.document();
let expectations = doc
.clips
.iter()
.map(|c| config.expectations_for(&c.name))
.collect();
Self {
doc,
roles,
config,
grids,
expectations,
}
}
pub fn grid(&self, clip_index: usize) -> Option<Rc<PoseGrid>> {
self.grids.grid(clip_index)
}
pub fn expectations(&self, clip_index: usize) -> &ClipExpectations {
&self.expectations[clip_index]
}
pub fn clip_expectations(&self) -> &[ClipExpectations] {
&self.expectations
}
}
pub enum Readiness {
Ready,
Skipped(String),
Idle,
}
pub trait Check {
fn id(&self) -> &'static str;
fn readiness(&self, _ctx: &CheckCtx) -> Readiness {
Readiness::Ready
}
fn run(&self, ctx: &CheckCtx, out: &mut Vec<Finding>);
}
pub fn mechanical_checks() -> Vec<Box<dyn Check>> {
vec![
Box::new(crate::checks::nan::Nan),
Box::new(crate::checks::time_monotonic::TimeMonotonic),
Box::new(crate::checks::quat_norm::QuatNorm),
Box::new(crate::checks::quat_flip::QuatFlip),
Box::new(crate::checks::duration_sanity::DurationSanity),
Box::new(crate::checks::scale_keys::ScaleKeys),
Box::new(crate::checks::constant_track::ConstantTrack),
]
}
pub fn all_checks() -> Vec<Box<dyn Check>> {
let mut checks = mechanical_checks();
checks.push(Box::new(crate::checks::missing_bones::MissingBones));
checks.push(Box::new(crate::checks::frozen_bone::FrozenBone));
checks.push(Box::new(crate::checks::loop_seam::LoopSeam));
checks.push(Box::new(crate::checks::root_motion_speed::RootMotionSpeed));
checks.push(Box::new(crate::checks::gait_group::GaitGroup));
checks.push(Box::new(crate::checks::in_place::InPlace));
checks.push(Box::new(crate::checks::fps::Fps));
checks.push(Box::new(crate::checks::bind_pose::BindPose));
checks.push(Box::new(crate::checks::foot_slide::FootSlide));
checks
}
pub fn run_checks(ctx: &CheckCtx, checks: &[Box<dyn Check>]) -> Vec<Finding> {
use crate::config::SeveritySetting;
let mut out = Vec::new();
for check in checks {
let setting = ctx.config.check_settings(check.id()).severity;
if setting == Some(SeveritySetting::Off) {
continue; }
match check.readiness(ctx) {
Readiness::Idle => {}
Readiness::Skipped(reason) => {
out.push(
Finding::new(check.id(), Severity::Note, format!("skipped: {reason}"))
.as_diagnostic(),
);
}
Readiness::Ready => {
let mut findings = Vec::new();
check.run(ctx, &mut findings);
if let Some(severity) = setting.and_then(SeveritySetting::as_severity) {
for f in &mut findings {
if !f.diagnostic {
f.severity = severity;
}
}
}
out.append(&mut findings);
}
}
}
out
}