From 0b41e8efb721ec8048128fc0273792bd389c0201 Mon Sep 17 00:00:00 2001 From: Axel Meyer Date: Mon, 18 May 2026 21:57:01 +0200 Subject: [PATCH] =?UTF-8?q?fix(puppet):=20v0.4.2=20=E2=80=94=20per-channel?= =?UTF-8?q?=20write=5Fbone=20conflict=20(was=20per-track)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Allow procedural callbacks to write channels that no active anim writes on the same bone. Example: walk_anim writes leg.scl; procedural can now write leg.rot without track-conflict error. Previously the guard was track-level (any anim on the track blocked ALL writes). Enables hip-anchor-stable + leg-orientation-procedural pattern in vagrant-skeleton (legs parent=body for body-relative hips, leg.rot overridden per-frame to walk-direction while walk_fwd_lower drives leg.scl). Existing Group 4 track-conflict test still passes: walk_anim writes leg.rot in keyframes; procedural-write of leg.rot still errors (same channel). 42/42 assertions GREEN. --- init.lua | 28 ++++++++++++++++++++++++---- manifest.lib | 2 +- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/init.lua b/init.lua index 0000c6c..a74af94 100644 --- a/init.lua +++ b/init.lua @@ -582,13 +582,33 @@ function M.write_bone(handle, bone_id, values) end local track_id = p.rig.bone_to_track[bone_id] if track_id ~= nil then - -- Only check track-conflict if bone is in a track. + -- v0.4.2: Per-channel conflict check (was per-track). Allow procedural + -- to write channels that no active anim writes on this bone (e.g., + -- walk_anim writes leg.scl while procedural writes leg.rot — disjoint). + local writing = {} + if values.rot ~= nil then writing.rot = true end + if values.pos ~= nil then writing.pos = true end + if values.scl ~= nil then writing.scl = true end for anim_id, _ in pairs(p.playing) do local anim = p.animations[anim_id] if anim and anim.track == track_id then - error("puppet.write_bone: bone '" .. bone_id - .. "' is on track '" .. track_id - .. "' currently active by keyframe '" .. anim_id .. "'") + for _, kf in ipairs(anim.keyframes) do + local bkf = kf.bones[bone_id] + if bkf then + if writing.rot and bkf.rot ~= nil then + error("puppet.write_bone: bone '" .. bone_id + .. "' channel 'rot' conflicts with anim '" .. anim_id .. "'") + end + if writing.pos and bkf.pos ~= nil then + error("puppet.write_bone: bone '" .. bone_id + .. "' channel 'pos' conflicts with anim '" .. anim_id .. "'") + end + if writing.scl and bkf.scl ~= nil then + error("puppet.write_bone: bone '" .. bone_id + .. "' channel 'scl' conflicts with anim '" .. anim_id .. "'") + end + end + end end end end diff --git a/manifest.lib b/manifest.lib index b8e52c1..4c56d54 100644 --- a/manifest.lib +++ b/manifest.lib @@ -1,6 +1,6 @@ { "id": "lib-core.puppet", - "version": "0.4.1", + "version": "0.4.2", "api_min": "0.1", "deps": [] }