Skip to main content

Crate animsmith_core

Crate animsmith_core 

Source
Expand description

Engine-agnostic animation linting primitives for Rust pipelines.

This crate is the embedding boundary for animsmith. It owns the core data model (Document, Skeleton, Clip, Track), rig-role resolution (detect_profile, ResolvedRoles::from_names), typed configuration (Config), measurement generation (measure::measure_document), versioned result envelopes (contract::MeasureEnvelope, contract::LintEnvelope), measurement diffs (diff::diff_measurements), structured findings (Finding), and check execution (CheckCtx, all_checks, evaluate_checks). The source_facts module owns the bounded, format-neutral V1 vocabulary that format loaders bind to the exact primary bytes in an immutable LoadedSource. A mutable normalized Document does not reconstruct importer-sensitive source declarations; consuming the wrapper as a document deliberately discards those facts. The separate dependency_closure sidecar records bounded, same-load primary/external content identities over the raw resource-declaration domain; format crates own rooted I/O while core owns its validated value and canonical digest contract. The borrowing facts view reuses the canonical model::SourceSkeletonAssets table and remains separate from scale’s operation-specific capability and proof ledgers. The opt-in bake_static_mesh_transforms operation canonicalizes supported unanimated, unskinned mesh scenes into identity-root geometry and returns deterministic producer evidence. The opt-in transform::prune_constant_tracks helper removes only interpolation-aware constant-track candidates whose sampled local and model-space pose evidence remains within its documented tolerances. The scale module owns the format-neutral plan/proof contracts for the two distinct DESIGN.md Appendix D scale operations — scale::ScaleOperation::WholeDocumentLinearUnits and scale::ScaleOperation::RestBindUniformScale — through pure, fail-closed scale::plan_scale and independent scale::prove_scale. A format frontend owns exact source rewriting and hands the reloaded emitted document back through scale::ScaleCandidate::from_document; core does not expose a production candidate builder, decide selectors, publish artifacts, or write files. The animsmith-gltf and animsmith-fbx loader crates translate file formats into this model; their docs.rs pages continue the library path for format-specific loading and, for glTF, writing.

The embedding guide explains crate selection and integration boundaries. The pipeline scenario guide shows where an embedded gate fits in marketplace intake, mocap cleanup, outsourced acceptance, and CI. A runnable example exercises the complete library flow.

§Quick start

After a format crate has loaded a Document, resolve rig roles, build a Config from the host pipeline’s contract, and share one MetricGrids between measurements, checks, and optional report generation:

use animsmith_core::{
    CheckCtx, CheckSelection, Config, Document, MetricGrids, all_checks,
    evaluate_checks, resolve_configured_roles,
};
use animsmith_core::measure::measure_document;

let doc = Document::default();
let config = Config::default();
config.validate()?;
let roles = resolve_configured_roles(&doc.skeleton, &config.rig);
let grids = MetricGrids::new(&doc);

let measurements = measure_document(&grids, &roles, &config);
let ctx = CheckCtx::new(&grids, &roles, &config);
let results = evaluate_checks(&ctx, &all_checks(), CheckSelection::All)?;

assert!(measurements.is_empty());
assert!(results.iter().all(|result| result.findings().is_empty()));

CheckCtx::new consumes already-resolved roles; it does not interpret Config::rig automatically. Frontends may use detect_profile, resolve_configured_roles for the same named-profile plus inline-override policy as the CLI. Missing prerequisites are represented as typed coverage gaps rather than false findings.

§API status

The Rust API is pre-1.0 and may still change before the first stable release. The intended extension points are the data model, configuration types, measurement and diff APIs, rig-profile APIs, the Check trait for custom checks, and the check catalog functions re-exported from this crate root. Built-in check ids, CLI exit-code semantics, and the shared versioned JSON envelope/schema ids are treated as the most stable automation contracts. The contract module owns the same envelope types and immutable identities for CLI and embedded producers. The scene-asset structs in model and the pipeline-mechanical helpers in transform and static_bake are public so the loader, writer, and CLI crates can share the same model, but they are less settled than the measurement/check embedding flow while the crate is pre-1.0. Metric formulas and individual Rust symbols are still subject to pre-1.0 refinement.

Public APIs that return Result document their # Errors cases. Index-based accessors and transform helpers that rely on loader-established invariants document their # Panics contracts. Loader-valid documents from the format crates should flow through checking, sampling, and measurement without panicking on untrusted input.

Re-exports§

