Core/Validation/PoseValidator.cs
#nullable enable
using HumanoidHandRetargeter.Maths;
using HumanoidHandRetargeter.Skeleton;
using SkeletonModel = HumanoidHandRetargeter.Skeleton.Skeleton;

namespace HumanoidHandRetargeter.Validation;

public static class PoseValidator
{
    public static IReadOnlyList<RigIssue> Validate(SkeletonModel skeleton, Pose pose)
    {
        ArgumentNullException.ThrowIfNull(skeleton);
        ArgumentNullException.ThrowIfNull(pose);
        var errors = new List<RigIssue>();
        if (pose.Locals.Length != skeleton.Count)
            errors.Add(new("pose-size", $"Pose has {pose.Locals.Length} bones; expected {skeleton.Count}."));
        else
            for (var i = 0; i < pose.Locals.Length; i++)
                if (!ValidTransform(pose.Locals[i]))
                    errors.Add(new("pose-transform", $"Bone '{skeleton[i].Name}' has a non-finite position or invalid rotation.", Bone: i));
        return errors.AsReadOnly();
    }

    internal static bool ValidTransform(XForm value)
    {
        var p = value.Pos;
        var length = value.Rot.LengthSquared();
        return float.IsFinite(p.X) && float.IsFinite(p.Y) && float.IsFinite(p.Z)
            && float.IsFinite(length) && MathF.Abs(length - 1f) <= 1e-3f;
    }
}