@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/cli.js
CHANGED
|
@@ -251,7 +251,7 @@ var Key = z.object({
|
|
|
251
251
|
/** Optional independent easing for numeric-array components (for example Spine RGBA curves). */
|
|
252
252
|
componentEase: z.array(Ease).min(1).optional()
|
|
253
253
|
});
|
|
254
|
-
var Interpolation = { interpolation: z.enum(["segment", "pchip"]).optional() };
|
|
254
|
+
var Interpolation = { interpolation: z.enum(["segment", "pchip", "spline"]).optional() };
|
|
255
255
|
var Track = z.discriminatedUnion("target", [
|
|
256
256
|
z.object({ target: z.literal("control"), id: Id, prop: z.literal("value"), ...Interpolation, keys: z.array(Key).min(1) }),
|
|
257
257
|
z.object({ target: z.literal("bone"), id: Id, prop: BoneProp, ...Interpolation, keys: z.array(Key).min(1) }),
|
|
@@ -737,6 +737,57 @@ function smoothValue(track, i, t, component, loop) {
|
|
|
737
737
|
const u = (t - k[i].t) / h(i), u2 = u * u, u3 = u2 * u;
|
|
738
738
|
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);
|
|
739
739
|
}
|
|
740
|
+
function tridiagonal(a, b, c, r) {
|
|
741
|
+
const n = b.length, cp = [], x = [];
|
|
742
|
+
let beta = b[0];
|
|
743
|
+
x[0] = r[0] / beta;
|
|
744
|
+
for (let i = 1; i < n; i++) {
|
|
745
|
+
cp[i] = c[i - 1] / beta;
|
|
746
|
+
beta = b[i] - a[i] * cp[i];
|
|
747
|
+
x[i] = (r[i] - a[i] * x[i - 1]) / beta;
|
|
748
|
+
}
|
|
749
|
+
for (let i = n - 2; i >= 0; i--) x[i] -= cp[i + 1] * x[i + 1];
|
|
750
|
+
return x;
|
|
751
|
+
}
|
|
752
|
+
function splineMoments(t, v, periodic) {
|
|
753
|
+
const n = t.length, h = (j2) => t[j2 + 1] - t[j2], d = (j2) => (v[j2 + 1] - v[j2]) / h(j2);
|
|
754
|
+
if (!periodic) {
|
|
755
|
+
const a2 = [], b2 = [], c2 = [], r2 = [];
|
|
756
|
+
for (let i = 0; i < n; i++) {
|
|
757
|
+
const hp = i > 0 ? h(i - 1) : 0, hn = i < n - 1 ? h(i) : 0;
|
|
758
|
+
a2.push(hp);
|
|
759
|
+
b2.push(2 * (hp + hn));
|
|
760
|
+
c2.push(hn);
|
|
761
|
+
r2.push(6 * ((i < n - 1 ? d(i) : 0) - (i > 0 ? d(i - 1) : 0)));
|
|
762
|
+
}
|
|
763
|
+
return tridiagonal(a2, b2, c2, r2);
|
|
764
|
+
}
|
|
765
|
+
const m = n - 1, a = [], b = [], c = [], r = [];
|
|
766
|
+
for (let i = 0; i < m; i++) {
|
|
767
|
+
const p = (i + m - 1) % m;
|
|
768
|
+
a.push(h(p));
|
|
769
|
+
b.push(2 * (h(p) + h(i)));
|
|
770
|
+
c.push(h(i));
|
|
771
|
+
r.push(6 * (d(i) - d(p)));
|
|
772
|
+
}
|
|
773
|
+
const top = a[0], bottom = c[m - 1], gamma = -b[0];
|
|
774
|
+
const bb = [...b];
|
|
775
|
+
bb[0] -= gamma;
|
|
776
|
+
bb[m - 1] -= bottom * top / gamma;
|
|
777
|
+
const x = tridiagonal(a, bb, c, r), u = new Array(m).fill(0);
|
|
778
|
+
u[0] = gamma;
|
|
779
|
+
u[m - 1] = bottom;
|
|
780
|
+
const z4 = tridiagonal(a, bb, c, u);
|
|
781
|
+
const f = (x[0] + top * x[m - 1] / gamma) / (1 + z4[0] + top * z4[m - 1] / gamma);
|
|
782
|
+
const M = x.map((xi, i) => xi - f * z4[i]);
|
|
783
|
+
return [...M, M[0]];
|
|
784
|
+
}
|
|
785
|
+
function splineValue(track, i, t, component, loop) {
|
|
786
|
+
const k = track.keys, ts = k.map((x) => x.t), vs = k.map((x) => component === void 0 ? x.v : x.v[component]);
|
|
787
|
+
const M = splineMoments(ts, vs, loop && k.length > 3 && vs[0] === vs[k.length - 1]);
|
|
788
|
+
const H = ts[i + 1] - ts[i], A = (ts[i + 1] - t) / H, B = (t - ts[i]) / H;
|
|
789
|
+
return A * vs[i] + B * vs[i + 1] + ((A ** 3 - A) * M[i] + (B ** 3 - B) * M[i + 1]) * H * H / 6;
|
|
790
|
+
}
|
|
740
791
|
function sample(track, t, loop = false) {
|
|
741
792
|
const keys = track.keys;
|
|
742
793
|
if (t <= keys[0].t) return keys[0].v;
|
|
@@ -748,11 +799,13 @@ function sample(track, t, loop = false) {
|
|
|
748
799
|
if (a.ease === "step") return a.v;
|
|
749
800
|
if (typeof a.v === "number" && typeof b.v === "number") {
|
|
750
801
|
if (track.interpolation === "pchip") return smoothValue(track, i, t, void 0, loop);
|
|
802
|
+
if (track.interpolation === "spline") return splineValue(track, i, t, void 0, loop);
|
|
751
803
|
const u = ease(a.ease)((t - a.t) / (b.t - a.t));
|
|
752
804
|
return a.v + (b.v - a.v) * u;
|
|
753
805
|
}
|
|
754
806
|
if (Array.isArray(a.v) && Array.isArray(b.v) && typeof a.v[0] === "number") {
|
|
755
807
|
if (track.interpolation === "pchip") return a.v.map((_, j2) => smoothValue(track, i, t, j2, loop));
|
|
808
|
+
if (track.interpolation === "spline") return a.v.map((_, j2) => splineValue(track, i, t, j2, loop));
|
|
756
809
|
const phase = (t - a.t) / (b.t - a.t), av = a.v, bv = b.v;
|
|
757
810
|
return av.map((v, k) => v + ((bv[k] ?? v) - v) * ease(a.componentEase?.[k] ?? a.ease)(phase));
|
|
758
811
|
}
|
|
@@ -2484,7 +2537,8 @@ function adjustTrack(doc, animId, track, adj) {
|
|
|
2484
2537
|
return { ...k, t: r42(adj.timeScale ? k.t * adj.timeScale : k.t), v: r42(v) };
|
|
2485
2538
|
});
|
|
2486
2539
|
const duration = Math.max(anim.duration, keys[keys.length - 1].t);
|
|
2487
|
-
const
|
|
2540
|
+
const interpolation = track.interpolation ?? tr.interpolation;
|
|
2541
|
+
const next = { ...anim, duration, tracks: anim.tracks.map((t) => same(t) ? { ...t, keys, ...interpolation && { interpolation } } : t), preset: void 0 };
|
|
2488
2542
|
return { ...doc, animations: doc.animations.map((a) => a.id === animId ? next : a) };
|
|
2489
2543
|
}
|
|
2490
2544
|
function removeAnimation(doc, id) {
|
|
@@ -2901,19 +2955,20 @@ var requireSlot = (doc, id) => {
|
|
|
2901
2955
|
if (!s2) throw new Error(`unknown slot "${id}"`);
|
|
2902
2956
|
return s2;
|
|
2903
2957
|
};
|
|
2904
|
-
function sampled(duration, n, f
|
|
2958
|
+
function sampled(duration, n, f) {
|
|
2905
2959
|
const keys = [];
|
|
2906
2960
|
for (let i = 0; i <= n; i++) {
|
|
2907
2961
|
const t = +(duration * i / n).toFixed(4);
|
|
2908
|
-
keys.push({ t, v: +f(t).toFixed(4) + 0, ease:
|
|
2962
|
+
keys.push({ t, v: +f(t).toFixed(4) + 0, ease: "linear" });
|
|
2909
2963
|
}
|
|
2910
2964
|
return keys;
|
|
2911
2965
|
}
|
|
2912
|
-
|
|
2913
|
-
|
|
2966
|
+
var smooth = "spline";
|
|
2967
|
+
function boneTrack(bone, prop, keys, interpolation) {
|
|
2968
|
+
return { target: "bone", id: bone.id, prop, keys, ...interpolation && { interpolation } };
|
|
2914
2969
|
}
|
|
2915
|
-
function deformTrack(slot, prop, keys) {
|
|
2916
|
-
return slot.attachment ? { target: "deform", id: slot.attachment, prop, keys } : void 0;
|
|
2970
|
+
function deformTrack(slot, prop, keys, interpolation) {
|
|
2971
|
+
return slot.attachment ? { target: "deform", id: slot.attachment, prop, keys, ...interpolation && { interpolation } } : void 0;
|
|
2917
2972
|
}
|
|
2918
2973
|
var pushTrack = (tracks, t) => {
|
|
2919
2974
|
if (t) tracks.push(t);
|
|
@@ -2937,18 +2992,18 @@ var idleBreathing = {
|
|
|
2937
2992
|
const armsR = byRole(doc, "arm_r", "arm_upper_r");
|
|
2938
2993
|
const chest = slotOfBone(doc, torso);
|
|
2939
2994
|
if (chest?.attachment) {
|
|
2940
|
-
pushTrack(tracks, deformTrack(chest, "bulge", sampled(T2, 8, (t) => p.amplitude * Math.sin(w * t),
|
|
2995
|
+
pushTrack(tracks, deformTrack(chest, "bulge", sampled(T2, 8, (t) => p.amplitude * Math.sin(w * t)), smooth));
|
|
2941
2996
|
} else {
|
|
2942
2997
|
const r = torso.transform;
|
|
2943
|
-
tracks.push(boneTrack(torso, "scaleY", sampled(T2, 8, (t) => r.scaleY + p.amplitude * Math.sin(w * t),
|
|
2944
|
-
tracks.push(boneTrack(torso, "scaleX", sampled(T2, 8, (t) => r.scaleX - p.amplitude * 0.4 * Math.sin(w * t),
|
|
2998
|
+
tracks.push(boneTrack(torso, "scaleY", sampled(T2, 8, (t) => r.scaleY + p.amplitude * Math.sin(w * t)), smooth));
|
|
2999
|
+
tracks.push(boneTrack(torso, "scaleX", sampled(T2, 8, (t) => r.scaleX - p.amplitude * 0.4 * Math.sin(w * t)), smooth));
|
|
2945
3000
|
}
|
|
2946
3001
|
for (const h of heads)
|
|
2947
|
-
tracks.push(boneTrack(h, "y", sampled(T2, 8, (t) => h.transform.y - p.headBob * Math.sin(w * t + 0.4),
|
|
3002
|
+
tracks.push(boneTrack(h, "y", sampled(T2, 8, (t) => h.transform.y - p.headBob * Math.sin(w * t + 0.4)), smooth));
|
|
2948
3003
|
for (const a of armsL)
|
|
2949
|
-
tracks.push(boneTrack(a, "rotation", sampled(T2, 8, (t) => a.transform.rotation + p.armSway * Math.sin(w * t + 0.8),
|
|
3004
|
+
tracks.push(boneTrack(a, "rotation", sampled(T2, 8, (t) => a.transform.rotation + p.armSway * Math.sin(w * t + 0.8)), smooth));
|
|
2950
3005
|
for (const a of armsR)
|
|
2951
|
-
tracks.push(boneTrack(a, "rotation", sampled(T2, 8, (t) => a.transform.rotation - p.armSway * Math.sin(w * t + 0.8),
|
|
3006
|
+
tracks.push(boneTrack(a, "rotation", sampled(T2, 8, (t) => a.transform.rotation - p.armSway * Math.sin(w * t + 0.8)), smooth));
|
|
2952
3007
|
return finish(this.name, p.id, T2, true, tracks, p);
|
|
2953
3008
|
}
|
|
2954
3009
|
};
|
|
@@ -2957,22 +3012,20 @@ var winBounce = {
|
|
|
2957
3012
|
defaults: { id: "win", duration: 1.2, height: 40, bounces: 2, squash: 0.15 },
|
|
2958
3013
|
build(doc, p) {
|
|
2959
3014
|
const b = root(doc), r = b.transform, tracks = [];
|
|
2960
|
-
const yK = [[0, r.y,
|
|
2961
|
-
const sxK = [[0, r.scaleX, "ease_out"]];
|
|
2962
|
-
const syK = [[0, r.scaleY, "ease_out"]];
|
|
3015
|
+
const yK = [[0, r.y]], sxK = [[0, r.scaleX]], syK = [[0, r.scaleY]];
|
|
2963
3016
|
const per = p.duration / p.bounces;
|
|
2964
3017
|
for (let i = 0; i < p.bounces; i++) {
|
|
2965
3018
|
const t0 = i * per, h = p.height * 0.55 ** i, s2 = p.squash * 0.7 ** i;
|
|
2966
|
-
sxK.push([t0 + per * 0.08, r.scaleX * (1 + s2)
|
|
2967
|
-
syK.push([t0 + per * 0.08, r.scaleY * (1 - s2)
|
|
2968
|
-
yK.push([t0 + per * 0.08, r.y
|
|
3019
|
+
sxK.push([t0 + per * 0.08, r.scaleX * (1 + s2)], [t0 + per * 0.5, r.scaleX * (1 - s2 * 0.6)], [t0 + per * 0.95, r.scaleX * (1 + s2)]);
|
|
3020
|
+
syK.push([t0 + per * 0.08, r.scaleY * (1 - s2)], [t0 + per * 0.5, r.scaleY * (1 + s2 * 0.6)], [t0 + per * 0.95, r.scaleY * (1 - s2)]);
|
|
3021
|
+
yK.push([t0 + per * 0.08, r.y], [t0 + per * 0.5, r.y - h], [t0 + per * 0.95, r.y]);
|
|
2969
3022
|
}
|
|
2970
3023
|
yK.push([p.duration, r.y]);
|
|
2971
3024
|
sxK.push([p.duration, r.scaleX]);
|
|
2972
3025
|
syK.push([p.duration, r.scaleY]);
|
|
2973
|
-
tracks.push(boneTrack(b, "y", K(...yK)), boneTrack(b, "scaleX", K(...sxK)), boneTrack(b, "scaleY", K(...syK)));
|
|
3026
|
+
tracks.push(boneTrack(b, "y", K(...yK), "pchip"), boneTrack(b, "scaleX", K(...sxK), smooth), boneTrack(b, "scaleY", K(...syK), smooth));
|
|
2974
3027
|
for (const h of byRole(doc, "head"))
|
|
2975
|
-
tracks.push(boneTrack(h, "rotation", K([0, h.transform.rotation], [p.duration * 0.25, h.transform.rotation - 6
|
|
3028
|
+
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));
|
|
2976
3029
|
const anim = finish(this.name, p.id, p.duration, false, tracks, p);
|
|
2977
3030
|
anim.events.push({ t: 0, name: "win_start" }, { t: per * 0.95, name: "land" });
|
|
2978
3031
|
return anim;
|
|
@@ -2985,8 +3038,8 @@ var anticipationShake = {
|
|
|
2985
3038
|
const b = root(doc), r = b.transform, tracks = [];
|
|
2986
3039
|
const n = Math.ceil(p.duration * p.frequency * 4);
|
|
2987
3040
|
const env = (t) => p.grow ? t / p.duration : 1;
|
|
2988
|
-
tracks.push(boneTrack(b, "x", sampled(p.duration, n, (t) => r.x + p.amplitude * env(t) * Math.sin(2 * Math.PI * p.frequency * t),
|
|
2989
|
-
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),
|
|
3041
|
+
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));
|
|
3042
|
+
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));
|
|
2990
3043
|
tracks.push(boneTrack(b, "scaleX", K([0, r.scaleX, "ease_in"], [p.duration, r.scaleX * p.scaleUp])));
|
|
2991
3044
|
tracks.push(boneTrack(b, "scaleY", K([0, r.scaleY, "ease_in"], [p.duration, r.scaleY * p.scaleUp])));
|
|
2992
3045
|
return finish(this.name, p.id, p.duration, false, tracks, p);
|
|
@@ -3011,6 +3064,7 @@ var mouthTalk = {
|
|
|
3011
3064
|
if (!p.frames.length) throw new Error("mouth_talk: frames must not be empty");
|
|
3012
3065
|
const n = Math.max(1, Math.round(p.duration * p.rate));
|
|
3013
3066
|
const keys = S(...Array.from({ length: n }, (_, i) => [+(i / p.rate).toFixed(4), p.frames[i % p.frames.length]]));
|
|
3067
|
+
if (keys.at(-1).t < p.duration && keys.at(-1).v !== keys[0].v) keys.push(...S([p.duration, keys[0].v]));
|
|
3014
3068
|
return finish(this.name, p.id, p.duration, true, [slotTrack(slot, "attachment", keys)], p);
|
|
3015
3069
|
}
|
|
3016
3070
|
};
|
|
@@ -3028,10 +3082,10 @@ var symbolIdle = {
|
|
|
3028
3082
|
build(doc, p) {
|
|
3029
3083
|
const b = root(doc), r = b.transform, T2 = p.period, w = 2 * Math.PI / T2, tracks = [];
|
|
3030
3084
|
const rise = (t) => 0.5 - 0.5 * Math.cos(w * t);
|
|
3031
|
-
tracks.push(boneTrack(b, "y", sampled(T2, 8, (t) => r.y - p.float * rise(t),
|
|
3032
|
-
if (p.tilt) tracks.push(boneTrack(b, "rotation", sampled(T2, 8, (t) => r.rotation + p.tilt * Math.sin(w * t),
|
|
3085
|
+
tracks.push(boneTrack(b, "y", sampled(T2, 8, (t) => r.y - p.float * rise(t)), smooth));
|
|
3086
|
+
if (p.tilt) tracks.push(boneTrack(b, "rotation", sampled(T2, 8, (t) => r.rotation + p.tilt * Math.sin(w * t)), smooth));
|
|
3033
3087
|
if (p.pulse) for (const s2 of doc.slots.filter((s3) => s3.bone === b.id && s3.attachment))
|
|
3034
|
-
pushTrack(tracks, deformTrack(s2, "bulge", sampled(T2, 8, (t) => p.pulse * rise(t),
|
|
3088
|
+
pushTrack(tracks, deformTrack(s2, "bulge", sampled(T2, 8, (t) => p.pulse * rise(t)), smooth));
|
|
3035
3089
|
return finish(this.name, p.id, T2, true, tracks, p);
|
|
3036
3090
|
}
|
|
3037
3091
|
};
|
|
@@ -3041,12 +3095,12 @@ var symbolWin = {
|
|
|
3041
3095
|
build(doc, p) {
|
|
3042
3096
|
const b = root(doc), r = b.transform, d = p.duration, tracks = [];
|
|
3043
3097
|
for (const [prop, rest] of [["scaleX", r.scaleX], ["scaleY", r.scaleY]])
|
|
3044
|
-
tracks.push(boneTrack(b, prop, K([0, rest
|
|
3098
|
+
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));
|
|
3045
3099
|
if (p.wobble) {
|
|
3046
3100
|
const n = Math.max(8, p.wobbles * 4);
|
|
3047
|
-
const keys = sampled(d, n, (t) => p.wobble * Math.exp(-3 * t / d) * Math.sin(2 * Math.PI * p.wobbles * t / d)
|
|
3101
|
+
const keys = sampled(d, n, (t) => p.wobble * Math.exp(-3 * t / d) * Math.sin(2 * Math.PI * p.wobbles * t / d));
|
|
3048
3102
|
keys[keys.length - 1].v = 0;
|
|
3049
|
-
for (const s2 of doc.slots.filter((s3) => s3.bone === b.id && s3.attachment)) pushTrack(tracks, deformTrack(s2, "sway", keys));
|
|
3103
|
+
for (const s2 of doc.slots.filter((s3) => s3.bone === b.id && s3.attachment)) pushTrack(tracks, deformTrack(s2, "sway", keys, smooth));
|
|
3050
3104
|
}
|
|
3051
3105
|
const anim = finish(this.name, p.id, d, false, tracks, p);
|
|
3052
3106
|
anim.events.push({ t: 0, name: "win_start" });
|
|
@@ -3060,11 +3114,11 @@ var weightShift = {
|
|
|
3060
3114
|
const T2 = p.period, w = 2 * Math.PI / T2, N = 16, tracks = [];
|
|
3061
3115
|
const hips = byRole(doc, "hips")[0] ?? root(doc), hr = hips.transform;
|
|
3062
3116
|
const s2 = (t) => Math.sin(w * t);
|
|
3063
|
-
tracks.push(boneTrack(hips, "x", sampled(T2, N, (t) => hr.x + p.shift * s2(t))));
|
|
3064
|
-
if (p.dip) tracks.push(boneTrack(hips, "y", sampled(T2, N, (t) => hr.y + p.dip * s2(t) ** 2)));
|
|
3117
|
+
tracks.push(boneTrack(hips, "x", sampled(T2, N, (t) => hr.x + p.shift * s2(t)), smooth));
|
|
3118
|
+
if (p.dip) tracks.push(boneTrack(hips, "y", sampled(T2, N, (t) => hr.y + p.dip * s2(t) ** 2), smooth));
|
|
3065
3119
|
const torso = byRole(doc, "torso", "body")[0];
|
|
3066
|
-
if (torso && torso !== hips) tracks.push(boneTrack(torso, "rotation", sampled(T2, N, (t) => torso.transform.rotation - p.lean * s2(t))));
|
|
3067
|
-
for (const h of byRole(doc, "head")) tracks.push(boneTrack(h, "rotation", sampled(T2, N, (t) => h.transform.rotation + p.lean / 2 * s2(t))));
|
|
3120
|
+
if (torso && torso !== hips) tracks.push(boneTrack(torso, "rotation", sampled(T2, N, (t) => torso.transform.rotation - p.lean * s2(t)), smooth));
|
|
3121
|
+
for (const h of byRole(doc, "head")) tracks.push(boneTrack(h, "rotation", sampled(T2, N, (t) => h.transform.rotation + p.lean / 2 * s2(t)), smooth));
|
|
3068
3122
|
for (const leg of byRole(doc, "leg_l", "leg_r")) {
|
|
3069
3123
|
const slot = slotOfBone(doc, leg), att = slot && doc.attachments.find((a) => a.id === slot.attachment && a.type === "region");
|
|
3070
3124
|
const asset = att && att.type === "region" ? doc.assets.find((a) => a.id === att.asset) : void 0;
|
|
@@ -3074,8 +3128,8 @@ var weightShift = {
|
|
|
3074
3128
|
length = Math.max(...[[0, -h / 2], [w2 / 2, 0], [0, h / 2], [-w2 / 2, 0]].map(([x, y]) => Math.hypot(...apply(m, x, y))));
|
|
3075
3129
|
}
|
|
3076
3130
|
if (!(length > p.shift)) continue;
|
|
3077
|
-
tracks.push(boneTrack(leg, "rotation", sampled(T2, N, (t) => leg.transform.rotation + Math.asin(p.shift * s2(t) / length) * 180 / Math.PI)));
|
|
3078
|
-
if (p.dip) tracks.push(boneTrack(leg, "y", sampled(T2, N, (t) => leg.transform.y - p.dip * s2(t) ** 2)));
|
|
3131
|
+
tracks.push(boneTrack(leg, "rotation", sampled(T2, N, (t) => leg.transform.rotation + Math.asin(p.shift * s2(t) / length) * 180 / Math.PI), smooth));
|
|
3132
|
+
if (p.dip) tracks.push(boneTrack(leg, "y", sampled(T2, N, (t) => leg.transform.y - p.dip * s2(t) ** 2), smooth));
|
|
3079
3133
|
}
|
|
3080
3134
|
return finish(this.name, p.id, T2, true, tracks, p);
|
|
3081
3135
|
}
|
|
@@ -3318,13 +3372,13 @@ function motionQuality(doc, anim, opts = {}) {
|
|
|
3318
3372
|
const before = delta(values(k.v), values(p.v)), after = delta(values(n.v), values(k.v));
|
|
3319
3373
|
const pass = before.some((v, j2) => v * after[j2] > 1e-6);
|
|
3320
3374
|
const speed = Math.max(magnitude(before) / (k.t - p.t), magnitude(after) / (n.t - k.t));
|
|
3321
|
-
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);
|
|
3375
|
+
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);
|
|
3322
3376
|
const jump = magnitude(delta(left, right));
|
|
3323
3377
|
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);
|
|
3324
3378
|
const acceleration = (a, b, c) => values(at(b)).map((v, j2) => (values(at(c))[j2] - 2 * v + values(at(a))[j2]) / (h * h));
|
|
3325
3379
|
const al = acceleration(k.t - 2 * h, k.t - h, k.t), ar = acceleration(k.t, k.t + h, k.t + 2 * h);
|
|
3326
3380
|
const aj = magnitude(delta(al, ar));
|
|
3327
|
-
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");
|
|
3381
|
+
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");
|
|
3328
3382
|
}
|
|
3329
3383
|
}
|
|
3330
3384
|
const steps = Math.max(2, Math.ceil(D * 60));
|
|
@@ -3806,6 +3860,17 @@ function checkRig(doc, opts = {}) {
|
|
|
3806
3860
|
out.push({ level: "warn", code: "never_visible", subject: s2.id, message: `slot "${s2.id}" has no attachment and no animation ever shows one` });
|
|
3807
3861
|
return out;
|
|
3808
3862
|
}
|
|
3863
|
+
function summarizeIgnoring(findings, rules, max) {
|
|
3864
|
+
const hits = (r, f) => r.code === f.code && (r.subject ?? f.subject) === f.subject && (r.animation ?? f.animation) === f.animation;
|
|
3865
|
+
const kept = findings.filter((f) => !rules.some((r) => hits(r, f)));
|
|
3866
|
+
const idle = rules.filter((r) => !findings.some((f) => hits(r, f)));
|
|
3867
|
+
const notes = [
|
|
3868
|
+
...kept.length < findings.length ? [` (${findings.length - kept.length} ignored)`] : [],
|
|
3869
|
+
...idle.map((r) => ` ignore matched nothing: ${JSON.stringify(r)}`)
|
|
3870
|
+
];
|
|
3871
|
+
return [summarize(kept, max), ...notes].join("\n");
|
|
3872
|
+
}
|
|
3873
|
+
var where = (f) => `${f.subject}${f.animation ? ` in ${f.animation}` : ""}${f.time !== void 0 ? ` @${+f.time.toFixed(4)}s` : ""}`;
|
|
3809
3874
|
function summarize(findings, max = 12) {
|
|
3810
3875
|
if (!findings.length) return "check: OK";
|
|
3811
3876
|
const rank = { error: 0, warn: 1, info: 2 };
|
|
@@ -3820,7 +3885,7 @@ function summarize(findings, max = 12) {
|
|
|
3820
3885
|
const lines = [];
|
|
3821
3886
|
for (const f of sorted) {
|
|
3822
3887
|
const shown = shownPerCode.get(f.code) ?? 0;
|
|
3823
|
-
if (shown < PER_CODE) lines.push(` [${f.level}] ${f.code}: ${f.message}`);
|
|
3888
|
+
if (shown < PER_CODE) lines.push(` [${f.level}] ${f.code} ${where(f)}: ${f.message}`);
|
|
3824
3889
|
else if (shown === PER_CODE) lines.push(` \u2026 ${codeTotal.get(f.code) - PER_CODE} more ${f.code}`);
|
|
3825
3890
|
shownPerCode.set(f.code, shown + 1);
|
|
3826
3891
|
}
|
|
@@ -4011,7 +4076,7 @@ function packRects(rects, opts = {}) {
|
|
|
4011
4076
|
shelf = Math.max(shelf, r.height);
|
|
4012
4077
|
}
|
|
4013
4078
|
const height = y + shelf + pad;
|
|
4014
|
-
if (height <= max) return { width, height, frames };
|
|
4079
|
+
if (height <= max) return { width: Math.max(pad, ...[...frames.values()].map(([fx, , fw]) => fx + fw)) + pad, height, frames };
|
|
4015
4080
|
if (width >= max) throw new Error(`atlas does not fit in ${max}\xD7${max} (needs ${width}\xD7${height})`);
|
|
4016
4081
|
width *= 2;
|
|
4017
4082
|
}
|
|
@@ -5618,7 +5683,8 @@ function previewCheck(doc, animation) {
|
|
|
5618
5683
|
const a = animation ? doc.animations.find((x) => x.id === animation) : void 0;
|
|
5619
5684
|
return a ? `check: OK for "${a.id}" (${a.tracks.length} tracks, ${a.duration}s${a.loop ? " loop" : ""})` : "check: OK (rest pose)";
|
|
5620
5685
|
}
|
|
5621
|
-
var
|
|
5686
|
+
var TrackId = { target: z3.enum(["bone", "slot", "deform", "ik", "transform", "path", "drawOrder", "control"]), id: z3.string(), prop: z3.string() };
|
|
5687
|
+
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 };
|
|
5622
5688
|
tool(
|
|
5623
5689
|
"rig_set_control",
|
|
5624
5690
|
"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.",
|
|
@@ -5811,11 +5877,21 @@ tool(
|
|
|
5811
5877
|
);
|
|
5812
5878
|
tool(
|
|
5813
5879
|
"rig_check",
|
|
5814
|
-
"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.",
|
|
5815
|
-
{
|
|
5816
|
-
|
|
5880
|
+
"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.",
|
|
5881
|
+
{
|
|
5882
|
+
path: Path,
|
|
5883
|
+
animation: z3.string().optional().describe("limit to one animation (rest pose is always checked)"),
|
|
5884
|
+
fps: z3.number().positive().optional(),
|
|
5885
|
+
deformFps: z3.number().positive().optional(),
|
|
5886
|
+
secondaryBones: z3.array(z3.string()).optional(),
|
|
5887
|
+
frontOf: z3.array(z3.tuple([z3.string(), z3.string()])).optional(),
|
|
5888
|
+
landmarks: z3.record(z3.record(z3.tuple([z3.number(), z3.number()]))).optional(),
|
|
5889
|
+
landmarkTolerance: z3.number().nonnegative().optional(),
|
|
5890
|
+
ignore: z3.array(z3.object({ code: z3.string(), subject: z3.string().optional(), animation: z3.string().optional() }).strict()).optional()
|
|
5891
|
+
},
|
|
5892
|
+
async ({ path, ignore = [], ...opts }) => {
|
|
5817
5893
|
try {
|
|
5818
|
-
return text(
|
|
5894
|
+
return text(summarizeIgnoring(checkRig(await load(path), opts), ignore, 100));
|
|
5819
5895
|
} catch (e) {
|
|
5820
5896
|
return fail(e);
|
|
5821
5897
|
}
|
|
@@ -6432,9 +6508,9 @@ tool(
|
|
|
6432
6508
|
);
|
|
6433
6509
|
tool(
|
|
6434
6510
|
"rig_adjust_track",
|
|
6435
|
-
"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.",
|
|
6511
|
+
"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.",
|
|
6436
6512
|
{ path: Path, animation: z3.string(), ...TrackRef, amplitude: z3.number().optional(), offset: z3.number().optional(), timeScale: z3.number().positive().optional() },
|
|
6437
|
-
async ({ path, animation, target, id, prop, ...adj }) => mutate(path, (d) => adjustTrack(d, animation, { target, id, prop }, adj))
|
|
6513
|
+
async ({ path, animation, target, id, prop, interpolation, ...adj }) => mutate(path, (d) => adjustTrack(d, animation, { target, id, prop, interpolation }, adj))
|
|
6438
6514
|
);
|
|
6439
6515
|
tool(
|
|
6440
6516
|
"rig_set_animation",
|
|
@@ -6452,7 +6528,7 @@ tool(
|
|
|
6452
6528
|
tool(
|
|
6453
6529
|
"rig_remove_keys",
|
|
6454
6530
|
"Remove the keys of one track at the given times; an emptied track is dropped. Drops the preset link.",
|
|
6455
|
-
{ path: Path, animation: z3.string(), ...
|
|
6531
|
+
{ path: Path, animation: z3.string(), ...TrackId, times: z3.array(z3.number()).min(1) },
|
|
6456
6532
|
async ({ path, animation, target, id, prop, times: times2 }) => mutate(path, (d) => removeKeys(d, animation, { target, id, prop }, times2))
|
|
6457
6533
|
);
|
|
6458
6534
|
tool(
|