pub use check::Check;
pub use check::CheckCtx;
pub use check::all_checks;
pub use check::mechanical_checks;
pub use config::ClipExpectations;
pub use config::Config;
pub use config::ConfigValidationError;
pub use config::GaitGroup;
pub use config::MovementOwner;
pub use config::Pinned;
pub use config::RuntimeNodeSelectorResolution;
pub use config::RuntimeNodeSelectors;
pub use config::RuntimeNodesConfig;
pub use config::SeveritySetting;
pub use config::SyncGroup;
pub use config::TimeComplementSettings;
pub use contract::DiffEnvelope;
pub use contract::InputIdentity;
pub use contract::LintEnvelope;
pub use contract::LintFileReport;
pub use contract::MEASUREMENTS_SCHEMA_ID;
pub use contract::MEASUREMENTS_SCHEMA_VERSION;
pub use contract::MeasureEnvelope;
pub use contract::MeasureFileReport;
pub use contract::MeasurementContract;
pub use contract::MeasurementContractError;
pub use contract::MeasurementFileError;
pub use contract::MeasurementReportError;
pub use contract::MeasurementReportFile;
pub use contract::MeasurementReportInput;
pub use contract::MeasurementReportReadError;
pub use contract::OUTPUT_SCHEMA_ID;
pub use contract::OUTPUT_SCHEMA_VERSION;
pub use contract::OUTPUT_V10_MAX_CHECKS_PER_FILE;
pub use contract::OUTPUT_V10_MAX_FILES;
pub use contract::OUTPUT_V10_MAX_REPORT_BYTES;
pub use contract::OutputContractError;
pub use contract::RigInfo;
pub use contract::RigInfoError;
pub use contract::ToolInfo;
pub use contract::ToolSource;
pub use contract::sha256_hex;
pub use dependency_closure::DEPENDENCY_CLOSURE_BUDGET_V1_ID;
pub use dependency_closure::DEPENDENCY_CLOSURE_V1_ID;
pub use dependency_closure::DEPENDENCY_CLOSURE_V1_MAX_DEDUP_PROBES;
pub use dependency_closure::DEPENDENCY_CLOSURE_V1_MAX_EXTERNAL_RESOURCES;
pub use dependency_closure::DEPENDENCY_CLOSURE_V1_MAX_KEY_BYTES;
pub use dependency_closure::DEPENDENCY_CLOSURE_V1_MAX_NORMALIZATION_BYTES;
pub use dependency_closure::DEPENDENCY_CLOSURE_V1_MAX_PATH_COMPONENTS;
pub use dependency_closure::DEPENDENCY_CLOSURE_V1_MAX_REFERENCES;
pub use dependency_closure::DEPENDENCY_CLOSURE_V1_MAX_RESOURCE_BYTES;
pub use dependency_closure::DEPENDENCY_CLOSURE_V1_MAX_TOTAL_RESOURCE_BYTES;
pub use dependency_closure::DependencyClosureBuilderV1;
pub use dependency_closure::DependencyClosureCoverageReasonV1;
pub use dependency_closure::DependencyClosureCoverageV1;
pub use dependency_closure::DependencyClosureError;
pub use dependency_closure::DependencyClosureIdentityV1;
pub use dependency_closure::DependencyClosureReferenceV1;
pub use dependency_closure::DependencyClosureV1;
pub use dependency_closure::DependencyClosureWorkV1;
pub use dependency_closure::DependencyReferenceTargetV1;
pub use dependency_closure::DependencyResourceKeyV1;
pub use dependency_closure::DependencyResourcePurposeV1;
pub use dependency_closure::DependencyResourceRefusalReasonV1;
pub use dependency_closure::DependencyResourceUnavailableReasonV1;
pub use dependency_closure::ExternalResourceIdentityV1;
pub use dependency_closure::ResourceClosureBudgetV1;
pub use dependency_closure::ResourceKeySyntaxV1;
pub use engine_contract::ENGINE_CONTRACT_V1_MAX_AGGREGATE_ROWS;
pub use engine_contract::ENGINE_CONTRACT_V1_MAX_COLLECTION_ROWS;
pub use engine_contract::ENGINE_CONTRACT_V1_MAX_TEXT_BYTES;
pub use engine_contract::ENGINE_CONTRACT_V1_MAX_TOTAL_TEXT_BYTES;
pub use engine_contract::ENGINE_PROFILE_FACTS_V1_ID;
pub use engine_contract::EngineAnimationAddressabilityV1;
pub use engine_contract::EngineBakeOrExtractV1;
pub use engine_contract::EngineClipSettingsV1;
pub use engine_contract::EngineContractError;
pub use engine_contract::EngineConversionControlV1;
pub use engine_contract::EngineCoordinateBasisV1;
pub use engine_contract::EngineDefaultStatusV1;
pub use engine_contract::EngineFactIdV1;
pub use engine_contract::EngineFactStateV1;
pub use engine_contract::EngineFactValueV1;
pub use engine_contract::EngineForwardAxisV1;
pub use engine_contract::EngineHandednessV1;
pub use engine_contract::EngineImportHandlingV1;
pub use engine_contract::EngineLinearUnitV1;
pub use engine_contract::EnginePrimarySourceV1;
pub use engine_contract::EngineProfileFactV1;
pub use engine_contract::EngineProfileSelectionV1;
pub use engine_contract::EngineRootMotionAddressabilityV1;
pub use engine_contract::EngineSettingApplicabilityV1;
pub use engine_contract::EngineSettingDescriptorV1;
pub use engine_contract::EngineSettingDomainV1;
pub use engine_contract::EngineSettingIdV1;
pub use engine_contract::EngineSettingRowV1;
pub use engine_contract::EngineSettingScopeV1;
pub use engine_contract::EngineSettingValueV1;
pub use engine_contract::EngineTargetAddressabilityV1;
pub use engine_contract::EngineUpAxisV1;
pub use engine_contract::RESOLVED_ENGINE_SETTINGS_V1_ID;
pub use engine_contract::ResolvedEngineProfileV1;
pub use engine_contract::ResolvedEngineSettingsV1;
pub use evaluation::Applicability;
pub use evaluation::BUILTIN_COVERAGE_GAP_CODES;
pub use evaluation::BUILTIN_EVALUATION_SCOPE_CODES;
pub use evaluation::CheckEvaluation;
pub use evaluation::CheckOutput;
pub use evaluation::CheckSelection;
pub use evaluation::ConfigurationState;
pub use evaluation::CoverageGap;
pub use evaluation::CoverageGapCode;
pub use evaluation::EvaluationError;
pub use evaluation::EvaluationScope;
pub use evaluation::EvaluationScopeCode;
pub use evaluation::EvaluationState;
pub use evaluation::SelectionState;
pub use evaluation::evaluate_checks;
pub use evaluation::lint_requires_failure;
pub use finding::Finding;
pub use finding::MemberMeasurement;
pub use finding::Severity;
pub use finding::Value;
pub use metrics::MetricGrids;
pub use model::AdditionalInfluenceSet;
pub use model::AffineDomainViolation;
pub use model::Bone;
pub use model::BoneId;
pub use model::Clip;
pub use model::DecodedImageColorType;
pub use model::Document;
pub use model::DocumentShapeError;
pub use model::ImageContainerFormat;
pub use model::ImageSourceKind;
pub use model::ImageUnavailableReason;
pub use model::Interpolation;
pub use model::MaterialResourceAssets;
pub use model::MaterialResourceCoverage;
pub use model::MaterialTextureSlot;
pub use model::MeshInstanceShapeViolation;
pub use model::Property;
pub use model::Skeleton;
pub use model::SourceImageAsset;
pub use model::SourceImageInspection;
pub use model::SourceInfo;
pub use model::SourceInverseBindAccessor;
pub use model::SourceInverseBindAccessorStatus;
pub use model::SourceMaterialAsset;
pub use model::SourceMaterialTextureBinding;
pub use model::SourceNodeAsset;
pub use model::SourceNodeLocalRest;
pub use model::SourceProjectionViolation;
pub use model::SourceSkeletonAssets;
pub use model::SourceSkeletonCoverage;
pub use model::SourceSkinAsset;
pub use model::SourceSkinAttachment;
pub use model::SourceTextureAsset;
pub use model::Track;
pub use model::TrackShapeViolation;
pub use model::TrackValues;
pub use model::Transform;
pub use model::validate_document_shape;
pub use prediction::ENGINE_PREDICTION_V1_ID;
pub use prediction::EnginePredictionBasisV1;
pub use prediction::EnginePredictionFacetStateV1;
pub use prediction::EnginePredictionFacetV1;
pub use prediction::EnginePredictionV1;
pub use prediction::FinitePredictionNumberV1;
pub use prediction::MeasurementPointerV1;
pub use prediction::PREDICTION_PROVENANCE_V1_ID;
pub use prediction::PREDICTION_V1_MAX_AGGREGATE_PROVENANCE_ROWS;
pub use prediction::PREDICTION_V1_MAX_BASIS_REFERENCES_PER_FACET;
pub use prediction::PREDICTION_V1_MAX_BASIS_REFERENCES_PER_FILE;
pub use prediction::PREDICTION_V1_MAX_FACETS_PER_FILE;
pub use prediction::PREDICTION_V1_MAX_MEASUREMENT_POINTER_COMPONENTS;
pub use prediction::PREDICTION_V1_MAX_REASONS_PER_FACET;
pub use prediction::PREDICTION_V1_MAX_TEXT_BYTES;
pub use prediction::PREDICTION_V1_MAX_TOTAL_TEXT_BYTES_PER_FILE;
pub use prediction::PredictionBasisIdentityV1;
pub use prediction::PredictionBasisReferenceV1;
pub use prediction::PredictionContractError;
pub use prediction::PredictionProvenanceIdentityV1;
pub use prediction::PredictionProvenanceV1;
pub use prediction::PredictionScalarV1;
pub use prediction::PredictionUnavailableReasonV1;
pub use prediction::RawSourceAxisV1;
pub use prediction::RawSourceBasisReferenceV1;
pub use prediction::RawSourceBindingV1;
pub use prediction::RawSourceCoordinateBasisV1;
pub use prediction::RawSourceDispositionV1;
pub use prediction::RawSourceDomainV1;
pub use prediction::RawSourceFieldIdV1;
pub use prediction::RawSourceKeyV1;
pub use prediction::RawSourceObservationStateWireV1;
pub use prediction::RawSourceObservationWireV1;
pub use prediction::RawSourceProjectionWorkWireV1;
pub use prediction::RawSourceProvenanceKindV1;
pub use prediction::RawSourceProvenanceV1;
pub use prediction::RawSourceSetCoverageStateV1;
pub use prediction::RawSourceSetCoverageV1;
pub use prediction::RawSourceUnavailableReasonV1;
pub use prediction::ResolvedSettingLocationV1;
pub use prediction::SourceSkeletonRowKindV1;
pub use profile::ResolvedRoles;
pub use profile::RigProfile;
pub use profile::Role;
pub use profile::builtin_profiles;
pub use profile::detect_profile;
pub use profile::resolve_configured_roles;
pub use sample::PoseGrid;
pub use sample::TrackSample;
pub use sample::default_frame_count;
pub use sample::sample_clip;
pub use sample::sample_track;
pub use scale::ProofResidualKind;
pub use scale::ScaleBoneRestField;
pub use scale::ScaleCandidate;
pub use scale::ScaleCapabilityCoverage;
pub use scale::ScaleCapabilityFacts;
pub use scale::ScaleError;
pub use scale::ScaleFieldDisposition;
pub use scale::ScaleFieldPlan;
pub use scale::ScaleFieldTarget;
pub use scale::ScaleOperation;
pub use scale::ScalePayloadShapeRow;
pub use scale::ScalePlan;
pub use scale::ScalePlanLedger;
pub use scale::ScaleProjectedRole;
pub use scale::ScaleProof;
pub use scale::ScaleProofObligation;
pub use scale::ScaleProofResidual;
pub use scale::ScaleRequest;
pub use scale::ScaleRewriteRule;
pub use scale::ScaleSourceNodeKind;
pub use scale::ScaleSourceRestField;
pub use scale::ScaleSourceTopologyRow;
pub use scale::ScaleTolerancePolicy;
pub use scale::plan_scale;
pub use scale::prove_scale;
pub use skinned_canonical::SkinnedBindPoseCanonicalization;
pub use skinned_canonical::SkinnedBindPoseCanonicalizationError;
pub use skinned_canonical::SkinnedBindPoseCanonicalizationOptions;
pub use skinned_canonical::SkinnedBindPosePlacement;
pub use skinned_canonical::canonicalize_skinned_bind_pose;
pub use source_facts::LoadedSource;
pub use source_facts::RAW_SOURCE_FACTS_V1_ID;
pub use source_facts::RAW_SOURCE_V1_MAX_CLIPS;
pub use source_facts::RAW_SOURCE_V1_MAX_OBSERVATIONS;
pub use source_facts::RAW_SOURCE_V1_MAX_RESOURCE_REFERENCES;
pub use source_facts::RAW_SOURCE_V1_MAX_TEXT_BYTES;
pub use source_facts::RAW_SOURCE_V1_MAX_TOTAL_TEXT_BYTES;
pub use source_facts::RAW_SOURCE_V1_MAX_TRAVERSAL_DEPTH;
pub use source_facts::RawSourceFactsBuilderV1;
pub use source_facts::RawSourceFactsV1;
pub use source_facts::SourceAxisV1;
pub use source_facts::SourceChannelFactV1;
pub use source_facts::SourceChannelPropertyV1;
pub use source_facts::SourceClipFactV1;
pub use source_facts::SourceComponentMaskV1;
pub use source_facts::SourceConstructFactV1;
pub use source_facts::SourceConstructKindV1;
pub use source_facts::SourceCoordinateBasisV1;
pub use source_facts::SourceFactDomainV1;
pub use source_facts::SourceFactSetV1;
pub use source_facts::SourceFactsError;
pub use source_facts::SourceFactsViewV1;
pub use source_facts::SourceFormatV1;
pub use source_facts::SourceFramesPerSecondV1;
pub use source_facts::SourceHandednessV1;
pub use source_facts::SourceInterpolationV1;
pub use source_facts::SourceLinearUnitV1;
pub use source_facts::SourceLoaderDispositionV1;
pub use source_facts::SourceLogicalLocatorV1;
pub use source_facts::SourceObservationStateV1;
pub use source_facts::SourceObservationV1;
pub use source_facts::SourceProjectionWorkV1;
pub use source_facts::SourceProvenanceKindV1;
pub use source_facts::SourceProvenanceV1;
pub use source_facts::SourceRelativeLocatorV1;
pub use source_facts::SourceResourceKindV1;
pub use source_facts::SourceResourceLocatorV1;
pub use source_facts::SourceResourceReferenceV1;
pub use source_facts::SourceSetCoverageStateV1;
pub use source_facts::SourceSetCoverageV1;
pub use source_facts::SourceTargetKindV1;
pub use source_facts::SourceTargetV1;
pub use source_facts::SourceTextV1;
pub use source_facts::SourceTimeRangeV1;
pub use source_facts::SourceUnavailableReasonV1;
pub use static_bake::StaticMeshBake;
pub use static_bake::StaticMeshBakeError;
pub use static_bake::StaticMeshBakeEvidence;
pub use static_bake::StaticMeshBakeInstanceEvidence;
pub use static_bake::bake_static_mesh_transforms;
pub use glam;

