@energy8platform/golem 0.7.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/editor.js +94 -40
- package/dist/lib/cli.js +124 -48
- package/dist/lib/cli.js.map +3 -3
- package/dist/lib/e8/agent.d.ts +1 -1
- package/dist/lib/e8-agent.js +133 -49
- package/dist/lib/e8-agent.js.map +3 -3
- package/dist/lib/e8-host.js +124 -48
- package/dist/lib/e8-host.js.map +3 -3
- package/dist/lib/e8-runtime.js +56 -1
- package/dist/lib/e8-runtime.js.map +3 -3
- package/dist/lib/editor/store.d.ts +32 -32
- package/dist/lib/editor-entry.js +124 -48
- package/dist/lib/editor-entry.js.map +3 -3
- package/dist/lib/harness.js +54 -1
- package/dist/lib/interactive-editor.js.map +1 -1
- package/dist/lib/rig-check.d.ts +11 -0
- package/dist/lib/rig-control-qa.d.ts +8 -8
- package/dist/lib/rig-format.d.ts +184 -184
- package/dist/lib/rig-symbol.d.ts +5 -2
- package/dist/lib/rig-tools.d.ts +2 -1
- package/dist/lib/runtime.js +60 -3
- package/dist/lib/runtime.js.map +3 -3
- package/dist/lib/tool-schema.d.ts +1 -1
- package/dist/lib/tools.js +131 -49
- package/dist/lib/tools.js.map +3 -3
- package/package.json +1 -1
- package/skills/e8-golem/SKILL.md +1 -1
- package/skills/golem-symbol-animation/references/motion-craft.md +5 -2
- package/skills/golem-symbol-animation/references/quantitative-qa.md +4 -1
package/dist/lib/e8-host.js
CHANGED
|
@@ -250,7 +250,7 @@ var Key = z.object({
|
|
|
250
250
|
/** Optional independent easing for numeric-array components (for example Spine RGBA curves). */
|
|
251
251
|
componentEase: z.array(Ease).min(1).optional()
|
|
252
252
|
});
|
|
253
|
-
var Interpolation = { interpolation: z.enum(["segment", "pchip"]).optional() };
|
|
253
|
+
var Interpolation = { interpolation: z.enum(["segment", "pchip", "spline"]).optional() };
|
|
254
254
|
var Track = z.discriminatedUnion("target", [
|
|
255
255
|
z.object({ target: z.literal("control"), id: Id, prop: z.literal("value"), ...Interpolation, keys: z.array(Key).min(1) }),
|
|
256
256
|
z.object({ target: z.literal("bone"), id: Id, prop: BoneProp, ...Interpolation, keys: z.array(Key).min(1) }),
|
|
@@ -736,6 +736,57 @@ function smoothValue(track, i, t, component, loop) {
|
|
|
736
736
|
const u = (t - k[i].t) / h(i), u2 = u * u, u3 = u2 * u;
|
|
737
737
|
return (2 * u3 - 3 * u2 + 1) * v(i) + (u3 - 2 * u2 + u) * h(i) * tangent(i) + (-2 * u3 + 3 * u2) * v(i + 1) + (u3 - u2) * h(i) * tangent(i + 1);
|
|
738
738
|
}
|
|
739
|
+
function tridiagonal(a, b, c, r) {
|
|
740
|
+
const n = b.length, cp = [], x = [];
|
|
741
|
+
let beta = b[0];
|
|
742
|
+
x[0] = r[0] / beta;
|
|
743
|
+
for (let i = 1; i < n; i++) {
|
|
744
|
+
cp[i] = c[i - 1] / beta;
|
|
745
|
+
beta = b[i] - a[i] * cp[i];
|
|
746
|
+
x[i] = (r[i] - a[i] * x[i - 1]) / beta;
|
|
747
|
+
}
|
|
748
|
+
for (let i = n - 2; i >= 0; i--) x[i] -= cp[i + 1] * x[i + 1];
|
|
749
|
+
return x;
|
|
750
|
+
}
|
|
751
|
+
function splineMoments(t, v, periodic) {
|
|
752
|
+
const n = t.length, h = (j2) => t[j2 + 1] - t[j2], d = (j2) => (v[j2 + 1] - v[j2]) / h(j2);
|
|
753
|
+
if (!periodic) {
|
|
754
|
+
const a2 = [], b2 = [], c2 = [], r2 = [];
|
|
755
|
+
for (let i = 0; i < n; i++) {
|
|
756
|
+
const hp = i > 0 ? h(i - 1) : 0, hn = i < n - 1 ? h(i) : 0;
|
|
757
|
+
a2.push(hp);
|
|
758
|
+
b2.push(2 * (hp + hn));
|
|
759
|
+
c2.push(hn);
|
|
760
|
+
r2.push(6 * ((i < n - 1 ? d(i) : 0) - (i > 0 ? d(i - 1) : 0)));
|
|
761
|
+
}
|
|
762
|
+
return tridiagonal(a2, b2, c2, r2);
|
|
763
|
+
}
|
|
764
|
+
const m = n - 1, a = [], b = [], c = [], r = [];
|
|
765
|
+
for (let i = 0; i < m; i++) {
|
|
766
|
+
const p = (i + m - 1) % m;
|
|
767
|
+
a.push(h(p));
|
|
768
|
+
b.push(2 * (h(p) + h(i)));
|
|
769
|
+
c.push(h(i));
|
|
770
|
+
r.push(6 * (d(i) - d(p)));
|
|
771
|
+
}
|
|
772
|
+
const top = a[0], bottom = c[m - 1], gamma = -b[0];
|
|
773
|
+
const bb = [...b];
|
|
774
|
+
bb[0] -= gamma;
|
|
775
|
+
bb[m - 1] -= bottom * top / gamma;
|
|
776
|
+
const x = tridiagonal(a, bb, c, r), u = new Array(m).fill(0);
|
|
777
|
+
u[0] = gamma;
|
|
778
|
+
u[m - 1] = bottom;
|
|
779
|
+
const z4 = tridiagonal(a, bb, c, u);
|
|
780
|
+
const f = (x[0] + top * x[m - 1] / gamma) / (1 + z4[0] + top * z4[m - 1] / gamma);
|
|
781
|
+
const M = x.map((xi, i) => xi - f * z4[i]);
|
|
782
|
+
return [...M, M[0]];
|
|
783
|
+
}
|
|
784
|
+
function splineValue(track, i, t, component, loop) {
|
|
785
|
+
const k = track.keys, ts = k.map((x) => x.t), vs = k.map((x) => component === void 0 ? x.v : x.v[component]);
|
|
786
|
+
const M = splineMoments(ts, vs, loop && k.length > 3 && vs[0] === vs[k.length - 1]);
|
|
787
|
+
const H = ts[i + 1] - ts[i], A = (ts[i + 1] - t) / H, B = (t - ts[i]) / H;
|
|
788
|
+
return A * vs[i] + B * vs[i + 1] + ((A ** 3 - A) * M[i] + (B ** 3 - B) * M[i + 1]) * H * H / 6;
|
|
789
|
+
}
|
|
739
790
|
function sample(track, t, loop = false) {
|
|
740
791
|
const keys = track.keys;
|
|
741
792
|
if (t <= keys[0].t) return keys[0].v;
|
|
@@ -747,11 +798,13 @@ function sample(track, t, loop = false) {
|
|
|
747
798
|
if (a.ease === "step") return a.v;
|
|
748
799
|
if (typeof a.v === "number" && typeof b.v === "number") {
|
|
749
800
|
if (track.interpolation === "pchip") return smoothValue(track, i, t, void 0, loop);
|
|
801
|
+
if (track.interpolation === "spline") return splineValue(track, i, t, void 0, loop);
|
|
750
802
|
const u = ease(a.ease)((t - a.t) / (b.t - a.t));
|
|
751
803
|
return a.v + (b.v - a.v) * u;
|
|
752
804
|
}
|
|
753
805
|
if (Array.isArray(a.v) && Array.isArray(b.v) && typeof a.v[0] === "number") {
|
|
754
806
|
if (track.interpolation === "pchip") return a.v.map((_, j2) => smoothValue(track, i, t, j2, loop));
|
|
807
|
+
if (track.interpolation === "spline") return a.v.map((_, j2) => splineValue(track, i, t, j2, loop));
|
|
755
808
|
const phase = (t - a.t) / (b.t - a.t), av = a.v, bv = b.v;
|
|
756
809
|
return av.map((v, k) => v + ((bv[k] ?? v) - v) * ease(a.componentEase?.[k] ?? a.ease)(phase));
|
|
757
810
|
}
|
|
@@ -2483,7 +2536,8 @@ function adjustTrack(doc, animId, track, adj) {
|
|
|
2483
2536
|
return { ...k, t: r42(adj.timeScale ? k.t * adj.timeScale : k.t), v: r42(v) };
|
|
2484
2537
|
});
|
|
2485
2538
|
const duration = Math.max(anim.duration, keys[keys.length - 1].t);
|
|
2486
|
-
const
|
|
2539
|
+
const interpolation = track.interpolation ?? tr.interpolation;
|
|
2540
|
+
const next = { ...anim, duration, tracks: anim.tracks.map((t) => same(t) ? { ...t, keys, ...interpolation && { interpolation } } : t), preset: void 0 };
|
|
2487
2541
|
return { ...doc, animations: doc.animations.map((a) => a.id === animId ? next : a) };
|
|
2488
2542
|
}
|
|
2489
2543
|
function removeAnimation(doc, id) {
|
|
@@ -2900,19 +2954,20 @@ var requireSlot = (doc, id) => {
|
|
|
2900
2954
|
if (!s) throw new Error(`unknown slot "${id}"`);
|
|
2901
2955
|
return s;
|
|
2902
2956
|
};
|
|
2903
|
-
function sampled(duration, n, f
|
|
2957
|
+
function sampled(duration, n, f) {
|
|
2904
2958
|
const keys = [];
|
|
2905
2959
|
for (let i = 0; i <= n; i++) {
|
|
2906
2960
|
const t = +(duration * i / n).toFixed(4);
|
|
2907
|
-
keys.push({ t, v: +f(t).toFixed(4) + 0, ease:
|
|
2961
|
+
keys.push({ t, v: +f(t).toFixed(4) + 0, ease: "linear" });
|
|
2908
2962
|
}
|
|
2909
2963
|
return keys;
|
|
2910
2964
|
}
|
|
2911
|
-
|
|
2912
|
-
|
|
2965
|
+
var smooth = "spline";
|
|
2966
|
+
function boneTrack(bone, prop, keys, interpolation) {
|
|
2967
|
+
return { target: "bone", id: bone.id, prop, keys, ...interpolation && { interpolation } };
|
|
2913
2968
|
}
|
|
2914
|
-
function deformTrack(slot, prop, keys) {
|
|
2915
|
-
return slot.attachment ? { target: "deform", id: slot.attachment, prop, keys } : void 0;
|
|
2969
|
+
function deformTrack(slot, prop, keys, interpolation) {
|
|
2970
|
+
return slot.attachment ? { target: "deform", id: slot.attachment, prop, keys, ...interpolation && { interpolation } } : void 0;
|
|
2916
2971
|
}
|
|
2917
2972
|
var pushTrack = (tracks, t) => {
|
|
2918
2973
|
if (t) tracks.push(t);
|
|
@@ -2936,18 +2991,18 @@ var idleBreathing = {
|
|
|
2936
2991
|
const armsR = byRole(doc, "arm_r", "arm_upper_r");
|
|
2937
2992
|
const chest = slotOfBone(doc, torso);
|
|
2938
2993
|
if (chest?.attachment) {
|
|
2939
|
-
pushTrack(tracks, deformTrack(chest, "bulge", sampled(T2, 8, (t) => p.amplitude * Math.sin(w * t),
|
|
2994
|
+
pushTrack(tracks, deformTrack(chest, "bulge", sampled(T2, 8, (t) => p.amplitude * Math.sin(w * t)), smooth));
|
|
2940
2995
|
} else {
|
|
2941
2996
|
const r = torso.transform;
|
|
2942
|
-
tracks.push(boneTrack(torso, "scaleY", sampled(T2, 8, (t) => r.scaleY + p.amplitude * Math.sin(w * t),
|
|
2943
|
-
tracks.push(boneTrack(torso, "scaleX", sampled(T2, 8, (t) => r.scaleX - p.amplitude * 0.4 * Math.sin(w * t),
|
|
2997
|
+
tracks.push(boneTrack(torso, "scaleY", sampled(T2, 8, (t) => r.scaleY + p.amplitude * Math.sin(w * t)), smooth));
|
|
2998
|
+
tracks.push(boneTrack(torso, "scaleX", sampled(T2, 8, (t) => r.scaleX - p.amplitude * 0.4 * Math.sin(w * t)), smooth));
|
|
2944
2999
|
}
|
|
2945
3000
|
for (const h of heads)
|
|
2946
|
-
tracks.push(boneTrack(h, "y", sampled(T2, 8, (t) => h.transform.y - p.headBob * Math.sin(w * t + 0.4),
|
|
3001
|
+
tracks.push(boneTrack(h, "y", sampled(T2, 8, (t) => h.transform.y - p.headBob * Math.sin(w * t + 0.4)), smooth));
|
|
2947
3002
|
for (const a of armsL)
|
|
2948
|
-
tracks.push(boneTrack(a, "rotation", sampled(T2, 8, (t) => a.transform.rotation + p.armSway * Math.sin(w * t + 0.8),
|
|
3003
|
+
tracks.push(boneTrack(a, "rotation", sampled(T2, 8, (t) => a.transform.rotation + p.armSway * Math.sin(w * t + 0.8)), smooth));
|
|
2949
3004
|
for (const a of armsR)
|
|
2950
|
-
tracks.push(boneTrack(a, "rotation", sampled(T2, 8, (t) => a.transform.rotation - p.armSway * Math.sin(w * t + 0.8),
|
|
3005
|
+
tracks.push(boneTrack(a, "rotation", sampled(T2, 8, (t) => a.transform.rotation - p.armSway * Math.sin(w * t + 0.8)), smooth));
|
|
2951
3006
|
return finish(this.name, p.id, T2, true, tracks, p);
|
|
2952
3007
|
}
|
|
2953
3008
|
};
|
|
@@ -2956,22 +3011,20 @@ var winBounce = {
|
|
|
2956
3011
|
defaults: { id: "win", duration: 1.2, height: 40, bounces: 2, squash: 0.15 },
|
|
2957
3012
|
build(doc, p) {
|
|
2958
3013
|
const b = root(doc), r = b.transform, tracks = [];
|
|
2959
|
-
const yK = [[0, r.y,
|
|
2960
|
-
const sxK = [[0, r.scaleX, "ease_out"]];
|
|
2961
|
-
const syK = [[0, r.scaleY, "ease_out"]];
|
|
3014
|
+
const yK = [[0, r.y]], sxK = [[0, r.scaleX]], syK = [[0, r.scaleY]];
|
|
2962
3015
|
const per = p.duration / p.bounces;
|
|
2963
3016
|
for (let i = 0; i < p.bounces; i++) {
|
|
2964
3017
|
const t0 = i * per, h = p.height * 0.55 ** i, s = p.squash * 0.7 ** i;
|
|
2965
|
-
sxK.push([t0 + per * 0.08, r.scaleX * (1 + s)
|
|
2966
|
-
syK.push([t0 + per * 0.08, r.scaleY * (1 - s)
|
|
2967
|
-
yK.push([t0 + per * 0.08, r.y
|
|
3018
|
+
sxK.push([t0 + per * 0.08, r.scaleX * (1 + s)], [t0 + per * 0.5, r.scaleX * (1 - s * 0.6)], [t0 + per * 0.95, r.scaleX * (1 + s)]);
|
|
3019
|
+
syK.push([t0 + per * 0.08, r.scaleY * (1 - s)], [t0 + per * 0.5, r.scaleY * (1 + s * 0.6)], [t0 + per * 0.95, r.scaleY * (1 - s)]);
|
|
3020
|
+
yK.push([t0 + per * 0.08, r.y], [t0 + per * 0.5, r.y - h], [t0 + per * 0.95, r.y]);
|
|
2968
3021
|
}
|
|
2969
3022
|
yK.push([p.duration, r.y]);
|
|
2970
3023
|
sxK.push([p.duration, r.scaleX]);
|
|
2971
3024
|
syK.push([p.duration, r.scaleY]);
|
|
2972
|
-
tracks.push(boneTrack(b, "y", K(...yK)), boneTrack(b, "scaleX", K(...sxK)), boneTrack(b, "scaleY", K(...syK)));
|
|
3025
|
+
tracks.push(boneTrack(b, "y", K(...yK), "pchip"), boneTrack(b, "scaleX", K(...sxK), smooth), boneTrack(b, "scaleY", K(...syK), smooth));
|
|
2973
3026
|
for (const h of byRole(doc, "head"))
|
|
2974
|
-
tracks.push(boneTrack(h, "rotation", K([0, h.transform.rotation], [p.duration * 0.25, h.transform.rotation - 6
|
|
3027
|
+
tracks.push(boneTrack(h, "rotation", K([0, h.transform.rotation], [p.duration * 0.25, h.transform.rotation - 6], [p.duration * 0.6, h.transform.rotation + 4], [p.duration, h.transform.rotation]), smooth));
|
|
2975
3028
|
const anim = finish(this.name, p.id, p.duration, false, tracks, p);
|
|
2976
3029
|
anim.events.push({ t: 0, name: "win_start" }, { t: per * 0.95, name: "land" });
|
|
2977
3030
|
return anim;
|
|
@@ -2984,8 +3037,8 @@ var anticipationShake = {
|
|
|
2984
3037
|
const b = root(doc), r = b.transform, tracks = [];
|
|
2985
3038
|
const n = Math.ceil(p.duration * p.frequency * 4);
|
|
2986
3039
|
const env = (t) => p.grow ? t / p.duration : 1;
|
|
2987
|
-
tracks.push(boneTrack(b, "x", sampled(p.duration, n, (t) => r.x + p.amplitude * env(t) * Math.sin(2 * Math.PI * p.frequency * t),
|
|
2988
|
-
tracks.push(boneTrack(b, "rotation", sampled(p.duration, n, (t) => r.rotation + 0.6 * p.amplitude * env(t) * Math.sin(2 * Math.PI * p.frequency * t + 1.2),
|
|
3040
|
+
tracks.push(boneTrack(b, "x", sampled(p.duration, n, (t) => r.x + p.amplitude * env(t) * Math.sin(2 * Math.PI * p.frequency * t)), smooth));
|
|
3041
|
+
tracks.push(boneTrack(b, "rotation", sampled(p.duration, n, (t) => r.rotation + 0.6 * p.amplitude * env(t) * Math.sin(2 * Math.PI * p.frequency * t + 1.2)), smooth));
|
|
2989
3042
|
tracks.push(boneTrack(b, "scaleX", K([0, r.scaleX, "ease_in"], [p.duration, r.scaleX * p.scaleUp])));
|
|
2990
3043
|
tracks.push(boneTrack(b, "scaleY", K([0, r.scaleY, "ease_in"], [p.duration, r.scaleY * p.scaleUp])));
|
|
2991
3044
|
return finish(this.name, p.id, p.duration, false, tracks, p);
|
|
@@ -3010,6 +3063,7 @@ var mouthTalk = {
|
|
|
3010
3063
|
if (!p.frames.length) throw new Error("mouth_talk: frames must not be empty");
|
|
3011
3064
|
const n = Math.max(1, Math.round(p.duration * p.rate));
|
|
3012
3065
|
const keys = S(...Array.from({ length: n }, (_, i) => [+(i / p.rate).toFixed(4), p.frames[i % p.frames.length]]));
|
|
3066
|
+
if (keys.at(-1).t < p.duration && keys.at(-1).v !== keys[0].v) keys.push(...S([p.duration, keys[0].v]));
|
|
3013
3067
|
return finish(this.name, p.id, p.duration, true, [slotTrack(slot, "attachment", keys)], p);
|
|
3014
3068
|
}
|
|
3015
3069
|
};
|
|
@@ -3027,10 +3081,10 @@ var symbolIdle = {
|
|
|
3027
3081
|
build(doc, p) {
|
|
3028
3082
|
const b = root(doc), r = b.transform, T2 = p.period, w = 2 * Math.PI / T2, tracks = [];
|
|
3029
3083
|
const rise = (t) => 0.5 - 0.5 * Math.cos(w * t);
|
|
3030
|
-
tracks.push(boneTrack(b, "y", sampled(T2, 8, (t) => r.y - p.float * rise(t),
|
|
3031
|
-
if (p.tilt) tracks.push(boneTrack(b, "rotation", sampled(T2, 8, (t) => r.rotation + p.tilt * Math.sin(w * t),
|
|
3084
|
+
tracks.push(boneTrack(b, "y", sampled(T2, 8, (t) => r.y - p.float * rise(t)), smooth));
|
|
3085
|
+
if (p.tilt) tracks.push(boneTrack(b, "rotation", sampled(T2, 8, (t) => r.rotation + p.tilt * Math.sin(w * t)), smooth));
|
|
3032
3086
|
if (p.pulse) for (const s of doc.slots.filter((s2) => s2.bone === b.id && s2.attachment))
|
|
3033
|
-
pushTrack(tracks, deformTrack(s, "bulge", sampled(T2, 8, (t) => p.pulse * rise(t),
|
|
3087
|
+
pushTrack(tracks, deformTrack(s, "bulge", sampled(T2, 8, (t) => p.pulse * rise(t)), smooth));
|
|
3034
3088
|
return finish(this.name, p.id, T2, true, tracks, p);
|
|
3035
3089
|
}
|
|
3036
3090
|
};
|
|
@@ -3040,12 +3094,12 @@ var symbolWin = {
|
|
|
3040
3094
|
build(doc, p) {
|
|
3041
3095
|
const b = root(doc), r = b.transform, d = p.duration, tracks = [];
|
|
3042
3096
|
for (const [prop, rest] of [["scaleX", r.scaleX], ["scaleY", r.scaleY]])
|
|
3043
|
-
tracks.push(boneTrack(b, prop, K([0, rest
|
|
3097
|
+
tracks.push(boneTrack(b, prop, K([0, rest], [d * 0.2, rest * (1 + p.pop)], [d * 0.5, rest * (1 - p.pop * 0.3)], [d, rest]), smooth));
|
|
3044
3098
|
if (p.wobble) {
|
|
3045
3099
|
const n = Math.max(8, p.wobbles * 4);
|
|
3046
|
-
const keys = sampled(d, n, (t) => p.wobble * Math.exp(-3 * t / d) * Math.sin(2 * Math.PI * p.wobbles * t / d)
|
|
3100
|
+
const keys = sampled(d, n, (t) => p.wobble * Math.exp(-3 * t / d) * Math.sin(2 * Math.PI * p.wobbles * t / d));
|
|
3047
3101
|
keys[keys.length - 1].v = 0;
|
|
3048
|
-
for (const s of doc.slots.filter((s2) => s2.bone === b.id && s2.attachment)) pushTrack(tracks, deformTrack(s, "sway", keys));
|
|
3102
|
+
for (const s of doc.slots.filter((s2) => s2.bone === b.id && s2.attachment)) pushTrack(tracks, deformTrack(s, "sway", keys, smooth));
|
|
3049
3103
|
}
|
|
3050
3104
|
const anim = finish(this.name, p.id, d, false, tracks, p);
|
|
3051
3105
|
anim.events.push({ t: 0, name: "win_start" });
|
|
@@ -3059,11 +3113,11 @@ var weightShift = {
|
|
|
3059
3113
|
const T2 = p.period, w = 2 * Math.PI / T2, N = 16, tracks = [];
|
|
3060
3114
|
const hips = byRole(doc, "hips")[0] ?? root(doc), hr = hips.transform;
|
|
3061
3115
|
const s = (t) => Math.sin(w * t);
|
|
3062
|
-
tracks.push(boneTrack(hips, "x", sampled(T2, N, (t) => hr.x + p.shift * s(t))));
|
|
3063
|
-
if (p.dip) tracks.push(boneTrack(hips, "y", sampled(T2, N, (t) => hr.y + p.dip * s(t) ** 2)));
|
|
3116
|
+
tracks.push(boneTrack(hips, "x", sampled(T2, N, (t) => hr.x + p.shift * s(t)), smooth));
|
|
3117
|
+
if (p.dip) tracks.push(boneTrack(hips, "y", sampled(T2, N, (t) => hr.y + p.dip * s(t) ** 2), smooth));
|
|
3064
3118
|
const torso = byRole(doc, "torso", "body")[0];
|
|
3065
|
-
if (torso && torso !== hips) tracks.push(boneTrack(torso, "rotation", sampled(T2, N, (t) => torso.transform.rotation - p.lean * s(t))));
|
|
3066
|
-
for (const h of byRole(doc, "head")) tracks.push(boneTrack(h, "rotation", sampled(T2, N, (t) => h.transform.rotation + p.lean / 2 * s(t))));
|
|
3119
|
+
if (torso && torso !== hips) tracks.push(boneTrack(torso, "rotation", sampled(T2, N, (t) => torso.transform.rotation - p.lean * s(t)), smooth));
|
|
3120
|
+
for (const h of byRole(doc, "head")) tracks.push(boneTrack(h, "rotation", sampled(T2, N, (t) => h.transform.rotation + p.lean / 2 * s(t)), smooth));
|
|
3067
3121
|
for (const leg of byRole(doc, "leg_l", "leg_r")) {
|
|
3068
3122
|
const slot = slotOfBone(doc, leg), att = slot && doc.attachments.find((a) => a.id === slot.attachment && a.type === "region");
|
|
3069
3123
|
const asset = att && att.type === "region" ? doc.assets.find((a) => a.id === att.asset) : void 0;
|
|
@@ -3073,8 +3127,8 @@ var weightShift = {
|
|
|
3073
3127
|
length = Math.max(...[[0, -h / 2], [w2 / 2, 0], [0, h / 2], [-w2 / 2, 0]].map(([x, y]) => Math.hypot(...apply(m, x, y))));
|
|
3074
3128
|
}
|
|
3075
3129
|
if (!(length > p.shift)) continue;
|
|
3076
|
-
tracks.push(boneTrack(leg, "rotation", sampled(T2, N, (t) => leg.transform.rotation + Math.asin(p.shift * s(t) / length) * 180 / Math.PI)));
|
|
3077
|
-
if (p.dip) tracks.push(boneTrack(leg, "y", sampled(T2, N, (t) => leg.transform.y - p.dip * s(t) ** 2)));
|
|
3130
|
+
tracks.push(boneTrack(leg, "rotation", sampled(T2, N, (t) => leg.transform.rotation + Math.asin(p.shift * s(t) / length) * 180 / Math.PI), smooth));
|
|
3131
|
+
if (p.dip) tracks.push(boneTrack(leg, "y", sampled(T2, N, (t) => leg.transform.y - p.dip * s(t) ** 2), smooth));
|
|
3078
3132
|
}
|
|
3079
3133
|
return finish(this.name, p.id, T2, true, tracks, p);
|
|
3080
3134
|
}
|
|
@@ -3317,13 +3371,13 @@ function motionQuality(doc, anim, opts = {}) {
|
|
|
3317
3371
|
const before = delta(values(k.v), values(p.v)), after = delta(values(n.v), values(k.v));
|
|
3318
3372
|
const pass = before.some((v, j2) => v * after[j2] > 1e-6);
|
|
3319
3373
|
const speed = Math.max(magnitude(before) / (k.t - p.t), magnitude(after) / (n.t - k.t));
|
|
3320
|
-
if (pass && Math.max(magnitude(left), magnitude(right)) < speed * 0.02) report("pass_key_stop", id, `motion stops at a pass-through key; use track interpolation pchip or matched tangents`, k.t);
|
|
3374
|
+
if (pass && Math.max(magnitude(left), magnitude(right)) < speed * 0.02) report("pass_key_stop", id, `motion stops at a pass-through key; use track interpolation pchip or spline, or matched tangents`, k.t);
|
|
3321
3375
|
const jump = magnitude(delta(left, right));
|
|
3322
3376
|
if (jump > Math.max(0.1, speed * 0.1)) report("velocity_jump", id, `velocity jumps ${jump.toFixed(3)} units/s across this key`, k.t);
|
|
3323
3377
|
const acceleration = (a, b, c) => values(at(b)).map((v, j2) => (values(at(c))[j2] - 2 * v + values(at(a))[j2]) / (h * h));
|
|
3324
3378
|
const al = acceleration(k.t - 2 * h, k.t - h, k.t), ar = acceleration(k.t, k.t + h, k.t + 2 * h);
|
|
3325
3379
|
const aj = magnitude(delta(al, ar));
|
|
3326
|
-
if (aj > Math.max(1, speed / Math.min(k.t - p.t, n.t - k.t) * 2)) report("acceleration_jump", id, `acceleration changes by ${aj.toFixed(2)} units/s\xB2 at this key; PCHIP guarantees velocity, not acceleration continuity`, k.t, "info");
|
|
3380
|
+
if (aj > Math.max(1, speed / Math.min(k.t - p.t, n.t - k.t) * 2)) report("acceleration_jump", id, `acceleration changes by ${aj.toFixed(2)} units/s\xB2 at this key; PCHIP guarantees velocity, not acceleration continuity \u2014 spline does both`, k.t, "info");
|
|
3327
3381
|
}
|
|
3328
3382
|
}
|
|
3329
3383
|
const steps = Math.max(2, Math.ceil(D * 60));
|
|
@@ -3805,6 +3859,17 @@ function checkRig(doc, opts = {}) {
|
|
|
3805
3859
|
out.push({ level: "warn", code: "never_visible", subject: s.id, message: `slot "${s.id}" has no attachment and no animation ever shows one` });
|
|
3806
3860
|
return out;
|
|
3807
3861
|
}
|
|
3862
|
+
function summarizeIgnoring(findings, rules, max) {
|
|
3863
|
+
const hits = (r, f) => r.code === f.code && (r.subject ?? f.subject) === f.subject && (r.animation ?? f.animation) === f.animation;
|
|
3864
|
+
const kept = findings.filter((f) => !rules.some((r) => hits(r, f)));
|
|
3865
|
+
const idle = rules.filter((r) => !findings.some((f) => hits(r, f)));
|
|
3866
|
+
const notes = [
|
|
3867
|
+
...kept.length < findings.length ? [` (${findings.length - kept.length} ignored)`] : [],
|
|
3868
|
+
...idle.map((r) => ` ignore matched nothing: ${JSON.stringify(r)}`)
|
|
3869
|
+
];
|
|
3870
|
+
return [summarize(kept, max), ...notes].join("\n");
|
|
3871
|
+
}
|
|
3872
|
+
var where = (f) => `${f.subject}${f.animation ? ` in ${f.animation}` : ""}${f.time !== void 0 ? ` @${+f.time.toFixed(4)}s` : ""}`;
|
|
3808
3873
|
function summarize(findings, max = 12) {
|
|
3809
3874
|
if (!findings.length) return "check: OK";
|
|
3810
3875
|
const rank = { error: 0, warn: 1, info: 2 };
|
|
@@ -3819,7 +3884,7 @@ function summarize(findings, max = 12) {
|
|
|
3819
3884
|
const lines = [];
|
|
3820
3885
|
for (const f of sorted) {
|
|
3821
3886
|
const shown = shownPerCode.get(f.code) ?? 0;
|
|
3822
|
-
if (shown < PER_CODE) lines.push(` [${f.level}] ${f.code}: ${f.message}`);
|
|
3887
|
+
if (shown < PER_CODE) lines.push(` [${f.level}] ${f.code} ${where(f)}: ${f.message}`);
|
|
3823
3888
|
else if (shown === PER_CODE) lines.push(` \u2026 ${codeTotal.get(f.code) - PER_CODE} more ${f.code}`);
|
|
3824
3889
|
shownPerCode.set(f.code, shown + 1);
|
|
3825
3890
|
}
|
|
@@ -4010,7 +4075,7 @@ function packRects(rects, opts = {}) {
|
|
|
4010
4075
|
shelf = Math.max(shelf, r.height);
|
|
4011
4076
|
}
|
|
4012
4077
|
const height = y + shelf + pad;
|
|
4013
|
-
if (height <= max) return { width, height, frames };
|
|
4078
|
+
if (height <= max) return { width: Math.max(pad, ...[...frames.values()].map(([fx, , fw]) => fx + fw)) + pad, height, frames };
|
|
4014
4079
|
if (width >= max) throw new Error(`atlas does not fit in ${max}\xD7${max} (needs ${width}\xD7${height})`);
|
|
4015
4080
|
width *= 2;
|
|
4016
4081
|
}
|
|
@@ -5617,7 +5682,8 @@ function previewCheck(doc, animation) {
|
|
|
5617
5682
|
const a = animation ? doc.animations.find((x) => x.id === animation) : void 0;
|
|
5618
5683
|
return a ? `check: OK for "${a.id}" (${a.tracks.length} tracks, ${a.duration}s${a.loop ? " loop" : ""})` : "check: OK (rest pose)";
|
|
5619
5684
|
}
|
|
5620
|
-
var
|
|
5685
|
+
var TrackId = { target: z3.enum(["bone", "slot", "deform", "ik", "transform", "path", "drawOrder", "control"]), id: z3.string(), prop: z3.string() };
|
|
5686
|
+
var TrackRef = { interpolation: z3.enum(["segment", "pchip", "spline"]).optional().describe("segment: per-key easing; pchip: smooth through numeric keys, never past them (continuous velocity); spline: C2 cubic through them, continuous acceleration too, may overshoot between keys \u2014 for sampled curves"), ...TrackId };
|
|
5621
5687
|
tool(
|
|
5622
5688
|
"rig_set_control",
|
|
5623
5689
|
"Create or replace a semantic control with a finite min/max/default range. Animate target=control, prop=value. Changing a range requires matching binding grids.",
|
|
@@ -5810,11 +5876,21 @@ tool(
|
|
|
5810
5876
|
);
|
|
5811
5877
|
tool(
|
|
5812
5878
|
"rig_check",
|
|
5813
|
-
"Cheap self-critique without rendering: out_of_canvas (slot leaves the canvas, worst time), static_track, loop_seam, deform_flip (lattice folds over itself \u2014 an error), mesh_fold (a skinned/deformed mesh folds over itself: error when \u2265 5 % of the mesh folds, warning below \u2014 a dense mesh bent hard at a joint pinches the odd hairline triangle), weights_sum (skinned mesh vertex weights not summing to 1), patch_offset (a slot sits away from its asset's source bbox), rest_overlap, unused_asset, never_visible, and for constraints: constraint_dead (every mix is 0 at rest and no track lifts one \u2014 the solver skips it), constraint_cycle (the target is a bone the constraint drives, or hangs off one: it reads what it writes \u2014 an error), ik_unreachable (the target sits beyond the chain's reach for over 20 % of an animation, so the limb hangs straight), path_missing (a path constraint whose target slot never shows a path attachment \u2014 an error). Motion diagnostics also cover pass_key_stop, velocity_jump, acceleration_jump, loop_velocity and constraint_order; provide secondaryBones, frontOf slot pairs and named attachment UV landmarks for brief-specific frozen-motion, ordering and swap-registration checks. Findings are sorted by severity. Run it before spending vision tokens on a preview.",
|
|
5814
|
-
{
|
|
5815
|
-
|
|
5879
|
+
"Cheap self-critique without rendering: out_of_canvas (slot leaves the canvas, worst time), static_track, loop_seam, deform_flip (lattice folds over itself \u2014 an error), mesh_fold (a skinned/deformed mesh folds over itself: error when \u2265 5 % of the mesh folds, warning below \u2014 a dense mesh bent hard at a joint pinches the odd hairline triangle), weights_sum (skinned mesh vertex weights not summing to 1), patch_offset (a slot sits away from its asset's source bbox), rest_overlap, unused_asset, never_visible, and for constraints: constraint_dead (every mix is 0 at rest and no track lifts one \u2014 the solver skips it), constraint_cycle (the target is a bone the constraint drives, or hangs off one: it reads what it writes \u2014 an error), ik_unreachable (the target sits beyond the chain's reach for over 20 % of an animation, so the limb hangs straight), path_missing (a path constraint whose target slot never shows a path attachment \u2014 an error). Motion diagnostics also cover pass_key_stop, velocity_jump, acceleration_jump, loop_velocity and constraint_order; provide secondaryBones, frontOf slot pairs and named attachment UV landmarks for brief-specific frozen-motion, ordering and swap-registration checks. Findings are sorted by severity; each line names its subject (a track as target.id.prop), animation and time. `ignore` drops findings you have read and accept \u2014 by code, narrowed by subject and animation; the reply counts them and names a rule that matched nothing. Run it before spending vision tokens on a preview.",
|
|
5880
|
+
{
|
|
5881
|
+
path: Path,
|
|
5882
|
+
animation: z3.string().optional().describe("limit to one animation (rest pose is always checked)"),
|
|
5883
|
+
fps: z3.number().positive().optional(),
|
|
5884
|
+
deformFps: z3.number().positive().optional(),
|
|
5885
|
+
secondaryBones: z3.array(z3.string()).optional(),
|
|
5886
|
+
frontOf: z3.array(z3.tuple([z3.string(), z3.string()])).optional(),
|
|
5887
|
+
landmarks: z3.record(z3.record(z3.tuple([z3.number(), z3.number()]))).optional(),
|
|
5888
|
+
landmarkTolerance: z3.number().nonnegative().optional(),
|
|
5889
|
+
ignore: z3.array(z3.object({ code: z3.string(), subject: z3.string().optional(), animation: z3.string().optional() }).strict()).optional()
|
|
5890
|
+
},
|
|
5891
|
+
async ({ path, ignore = [], ...opts }) => {
|
|
5816
5892
|
try {
|
|
5817
|
-
return text(
|
|
5893
|
+
return text(summarizeIgnoring(checkRig(await load(path), opts), ignore, 100));
|
|
5818
5894
|
} catch (e) {
|
|
5819
5895
|
return fail(e);
|
|
5820
5896
|
}
|
|
@@ -6431,9 +6507,9 @@ tool(
|
|
|
6431
6507
|
);
|
|
6432
6508
|
tool(
|
|
6433
6509
|
"rig_adjust_track",
|
|
6434
|
-
"Re-tune an existing track without retyping keys: amplitude scales its deviation from the rest value (0.5 = half as strong, 2 = twice; scale props as ratios), offset adds a constant, timeScale stretches its key times (duration grows to fit). Drops the preset link.",
|
|
6510
|
+
"Re-tune an existing track without retyping keys: amplitude scales its deviation from the rest value (0.5 = half as strong, 2 = twice; scale props as ratios), offset adds a constant, timeScale stretches its key times (duration grows to fit), interpolation switches segment / pchip / spline. Drops the preset link.",
|
|
6435
6511
|
{ path: Path, animation: z3.string(), ...TrackRef, amplitude: z3.number().optional(), offset: z3.number().optional(), timeScale: z3.number().positive().optional() },
|
|
6436
|
-
async ({ path, animation, target, id, prop, ...adj }) => mutate(path, (d) => adjustTrack(d, animation, { target, id, prop }, adj))
|
|
6512
|
+
async ({ path, animation, target, id, prop, interpolation, ...adj }) => mutate(path, (d) => adjustTrack(d, animation, { target, id, prop, interpolation }, adj))
|
|
6437
6513
|
);
|
|
6438
6514
|
tool(
|
|
6439
6515
|
"rig_set_animation",
|
|
@@ -6451,7 +6527,7 @@ tool(
|
|
|
6451
6527
|
tool(
|
|
6452
6528
|
"rig_remove_keys",
|
|
6453
6529
|
"Remove the keys of one track at the given times; an emptied track is dropped. Drops the preset link.",
|
|
6454
|
-
{ path: Path, animation: z3.string(), ...
|
|
6530
|
+
{ path: Path, animation: z3.string(), ...TrackId, times: z3.array(z3.number()).min(1) },
|
|
6455
6531
|
async ({ path, animation, target, id, prop, times: times2 }) => mutate(path, (d) => removeKeys(d, animation, { target, id, prop }, times2))
|
|
6456
6532
|
);
|
|
6457
6533
|
tool(
|