@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/agent.d.ts
CHANGED
|
@@ -6,7 +6,7 @@ export declare const TEXT_ARGS: readonly ["animation", "asset", "attachment", "b
|
|
|
6
6
|
/** Nested arguments whose string values golem reads from disk at call time (resolved from the rig's directory); each value is checked and kept as written. */
|
|
7
7
|
export declare const NESTED_PATH_ARGS: readonly ["debugFillMasks"];
|
|
8
8
|
/** Strings nested inside other arguments that are not paths (or only land in rig.json as data); the test enumerates them. */
|
|
9
|
-
export declare const NESTED_TEXT_ARGS: readonly ["a", "animation", "b", "bone", "control", "from", "id", "label", "name", "prompt", "prop", "requiredAnimations", "sourceImage", "src", "to", "v", "whenSlotVisible"];
|
|
9
|
+
export declare const NESTED_TEXT_ARGS: readonly ["a", "animation", "b", "bone", "code", "control", "from", "id", "label", "name", "prompt", "prop", "requiredAnimations", "sourceImage", "src", "subject", "to", "v", "whenSlotVisible"];
|
|
10
10
|
/**
|
|
11
11
|
* `args` with every path made absolute and checked to stay under `root`; throws naming the argument otherwise. Top-level PATH_ARGS are resolved from
|
|
12
12
|
* the rig's directory and checked (an empty one is left as given, STORED ones stay relative); an empty `path` is refused; NESTED_PATH_ARGS (debugFillMasks values) are checked against it and kept as written.
|
package/dist/lib/e8-agent.js
CHANGED
|
@@ -297,7 +297,7 @@ var Key = z.object({
|
|
|
297
297
|
/** Optional independent easing for numeric-array components (for example Spine RGBA curves). */
|
|
298
298
|
componentEase: z.array(Ease).min(1).optional()
|
|
299
299
|
});
|
|
300
|
-
var Interpolation = { interpolation: z.enum(["segment", "pchip"]).optional() };
|
|
300
|
+
var Interpolation = { interpolation: z.enum(["segment", "pchip", "spline"]).optional() };
|
|
301
301
|
var Track = z.discriminatedUnion("target", [
|
|
302
302
|
z.object({ target: z.literal("control"), id: Id, prop: z.literal("value"), ...Interpolation, keys: z.array(Key).min(1) }),
|
|
303
303
|
z.object({ target: z.literal("bone"), id: Id, prop: BoneProp, ...Interpolation, keys: z.array(Key).min(1) }),
|
|
@@ -731,6 +731,57 @@ function smoothValue(track, i, t, component, loop) {
|
|
|
731
731
|
const u = (t - k[i].t) / h(i), u2 = u * u, u3 = u2 * u;
|
|
732
732
|
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);
|
|
733
733
|
}
|
|
734
|
+
function tridiagonal(a, b, c, r) {
|
|
735
|
+
const n = b.length, cp = [], x = [];
|
|
736
|
+
let beta = b[0];
|
|
737
|
+
x[0] = r[0] / beta;
|
|
738
|
+
for (let i = 1; i < n; i++) {
|
|
739
|
+
cp[i] = c[i - 1] / beta;
|
|
740
|
+
beta = b[i] - a[i] * cp[i];
|
|
741
|
+
x[i] = (r[i] - a[i] * x[i - 1]) / beta;
|
|
742
|
+
}
|
|
743
|
+
for (let i = n - 2; i >= 0; i--) x[i] -= cp[i + 1] * x[i + 1];
|
|
744
|
+
return x;
|
|
745
|
+
}
|
|
746
|
+
function splineMoments(t, v, periodic) {
|
|
747
|
+
const n = t.length, h = (j2) => t[j2 + 1] - t[j2], d = (j2) => (v[j2 + 1] - v[j2]) / h(j2);
|
|
748
|
+
if (!periodic) {
|
|
749
|
+
const a2 = [], b2 = [], c2 = [], r2 = [];
|
|
750
|
+
for (let i = 0; i < n; i++) {
|
|
751
|
+
const hp = i > 0 ? h(i - 1) : 0, hn = i < n - 1 ? h(i) : 0;
|
|
752
|
+
a2.push(hp);
|
|
753
|
+
b2.push(2 * (hp + hn));
|
|
754
|
+
c2.push(hn);
|
|
755
|
+
r2.push(6 * ((i < n - 1 ? d(i) : 0) - (i > 0 ? d(i - 1) : 0)));
|
|
756
|
+
}
|
|
757
|
+
return tridiagonal(a2, b2, c2, r2);
|
|
758
|
+
}
|
|
759
|
+
const m = n - 1, a = [], b = [], c = [], r = [];
|
|
760
|
+
for (let i = 0; i < m; i++) {
|
|
761
|
+
const p = (i + m - 1) % m;
|
|
762
|
+
a.push(h(p));
|
|
763
|
+
b.push(2 * (h(p) + h(i)));
|
|
764
|
+
c.push(h(i));
|
|
765
|
+
r.push(6 * (d(i) - d(p)));
|
|
766
|
+
}
|
|
767
|
+
const top = a[0], bottom = c[m - 1], gamma = -b[0];
|
|
768
|
+
const bb = [...b];
|
|
769
|
+
bb[0] -= gamma;
|
|
770
|
+
bb[m - 1] -= bottom * top / gamma;
|
|
771
|
+
const x = tridiagonal(a, bb, c, r), u = new Array(m).fill(0);
|
|
772
|
+
u[0] = gamma;
|
|
773
|
+
u[m - 1] = bottom;
|
|
774
|
+
const z4 = tridiagonal(a, bb, c, u);
|
|
775
|
+
const f = (x[0] + top * x[m - 1] / gamma) / (1 + z4[0] + top * z4[m - 1] / gamma);
|
|
776
|
+
const M = x.map((xi, i) => xi - f * z4[i]);
|
|
777
|
+
return [...M, M[0]];
|
|
778
|
+
}
|
|
779
|
+
function splineValue(track, i, t, component, loop) {
|
|
780
|
+
const k = track.keys, ts = k.map((x) => x.t), vs = k.map((x) => component === void 0 ? x.v : x.v[component]);
|
|
781
|
+
const M = splineMoments(ts, vs, loop && k.length > 3 && vs[0] === vs[k.length - 1]);
|
|
782
|
+
const H = ts[i + 1] - ts[i], A = (ts[i + 1] - t) / H, B = (t - ts[i]) / H;
|
|
783
|
+
return A * vs[i] + B * vs[i + 1] + ((A ** 3 - A) * M[i] + (B ** 3 - B) * M[i + 1]) * H * H / 6;
|
|
784
|
+
}
|
|
734
785
|
function sample(track, t, loop = false) {
|
|
735
786
|
const keys = track.keys;
|
|
736
787
|
if (t <= keys[0].t) return keys[0].v;
|
|
@@ -742,11 +793,13 @@ function sample(track, t, loop = false) {
|
|
|
742
793
|
if (a.ease === "step") return a.v;
|
|
743
794
|
if (typeof a.v === "number" && typeof b.v === "number") {
|
|
744
795
|
if (track.interpolation === "pchip") return smoothValue(track, i, t, void 0, loop);
|
|
796
|
+
if (track.interpolation === "spline") return splineValue(track, i, t, void 0, loop);
|
|
745
797
|
const u = ease(a.ease)((t - a.t) / (b.t - a.t));
|
|
746
798
|
return a.v + (b.v - a.v) * u;
|
|
747
799
|
}
|
|
748
800
|
if (Array.isArray(a.v) && Array.isArray(b.v) && typeof a.v[0] === "number") {
|
|
749
801
|
if (track.interpolation === "pchip") return a.v.map((_, j2) => smoothValue(track, i, t, j2, loop));
|
|
802
|
+
if (track.interpolation === "spline") return a.v.map((_, j2) => splineValue(track, i, t, j2, loop));
|
|
750
803
|
const phase = (t - a.t) / (b.t - a.t), av = a.v, bv = b.v;
|
|
751
804
|
return av.map((v, k) => v + ((bv[k] ?? v) - v) * ease(a.componentEase?.[k] ?? a.ease)(phase));
|
|
752
805
|
}
|
|
@@ -2478,7 +2531,8 @@ function adjustTrack(doc, animId, track, adj) {
|
|
|
2478
2531
|
return { ...k, t: r42(adj.timeScale ? k.t * adj.timeScale : k.t), v: r42(v) };
|
|
2479
2532
|
});
|
|
2480
2533
|
const duration = Math.max(anim.duration, keys[keys.length - 1].t);
|
|
2481
|
-
const
|
|
2534
|
+
const interpolation = track.interpolation ?? tr.interpolation;
|
|
2535
|
+
const next = { ...anim, duration, tracks: anim.tracks.map((t) => same(t) ? { ...t, keys, ...interpolation && { interpolation } } : t), preset: void 0 };
|
|
2482
2536
|
return { ...doc, animations: doc.animations.map((a) => a.id === animId ? next : a) };
|
|
2483
2537
|
}
|
|
2484
2538
|
function removeAnimation(doc, id) {
|
|
@@ -2895,19 +2949,20 @@ var requireSlot = (doc, id) => {
|
|
|
2895
2949
|
if (!s) throw new Error(`unknown slot "${id}"`);
|
|
2896
2950
|
return s;
|
|
2897
2951
|
};
|
|
2898
|
-
function sampled(duration, n, f
|
|
2952
|
+
function sampled(duration, n, f) {
|
|
2899
2953
|
const keys = [];
|
|
2900
2954
|
for (let i = 0; i <= n; i++) {
|
|
2901
2955
|
const t = +(duration * i / n).toFixed(4);
|
|
2902
|
-
keys.push({ t, v: +f(t).toFixed(4) + 0, ease:
|
|
2956
|
+
keys.push({ t, v: +f(t).toFixed(4) + 0, ease: "linear" });
|
|
2903
2957
|
}
|
|
2904
2958
|
return keys;
|
|
2905
2959
|
}
|
|
2906
|
-
|
|
2907
|
-
|
|
2960
|
+
var smooth = "spline";
|
|
2961
|
+
function boneTrack(bone, prop, keys, interpolation) {
|
|
2962
|
+
return { target: "bone", id: bone.id, prop, keys, ...interpolation && { interpolation } };
|
|
2908
2963
|
}
|
|
2909
|
-
function deformTrack(slot, prop, keys) {
|
|
2910
|
-
return slot.attachment ? { target: "deform", id: slot.attachment, prop, keys } : void 0;
|
|
2964
|
+
function deformTrack(slot, prop, keys, interpolation) {
|
|
2965
|
+
return slot.attachment ? { target: "deform", id: slot.attachment, prop, keys, ...interpolation && { interpolation } } : void 0;
|
|
2911
2966
|
}
|
|
2912
2967
|
var pushTrack = (tracks, t) => {
|
|
2913
2968
|
if (t) tracks.push(t);
|
|
@@ -2931,18 +2986,18 @@ var idleBreathing = {
|
|
|
2931
2986
|
const armsR = byRole(doc, "arm_r", "arm_upper_r");
|
|
2932
2987
|
const chest = slotOfBone(doc, torso);
|
|
2933
2988
|
if (chest?.attachment) {
|
|
2934
|
-
pushTrack(tracks, deformTrack(chest, "bulge", sampled(T2, 8, (t) => p.amplitude * Math.sin(w * t),
|
|
2989
|
+
pushTrack(tracks, deformTrack(chest, "bulge", sampled(T2, 8, (t) => p.amplitude * Math.sin(w * t)), smooth));
|
|
2935
2990
|
} else {
|
|
2936
2991
|
const r = torso.transform;
|
|
2937
|
-
tracks.push(boneTrack(torso, "scaleY", sampled(T2, 8, (t) => r.scaleY + p.amplitude * Math.sin(w * t),
|
|
2938
|
-
tracks.push(boneTrack(torso, "scaleX", sampled(T2, 8, (t) => r.scaleX - p.amplitude * 0.4 * Math.sin(w * t),
|
|
2992
|
+
tracks.push(boneTrack(torso, "scaleY", sampled(T2, 8, (t) => r.scaleY + p.amplitude * Math.sin(w * t)), smooth));
|
|
2993
|
+
tracks.push(boneTrack(torso, "scaleX", sampled(T2, 8, (t) => r.scaleX - p.amplitude * 0.4 * Math.sin(w * t)), smooth));
|
|
2939
2994
|
}
|
|
2940
2995
|
for (const h of heads)
|
|
2941
|
-
tracks.push(boneTrack(h, "y", sampled(T2, 8, (t) => h.transform.y - p.headBob * Math.sin(w * t + 0.4),
|
|
2996
|
+
tracks.push(boneTrack(h, "y", sampled(T2, 8, (t) => h.transform.y - p.headBob * Math.sin(w * t + 0.4)), smooth));
|
|
2942
2997
|
for (const a of armsL)
|
|
2943
|
-
tracks.push(boneTrack(a, "rotation", sampled(T2, 8, (t) => a.transform.rotation + p.armSway * Math.sin(w * t + 0.8),
|
|
2998
|
+
tracks.push(boneTrack(a, "rotation", sampled(T2, 8, (t) => a.transform.rotation + p.armSway * Math.sin(w * t + 0.8)), smooth));
|
|
2944
2999
|
for (const a of armsR)
|
|
2945
|
-
tracks.push(boneTrack(a, "rotation", sampled(T2, 8, (t) => a.transform.rotation - p.armSway * Math.sin(w * t + 0.8),
|
|
3000
|
+
tracks.push(boneTrack(a, "rotation", sampled(T2, 8, (t) => a.transform.rotation - p.armSway * Math.sin(w * t + 0.8)), smooth));
|
|
2946
3001
|
return finish(this.name, p.id, T2, true, tracks, p);
|
|
2947
3002
|
}
|
|
2948
3003
|
};
|
|
@@ -2951,22 +3006,20 @@ var winBounce = {
|
|
|
2951
3006
|
defaults: { id: "win", duration: 1.2, height: 40, bounces: 2, squash: 0.15 },
|
|
2952
3007
|
build(doc, p) {
|
|
2953
3008
|
const b = root(doc), r = b.transform, tracks = [];
|
|
2954
|
-
const yK = [[0, r.y,
|
|
2955
|
-
const sxK = [[0, r.scaleX, "ease_out"]];
|
|
2956
|
-
const syK = [[0, r.scaleY, "ease_out"]];
|
|
3009
|
+
const yK = [[0, r.y]], sxK = [[0, r.scaleX]], syK = [[0, r.scaleY]];
|
|
2957
3010
|
const per = p.duration / p.bounces;
|
|
2958
3011
|
for (let i = 0; i < p.bounces; i++) {
|
|
2959
3012
|
const t0 = i * per, h = p.height * 0.55 ** i, s = p.squash * 0.7 ** i;
|
|
2960
|
-
sxK.push([t0 + per * 0.08, r.scaleX * (1 + s)
|
|
2961
|
-
syK.push([t0 + per * 0.08, r.scaleY * (1 - s)
|
|
2962
|
-
yK.push([t0 + per * 0.08, r.y
|
|
3013
|
+
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)]);
|
|
3014
|
+
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)]);
|
|
3015
|
+
yK.push([t0 + per * 0.08, r.y], [t0 + per * 0.5, r.y - h], [t0 + per * 0.95, r.y]);
|
|
2963
3016
|
}
|
|
2964
3017
|
yK.push([p.duration, r.y]);
|
|
2965
3018
|
sxK.push([p.duration, r.scaleX]);
|
|
2966
3019
|
syK.push([p.duration, r.scaleY]);
|
|
2967
|
-
tracks.push(boneTrack(b, "y", K(...yK)), boneTrack(b, "scaleX", K(...sxK)), boneTrack(b, "scaleY", K(...syK)));
|
|
3020
|
+
tracks.push(boneTrack(b, "y", K(...yK), "pchip"), boneTrack(b, "scaleX", K(...sxK), smooth), boneTrack(b, "scaleY", K(...syK), smooth));
|
|
2968
3021
|
for (const h of byRole(doc, "head"))
|
|
2969
|
-
tracks.push(boneTrack(h, "rotation", K([0, h.transform.rotation], [p.duration * 0.25, h.transform.rotation - 6
|
|
3022
|
+
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));
|
|
2970
3023
|
const anim = finish(this.name, p.id, p.duration, false, tracks, p);
|
|
2971
3024
|
anim.events.push({ t: 0, name: "win_start" }, { t: per * 0.95, name: "land" });
|
|
2972
3025
|
return anim;
|
|
@@ -2979,8 +3032,8 @@ var anticipationShake = {
|
|
|
2979
3032
|
const b = root(doc), r = b.transform, tracks = [];
|
|
2980
3033
|
const n = Math.ceil(p.duration * p.frequency * 4);
|
|
2981
3034
|
const env = (t) => p.grow ? t / p.duration : 1;
|
|
2982
|
-
tracks.push(boneTrack(b, "x", sampled(p.duration, n, (t) => r.x + p.amplitude * env(t) * Math.sin(2 * Math.PI * p.frequency * t),
|
|
2983
|
-
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),
|
|
3035
|
+
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));
|
|
3036
|
+
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));
|
|
2984
3037
|
tracks.push(boneTrack(b, "scaleX", K([0, r.scaleX, "ease_in"], [p.duration, r.scaleX * p.scaleUp])));
|
|
2985
3038
|
tracks.push(boneTrack(b, "scaleY", K([0, r.scaleY, "ease_in"], [p.duration, r.scaleY * p.scaleUp])));
|
|
2986
3039
|
return finish(this.name, p.id, p.duration, false, tracks, p);
|
|
@@ -3005,6 +3058,7 @@ var mouthTalk = {
|
|
|
3005
3058
|
if (!p.frames.length) throw new Error("mouth_talk: frames must not be empty");
|
|
3006
3059
|
const n = Math.max(1, Math.round(p.duration * p.rate));
|
|
3007
3060
|
const keys = S(...Array.from({ length: n }, (_, i) => [+(i / p.rate).toFixed(4), p.frames[i % p.frames.length]]));
|
|
3061
|
+
if (keys.at(-1).t < p.duration && keys.at(-1).v !== keys[0].v) keys.push(...S([p.duration, keys[0].v]));
|
|
3008
3062
|
return finish(this.name, p.id, p.duration, true, [slotTrack(slot, "attachment", keys)], p);
|
|
3009
3063
|
}
|
|
3010
3064
|
};
|
|
@@ -3022,10 +3076,10 @@ var symbolIdle = {
|
|
|
3022
3076
|
build(doc, p) {
|
|
3023
3077
|
const b = root(doc), r = b.transform, T2 = p.period, w = 2 * Math.PI / T2, tracks = [];
|
|
3024
3078
|
const rise = (t) => 0.5 - 0.5 * Math.cos(w * t);
|
|
3025
|
-
tracks.push(boneTrack(b, "y", sampled(T2, 8, (t) => r.y - p.float * rise(t),
|
|
3026
|
-
if (p.tilt) tracks.push(boneTrack(b, "rotation", sampled(T2, 8, (t) => r.rotation + p.tilt * Math.sin(w * t),
|
|
3079
|
+
tracks.push(boneTrack(b, "y", sampled(T2, 8, (t) => r.y - p.float * rise(t)), smooth));
|
|
3080
|
+
if (p.tilt) tracks.push(boneTrack(b, "rotation", sampled(T2, 8, (t) => r.rotation + p.tilt * Math.sin(w * t)), smooth));
|
|
3027
3081
|
if (p.pulse) for (const s of doc.slots.filter((s2) => s2.bone === b.id && s2.attachment))
|
|
3028
|
-
pushTrack(tracks, deformTrack(s, "bulge", sampled(T2, 8, (t) => p.pulse * rise(t),
|
|
3082
|
+
pushTrack(tracks, deformTrack(s, "bulge", sampled(T2, 8, (t) => p.pulse * rise(t)), smooth));
|
|
3029
3083
|
return finish(this.name, p.id, T2, true, tracks, p);
|
|
3030
3084
|
}
|
|
3031
3085
|
};
|
|
@@ -3035,12 +3089,12 @@ var symbolWin = {
|
|
|
3035
3089
|
build(doc, p) {
|
|
3036
3090
|
const b = root(doc), r = b.transform, d = p.duration, tracks = [];
|
|
3037
3091
|
for (const [prop, rest] of [["scaleX", r.scaleX], ["scaleY", r.scaleY]])
|
|
3038
|
-
tracks.push(boneTrack(b, prop, K([0, rest
|
|
3092
|
+
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));
|
|
3039
3093
|
if (p.wobble) {
|
|
3040
3094
|
const n = Math.max(8, p.wobbles * 4);
|
|
3041
|
-
const keys = sampled(d, n, (t) => p.wobble * Math.exp(-3 * t / d) * Math.sin(2 * Math.PI * p.wobbles * t / d)
|
|
3095
|
+
const keys = sampled(d, n, (t) => p.wobble * Math.exp(-3 * t / d) * Math.sin(2 * Math.PI * p.wobbles * t / d));
|
|
3042
3096
|
keys[keys.length - 1].v = 0;
|
|
3043
|
-
for (const s of doc.slots.filter((s2) => s2.bone === b.id && s2.attachment)) pushTrack(tracks, deformTrack(s, "sway", keys));
|
|
3097
|
+
for (const s of doc.slots.filter((s2) => s2.bone === b.id && s2.attachment)) pushTrack(tracks, deformTrack(s, "sway", keys, smooth));
|
|
3044
3098
|
}
|
|
3045
3099
|
const anim = finish(this.name, p.id, d, false, tracks, p);
|
|
3046
3100
|
anim.events.push({ t: 0, name: "win_start" });
|
|
@@ -3054,11 +3108,11 @@ var weightShift = {
|
|
|
3054
3108
|
const T2 = p.period, w = 2 * Math.PI / T2, N = 16, tracks = [];
|
|
3055
3109
|
const hips = byRole(doc, "hips")[0] ?? root(doc), hr = hips.transform;
|
|
3056
3110
|
const s = (t) => Math.sin(w * t);
|
|
3057
|
-
tracks.push(boneTrack(hips, "x", sampled(T2, N, (t) => hr.x + p.shift * s(t))));
|
|
3058
|
-
if (p.dip) tracks.push(boneTrack(hips, "y", sampled(T2, N, (t) => hr.y + p.dip * s(t) ** 2)));
|
|
3111
|
+
tracks.push(boneTrack(hips, "x", sampled(T2, N, (t) => hr.x + p.shift * s(t)), smooth));
|
|
3112
|
+
if (p.dip) tracks.push(boneTrack(hips, "y", sampled(T2, N, (t) => hr.y + p.dip * s(t) ** 2), smooth));
|
|
3059
3113
|
const torso = byRole(doc, "torso", "body")[0];
|
|
3060
|
-
if (torso && torso !== hips) tracks.push(boneTrack(torso, "rotation", sampled(T2, N, (t) => torso.transform.rotation - p.lean * s(t))));
|
|
3061
|
-
for (const h of byRole(doc, "head")) tracks.push(boneTrack(h, "rotation", sampled(T2, N, (t) => h.transform.rotation + p.lean / 2 * s(t))));
|
|
3114
|
+
if (torso && torso !== hips) tracks.push(boneTrack(torso, "rotation", sampled(T2, N, (t) => torso.transform.rotation - p.lean * s(t)), smooth));
|
|
3115
|
+
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));
|
|
3062
3116
|
for (const leg of byRole(doc, "leg_l", "leg_r")) {
|
|
3063
3117
|
const slot = slotOfBone(doc, leg), att = slot && doc.attachments.find((a) => a.id === slot.attachment && a.type === "region");
|
|
3064
3118
|
const asset = att && att.type === "region" ? doc.assets.find((a) => a.id === att.asset) : void 0;
|
|
@@ -3068,8 +3122,8 @@ var weightShift = {
|
|
|
3068
3122
|
length = Math.max(...[[0, -h / 2], [w2 / 2, 0], [0, h / 2], [-w2 / 2, 0]].map(([x, y]) => Math.hypot(...apply(m, x, y))));
|
|
3069
3123
|
}
|
|
3070
3124
|
if (!(length > p.shift)) continue;
|
|
3071
|
-
tracks.push(boneTrack(leg, "rotation", sampled(T2, N, (t) => leg.transform.rotation + Math.asin(p.shift * s(t) / length) * 180 / Math.PI)));
|
|
3072
|
-
if (p.dip) tracks.push(boneTrack(leg, "y", sampled(T2, N, (t) => leg.transform.y - p.dip * s(t) ** 2)));
|
|
3125
|
+
tracks.push(boneTrack(leg, "rotation", sampled(T2, N, (t) => leg.transform.rotation + Math.asin(p.shift * s(t) / length) * 180 / Math.PI), smooth));
|
|
3126
|
+
if (p.dip) tracks.push(boneTrack(leg, "y", sampled(T2, N, (t) => leg.transform.y - p.dip * s(t) ** 2), smooth));
|
|
3073
3127
|
}
|
|
3074
3128
|
return finish(this.name, p.id, T2, true, tracks, p);
|
|
3075
3129
|
}
|
|
@@ -3312,13 +3366,13 @@ function motionQuality(doc, anim, opts = {}) {
|
|
|
3312
3366
|
const before = delta(values(k.v), values(p.v)), after = delta(values(n.v), values(k.v));
|
|
3313
3367
|
const pass = before.some((v, j2) => v * after[j2] > 1e-6);
|
|
3314
3368
|
const speed = Math.max(magnitude(before) / (k.t - p.t), magnitude(after) / (n.t - k.t));
|
|
3315
|
-
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);
|
|
3369
|
+
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);
|
|
3316
3370
|
const jump = magnitude(delta(left, right));
|
|
3317
3371
|
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);
|
|
3318
3372
|
const acceleration = (a, b, c) => values(at(b)).map((v, j2) => (values(at(c))[j2] - 2 * v + values(at(a))[j2]) / (h * h));
|
|
3319
3373
|
const al = acceleration(k.t - 2 * h, k.t - h, k.t), ar = acceleration(k.t, k.t + h, k.t + 2 * h);
|
|
3320
3374
|
const aj = magnitude(delta(al, ar));
|
|
3321
|
-
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");
|
|
3375
|
+
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");
|
|
3322
3376
|
}
|
|
3323
3377
|
}
|
|
3324
3378
|
const steps = Math.max(2, Math.ceil(D * 60));
|
|
@@ -3800,6 +3854,17 @@ function checkRig(doc, opts = {}) {
|
|
|
3800
3854
|
out.push({ level: "warn", code: "never_visible", subject: s.id, message: `slot "${s.id}" has no attachment and no animation ever shows one` });
|
|
3801
3855
|
return out;
|
|
3802
3856
|
}
|
|
3857
|
+
function summarizeIgnoring(findings, rules, max) {
|
|
3858
|
+
const hits = (r, f) => r.code === f.code && (r.subject ?? f.subject) === f.subject && (r.animation ?? f.animation) === f.animation;
|
|
3859
|
+
const kept = findings.filter((f) => !rules.some((r) => hits(r, f)));
|
|
3860
|
+
const idle = rules.filter((r) => !findings.some((f) => hits(r, f)));
|
|
3861
|
+
const notes = [
|
|
3862
|
+
...kept.length < findings.length ? [` (${findings.length - kept.length} ignored)`] : [],
|
|
3863
|
+
...idle.map((r) => ` ignore matched nothing: ${JSON.stringify(r)}`)
|
|
3864
|
+
];
|
|
3865
|
+
return [summarize(kept, max), ...notes].join("\n");
|
|
3866
|
+
}
|
|
3867
|
+
var where = (f) => `${f.subject}${f.animation ? ` in ${f.animation}` : ""}${f.time !== void 0 ? ` @${+f.time.toFixed(4)}s` : ""}`;
|
|
3803
3868
|
function summarize(findings, max = 12) {
|
|
3804
3869
|
if (!findings.length) return "check: OK";
|
|
3805
3870
|
const rank = { error: 0, warn: 1, info: 2 };
|
|
@@ -3814,7 +3879,7 @@ function summarize(findings, max = 12) {
|
|
|
3814
3879
|
const lines = [];
|
|
3815
3880
|
for (const f of sorted) {
|
|
3816
3881
|
const shown = shownPerCode.get(f.code) ?? 0;
|
|
3817
|
-
if (shown < PER_CODE) lines.push(` [${f.level}] ${f.code}: ${f.message}`);
|
|
3882
|
+
if (shown < PER_CODE) lines.push(` [${f.level}] ${f.code} ${where(f)}: ${f.message}`);
|
|
3818
3883
|
else if (shown === PER_CODE) lines.push(` \u2026 ${codeTotal.get(f.code) - PER_CODE} more ${f.code}`);
|
|
3819
3884
|
shownPerCode.set(f.code, shown + 1);
|
|
3820
3885
|
}
|
|
@@ -4005,7 +4070,7 @@ function packRects(rects, opts = {}) {
|
|
|
4005
4070
|
shelf = Math.max(shelf, r.height);
|
|
4006
4071
|
}
|
|
4007
4072
|
const height = y + shelf + pad;
|
|
4008
|
-
if (height <= max) return { width, height, frames };
|
|
4073
|
+
if (height <= max) return { width: Math.max(pad, ...[...frames.values()].map(([fx, , fw]) => fx + fw)) + pad, height, frames };
|
|
4009
4074
|
if (width >= max) throw new Error(`atlas does not fit in ${max}\xD7${max} (needs ${width}\xD7${height})`);
|
|
4010
4075
|
width *= 2;
|
|
4011
4076
|
}
|
|
@@ -5612,7 +5677,8 @@ function previewCheck(doc, animation) {
|
|
|
5612
5677
|
const a = animation ? doc.animations.find((x) => x.id === animation) : void 0;
|
|
5613
5678
|
return a ? `check: OK for "${a.id}" (${a.tracks.length} tracks, ${a.duration}s${a.loop ? " loop" : ""})` : "check: OK (rest pose)";
|
|
5614
5679
|
}
|
|
5615
|
-
var
|
|
5680
|
+
var TrackId = { target: z3.enum(["bone", "slot", "deform", "ik", "transform", "path", "drawOrder", "control"]), id: z3.string(), prop: z3.string() };
|
|
5681
|
+
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 };
|
|
5616
5682
|
tool(
|
|
5617
5683
|
"rig_set_control",
|
|
5618
5684
|
"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.",
|
|
@@ -5805,11 +5871,21 @@ tool(
|
|
|
5805
5871
|
);
|
|
5806
5872
|
tool(
|
|
5807
5873
|
"rig_check",
|
|
5808
|
-
"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.",
|
|
5809
|
-
{
|
|
5810
|
-
|
|
5874
|
+
"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.",
|
|
5875
|
+
{
|
|
5876
|
+
path: Path,
|
|
5877
|
+
animation: z3.string().optional().describe("limit to one animation (rest pose is always checked)"),
|
|
5878
|
+
fps: z3.number().positive().optional(),
|
|
5879
|
+
deformFps: z3.number().positive().optional(),
|
|
5880
|
+
secondaryBones: z3.array(z3.string()).optional(),
|
|
5881
|
+
frontOf: z3.array(z3.tuple([z3.string(), z3.string()])).optional(),
|
|
5882
|
+
landmarks: z3.record(z3.record(z3.tuple([z3.number(), z3.number()]))).optional(),
|
|
5883
|
+
landmarkTolerance: z3.number().nonnegative().optional(),
|
|
5884
|
+
ignore: z3.array(z3.object({ code: z3.string(), subject: z3.string().optional(), animation: z3.string().optional() }).strict()).optional()
|
|
5885
|
+
},
|
|
5886
|
+
async ({ path, ignore = [], ...opts }) => {
|
|
5811
5887
|
try {
|
|
5812
|
-
return text(
|
|
5888
|
+
return text(summarizeIgnoring(checkRig(await load(path), opts), ignore, 100));
|
|
5813
5889
|
} catch (e) {
|
|
5814
5890
|
return fail(e);
|
|
5815
5891
|
}
|
|
@@ -6426,9 +6502,9 @@ tool(
|
|
|
6426
6502
|
);
|
|
6427
6503
|
tool(
|
|
6428
6504
|
"rig_adjust_track",
|
|
6429
|
-
"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.",
|
|
6505
|
+
"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.",
|
|
6430
6506
|
{ path: Path, animation: z3.string(), ...TrackRef, amplitude: z3.number().optional(), offset: z3.number().optional(), timeScale: z3.number().positive().optional() },
|
|
6431
|
-
async ({ path, animation, target, id, prop, ...adj }) => mutate(path, (d) => adjustTrack(d, animation, { target, id, prop }, adj))
|
|
6507
|
+
async ({ path, animation, target, id, prop, interpolation, ...adj }) => mutate(path, (d) => adjustTrack(d, animation, { target, id, prop, interpolation }, adj))
|
|
6432
6508
|
);
|
|
6433
6509
|
tool(
|
|
6434
6510
|
"rig_set_animation",
|
|
@@ -6446,7 +6522,7 @@ tool(
|
|
|
6446
6522
|
tool(
|
|
6447
6523
|
"rig_remove_keys",
|
|
6448
6524
|
"Remove the keys of one track at the given times; an emptied track is dropped. Drops the preset link.",
|
|
6449
|
-
{ path: Path, animation: z3.string(), ...
|
|
6525
|
+
{ path: Path, animation: z3.string(), ...TrackId, times: z3.array(z3.number()).min(1) },
|
|
6450
6526
|
async ({ path, animation, target, id, prop, times: times2 }) => mutate(path, (d) => removeKeys(d, animation, { target, id, prop }, times2))
|
|
6451
6527
|
);
|
|
6452
6528
|
tool(
|
|
@@ -6624,9 +6700,15 @@ async function callToolVersioned(name2, args) {
|
|
|
6624
6700
|
|
|
6625
6701
|
// src/tool-schema.ts
|
|
6626
6702
|
import { zodToJsonSchema } from "zod-to-json-schema";
|
|
6703
|
+
function prefixItems(node) {
|
|
6704
|
+
if (Array.isArray(node)) return node.map(prefixItems);
|
|
6705
|
+
if (!node || typeof node !== "object") return node;
|
|
6706
|
+
const { items, ...rest } = Object.fromEntries(Object.entries(node).map(([k, v]) => [k, prefixItems(v)]));
|
|
6707
|
+
return items === void 0 ? rest : Array.isArray(items) ? { ...rest, prefixItems: items } : { ...rest, items };
|
|
6708
|
+
}
|
|
6627
6709
|
function jsonSchemaOf(tool2) {
|
|
6628
6710
|
const { $schema: _drop, ...schema } = zodToJsonSchema(tool2.input, { $refStrategy: "none" });
|
|
6629
|
-
return schema;
|
|
6711
|
+
return prefixItems(schema);
|
|
6630
6712
|
}
|
|
6631
6713
|
|
|
6632
6714
|
// src/e8/agent.ts
|
|
@@ -6672,6 +6754,7 @@ var NESTED_TEXT_ARGS = [
|
|
|
6672
6754
|
"animation",
|
|
6673
6755
|
"b",
|
|
6674
6756
|
"bone",
|
|
6757
|
+
"code",
|
|
6675
6758
|
"control",
|
|
6676
6759
|
"from",
|
|
6677
6760
|
"id",
|
|
@@ -6682,6 +6765,7 @@ var NESTED_TEXT_ARGS = [
|
|
|
6682
6765
|
"requiredAnimations",
|
|
6683
6766
|
"sourceImage",
|
|
6684
6767
|
"src",
|
|
6768
|
+
"subject",
|
|
6685
6769
|
"to",
|
|
6686
6770
|
"v",
|
|
6687
6771
|
"whenSlotVisible"
|