@energy8platform/golem 0.2.0 → 0.3.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/README.md +11 -2
- package/dist/editor.js +502 -87
- package/dist/lib/cli.js +1309 -359
- package/dist/lib/cli.js.map +4 -4
- package/dist/lib/editor/stage.d.ts +7 -0
- package/dist/lib/editor/store.d.ts +72 -32
- package/dist/lib/editor-entry.js +1309 -359
- package/dist/lib/editor-entry.js.map +4 -4
- package/dist/lib/harness.js +162 -27
- package/dist/lib/preview/export-video.d.ts +12 -0
- package/dist/lib/preview/harness.d.ts +2 -0
- package/dist/lib/preview/render-preview.d.ts +6 -0
- package/dist/lib/rig-anim.d.ts +1 -1
- package/dist/lib/rig-check.d.ts +2 -1
- package/dist/lib/rig-constraints.d.ts +63 -0
- package/dist/lib/rig-format.d.ts +551 -325
- package/dist/lib/rig-gates.d.ts +209 -0
- package/dist/lib/rig-mesh.d.ts +1 -0
- package/dist/lib/rig-motion.d.ts +22 -0
- package/dist/lib/rig-qa.d.ts +35 -0
- package/dist/lib/rig-quality.d.ts +12 -0
- package/dist/lib/rig-runtime.d.ts +10 -1
- package/dist/lib/rig-state.d.ts +3 -0
- package/dist/lib/rig-tools.d.ts +5 -1
- package/dist/lib/runtime.js +146 -25
- package/dist/lib/runtime.js.map +3 -3
- package/dist/lib/tools.js +1291 -341
- package/dist/lib/tools.js.map +4 -4
- package/package.json +2 -2
package/dist/lib/harness.js
CHANGED
|
@@ -49413,7 +49413,7 @@ ${parts.join("\n")}
|
|
|
49413
49413
|
var ID_RE = /^[a-z][a-z0-9_]*$/;
|
|
49414
49414
|
var Id = external_exports.string().regex(ID_RE, "snake_case id");
|
|
49415
49415
|
var Ease = external_exports.union([
|
|
49416
|
-
external_exports.enum(["linear", "step", "ease_in", "ease_out", "ease_in_out", "back_out", "elastic_out"]),
|
|
49416
|
+
external_exports.enum(["linear", "step", "ease_in", "ease_out", "ease_in_out", "minimum_jerk", "back_out", "elastic_out"]),
|
|
49417
49417
|
// cubic-bezier control points, same semantics as CSS
|
|
49418
49418
|
external_exports.object({ bezier: external_exports.tuple([external_exports.number(), external_exports.number(), external_exports.number(), external_exports.number()]) })
|
|
49419
49419
|
]);
|
|
@@ -49445,6 +49445,7 @@ ${parts.join("\n")}
|
|
|
49445
49445
|
/** where this layer came from — provenance for agents */
|
|
49446
49446
|
origin: external_exports.object({
|
|
49447
49447
|
sourceImage: external_exports.string().optional(),
|
|
49448
|
+
bboxSpace: external_exports.enum(["source", "document"]).optional(),
|
|
49448
49449
|
/** bbox in source image: [x, y, w, h] */
|
|
49449
49450
|
bbox: external_exports.tuple([external_exports.number(), external_exports.number(), external_exports.number(), external_exports.number()]).optional(),
|
|
49450
49451
|
inpainted: external_exports.boolean().default(false),
|
|
@@ -49467,6 +49468,8 @@ ${parts.join("\n")}
|
|
|
49467
49468
|
var Slot = external_exports.object({
|
|
49468
49469
|
id: Id,
|
|
49469
49470
|
bone: Id,
|
|
49471
|
+
/** Closed path attachment used as a moving clipping mask. */
|
|
49472
|
+
clip: Id.optional(),
|
|
49470
49473
|
/** active attachment id; undefined = hidden */
|
|
49471
49474
|
attachment: Id.optional(),
|
|
49472
49475
|
alpha: external_exports.number().min(0).max(1).default(1),
|
|
@@ -49529,6 +49532,8 @@ ${parts.join("\n")}
|
|
|
49529
49532
|
order: external_exports.number().int().default(0),
|
|
49530
49533
|
bones: external_exports.array(Id).min(1),
|
|
49531
49534
|
target: Id,
|
|
49535
|
+
iterations: external_exports.number().int().min(1).max(1e3).optional(),
|
|
49536
|
+
tolerance: external_exports.number().positive().optional(),
|
|
49532
49537
|
mix: external_exports.number().default(1),
|
|
49533
49538
|
softness: external_exports.number().default(0),
|
|
49534
49539
|
bendPositive: external_exports.boolean().default(true),
|
|
@@ -49576,7 +49581,7 @@ ${parts.join("\n")}
|
|
|
49576
49581
|
var SlotProp = external_exports.enum(["alpha", "attachment", "z", "color"]);
|
|
49577
49582
|
var DeformProp = external_exports.string().regex(/^(bulge|bend|sway|squash|p[0-2][0-2][xy]|vertices)$/, "deform prop must be bulge|bend|sway|squash|p<row><col>x|y|vertices");
|
|
49578
49583
|
var IkProp = external_exports.enum(["mix", "softness", "bendPositive", "compress", "stretch"]);
|
|
49579
|
-
var TransformProp = external_exports.enum(["mixRotate", "mixX", "mixY", "mixScaleX", "mixScaleY", "mixShearY"]);
|
|
49584
|
+
var TransformProp = external_exports.enum(["mixRotate", "mixX", "mixY", "mixScaleX", "mixScaleY", "mixShearY", "x", "y", "rotation", "scaleX", "scaleY", "shearY"]);
|
|
49580
49585
|
var PathProp = external_exports.enum(["position", "spacing", "mixRotate", "mixX", "mixY"]);
|
|
49581
49586
|
var KeyValue = external_exports.union([external_exports.number(), external_exports.string(), external_exports.null(), external_exports.array(external_exports.number()), external_exports.array(external_exports.string())]);
|
|
49582
49587
|
var Key = external_exports.object({
|
|
@@ -49585,14 +49590,15 @@ ${parts.join("\n")}
|
|
|
49585
49590
|
/** easing INTO the next key */
|
|
49586
49591
|
ease: Ease.default("ease_in_out")
|
|
49587
49592
|
});
|
|
49593
|
+
var Interpolation = { interpolation: external_exports.enum(["segment", "pchip"]).optional() };
|
|
49588
49594
|
var Track = external_exports.discriminatedUnion("target", [
|
|
49589
|
-
external_exports.object({ target: external_exports.literal("bone"), id: Id, prop: BoneProp, keys: external_exports.array(Key).min(1) }),
|
|
49590
|
-
external_exports.object({ target: external_exports.literal("slot"), id: Id, prop: SlotProp, keys: external_exports.array(Key).min(1) }),
|
|
49591
|
-
external_exports.object({ target: external_exports.literal("deform"), id: Id, prop: DeformProp, keys: external_exports.array(Key).min(1) }),
|
|
49592
|
-
external_exports.object({ target: external_exports.literal("ik"), id: Id, prop: IkProp, keys: external_exports.array(Key).min(1) }),
|
|
49593
|
-
external_exports.object({ target: external_exports.literal("transform"), id: Id, prop: TransformProp, keys: external_exports.array(Key).min(1) }),
|
|
49594
|
-
external_exports.object({ target: external_exports.literal("path"), id: Id, prop: PathProp, keys: external_exports.array(Key).min(1) }),
|
|
49595
|
-
external_exports.object({ target: external_exports.literal("drawOrder"), id: Id.default("order"), prop: external_exports.literal("order").default("order"), keys: external_exports.array(Key).min(1) })
|
|
49595
|
+
external_exports.object({ target: external_exports.literal("bone"), id: Id, prop: BoneProp, ...Interpolation, keys: external_exports.array(Key).min(1) }),
|
|
49596
|
+
external_exports.object({ target: external_exports.literal("slot"), id: Id, prop: SlotProp, ...Interpolation, keys: external_exports.array(Key).min(1) }),
|
|
49597
|
+
external_exports.object({ target: external_exports.literal("deform"), id: Id, prop: DeformProp, ...Interpolation, keys: external_exports.array(Key).min(1) }),
|
|
49598
|
+
external_exports.object({ target: external_exports.literal("ik"), id: Id, prop: IkProp, ...Interpolation, keys: external_exports.array(Key).min(1) }),
|
|
49599
|
+
external_exports.object({ target: external_exports.literal("transform"), id: Id, prop: TransformProp, ...Interpolation, keys: external_exports.array(Key).min(1) }),
|
|
49600
|
+
external_exports.object({ target: external_exports.literal("path"), id: Id, prop: PathProp, ...Interpolation, keys: external_exports.array(Key).min(1) }),
|
|
49601
|
+
external_exports.object({ target: external_exports.literal("drawOrder"), id: Id.default("order"), prop: external_exports.literal("order").default("order"), ...Interpolation, keys: external_exports.array(Key).min(1) })
|
|
49596
49602
|
]);
|
|
49597
49603
|
var Event2 = external_exports.object({
|
|
49598
49604
|
t: external_exports.number().nonnegative(),
|
|
@@ -49734,6 +49740,10 @@ ${parts.join("\n")}
|
|
|
49734
49740
|
if (a2.frame && !a2.trim && !a2.rotate && (a2.frame[2] !== a2.width || a2.frame[3] !== a2.height)) errs.push(`asset "${a2.id}": frame size must equal width\xD7height (or declare trim/rotate)`);
|
|
49735
49741
|
}
|
|
49736
49742
|
for (const s2 of doc.slots) {
|
|
49743
|
+
if (s2.clip) {
|
|
49744
|
+
const mask = attachments.get(s2.clip);
|
|
49745
|
+
if (mask?.type !== "path" || !mask.closed) errs.push(`slot "${s2.id}": clip must name a closed path attachment`);
|
|
49746
|
+
}
|
|
49737
49747
|
if (!bones.has(s2.bone)) errs.push(`slot "${s2.id}": unknown bone "${s2.bone}"`);
|
|
49738
49748
|
if (s2.attachment) {
|
|
49739
49749
|
const a2 = attachments.get(s2.attachment);
|
|
@@ -49765,7 +49775,8 @@ ${parts.join("\n")}
|
|
|
49765
49775
|
}
|
|
49766
49776
|
}
|
|
49767
49777
|
for (const c2 of doc.ik) {
|
|
49768
|
-
|
|
49778
|
+
for (let i2 = 1; i2 < c2.bones.length; i2++) if (doc.bones.find((b2) => b2.id === c2.bones[i2])?.parent !== c2.bones[i2 - 1])
|
|
49779
|
+
errs.push(`ik "${c2.id}": bones must form a contiguous parent-to-child chain`);
|
|
49769
49780
|
for (const b2 of c2.bones) if (!bones.has(b2)) errs.push(`ik "${c2.id}": unknown bone "${b2}"`);
|
|
49770
49781
|
if (!bones.has(c2.target)) errs.push(`ik "${c2.id}": unknown target bone "${c2.target}"`);
|
|
49771
49782
|
}
|
|
@@ -49837,6 +49848,7 @@ ${parts.join("\n")}
|
|
|
49837
49848
|
ease_in: (t2) => t2 * t2,
|
|
49838
49849
|
ease_out: (t2) => 1 - (1 - t2) * (1 - t2),
|
|
49839
49850
|
ease_in_out: (t2) => t2 < 0.5 ? 2 * t2 * t2 : 1 - (-2 * t2 + 2) ** 2 / 2,
|
|
49851
|
+
minimum_jerk: (t2) => t2 * t2 * t2 * (10 + t2 * (-15 + 6 * t2)),
|
|
49840
49852
|
back_out: (t2) => 1 + 2.70158 * (t2 - 1) ** 3 + 1.70158 * (t2 - 1) ** 2,
|
|
49841
49853
|
elastic_out: (t2) => t2 === 0 ? 0 : t2 === 1 ? 1 : 2 ** (-10 * t2) * Math.sin((t2 * 10 - 0.75) * (2 * Math.PI / 3)) + 1
|
|
49842
49854
|
};
|
|
@@ -49868,7 +49880,25 @@ ${parts.join("\n")}
|
|
|
49868
49880
|
return f2;
|
|
49869
49881
|
}
|
|
49870
49882
|
var pk = (target, id, prop) => `${target}:${id}:${prop}`;
|
|
49871
|
-
function
|
|
49883
|
+
function smoothValue(track, i2, t2, component, loop) {
|
|
49884
|
+
const k2 = track.keys, n2 = k2.length;
|
|
49885
|
+
const v2 = (j2) => component === void 0 ? k2[j2].v : k2[j2].v[component];
|
|
49886
|
+
const h2 = (j2) => k2[j2 + 1].t - k2[j2].t;
|
|
49887
|
+
const d2 = (j2) => (v2(j2 + 1) - v2(j2)) / h2(j2);
|
|
49888
|
+
const harmonic = (a2, b2, h0, h1) => a2 * b2 <= 0 ? 0 : 3 * (h0 + h1) / ((2 * h1 + h0) / a2 + (h1 + 2 * h0) / b2);
|
|
49889
|
+
const tangent = (j2) => {
|
|
49890
|
+
if (j2 === 0 || j2 === n2 - 1) {
|
|
49891
|
+
if (loop && n2 > 2 && v2(0) === v2(n2 - 1) && k2[0].ease !== "step" && k2[n2 - 2].ease !== "step")
|
|
49892
|
+
return harmonic(d2(n2 - 2), d2(0), h2(n2 - 2), h2(0));
|
|
49893
|
+
return 0;
|
|
49894
|
+
}
|
|
49895
|
+
if (k2[j2 - 1].ease === "step" || k2[j2].ease === "step") return 0;
|
|
49896
|
+
return harmonic(d2(j2 - 1), d2(j2), h2(j2 - 1), h2(j2));
|
|
49897
|
+
};
|
|
49898
|
+
const u2 = (t2 - k2[i2].t) / h2(i2), u22 = u2 * u2, u3 = u22 * u2;
|
|
49899
|
+
return (2 * u3 - 3 * u22 + 1) * v2(i2) + (u3 - 2 * u22 + u2) * h2(i2) * tangent(i2) + (-2 * u3 + 3 * u22) * v2(i2 + 1) + (u3 - u22) * h2(i2) * tangent(i2 + 1);
|
|
49900
|
+
}
|
|
49901
|
+
function sample(track, t2, loop = false) {
|
|
49872
49902
|
const keys = track.keys;
|
|
49873
49903
|
if (t2 <= keys[0].t) return keys[0].v;
|
|
49874
49904
|
const last = keys[keys.length - 1];
|
|
@@ -49876,31 +49906,38 @@ ${parts.join("\n")}
|
|
|
49876
49906
|
let i2 = 0;
|
|
49877
49907
|
while (keys[i2 + 1].t <= t2) i2++;
|
|
49878
49908
|
const a2 = keys[i2], b2 = keys[i2 + 1];
|
|
49909
|
+
if (a2.ease === "step") return a2.v;
|
|
49879
49910
|
if (typeof a2.v === "number" && typeof b2.v === "number") {
|
|
49911
|
+
if (track.interpolation === "pchip") return smoothValue(track, i2, t2, void 0, loop);
|
|
49880
49912
|
const u2 = ease(a2.ease)((t2 - a2.t) / (b2.t - a2.t));
|
|
49881
49913
|
return a2.v + (b2.v - a2.v) * u2;
|
|
49882
49914
|
}
|
|
49883
49915
|
if (Array.isArray(a2.v) && Array.isArray(b2.v) && typeof a2.v[0] === "number") {
|
|
49916
|
+
if (track.interpolation === "pchip") return a2.v.map((_, j2) => smoothValue(track, i2, t2, j2, loop));
|
|
49884
49917
|
const u2 = ease(a2.ease)((t2 - a2.t) / (b2.t - a2.t)), av = a2.v, bv = b2.v;
|
|
49885
49918
|
return av.map((v2, k2) => v2 + ((bv[k2] ?? v2) - v2) * u2);
|
|
49886
49919
|
}
|
|
49887
49920
|
return a2.v;
|
|
49888
49921
|
}
|
|
49889
49922
|
function poseOf(anim, t2, out2 = /* @__PURE__ */ new Map()) {
|
|
49890
|
-
for (const tr of anim.tracks) out2.set(pk(tr.target, tr.id, tr.prop), sample(tr, t2));
|
|
49923
|
+
for (const tr of anim.tracks) out2.set(pk(tr.target, tr.id, tr.prop), sample(tr, t2, anim.loop && tr.keys[0].t === 0 && tr.keys.at(-1).t === anim.duration));
|
|
49891
49924
|
return out2;
|
|
49892
49925
|
}
|
|
49893
49926
|
function blendPoses(from, to, w2, restOf) {
|
|
49894
|
-
|
|
49895
|
-
|
|
49896
|
-
|
|
49897
|
-
|
|
49927
|
+
if (w2 >= 1) return new Map(to);
|
|
49928
|
+
const out2 = /* @__PURE__ */ new Map();
|
|
49929
|
+
for (const k2 of /* @__PURE__ */ new Set([...from.keys(), ...to.keys()])) {
|
|
49930
|
+
const f2 = from.has(k2) ? from.get(k2) : restOf(k2);
|
|
49931
|
+
const cur = to.has(k2) ? to.get(k2) : restOf(k2);
|
|
49932
|
+
if (typeof f2 === "number" && !Array.isArray(cur)) {
|
|
49898
49933
|
const target = typeof cur === "number" ? cur : restOf(k2);
|
|
49899
49934
|
out2.set(k2, f2 + (target - f2) * w2);
|
|
49900
49935
|
} else if (Array.isArray(f2) && typeof f2[0] === "number") {
|
|
49901
49936
|
const fv = f2, tv = Array.isArray(cur) && typeof cur[0] === "number" ? cur : fv.map(() => 0);
|
|
49902
49937
|
out2.set(k2, fv.map((v2, i2) => v2 + ((tv[i2] ?? 0) - v2) * w2));
|
|
49903
|
-
} else if (cur
|
|
49938
|
+
} else if (Array.isArray(cur) && typeof cur[0] === "number" && typeof f2 !== "string") {
|
|
49939
|
+
out2.set(k2, cur.map((v2) => v2 * w2));
|
|
49940
|
+
} else out2.set(k2, f2 ?? null);
|
|
49904
49941
|
}
|
|
49905
49942
|
return out2;
|
|
49906
49943
|
}
|
|
@@ -50069,10 +50106,43 @@ ${parts.join("\n")}
|
|
|
50069
50106
|
updateSubtree(skel, boneId);
|
|
50070
50107
|
}
|
|
50071
50108
|
var shortest = (deg) => ((deg + 180) % 360 + 360) % 360 - 180;
|
|
50109
|
+
function applyLongIk(skel, c2, tx, ty, mix) {
|
|
50110
|
+
const initial = c2.bones.map((id) => ({ ...skel.bones.get(id) }));
|
|
50111
|
+
const last = c2.bones.at(-1), len = boneOf(skel.doc, last).length;
|
|
50112
|
+
const end = () => apply(skel.bones.get(last).world, len, 0);
|
|
50113
|
+
const tolerance = c2.tolerance ?? 0.05;
|
|
50114
|
+
const root = skel.bones.get(c2.bones[0]).world, tip = end();
|
|
50115
|
+
if (Math.abs((tip[0] - root.tx) * (ty - root.ty) - (tip[1] - root.ty) * (tx - root.tx)) < 1e-7 && Math.hypot(tx - root.tx, ty - root.ty) < Math.hypot(tip[0] - root.tx, tip[1] - root.ty) - tolerance) {
|
|
50116
|
+
skel.bones.get(last).rotation += cnum(skel, "ik", c2.id, "bendPositive", c2.bendPositive) > 0.5 ? 5 : -5;
|
|
50117
|
+
updateSubtree(skel, last);
|
|
50118
|
+
}
|
|
50119
|
+
for (let iter = 0; iter < (c2.iterations ?? 100); iter++) {
|
|
50120
|
+
const e2 = end();
|
|
50121
|
+
if (Math.hypot(e2[0] - tx, e2[1] - ty) <= tolerance) break;
|
|
50122
|
+
for (let i2 = c2.bones.length - 1; i2 >= 0; i2--) {
|
|
50123
|
+
const id = c2.bones[i2], b2 = boneOf(skel.doc, id), st = skel.bones.get(id), w2 = st.world, e3 = end();
|
|
50124
|
+
const angle = wrapRad(Math.atan2(ty - w2.ty, tx - w2.tx) - Math.atan2(e3[1] - w2.ty, e3[0] - w2.tx));
|
|
50125
|
+
const cos = Math.cos(angle), sin = Math.sin(angle);
|
|
50126
|
+
setEffectiveWorld(skel, b2, st, { ...w2, a: cos * w2.a - sin * w2.b, b: sin * w2.a + cos * w2.b, c: cos * w2.c - sin * w2.d, d: sin * w2.c + cos * w2.d });
|
|
50127
|
+
}
|
|
50128
|
+
}
|
|
50129
|
+
if (mix < 1) {
|
|
50130
|
+
c2.bones.forEach((id, i2) => {
|
|
50131
|
+
const st = skel.bones.get(id), from = initial[i2];
|
|
50132
|
+
for (const p2 of ["rotation", "shearX", "shearY"]) st[p2] = from[p2] + shortest(st[p2] - from[p2]) * mix;
|
|
50133
|
+
for (const p2 of ["x", "y", "scaleX", "scaleY"]) st[p2] = from[p2] + (st[p2] - from[p2]) * mix;
|
|
50134
|
+
});
|
|
50135
|
+
updateSubtree(skel, c2.bones[0]);
|
|
50136
|
+
}
|
|
50137
|
+
}
|
|
50072
50138
|
function applyIk(skel, c2) {
|
|
50073
50139
|
const mix = cnum(skel, "ik", c2.id, "mix", c2.mix);
|
|
50074
50140
|
if (mix <= 0) return;
|
|
50075
50141
|
const target = skel.bones.get(c2.target).world;
|
|
50142
|
+
if (c2.bones.length > 2) {
|
|
50143
|
+
applyLongIk(skel, c2, target.tx, target.ty, mix);
|
|
50144
|
+
return;
|
|
50145
|
+
}
|
|
50076
50146
|
const bendDir = cnum(skel, "ik", c2.id, "bendPositive", c2.bendPositive) > 0.5 ? 1 : -1;
|
|
50077
50147
|
const compress = cnum(skel, "ik", c2.id, "compress", c2.compress) > 0.5, stretch = cnum(skel, "ik", c2.id, "stretch", c2.stretch) > 0.5;
|
|
50078
50148
|
let softness = cnum(skel, "ik", c2.id, "softness", c2.softness);
|
|
@@ -50207,6 +50277,7 @@ ${parts.join("\n")}
|
|
|
50207
50277
|
const mixRotate = cnum(skel, "transform", c2.id, "mixRotate", c2.mixRotate), mixX = cnum(skel, "transform", c2.id, "mixX", c2.mixX), mixY = cnum(skel, "transform", c2.id, "mixY", c2.mixY);
|
|
50208
50278
|
const mixScaleX = cnum(skel, "transform", c2.id, "mixScaleX", c2.mixScaleX), mixScaleY = cnum(skel, "transform", c2.id, "mixScaleY", c2.mixScaleY), mixShearY = cnum(skel, "transform", c2.id, "mixShearY", c2.mixShearY);
|
|
50209
50279
|
if (!mixRotate && !mixX && !mixY && !mixScaleX && !mixScaleY && !mixShearY) return;
|
|
50280
|
+
c2 = { ...c2, ...Object.fromEntries(["x", "y", "rotation", "scaleX", "scaleY", "shearY"].map((p2) => [p2, cnum(skel, "transform", c2.id, p2, c2[p2])])) };
|
|
50210
50281
|
const target = skel.bones.get(c2.target), tw = target.world;
|
|
50211
50282
|
const offsetRotation = c2.rotation * DEG2, offsetShearY = c2.shearY * DEG2;
|
|
50212
50283
|
for (const id of c2.bones) {
|
|
@@ -50534,6 +50605,7 @@ ${parts.join("\n")}
|
|
|
50534
50605
|
var AnimationState = class {
|
|
50535
50606
|
constructor(sink, opts = {}) {
|
|
50536
50607
|
this.sink = sink;
|
|
50608
|
+
this.opts = opts;
|
|
50537
50609
|
this.defaultMix = opts.mix ?? 0;
|
|
50538
50610
|
}
|
|
50539
50611
|
onEvent;
|
|
@@ -50558,11 +50630,18 @@ ${parts.join("\n")}
|
|
|
50558
50630
|
}
|
|
50559
50631
|
channel(id, loop, priority = 0) {
|
|
50560
50632
|
const anim = this.sink.doc.animations.find((a2) => a2.id === id);
|
|
50561
|
-
if (!anim)
|
|
50633
|
+
if (!anim) {
|
|
50634
|
+
const message = `unknown animation "${id}"; available: ${this.sink.doc.animations.map((a2) => a2.id).join(", ")}`;
|
|
50635
|
+
if (this.opts.missingAnimation === "throw") throw new Error(message);
|
|
50636
|
+
(this.opts.onWarning ?? console.warn)(message);
|
|
50637
|
+
return void 0;
|
|
50638
|
+
}
|
|
50562
50639
|
return { anim, time: 0, loop: loop ?? anim.loop, done: false, priority, fresh: true };
|
|
50563
50640
|
}
|
|
50564
50641
|
setBase(id, opts = {}) {
|
|
50565
|
-
|
|
50642
|
+
const next = id === void 0 ? void 0 : this.channel(id);
|
|
50643
|
+
if (id !== void 0 && !next) return;
|
|
50644
|
+
this.base = next;
|
|
50566
50645
|
if (!this.main || this.main.done) {
|
|
50567
50646
|
this.main = void 0;
|
|
50568
50647
|
this.beginMix(opts.mix);
|
|
@@ -50571,6 +50650,16 @@ ${parts.join("\n")}
|
|
|
50571
50650
|
}
|
|
50572
50651
|
play(id, opts = {}) {
|
|
50573
50652
|
const ch = this.channel(id, opts.loop, opts.priority ?? 0);
|
|
50653
|
+
if (!ch) {
|
|
50654
|
+
this.main?.settle?.("interrupted");
|
|
50655
|
+
for (const queued of this.queue) queued.settle?.("dropped");
|
|
50656
|
+
this.main = void 0;
|
|
50657
|
+
this.queue = [];
|
|
50658
|
+
this.beginMix(opts.mix);
|
|
50659
|
+
this.update(0);
|
|
50660
|
+
opts.onComplete?.("dropped");
|
|
50661
|
+
return Promise.resolve("dropped");
|
|
50662
|
+
}
|
|
50574
50663
|
const promise2 = new Promise((resolve) => {
|
|
50575
50664
|
ch.settle = (r2) => {
|
|
50576
50665
|
ch.settle = void 0;
|
|
@@ -50591,7 +50680,10 @@ ${parts.join("\n")}
|
|
|
50591
50680
|
/** Independent loop on top of the shown pose; `undefined` removes it. */
|
|
50592
50681
|
setOverlay(name, id) {
|
|
50593
50682
|
if (id === void 0) this.overlays.delete(name);
|
|
50594
|
-
else
|
|
50683
|
+
else {
|
|
50684
|
+
const ch = this.channel(id);
|
|
50685
|
+
if (ch) this.overlays.set(name, ch);
|
|
50686
|
+
}
|
|
50595
50687
|
}
|
|
50596
50688
|
/** Clear everything and show the setup pose. Pending plays resolve as "stopped". */
|
|
50597
50689
|
stop() {
|
|
@@ -50676,6 +50768,7 @@ ${parts.join("\n")}
|
|
|
50676
50768
|
// src/rig-runtime.ts
|
|
50677
50769
|
var DEG3 = Math.PI / 180;
|
|
50678
50770
|
var MESH_GRID = 9;
|
|
50771
|
+
var GHOST_ALPHA = 0.5;
|
|
50679
50772
|
async function createTextures(doc, loadSource) {
|
|
50680
50773
|
const bySrc = /* @__PURE__ */ new Map(), out2 = /* @__PURE__ */ new Map();
|
|
50681
50774
|
for (const a2 of doc.assets) {
|
|
@@ -50715,7 +50808,7 @@ ${parts.join("\n")}
|
|
|
50715
50808
|
var rgbOf = (c2) => Math.round(c2[0] * 255) << 16 | Math.round(c2[1] * 255) << 8 | Math.round(c2[2] * 255);
|
|
50716
50809
|
var sameNumbers = (a2, b2) => a2 === b2 || a2.length === b2.length && a2.every((v2, i2) => v2 === b2[i2]);
|
|
50717
50810
|
var RigPlayer = class {
|
|
50718
|
-
/**
|
|
50811
|
+
/** Default crossfade is 0.18 seconds; explicit mix: 0 requests a hard cut. */
|
|
50719
50812
|
constructor(doc, textures, opts = {}) {
|
|
50720
50813
|
this.doc = doc;
|
|
50721
50814
|
parse2(doc);
|
|
@@ -50754,7 +50847,7 @@ ${parts.join("\n")}
|
|
|
50754
50847
|
this.items.push(item);
|
|
50755
50848
|
this.view.addChild(item.node);
|
|
50756
50849
|
}
|
|
50757
|
-
this.state = new AnimationState(this, { mix: opts.mix ?? 0 });
|
|
50850
|
+
this.state = new AnimationState(this, { ...opts, mix: opts.mix ?? 0.18 });
|
|
50758
50851
|
this.setPose(/* @__PURE__ */ new Map());
|
|
50759
50852
|
}
|
|
50760
50853
|
view = new Container();
|
|
@@ -50765,6 +50858,9 @@ ${parts.join("\n")}
|
|
|
50765
50858
|
pages = /* @__PURE__ */ new Map();
|
|
50766
50859
|
/** slots the editor hides; honoured by setPose */
|
|
50767
50860
|
hidden = /* @__PURE__ */ new Set();
|
|
50861
|
+
/** slots the editor has selected; setPose draws them at GHOST_ALPHA at least */
|
|
50862
|
+
ghosts = /* @__PURE__ */ new Set();
|
|
50863
|
+
clipGraphics = /* @__PURE__ */ new Map();
|
|
50768
50864
|
/** whole atlas page as a plain texture (identity uv mapping) for skinned meshes */
|
|
50769
50865
|
pageTexture(tex) {
|
|
50770
50866
|
let page = this.pages.get(tex.source);
|
|
@@ -50776,7 +50872,7 @@ ${parts.join("\n")}
|
|
|
50776
50872
|
this.state.onEvent = opts.onEvent;
|
|
50777
50873
|
return this.state.play(animId, {
|
|
50778
50874
|
loop: opts.loop,
|
|
50779
|
-
mix: opts.mix
|
|
50875
|
+
mix: opts.mix,
|
|
50780
50876
|
ifBusy: "interrupt",
|
|
50781
50877
|
onComplete: (r2) => {
|
|
50782
50878
|
if (r2 === "completed") opts.onComplete?.();
|
|
@@ -50805,6 +50901,10 @@ ${parts.join("\n")}
|
|
|
50805
50901
|
if (visible) this.hidden.delete(slotId);
|
|
50806
50902
|
else this.hidden.add(slotId);
|
|
50807
50903
|
}
|
|
50904
|
+
/** Editor-only: draw these slots at GHOST_ALPHA at least, whatever the pose says (an empty list clears it). */
|
|
50905
|
+
setGhostSlots(slotIds) {
|
|
50906
|
+
this.ghosts = new Set(slotIds);
|
|
50907
|
+
}
|
|
50808
50908
|
/** Stop playback and immediately restore the complete setup pose. */
|
|
50809
50909
|
stop() {
|
|
50810
50910
|
this.state.stop();
|
|
@@ -50816,12 +50916,32 @@ ${parts.join("\n")}
|
|
|
50816
50916
|
/** Apply a pose (rest where silent) to the display objects. */
|
|
50817
50917
|
setPose(pose) {
|
|
50818
50918
|
const skel = this.skel = evaluate(this.doc, pose);
|
|
50919
|
+
const clips = new Set(this.doc.slots.flatMap((s2) => s2.clip ? [s2.clip] : []));
|
|
50920
|
+
for (const [id, g2] of this.clipGraphics) if (!clips.has(id)) {
|
|
50921
|
+
g2.destroy();
|
|
50922
|
+
this.clipGraphics.delete(id);
|
|
50923
|
+
}
|
|
50924
|
+
for (const id of clips) {
|
|
50925
|
+
const att = this.doc.attachments.find((a2) => a2.id === id);
|
|
50926
|
+
if (att?.type !== "path" || !att.closed) continue;
|
|
50927
|
+
let g2 = this.clipGraphics.get(id);
|
|
50928
|
+
if (!g2) {
|
|
50929
|
+
g2 = new Graphics();
|
|
50930
|
+
this.clipGraphics.set(id, g2);
|
|
50931
|
+
this.view.addChild(g2);
|
|
50932
|
+
}
|
|
50933
|
+
const vertices = attachmentWorldVertices(skel, att.slot, att);
|
|
50934
|
+
const n2 = Math.max(32, vertices.length * 4);
|
|
50935
|
+
const points = samplePath(vertices, true, att.lengths, true, Array.from({ length: n2 }, (_, i2) => i2 / n2), true);
|
|
50936
|
+
g2.clear().poly(points.flatMap((p2) => [p2.x, p2.y]), true).fill(16777215);
|
|
50937
|
+
}
|
|
50819
50938
|
const m2 = new Matrix();
|
|
50820
50939
|
const orderV = pose.get(pk("drawOrder", "order", "order"));
|
|
50821
50940
|
const order = /* @__PURE__ */ new Map();
|
|
50822
50941
|
if (Array.isArray(orderV)) orderV.forEach((id, i2) => order.set(id, i2));
|
|
50823
50942
|
for (const it of this.items) {
|
|
50824
50943
|
const s2 = it.slot, shown = skel.attachment.get(s2.id);
|
|
50944
|
+
it.node.mask = s2.clip ? this.clipGraphics.get(s2.clip) ?? null : null;
|
|
50825
50945
|
if (shown !== it.att || this.hidden.has(s2.id)) {
|
|
50826
50946
|
it.node.visible = false;
|
|
50827
50947
|
continue;
|
|
@@ -50829,7 +50949,8 @@ ${parts.join("\n")}
|
|
|
50829
50949
|
it.node.visible = true;
|
|
50830
50950
|
const colorV = pose.get(pk("slot", s2.id, "color"));
|
|
50831
50951
|
const color = Array.isArray(colorV) && typeof colorV[0] === "number" ? colorV : colorToArray(s2.color);
|
|
50832
|
-
|
|
50952
|
+
const alpha = numOf(pose, pk("slot", s2.id, "alpha"), s2.alpha) * color[3];
|
|
50953
|
+
it.node.alpha = this.ghosts.has(s2.id) ? Math.max(alpha, GHOST_ALPHA) : alpha;
|
|
50833
50954
|
it.node.tint = rgbOf(color);
|
|
50834
50955
|
it.node.zIndex = order.size ? order.get(s2.id) ?? 0 : numOf(pose, pk("slot", s2.id, "z"), s2.z);
|
|
50835
50956
|
if (it.skinned) {
|
|
@@ -50912,7 +51033,7 @@ ${parts.join("\n")}
|
|
|
50912
51033
|
if (!assets[src]) throw new Error(`missing asset data for "${src}"`);
|
|
50913
51034
|
return textureFromDataUrl(assets[src]);
|
|
50914
51035
|
});
|
|
50915
|
-
player = new RigPlayer(doc, textures);
|
|
51036
|
+
player = new RigPlayer(doc, textures, { mix: 0 });
|
|
50916
51037
|
player.view.scale.set(scale);
|
|
50917
51038
|
overlay = new Container();
|
|
50918
51039
|
overlay.scale.set(scale);
|
|
@@ -50957,7 +51078,7 @@ ${parts.join("\n")}
|
|
|
50957
51078
|
};
|
|
50958
51079
|
for (const t2 of opts.times) {
|
|
50959
51080
|
if (opts.animation) {
|
|
50960
|
-
player.play(opts.animation);
|
|
51081
|
+
player.play(opts.animation, { mix: 0 });
|
|
50961
51082
|
player.tick(t2);
|
|
50962
51083
|
} else {
|
|
50963
51084
|
player.stop();
|
|
@@ -50965,7 +51086,21 @@ ${parts.join("\n")}
|
|
|
50965
51086
|
}
|
|
50966
51087
|
if (opts.overlayBones) drawBones();
|
|
50967
51088
|
else clearOverlay();
|
|
50968
|
-
|
|
51089
|
+
if (opts.weightBone) {
|
|
51090
|
+
if (!player.doc.bones.some((b2) => b2.id === opts.weightBone)) throw new Error(`unknown weight bone "${opts.weightBone}"`);
|
|
51091
|
+
const anim = player.doc.animations.find((a2) => a2.id === opts.animation), pose = anim ? poseOf(anim, anim.loop ? t2 % anim.duration : t2) : /* @__PURE__ */ new Map();
|
|
51092
|
+
const skel = evaluate(player.doc, pose), g2 = new Graphics();
|
|
51093
|
+
for (const att of skel.attachment.values()) if (att?.type === "mesh" && att.weights) {
|
|
51094
|
+
const v2 = attachmentWorldVertices(skel, att.slot, att);
|
|
51095
|
+
for (let j2 = 0; j2 < att.triangles.length; j2 += 3) {
|
|
51096
|
+
const ids = att.triangles.slice(j2, j2 + 3);
|
|
51097
|
+
const weight = ids.reduce((sum, i2) => sum + att.weights[i2].filter((w3) => w3.bone === opts.weightBone).reduce((s2, w3) => s2 + w3.w, 0), 0) / 3;
|
|
51098
|
+
g2.poly(ids.flatMap((i2) => [v2[i2 * 2], v2[i2 * 2 + 1]]), true).fill({ color: Math.round(255 * weight) << 16 | Math.round(255 * (1 - weight)), alpha: 0.75 });
|
|
51099
|
+
}
|
|
51100
|
+
}
|
|
51101
|
+
overlay.addChild(g2);
|
|
51102
|
+
}
|
|
51103
|
+
const frame = grab(opts.transparent ? 0 : 1);
|
|
50969
51104
|
const ctx = frame.getContext("2d");
|
|
50970
51105
|
if (opts.onionSkin && prev) {
|
|
50971
51106
|
ctx.globalAlpha = 0.35;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { RigDocument } from '../rig-format';
|
|
2
|
+
import { type PreviewOptions } from './render-preview';
|
|
3
|
+
/** Encode deterministic runtime frames at the requested rate; replace output only after a successful encode. */
|
|
4
|
+
export declare function exportVideo(doc: RigDocument, opts: PreviewOptions & {
|
|
5
|
+
animation: string;
|
|
6
|
+
outFile: string;
|
|
7
|
+
fps?: number;
|
|
8
|
+
}): Promise<{
|
|
9
|
+
frames: number;
|
|
10
|
+
fps: number;
|
|
11
|
+
duration: number;
|
|
12
|
+
}>;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type RigDocument } from "../rig-format";
|
|
2
|
+
import { type CaptureIdentity } from "../rig-qa";
|
|
2
3
|
export interface PreviewOptions {
|
|
3
4
|
/** directory that asset.src paths are relative to */
|
|
4
5
|
assetsDir: string;
|
|
@@ -9,6 +10,10 @@ export interface PreviewOptions {
|
|
|
9
10
|
count?: number;
|
|
10
11
|
overlayBones?: boolean;
|
|
11
12
|
background?: string;
|
|
13
|
+
transparent?: boolean;
|
|
14
|
+
weightBone?: string;
|
|
15
|
+
/** Source image path -> grayscale mask PNG path, resolved relative to assetsDir. */
|
|
16
|
+
debugFillMasks?: Record<string, string>;
|
|
12
17
|
scale?: number;
|
|
13
18
|
sheetColumns?: number;
|
|
14
19
|
/** ghost the previous frame under each frame */
|
|
@@ -17,6 +22,7 @@ export interface PreviewOptions {
|
|
|
17
22
|
region?: [number, number, number, number];
|
|
18
23
|
}
|
|
19
24
|
export interface PreviewResult {
|
|
25
|
+
identity: CaptureIdentity;
|
|
20
26
|
frames: Buffer[];
|
|
21
27
|
sheet: Buffer;
|
|
22
28
|
times: number[];
|
package/dist/lib/rig-anim.d.ts
CHANGED
|
@@ -13,7 +13,7 @@ export type Val = number | string | null | number[] | string[];
|
|
|
13
13
|
/** key: "bone:id:prop" | "slot:id:prop" | "deform:id:prop" | "ik:id:prop" | "transform:id:prop" | "path:id:prop" | "drawOrder:order:order" */
|
|
14
14
|
export type Pose = Map<string, Val>;
|
|
15
15
|
export declare const pk: (target: string, id: string, prop: string) => string;
|
|
16
|
-
export declare function sample(track: Track, t: number): Val;
|
|
16
|
+
export declare function sample(track: Track, t: number, loop?: boolean): Val;
|
|
17
17
|
export declare function poseOf(anim: Animation, t: number, out?: Pose): Pose;
|
|
18
18
|
/** Crossfade: numbers (and number arrays) interpolate toward `to` (or rest when absent there); discrete values hold until w = 1. */
|
|
19
19
|
export declare function blendPoses(from: Pose, to: Pose, w: number, restOf: (key: string) => Val): Pose;
|
package/dist/lib/rig-check.d.ts
CHANGED
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
* path attachment — an error).
|
|
18
18
|
*/
|
|
19
19
|
import { type RigDocument } from "./rig-format";
|
|
20
|
+
import { type QualityOptions } from "./rig-quality";
|
|
20
21
|
export interface Finding {
|
|
21
22
|
level: "error" | "warn" | "info";
|
|
22
23
|
code: string;
|
|
@@ -26,7 +27,7 @@ export interface Finding {
|
|
|
26
27
|
animation?: string;
|
|
27
28
|
time?: number;
|
|
28
29
|
}
|
|
29
|
-
export interface CheckOptions {
|
|
30
|
+
export interface CheckOptions extends QualityOptions {
|
|
30
31
|
/** only this animation (plus the rest pose) */
|
|
31
32
|
animation?: string;
|
|
32
33
|
/** sampling rate for outlines (default 30) and for the fold check (default 60) */
|
|
@@ -57,3 +57,66 @@ export declare function pathFromPoints(skel: Skeleton, slotId: string, points: P
|
|
|
57
57
|
closed: boolean;
|
|
58
58
|
lengths: number[];
|
|
59
59
|
};
|
|
60
|
+
/** Solver-order dependencies, including ancestors and skinned path control points. */
|
|
61
|
+
export declare function constraintDependencies(doc: RigDocument): {
|
|
62
|
+
all: ({
|
|
63
|
+
id: string;
|
|
64
|
+
order: number;
|
|
65
|
+
bones: string[];
|
|
66
|
+
target: string;
|
|
67
|
+
mix: number;
|
|
68
|
+
softness: number;
|
|
69
|
+
bendPositive: boolean;
|
|
70
|
+
compress: boolean;
|
|
71
|
+
stretch: boolean;
|
|
72
|
+
uniform: boolean;
|
|
73
|
+
iterations?: number | undefined;
|
|
74
|
+
tolerance?: number | undefined;
|
|
75
|
+
kind: "ik";
|
|
76
|
+
} | {
|
|
77
|
+
x: number;
|
|
78
|
+
y: number;
|
|
79
|
+
rotation: number;
|
|
80
|
+
scaleX: number;
|
|
81
|
+
scaleY: number;
|
|
82
|
+
shearY: number;
|
|
83
|
+
id: string;
|
|
84
|
+
order: number;
|
|
85
|
+
bones: string[];
|
|
86
|
+
target: string;
|
|
87
|
+
local: boolean;
|
|
88
|
+
relative: boolean;
|
|
89
|
+
mixRotate: number;
|
|
90
|
+
mixX: number;
|
|
91
|
+
mixY: number;
|
|
92
|
+
mixScaleX: number;
|
|
93
|
+
mixScaleY: number;
|
|
94
|
+
mixShearY: number;
|
|
95
|
+
kind: "transform";
|
|
96
|
+
} | {
|
|
97
|
+
rotation: number;
|
|
98
|
+
id: string;
|
|
99
|
+
order: number;
|
|
100
|
+
bones: string[];
|
|
101
|
+
target: string;
|
|
102
|
+
mixRotate: number;
|
|
103
|
+
mixX: number;
|
|
104
|
+
mixY: number;
|
|
105
|
+
positionMode: "fixed" | "percent";
|
|
106
|
+
spacingMode: "length" | "fixed" | "percent" | "proportional";
|
|
107
|
+
rotateMode: "tangent" | "chain" | "chainScale";
|
|
108
|
+
position: number;
|
|
109
|
+
spacing: number;
|
|
110
|
+
kind: "path";
|
|
111
|
+
})[];
|
|
112
|
+
edges: {
|
|
113
|
+
reader: string;
|
|
114
|
+
readerKind: "path" | "transform" | "ik";
|
|
115
|
+
writer: string;
|
|
116
|
+
writerKind: "path" | "transform" | "ik";
|
|
117
|
+
bone: string;
|
|
118
|
+
late: boolean;
|
|
119
|
+
}[];
|
|
120
|
+
};
|
|
121
|
+
/** Stable topological order; cyclic dependencies require an authoring decision. */
|
|
122
|
+
export declare function orderConstraints(doc: RigDocument): RigDocument;
|