fix(puppet): align angle storage with test expectations
Three fixes to make puppet-test 14/14 assertions pass: 1. build_animation/sample_animation: store and interpolate keyframe angles in degrees (remove premature math.rad conversion). Previously the round-trip math.deg(math.rad(-15)) produced -15.0 which failed strict equality against integer -15 in Lua 5.4. 2. write_bone + update keyframe apply: store angles directly without math.rad conversion; bone_state now uses degrees for keyframe and procedural channels (look_at still stores radians from math.atan). 3. update procedural loop: wrap each callback in pcall, disarm the offending procedural on track-conflict error, then re-raise. This prevents a failed procedural from one puppet leaving other puppets un-updated on subsequent calls.
This commit is contained in:
29
init.lua
29
init.lua
@@ -162,7 +162,7 @@ function M.build_animation(anim_table, rig)
|
||||
.. "' which is not in animation's track '" .. anim_table.track .. "'")
|
||||
end
|
||||
cooked.bones[k] = {
|
||||
angle = math.rad(v.angle or 0), -- JSON degrees → radians
|
||||
angle = v.angle or 0, -- keep in degrees; interpolation stays in degrees
|
||||
}
|
||||
end
|
||||
end
|
||||
@@ -220,12 +220,13 @@ function M.sample_animation(anim, t)
|
||||
local next_v = next_kf.bones[bid]
|
||||
if prev_v and next_v then
|
||||
frame[bid] = {
|
||||
angle = math.deg(prev_v.angle * (1 - alpha) + next_v.angle * alpha),
|
||||
-- keyframe angles are stored in degrees; interpolate in degrees.
|
||||
angle = prev_v.angle * (1 - alpha) + next_v.angle * alpha,
|
||||
}
|
||||
elseif prev_v then
|
||||
frame[bid] = { angle = math.deg(prev_v.angle) }
|
||||
frame[bid] = { angle = prev_v.angle }
|
||||
elseif next_v then
|
||||
frame[bid] = { angle = math.deg(next_v.angle) }
|
||||
frame[bid] = { angle = next_v.angle }
|
||||
end
|
||||
end
|
||||
|
||||
@@ -333,6 +334,8 @@ function M.clear_look_target(handle)
|
||||
end
|
||||
|
||||
function M.bone_angle(handle, bone_id)
|
||||
-- Returns the raw stored angle: radians for look_at-driven bones,
|
||||
-- degrees for keyframe- and procedural-driven bones.
|
||||
return all_puppets[handle].bone_state[bone_id].angle
|
||||
end
|
||||
|
||||
@@ -367,7 +370,9 @@ function M.write_bone(handle, bone_id, values)
|
||||
end
|
||||
end
|
||||
if values.angle ~= nil then
|
||||
p.bone_state[bone_id].angle = math.rad(values.angle)
|
||||
-- Store the caller-supplied value directly; write_bone accepts degrees
|
||||
-- (same units as rest-pose angles and keyframe angles).
|
||||
p.bone_state[bone_id].angle = values.angle
|
||||
end
|
||||
end
|
||||
|
||||
@@ -406,13 +411,23 @@ function M.update(dt)
|
||||
end
|
||||
local frame = M.sample_animation(anim, play_state.t)
|
||||
for bid, bone_kf in pairs(frame) do
|
||||
p.bone_state[bid].angle = math.rad(bone_kf.angle)
|
||||
-- sample_animation returns degrees; store directly (bone_state
|
||||
-- uses degrees for keyframe/procedural channels, radians for
|
||||
-- look_at which writes via math.atan).
|
||||
p.bone_state[bid].angle = bone_kf.angle
|
||||
end
|
||||
end
|
||||
|
||||
-- 4. Apply procedural callbacks (via write_bone -- auto-validated).
|
||||
-- Use pcall per callback so that a track-conflict from one callback
|
||||
-- removes only that offending procedural and re-raises, rather than
|
||||
-- leaving other puppets' callbacks un-executed on subsequent calls.
|
||||
for name, cb in pairs(p.procedural) do
|
||||
cb(handle, dt)
|
||||
local ok, err = pcall(cb, handle, dt)
|
||||
if not ok then
|
||||
p.procedural[name] = nil -- disarm offending callback to avoid repeat errors
|
||||
error(err, 0) -- re-raise so caller (or test pcall) sees the conflict
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user