Modules§

assembly
Lossless, name-based clip operations for character-assembly pipelines.
check
The check abstraction, its execution context, and the built-in check sets.
config
Typed configuration: rig selection, per-check settings, per-clip expectations and movement ownership, and typed clip groups. The TOML file (animsmith.toml) is one constructor of this — embedding pipelines build it programmatically through this module and keep their own contract formats on their side.
contract
Versioned JSON result-contract types shared by CLI and embedded producers.
dependency_closure
Bounded identities for a source file and its declared resource closure.
diff
Compare measurement maps and report per-metric movement beyond significance thresholds.
engine_contract
Registry-independent engine profile facts and resolved settings.
evaluation
Typed check-evaluation records.
finding
Structured lint findings. The structured fields (not just a message string) are what make diff, the JSON schema, and the HTML report cheap downstream.
measure
Measurements: the raw per-clip metric map that measure emits and lint judges. Kept separate from findings so pipelines (e.g. a bake’s measured sidecar) can pin their own contracts to the numbers.
metrics
Locomotion clip metrics: loop-seam ratio, gait phase, root-motion speed, and sampled root trajectory. The loop-seam, gait, and speed metrics were ported from a production game pipeline’s reference implementation (verified there against Blender pose-matrix FK to <0.01×) — the algorithms are kept semantically identical so the numbers reproduce.
model
The loader-facing layer: clips, tracks, and the skeleton before metric resampling or repair. The glTF loader preserves authored animation values; the FBX loader normalizes scene coordinates and bakes takes to linear TRS tracks. Mechanical checks (NaN, quaternion flips, key density, …) read this layer; semantic checks read the sampled layer built from it (see crate::sample).
prediction
Registry-independent engine-prediction provenance and per-check evidence.
profile
Rig profiles: checks never reference bone names, they reference roles. A profile maps roles to name matchers; built-ins cover the common rigs and auto-detection scores every built-in by resolved-role coverage. A check whose required roles do not resolve reports a typed coverage gap — never a false failure.
sample
The sampled layer: what a game runtime sees. A PoseGrid is a uniform time grid over [0, duration] sampled with glTF-spec interpolation semantics (lerp for T/S, shortest-path slerp for R, STEP hold, cubic-spline Hermite; clamp at the ends), then FK’d to model space.
scale
Format-neutral scale plan and proof contracts (DESIGN.md Appendix D).
skinned_canonical
Bind-pose canonicalization for skinned character geometry.
source_facts
Bounded, format-neutral observations retained from a source loader.
static_bake
Canonical baking of static mesh node transforms into vertex data.
transform
Pipeline-mechanical clip transforms, ported from the incubating bake’s Python: frame-window slicing, hold-extension, duplicate-endpoint removal, and gait-anchor rotation. Scope rule (DESIGN.md §1): animsmith may rewrite a clip only in ways whose correctness its own checks can verify.