@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
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import type { ToolDef } from "./rig-api";
|
|
2
|
-
/** draft-
|
|
2
|
+
/** draft 2020-12, every ref inlined, no `$schema` key — the tool's own strict zod object, so unknown keys stay refused */
|
|
3
3
|
export declare function jsonSchemaOf(tool: ToolDef): Record<string, unknown>;
|
package/dist/lib/tools.js
CHANGED
|
@@ -293,7 +293,7 @@ var Key = z.object({
|
|
|
293
293
|
/** Optional independent easing for numeric-array components (for example Spine RGBA curves). */
|
|
294
294
|
componentEase: z.array(Ease).min(1).optional()
|
|
295
295
|
});
|
|
296
|
-
var Interpolation = { interpolation: z.enum(["segment", "pchip"]).optional() };
|
|
296
|
+
var Interpolation = { interpolation: z.enum(["segment", "pchip", "spline"]).optional() };
|
|
297
297
|
var Track = z.discriminatedUnion("target", [
|
|
298
298
|
z.object({ target: z.literal("control"), id: Id, prop: z.literal("value"), ...Interpolation, keys: z.array(Key).min(1) }),
|
|
299
299
|
z.object({ target: z.literal("bone"), id: Id, prop: BoneProp, ...Interpolation, keys: z.array(Key).min(1) }),
|
|
@@ -727,6 +727,57 @@ function smoothValue(track, i, t, component, loop) {
|
|
|
727
727
|
const u = (t - k[i].t) / h(i), u2 = u * u, u3 = u2 * u;
|
|
728
728
|
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);
|
|
729
729
|
}
|
|
730
|
+
function tridiagonal(a, b, c, r) {
|
|
731
|
+
const n = b.length, cp = [], x = [];
|
|
732
|
+
let beta = b[0];
|
|
733
|
+
x[0] = r[0] / beta;
|
|
734
|
+
for (let i = 1; i < n; i++) {
|
|
735
|
+
cp[i] = c[i - 1] / beta;
|
|
736
|
+
beta = b[i] - a[i] * cp[i];
|
|
737
|
+
x[i] = (r[i] - a[i] * x[i - 1]) / beta;
|
|
738
|
+
}
|
|
739
|
+
for (let i = n - 2; i >= 0; i--) x[i] -= cp[i + 1] * x[i + 1];
|
|
740
|
+
return x;
|
|
741
|
+
}
|
|
742
|
+
function splineMoments(t, v, periodic) {
|
|
743
|
+
const n = t.length, h = (j2) => t[j2 + 1] - t[j2], d = (j2) => (v[j2 + 1] - v[j2]) / h(j2);
|
|
744
|
+
if (!periodic) {
|
|
745
|
+
const a2 = [], b2 = [], c2 = [], r2 = [];
|
|
746
|
+
for (let i = 0; i < n; i++) {
|
|
747
|
+
const hp = i > 0 ? h(i - 1) : 0, hn = i < n - 1 ? h(i) : 0;
|
|
748
|
+
a2.push(hp);
|
|
749
|
+
b2.push(2 * (hp + hn));
|
|
750
|
+
c2.push(hn);
|
|
751
|
+
r2.push(6 * ((i < n - 1 ? d(i) : 0) - (i > 0 ? d(i - 1) : 0)));
|
|
752
|
+
}
|
|
753
|
+
return tridiagonal(a2, b2, c2, r2);
|
|
754
|
+
}
|
|
755
|
+
const m = n - 1, a = [], b = [], c = [], r = [];
|
|
756
|
+
for (let i = 0; i < m; i++) {
|
|
757
|
+
const p = (i + m - 1) % m;
|
|
758
|
+
a.push(h(p));
|
|
759
|
+
b.push(2 * (h(p) + h(i)));
|
|
760
|
+
c.push(h(i));
|
|
761
|
+
r.push(6 * (d(i) - d(p)));
|
|
762
|
+
}
|
|
763
|
+
const top = a[0], bottom = c[m - 1], gamma = -b[0];
|
|
764
|
+
const bb = [...b];
|
|
765
|
+
bb[0] -= gamma;
|
|
766
|
+
bb[m - 1] -= bottom * top / gamma;
|
|
767
|
+
const x = tridiagonal(a, bb, c, r), u = new Array(m).fill(0);
|
|
768
|
+
u[0] = gamma;
|
|
769
|
+
u[m - 1] = bottom;
|
|
770
|
+
const z4 = tridiagonal(a, bb, c, u);
|
|
771
|
+
const f = (x[0] + top * x[m - 1] / gamma) / (1 + z4[0] + top * z4[m - 1] / gamma);
|
|
772
|
+
const M = x.map((xi, i) => xi - f * z4[i]);
|
|
773
|
+
return [...M, M[0]];
|
|
774
|
+
}
|
|
775
|
+
function splineValue(track, i, t, component, loop) {
|
|
776
|
+
const k = track.keys, ts = k.map((x) => x.t), vs = k.map((x) => component === void 0 ? x.v : x.v[component]);
|
|
777
|
+
const M = splineMoments(ts, vs, loop && k.length > 3 && vs[0] === vs[k.length - 1]);
|
|
778
|
+
const H = ts[i + 1] - ts[i], A = (ts[i + 1] - t) / H, B = (t - ts[i]) / H;
|
|
779
|
+
return A * vs[i] + B * vs[i + 1] + ((A ** 3 - A) * M[i] + (B ** 3 - B) * M[i + 1]) * H * H / 6;
|
|
780
|
+
}
|
|
730
781
|
function sample(track, t, loop = false) {
|
|
731
782
|
const keys = track.keys;
|
|
732
783
|
if (t <= keys[0].t) return keys[0].v;
|
|
@@ -738,11 +789,13 @@ function sample(track, t, loop = false) {
|
|
|
738
789
|
if (a.ease === "step") return a.v;
|
|
739
790
|
if (typeof a.v === "number" && typeof b.v === "number") {
|
|
740
791
|
if (track.interpolation === "pchip") return smoothValue(track, i, t, void 0, loop);
|
|
792
|
+
if (track.interpolation === "spline") return splineValue(track, i, t, void 0, loop);
|
|
741
793
|
const u = ease(a.ease)((t - a.t) / (b.t - a.t));
|
|
742
794
|
return a.v + (b.v - a.v) * u;
|
|
743
795
|
}
|
|
744
796
|
if (Array.isArray(a.v) && Array.isArray(b.v) && typeof a.v[0] === "number") {
|
|
745
797
|
if (track.interpolation === "pchip") return a.v.map((_, j2) => smoothValue(track, i, t, j2, loop));
|
|
798
|
+
if (track.interpolation === "spline") return a.v.map((_, j2) => splineValue(track, i, t, j2, loop));
|
|
746
799
|
const phase = (t - a.t) / (b.t - a.t), av = a.v, bv = b.v;
|
|
747
800
|
return av.map((v, k) => v + ((bv[k] ?? v) - v) * ease(a.componentEase?.[k] ?? a.ease)(phase));
|
|
748
801
|
}
|
|
@@ -2474,7 +2527,8 @@ function adjustTrack(doc, animId, track, adj) {
|
|
|
2474
2527
|
return { ...k, t: r42(adj.timeScale ? k.t * adj.timeScale : k.t), v: r42(v) };
|
|
2475
2528
|
});
|
|
2476
2529
|
const duration = Math.max(anim.duration, keys[keys.length - 1].t);
|
|
2477
|
-
const
|
|
2530
|
+
const interpolation = track.interpolation ?? tr.interpolation;
|
|
2531
|
+
const next = { ...anim, duration, tracks: anim.tracks.map((t) => same(t) ? { ...t, keys, ...interpolation && { interpolation } } : t), preset: void 0 };
|
|
2478
2532
|
return { ...doc, animations: doc.animations.map((a) => a.id === animId ? next : a) };
|
|
2479
2533
|
}
|
|
2480
2534
|
function removeAnimation(doc, id) {
|
|
@@ -2891,19 +2945,20 @@ var requireSlot = (doc, id) => {
|
|
|
2891
2945
|
if (!s) throw new Error(`unknown slot "${id}"`);
|
|
2892
2946
|
return s;
|
|
2893
2947
|
};
|
|
2894
|
-
function sampled(duration, n, f
|
|
2948
|
+
function sampled(duration, n, f) {
|
|
2895
2949
|
const keys = [];
|
|
2896
2950
|
for (let i = 0; i <= n; i++) {
|
|
2897
2951
|
const t = +(duration * i / n).toFixed(4);
|
|
2898
|
-
keys.push({ t, v: +f(t).toFixed(4) + 0, ease:
|
|
2952
|
+
keys.push({ t, v: +f(t).toFixed(4) + 0, ease: "linear" });
|
|
2899
2953
|
}
|
|
2900
2954
|
return keys;
|
|
2901
2955
|
}
|
|
2902
|
-
|
|
2903
|
-
|
|
2956
|
+
var smooth = "spline";
|
|
2957
|
+
function boneTrack(bone, prop, keys, interpolation) {
|
|
2958
|
+
return { target: "bone", id: bone.id, prop, keys, ...interpolation && { interpolation } };
|
|
2904
2959
|
}
|
|
2905
|
-
function deformTrack(slot, prop, keys) {
|
|
2906
|
-
return slot.attachment ? { target: "deform", id: slot.attachment, prop, keys } : void 0;
|
|
2960
|
+
function deformTrack(slot, prop, keys, interpolation) {
|
|
2961
|
+
return slot.attachment ? { target: "deform", id: slot.attachment, prop, keys, ...interpolation && { interpolation } } : void 0;
|
|
2907
2962
|
}
|
|
2908
2963
|
var pushTrack = (tracks, t) => {
|
|
2909
2964
|
if (t) tracks.push(t);
|
|
@@ -2927,18 +2982,18 @@ var idleBreathing = {
|
|
|
2927
2982
|
const armsR = byRole(doc, "arm_r", "arm_upper_r");
|
|
2928
2983
|
const chest = slotOfBone(doc, torso);
|
|
2929
2984
|
if (chest?.attachment) {
|
|
2930
|
-
pushTrack(tracks, deformTrack(chest, "bulge", sampled(T2, 8, (t) => p.amplitude * Math.sin(w * t),
|
|
2985
|
+
pushTrack(tracks, deformTrack(chest, "bulge", sampled(T2, 8, (t) => p.amplitude * Math.sin(w * t)), smooth));
|
|
2931
2986
|
} else {
|
|
2932
2987
|
const r = torso.transform;
|
|
2933
|
-
tracks.push(boneTrack(torso, "scaleY", sampled(T2, 8, (t) => r.scaleY + p.amplitude * Math.sin(w * t),
|
|
2934
|
-
tracks.push(boneTrack(torso, "scaleX", sampled(T2, 8, (t) => r.scaleX - p.amplitude * 0.4 * Math.sin(w * t),
|
|
2988
|
+
tracks.push(boneTrack(torso, "scaleY", sampled(T2, 8, (t) => r.scaleY + p.amplitude * Math.sin(w * t)), smooth));
|
|
2989
|
+
tracks.push(boneTrack(torso, "scaleX", sampled(T2, 8, (t) => r.scaleX - p.amplitude * 0.4 * Math.sin(w * t)), smooth));
|
|
2935
2990
|
}
|
|
2936
2991
|
for (const h of heads)
|
|
2937
|
-
tracks.push(boneTrack(h, "y", sampled(T2, 8, (t) => h.transform.y - p.headBob * Math.sin(w * t + 0.4),
|
|
2992
|
+
tracks.push(boneTrack(h, "y", sampled(T2, 8, (t) => h.transform.y - p.headBob * Math.sin(w * t + 0.4)), smooth));
|
|
2938
2993
|
for (const a of armsL)
|
|
2939
|
-
tracks.push(boneTrack(a, "rotation", sampled(T2, 8, (t) => a.transform.rotation + p.armSway * Math.sin(w * t + 0.8),
|
|
2994
|
+
tracks.push(boneTrack(a, "rotation", sampled(T2, 8, (t) => a.transform.rotation + p.armSway * Math.sin(w * t + 0.8)), smooth));
|
|
2940
2995
|
for (const a of armsR)
|
|
2941
|
-
tracks.push(boneTrack(a, "rotation", sampled(T2, 8, (t) => a.transform.rotation - p.armSway * Math.sin(w * t + 0.8),
|
|
2996
|
+
tracks.push(boneTrack(a, "rotation", sampled(T2, 8, (t) => a.transform.rotation - p.armSway * Math.sin(w * t + 0.8)), smooth));
|
|
2942
2997
|
return finish(this.name, p.id, T2, true, tracks, p);
|
|
2943
2998
|
}
|
|
2944
2999
|
};
|
|
@@ -2947,22 +3002,20 @@ var winBounce = {
|
|
|
2947
3002
|
defaults: { id: "win", duration: 1.2, height: 40, bounces: 2, squash: 0.15 },
|
|
2948
3003
|
build(doc, p) {
|
|
2949
3004
|
const b = root(doc), r = b.transform, tracks = [];
|
|
2950
|
-
const yK = [[0, r.y,
|
|
2951
|
-
const sxK = [[0, r.scaleX, "ease_out"]];
|
|
2952
|
-
const syK = [[0, r.scaleY, "ease_out"]];
|
|
3005
|
+
const yK = [[0, r.y]], sxK = [[0, r.scaleX]], syK = [[0, r.scaleY]];
|
|
2953
3006
|
const per = p.duration / p.bounces;
|
|
2954
3007
|
for (let i = 0; i < p.bounces; i++) {
|
|
2955
3008
|
const t0 = i * per, h = p.height * 0.55 ** i, s = p.squash * 0.7 ** i;
|
|
2956
|
-
sxK.push([t0 + per * 0.08, r.scaleX * (1 + s)
|
|
2957
|
-
syK.push([t0 + per * 0.08, r.scaleY * (1 - s)
|
|
2958
|
-
yK.push([t0 + per * 0.08, r.y
|
|
3009
|
+
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)]);
|
|
3010
|
+
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)]);
|
|
3011
|
+
yK.push([t0 + per * 0.08, r.y], [t0 + per * 0.5, r.y - h], [t0 + per * 0.95, r.y]);
|
|
2959
3012
|
}
|
|
2960
3013
|
yK.push([p.duration, r.y]);
|
|
2961
3014
|
sxK.push([p.duration, r.scaleX]);
|
|
2962
3015
|
syK.push([p.duration, r.scaleY]);
|
|
2963
|
-
tracks.push(boneTrack(b, "y", K(...yK)), boneTrack(b, "scaleX", K(...sxK)), boneTrack(b, "scaleY", K(...syK)));
|
|
3016
|
+
tracks.push(boneTrack(b, "y", K(...yK), "pchip"), boneTrack(b, "scaleX", K(...sxK), smooth), boneTrack(b, "scaleY", K(...syK), smooth));
|
|
2964
3017
|
for (const h of byRole(doc, "head"))
|
|
2965
|
-
tracks.push(boneTrack(h, "rotation", K([0, h.transform.rotation], [p.duration * 0.25, h.transform.rotation - 6
|
|
3018
|
+
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));
|
|
2966
3019
|
const anim = finish(this.name, p.id, p.duration, false, tracks, p);
|
|
2967
3020
|
anim.events.push({ t: 0, name: "win_start" }, { t: per * 0.95, name: "land" });
|
|
2968
3021
|
return anim;
|
|
@@ -2975,8 +3028,8 @@ var anticipationShake = {
|
|
|
2975
3028
|
const b = root(doc), r = b.transform, tracks = [];
|
|
2976
3029
|
const n = Math.ceil(p.duration * p.frequency * 4);
|
|
2977
3030
|
const env = (t) => p.grow ? t / p.duration : 1;
|
|
2978
|
-
tracks.push(boneTrack(b, "x", sampled(p.duration, n, (t) => r.x + p.amplitude * env(t) * Math.sin(2 * Math.PI * p.frequency * t),
|
|
2979
|
-
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),
|
|
3031
|
+
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));
|
|
3032
|
+
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));
|
|
2980
3033
|
tracks.push(boneTrack(b, "scaleX", K([0, r.scaleX, "ease_in"], [p.duration, r.scaleX * p.scaleUp])));
|
|
2981
3034
|
tracks.push(boneTrack(b, "scaleY", K([0, r.scaleY, "ease_in"], [p.duration, r.scaleY * p.scaleUp])));
|
|
2982
3035
|
return finish(this.name, p.id, p.duration, false, tracks, p);
|
|
@@ -3001,6 +3054,7 @@ var mouthTalk = {
|
|
|
3001
3054
|
if (!p.frames.length) throw new Error("mouth_talk: frames must not be empty");
|
|
3002
3055
|
const n = Math.max(1, Math.round(p.duration * p.rate));
|
|
3003
3056
|
const keys = S(...Array.from({ length: n }, (_, i) => [+(i / p.rate).toFixed(4), p.frames[i % p.frames.length]]));
|
|
3057
|
+
if (keys.at(-1).t < p.duration && keys.at(-1).v !== keys[0].v) keys.push(...S([p.duration, keys[0].v]));
|
|
3004
3058
|
return finish(this.name, p.id, p.duration, true, [slotTrack(slot, "attachment", keys)], p);
|
|
3005
3059
|
}
|
|
3006
3060
|
};
|
|
@@ -3018,10 +3072,10 @@ var symbolIdle = {
|
|
|
3018
3072
|
build(doc, p) {
|
|
3019
3073
|
const b = root(doc), r = b.transform, T2 = p.period, w = 2 * Math.PI / T2, tracks = [];
|
|
3020
3074
|
const rise = (t) => 0.5 - 0.5 * Math.cos(w * t);
|
|
3021
|
-
tracks.push(boneTrack(b, "y", sampled(T2, 8, (t) => r.y - p.float * rise(t),
|
|
3022
|
-
if (p.tilt) tracks.push(boneTrack(b, "rotation", sampled(T2, 8, (t) => r.rotation + p.tilt * Math.sin(w * t),
|
|
3075
|
+
tracks.push(boneTrack(b, "y", sampled(T2, 8, (t) => r.y - p.float * rise(t)), smooth));
|
|
3076
|
+
if (p.tilt) tracks.push(boneTrack(b, "rotation", sampled(T2, 8, (t) => r.rotation + p.tilt * Math.sin(w * t)), smooth));
|
|
3023
3077
|
if (p.pulse) for (const s of doc.slots.filter((s2) => s2.bone === b.id && s2.attachment))
|
|
3024
|
-
pushTrack(tracks, deformTrack(s, "bulge", sampled(T2, 8, (t) => p.pulse * rise(t),
|
|
3078
|
+
pushTrack(tracks, deformTrack(s, "bulge", sampled(T2, 8, (t) => p.pulse * rise(t)), smooth));
|
|
3025
3079
|
return finish(this.name, p.id, T2, true, tracks, p);
|
|
3026
3080
|
}
|
|
3027
3081
|
};
|
|
@@ -3031,12 +3085,12 @@ var symbolWin = {
|
|
|
3031
3085
|
build(doc, p) {
|
|
3032
3086
|
const b = root(doc), r = b.transform, d = p.duration, tracks = [];
|
|
3033
3087
|
for (const [prop, rest] of [["scaleX", r.scaleX], ["scaleY", r.scaleY]])
|
|
3034
|
-
tracks.push(boneTrack(b, prop, K([0, rest
|
|
3088
|
+
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));
|
|
3035
3089
|
if (p.wobble) {
|
|
3036
3090
|
const n = Math.max(8, p.wobbles * 4);
|
|
3037
|
-
const keys = sampled(d, n, (t) => p.wobble * Math.exp(-3 * t / d) * Math.sin(2 * Math.PI * p.wobbles * t / d)
|
|
3091
|
+
const keys = sampled(d, n, (t) => p.wobble * Math.exp(-3 * t / d) * Math.sin(2 * Math.PI * p.wobbles * t / d));
|
|
3038
3092
|
keys[keys.length - 1].v = 0;
|
|
3039
|
-
for (const s of doc.slots.filter((s2) => s2.bone === b.id && s2.attachment)) pushTrack(tracks, deformTrack(s, "sway", keys));
|
|
3093
|
+
for (const s of doc.slots.filter((s2) => s2.bone === b.id && s2.attachment)) pushTrack(tracks, deformTrack(s, "sway", keys, smooth));
|
|
3040
3094
|
}
|
|
3041
3095
|
const anim = finish(this.name, p.id, d, false, tracks, p);
|
|
3042
3096
|
anim.events.push({ t: 0, name: "win_start" });
|
|
@@ -3050,11 +3104,11 @@ var weightShift = {
|
|
|
3050
3104
|
const T2 = p.period, w = 2 * Math.PI / T2, N = 16, tracks = [];
|
|
3051
3105
|
const hips = byRole(doc, "hips")[0] ?? root(doc), hr = hips.transform;
|
|
3052
3106
|
const s = (t) => Math.sin(w * t);
|
|
3053
|
-
tracks.push(boneTrack(hips, "x", sampled(T2, N, (t) => hr.x + p.shift * s(t))));
|
|
3054
|
-
if (p.dip) tracks.push(boneTrack(hips, "y", sampled(T2, N, (t) => hr.y + p.dip * s(t) ** 2)));
|
|
3107
|
+
tracks.push(boneTrack(hips, "x", sampled(T2, N, (t) => hr.x + p.shift * s(t)), smooth));
|
|
3108
|
+
if (p.dip) tracks.push(boneTrack(hips, "y", sampled(T2, N, (t) => hr.y + p.dip * s(t) ** 2), smooth));
|
|
3055
3109
|
const torso = byRole(doc, "torso", "body")[0];
|
|
3056
|
-
if (torso && torso !== hips) tracks.push(boneTrack(torso, "rotation", sampled(T2, N, (t) => torso.transform.rotation - p.lean * s(t))));
|
|
3057
|
-
for (const h of byRole(doc, "head")) tracks.push(boneTrack(h, "rotation", sampled(T2, N, (t) => h.transform.rotation + p.lean / 2 * s(t))));
|
|
3110
|
+
if (torso && torso !== hips) tracks.push(boneTrack(torso, "rotation", sampled(T2, N, (t) => torso.transform.rotation - p.lean * s(t)), smooth));
|
|
3111
|
+
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));
|
|
3058
3112
|
for (const leg of byRole(doc, "leg_l", "leg_r")) {
|
|
3059
3113
|
const slot = slotOfBone(doc, leg), att = slot && doc.attachments.find((a) => a.id === slot.attachment && a.type === "region");
|
|
3060
3114
|
const asset = att && att.type === "region" ? doc.assets.find((a) => a.id === att.asset) : void 0;
|
|
@@ -3064,8 +3118,8 @@ var weightShift = {
|
|
|
3064
3118
|
length = Math.max(...[[0, -h / 2], [w2 / 2, 0], [0, h / 2], [-w2 / 2, 0]].map(([x, y]) => Math.hypot(...apply(m, x, y))));
|
|
3065
3119
|
}
|
|
3066
3120
|
if (!(length > p.shift)) continue;
|
|
3067
|
-
tracks.push(boneTrack(leg, "rotation", sampled(T2, N, (t) => leg.transform.rotation + Math.asin(p.shift * s(t) / length) * 180 / Math.PI)));
|
|
3068
|
-
if (p.dip) tracks.push(boneTrack(leg, "y", sampled(T2, N, (t) => leg.transform.y - p.dip * s(t) ** 2)));
|
|
3121
|
+
tracks.push(boneTrack(leg, "rotation", sampled(T2, N, (t) => leg.transform.rotation + Math.asin(p.shift * s(t) / length) * 180 / Math.PI), smooth));
|
|
3122
|
+
if (p.dip) tracks.push(boneTrack(leg, "y", sampled(T2, N, (t) => leg.transform.y - p.dip * s(t) ** 2), smooth));
|
|
3069
3123
|
}
|
|
3070
3124
|
return finish(this.name, p.id, T2, true, tracks, p);
|
|
3071
3125
|
}
|
|
@@ -3308,13 +3362,13 @@ function motionQuality(doc, anim, opts = {}) {
|
|
|
3308
3362
|
const before = delta(values(k.v), values(p.v)), after = delta(values(n.v), values(k.v));
|
|
3309
3363
|
const pass = before.some((v, j2) => v * after[j2] > 1e-6);
|
|
3310
3364
|
const speed = Math.max(magnitude(before) / (k.t - p.t), magnitude(after) / (n.t - k.t));
|
|
3311
|
-
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);
|
|
3365
|
+
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);
|
|
3312
3366
|
const jump = magnitude(delta(left, right));
|
|
3313
3367
|
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);
|
|
3314
3368
|
const acceleration = (a, b, c) => values(at(b)).map((v, j2) => (values(at(c))[j2] - 2 * v + values(at(a))[j2]) / (h * h));
|
|
3315
3369
|
const al = acceleration(k.t - 2 * h, k.t - h, k.t), ar = acceleration(k.t, k.t + h, k.t + 2 * h);
|
|
3316
3370
|
const aj = magnitude(delta(al, ar));
|
|
3317
|
-
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");
|
|
3371
|
+
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");
|
|
3318
3372
|
}
|
|
3319
3373
|
}
|
|
3320
3374
|
const steps = Math.max(2, Math.ceil(D * 60));
|
|
@@ -3796,6 +3850,17 @@ function checkRig(doc, opts = {}) {
|
|
|
3796
3850
|
out.push({ level: "warn", code: "never_visible", subject: s.id, message: `slot "${s.id}" has no attachment and no animation ever shows one` });
|
|
3797
3851
|
return out;
|
|
3798
3852
|
}
|
|
3853
|
+
function summarizeIgnoring(findings, rules, max) {
|
|
3854
|
+
const hits = (r, f) => r.code === f.code && (r.subject ?? f.subject) === f.subject && (r.animation ?? f.animation) === f.animation;
|
|
3855
|
+
const kept = findings.filter((f) => !rules.some((r) => hits(r, f)));
|
|
3856
|
+
const idle = rules.filter((r) => !findings.some((f) => hits(r, f)));
|
|
3857
|
+
const notes = [
|
|
3858
|
+
...kept.length < findings.length ? [` (${findings.length - kept.length} ignored)`] : [],
|
|
3859
|
+
...idle.map((r) => ` ignore matched nothing: ${JSON.stringify(r)}`)
|
|
3860
|
+
];
|
|
3861
|
+
return [summarize(kept, max), ...notes].join("\n");
|
|
3862
|
+
}
|
|
3863
|
+
var where = (f) => `${f.subject}${f.animation ? ` in ${f.animation}` : ""}${f.time !== void 0 ? ` @${+f.time.toFixed(4)}s` : ""}`;
|
|
3799
3864
|
function summarize(findings, max = 12) {
|
|
3800
3865
|
if (!findings.length) return "check: OK";
|
|
3801
3866
|
const rank = { error: 0, warn: 1, info: 2 };
|
|
@@ -3810,7 +3875,7 @@ function summarize(findings, max = 12) {
|
|
|
3810
3875
|
const lines = [];
|
|
3811
3876
|
for (const f of sorted) {
|
|
3812
3877
|
const shown = shownPerCode.get(f.code) ?? 0;
|
|
3813
|
-
if (shown < PER_CODE) lines.push(` [${f.level}] ${f.code}: ${f.message}`);
|
|
3878
|
+
if (shown < PER_CODE) lines.push(` [${f.level}] ${f.code} ${where(f)}: ${f.message}`);
|
|
3814
3879
|
else if (shown === PER_CODE) lines.push(` \u2026 ${codeTotal.get(f.code) - PER_CODE} more ${f.code}`);
|
|
3815
3880
|
shownPerCode.set(f.code, shown + 1);
|
|
3816
3881
|
}
|
|
@@ -4001,7 +4066,7 @@ function packRects(rects, opts = {}) {
|
|
|
4001
4066
|
shelf = Math.max(shelf, r.height);
|
|
4002
4067
|
}
|
|
4003
4068
|
const height = y + shelf + pad;
|
|
4004
|
-
if (height <= max) return { width, height, frames };
|
|
4069
|
+
if (height <= max) return { width: Math.max(pad, ...[...frames.values()].map(([fx, , fw]) => fx + fw)) + pad, height, frames };
|
|
4005
4070
|
if (width >= max) throw new Error(`atlas does not fit in ${max}\xD7${max} (needs ${width}\xD7${height})`);
|
|
4006
4071
|
width *= 2;
|
|
4007
4072
|
}
|
|
@@ -5608,7 +5673,8 @@ function previewCheck(doc, animation) {
|
|
|
5608
5673
|
const a = animation ? doc.animations.find((x) => x.id === animation) : void 0;
|
|
5609
5674
|
return a ? `check: OK for "${a.id}" (${a.tracks.length} tracks, ${a.duration}s${a.loop ? " loop" : ""})` : "check: OK (rest pose)";
|
|
5610
5675
|
}
|
|
5611
|
-
var
|
|
5676
|
+
var TrackId = { target: z3.enum(["bone", "slot", "deform", "ik", "transform", "path", "drawOrder", "control"]), id: z3.string(), prop: z3.string() };
|
|
5677
|
+
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 };
|
|
5612
5678
|
tool(
|
|
5613
5679
|
"rig_set_control",
|
|
5614
5680
|
"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.",
|
|
@@ -5801,11 +5867,21 @@ tool(
|
|
|
5801
5867
|
);
|
|
5802
5868
|
tool(
|
|
5803
5869
|
"rig_check",
|
|
5804
|
-
"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.",
|
|
5805
|
-
{
|
|
5806
|
-
|
|
5870
|
+
"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.",
|
|
5871
|
+
{
|
|
5872
|
+
path: Path,
|
|
5873
|
+
animation: z3.string().optional().describe("limit to one animation (rest pose is always checked)"),
|
|
5874
|
+
fps: z3.number().positive().optional(),
|
|
5875
|
+
deformFps: z3.number().positive().optional(),
|
|
5876
|
+
secondaryBones: z3.array(z3.string()).optional(),
|
|
5877
|
+
frontOf: z3.array(z3.tuple([z3.string(), z3.string()])).optional(),
|
|
5878
|
+
landmarks: z3.record(z3.record(z3.tuple([z3.number(), z3.number()]))).optional(),
|
|
5879
|
+
landmarkTolerance: z3.number().nonnegative().optional(),
|
|
5880
|
+
ignore: z3.array(z3.object({ code: z3.string(), subject: z3.string().optional(), animation: z3.string().optional() }).strict()).optional()
|
|
5881
|
+
},
|
|
5882
|
+
async ({ path, ignore = [], ...opts }) => {
|
|
5807
5883
|
try {
|
|
5808
|
-
return text(
|
|
5884
|
+
return text(summarizeIgnoring(checkRig(await load(path), opts), ignore, 100));
|
|
5809
5885
|
} catch (e) {
|
|
5810
5886
|
return fail(e);
|
|
5811
5887
|
}
|
|
@@ -6422,9 +6498,9 @@ tool(
|
|
|
6422
6498
|
);
|
|
6423
6499
|
tool(
|
|
6424
6500
|
"rig_adjust_track",
|
|
6425
|
-
"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.",
|
|
6501
|
+
"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.",
|
|
6426
6502
|
{ path: Path, animation: z3.string(), ...TrackRef, amplitude: z3.number().optional(), offset: z3.number().optional(), timeScale: z3.number().positive().optional() },
|
|
6427
|
-
async ({ path, animation, target, id, prop, ...adj }) => mutate(path, (d) => adjustTrack(d, animation, { target, id, prop }, adj))
|
|
6503
|
+
async ({ path, animation, target, id, prop, interpolation, ...adj }) => mutate(path, (d) => adjustTrack(d, animation, { target, id, prop, interpolation }, adj))
|
|
6428
6504
|
);
|
|
6429
6505
|
tool(
|
|
6430
6506
|
"rig_set_animation",
|
|
@@ -6442,7 +6518,7 @@ tool(
|
|
|
6442
6518
|
tool(
|
|
6443
6519
|
"rig_remove_keys",
|
|
6444
6520
|
"Remove the keys of one track at the given times; an emptied track is dropped. Drops the preset link.",
|
|
6445
|
-
{ path: Path, animation: z3.string(), ...
|
|
6521
|
+
{ path: Path, animation: z3.string(), ...TrackId, times: z3.array(z3.number()).min(1) },
|
|
6446
6522
|
async ({ path, animation, target, id, prop, times: times2 }) => mutate(path, (d) => removeKeys(d, animation, { target, id, prop }, times2))
|
|
6447
6523
|
);
|
|
6448
6524
|
tool(
|
|
@@ -6620,9 +6696,15 @@ async function callToolVersioned(name, args) {
|
|
|
6620
6696
|
|
|
6621
6697
|
// src/tool-schema.ts
|
|
6622
6698
|
import { zodToJsonSchema } from "zod-to-json-schema";
|
|
6699
|
+
function prefixItems(node) {
|
|
6700
|
+
if (Array.isArray(node)) return node.map(prefixItems);
|
|
6701
|
+
if (!node || typeof node !== "object") return node;
|
|
6702
|
+
const { items, ...rest } = Object.fromEntries(Object.entries(node).map(([k, v]) => [k, prefixItems(v)]));
|
|
6703
|
+
return items === void 0 ? rest : Array.isArray(items) ? { ...rest, prefixItems: items } : { ...rest, items };
|
|
6704
|
+
}
|
|
6623
6705
|
function jsonSchemaOf(tool2) {
|
|
6624
6706
|
const { $schema: _drop, ...schema } = zodToJsonSchema(tool2.input, { $refStrategy: "none" });
|
|
6625
|
-
return schema;
|
|
6707
|
+
return prefixItems(schema);
|
|
6626
6708
|
}
|
|
6627
6709
|
export {
|
|
6628
6710
|
callTool,
|