@energy8platform/golem 0.5.0 → 0.6.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 +4 -0
- package/dist/editor.js +69 -14
- package/dist/lib/cli.js +209 -64
- package/dist/lib/cli.js.map +3 -3
- package/dist/lib/editor/store.d.ts +105 -0
- package/dist/lib/editor-entry.js +209 -64
- package/dist/lib/editor-entry.js.map +3 -3
- package/dist/lib/harness.js +69 -14
- package/dist/lib/interactive-editor.js.map +1 -1
- package/dist/lib/rig-anim.d.ts +2 -0
- package/dist/lib/rig-control-qa.d.ts +27 -0
- package/dist/lib/rig-format.d.ts +989 -0
- package/dist/lib/runtime.js +69 -14
- package/dist/lib/runtime.js.map +3 -3
- package/dist/lib/spine-import.d.ts +4 -0
- package/dist/lib/tools.js +209 -64
- package/dist/lib/tools.js.map +3 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -17,6 +17,10 @@
|
|
|
17
17
|
поверх параметрической мимики и сохраняются при смене анимации.
|
|
18
18
|
[Результаты объединения и проверок](docs/verification-2026-09-28.md).
|
|
19
19
|
|
|
20
|
+
В 0.6 по официальным примерам Spine и Rive обновлены скиллы нарезки и анимации, исправлены
|
|
21
|
+
импорт skins, linked meshes, кривые, преобразования и активные clipping-маски.
|
|
22
|
+
[Источники, результаты и ограничения проверки](docs/verification-2026-10-05.md).
|
|
23
|
+
|
|
20
24
|
```
|
|
21
25
|
src/rig-format.ts схема документа (zod) + семантическая валидация; v1: deform-треки, asset.frame (атлас)
|
|
22
26
|
src/rig-anim.ts чистая математика: easing, семплирование, Pose, аффинные матрицы костей, контуры слотов
|
package/dist/editor.js
CHANGED
|
@@ -48869,7 +48869,9 @@ var PathAttachment = external_exports.object({
|
|
|
48869
48869
|
/** curve lengths for constant-speed positioning */
|
|
48870
48870
|
lengths: external_exports.array(external_exports.number()).default([]),
|
|
48871
48871
|
closed: external_exports.boolean().default(false),
|
|
48872
|
-
constantSpeed: external_exports.boolean().default(true)
|
|
48872
|
+
constantSpeed: external_exports.boolean().default(true),
|
|
48873
|
+
/** Spine-style active clipping span in the posed draw order; end slot is inclusive. */
|
|
48874
|
+
clipping: external_exports.object({ end: Id.optional() }).optional()
|
|
48873
48875
|
});
|
|
48874
48876
|
var Attachment = external_exports.discriminatedUnion("type", [RegionAttachment, MeshAttachment, PathAttachment]);
|
|
48875
48877
|
var IkConstraint = external_exports.object({
|
|
@@ -48938,7 +48940,9 @@ var Key = external_exports.object({
|
|
|
48938
48940
|
t: external_exports.number().nonnegative(),
|
|
48939
48941
|
v: KeyValue,
|
|
48940
48942
|
/** easing INTO the next key */
|
|
48941
|
-
ease: Ease.default("ease_in_out")
|
|
48943
|
+
ease: Ease.default("ease_in_out"),
|
|
48944
|
+
/** Optional independent easing for numeric-array components (for example Spine RGBA curves). */
|
|
48945
|
+
componentEase: external_exports.array(Ease).min(1).optional()
|
|
48942
48946
|
});
|
|
48943
48947
|
var Interpolation = { interpolation: external_exports.enum(["segment", "pchip"]).optional() };
|
|
48944
48948
|
var Track = external_exports.discriminatedUnion("target", [
|
|
@@ -49125,6 +49129,10 @@ function validate(doc2) {
|
|
|
49125
49129
|
else if (a5.slot !== s6.id) errs.push(`slot "${s6.id}": attachment "${s6.attachment}" belongs to slot "${a5.slot}"`);
|
|
49126
49130
|
}
|
|
49127
49131
|
}
|
|
49132
|
+
for (const a5 of doc2.attachments) if (a5.type === "path" && a5.clipping) {
|
|
49133
|
+
if (!a5.closed) errs.push(`attachment "${a5.id}": clipping path must be closed`);
|
|
49134
|
+
if (a5.clipping.end && !slots.has(a5.clipping.end)) errs.push(`attachment "${a5.id}": unknown clipping end slot "${a5.clipping.end}"`);
|
|
49135
|
+
}
|
|
49128
49136
|
const vertexCount2 = (a5) => {
|
|
49129
49137
|
if (a5.type === "region") return 4;
|
|
49130
49138
|
if (a5.type === "mesh") return a5.uvs.length / 2;
|
|
@@ -49208,6 +49216,7 @@ function validate(doc2) {
|
|
|
49208
49216
|
const att = t5.target === "deform" ? attachments.get(t5.id) : void 0;
|
|
49209
49217
|
let prev = -1;
|
|
49210
49218
|
for (const k4 of t5.keys) {
|
|
49219
|
+
if (k4.componentEase && (!Array.isArray(k4.v) || !k4.v.every((v6) => typeof v6 === "number") || k4.componentEase.length !== k4.v.length)) errs.push(`animation "${a5.id}" ${tid}: componentEase must match the numeric-array value length`);
|
|
49211
49220
|
if (k4.t <= prev && prev >= 0) errs.push(`animation "${a5.id}" ${tid}: keys not strictly increasing`);
|
|
49212
49221
|
if (k4.t > a5.duration) errs.push(`animation "${a5.id}" ${tid}: key at ${k4.t}s exceeds duration`);
|
|
49213
49222
|
if (t5.target === "control") {
|
|
@@ -49372,8 +49381,8 @@ function sample(track, t5, loop2 = false) {
|
|
|
49372
49381
|
}
|
|
49373
49382
|
if (Array.isArray(a5.v) && Array.isArray(b4.v) && typeof a5.v[0] === "number") {
|
|
49374
49383
|
if (track.interpolation === "pchip") return a5.v.map((_4, j5) => smoothValue(track, i6, t5, j5, loop2));
|
|
49375
|
-
const
|
|
49376
|
-
return av.map((v6, k4) => v6 + ((bv[k4] ?? v6) - v6) *
|
|
49384
|
+
const phase = (t5 - a5.t) / (b4.t - a5.t), av = a5.v, bv = b4.v;
|
|
49385
|
+
return av.map((v6, k4) => v6 + ((bv[k4] ?? v6) - v6) * ease(a5.componentEase?.[k4] ?? a5.ease)(phase));
|
|
49377
49386
|
}
|
|
49378
49387
|
return a5.v;
|
|
49379
49388
|
}
|
|
@@ -49461,6 +49470,13 @@ function boneWorldOf(bone, s6, parent) {
|
|
|
49461
49470
|
if (!parent) return local;
|
|
49462
49471
|
if (bone.inherit === "normal") return mul(parent, local);
|
|
49463
49472
|
const [tx, ty] = apply(parent, s6.x, s6.y);
|
|
49473
|
+
if (bone.inherit === "noScale" || bone.inherit === "noScaleOrReflection") {
|
|
49474
|
+
const r5 = s6.rotation * DEG2, x4 = parent.a * Math.cos(r5) + parent.c * Math.sin(r5), y5 = parent.b * Math.cos(r5) + parent.d * Math.sin(r5);
|
|
49475
|
+
const length2 = Math.hypot(x4, y5), a5 = length2 > 1e-5 ? x4 / length2 : x4, b4 = length2 > 1e-5 ? y5 / length2 : y5;
|
|
49476
|
+
const sign = bone.inherit === "noScale" && parent.a * parent.d - parent.b * parent.c < 0 ? -1 : 1;
|
|
49477
|
+
const basis2 = { a: a5, b: b4, c: -b4 * sign, d: a5 * sign, tx: 0, ty: 0 };
|
|
49478
|
+
return { ...mul(basis2, affineOf(0, 0, 0, s6.scaleX, s6.scaleY, s6.shearX, s6.shearY)), tx, ty };
|
|
49479
|
+
}
|
|
49464
49480
|
return { ...mul(inheritedParentLinear(bone, parent), { ...local, tx: 0, ty: 0 }), tx, ty };
|
|
49465
49481
|
}
|
|
49466
49482
|
var orderCache = /* @__PURE__ */ new WeakMap();
|
|
@@ -49493,11 +49509,11 @@ var localOf = (doc2, pose2, b4) => {
|
|
|
49493
49509
|
shearY: num(pose2, pk("bone", id, "shearY"), r5.shearY)
|
|
49494
49510
|
};
|
|
49495
49511
|
};
|
|
49496
|
-
function updateSubtree(skel, root2) {
|
|
49512
|
+
function updateSubtree(skel, root2, includeRoot = true) {
|
|
49497
49513
|
const doc2 = skel.doc;
|
|
49498
49514
|
const kids = /* @__PURE__ */ new Map();
|
|
49499
49515
|
for (const b4 of doc2.bones) if (b4.parent) (kids.get(b4.parent) ?? kids.set(b4.parent, []).get(b4.parent)).push(b4);
|
|
49500
|
-
const stack = [root2];
|
|
49516
|
+
const stack = includeRoot ? [root2] : (kids.get(root2) ?? []).map((b4) => b4.id);
|
|
49501
49517
|
while (stack.length) {
|
|
49502
49518
|
const id = stack.pop(), b4 = boneOf(doc2, id), st = skel.bones.get(id);
|
|
49503
49519
|
st.world = boneWorldOf(b4, st, b4.parent ? skel.bones.get(b4.parent).world : void 0);
|
|
@@ -49531,6 +49547,23 @@ function evaluate(doc2, pose2, modifiers) {
|
|
|
49531
49547
|
}
|
|
49532
49548
|
return skel;
|
|
49533
49549
|
}
|
|
49550
|
+
function slotClips(skel) {
|
|
49551
|
+
const keyed = skel.pose.get(pk("drawOrder", "order", "order"));
|
|
49552
|
+
const order = Array.isArray(keyed) ? keyed : [...skel.doc.slots].sort((a5, b4) => a5.z - b4.z).map((s6) => s6.id);
|
|
49553
|
+
const masks = /* @__PURE__ */ new Map();
|
|
49554
|
+
let active;
|
|
49555
|
+
for (const id of order) {
|
|
49556
|
+
const att = skel.attachment.get(id);
|
|
49557
|
+
if (!active && att?.type === "path" && att.clipping) {
|
|
49558
|
+
active = att;
|
|
49559
|
+
continue;
|
|
49560
|
+
}
|
|
49561
|
+
if (active) masks.set(id, active.id);
|
|
49562
|
+
if (active?.clipping?.end === id) active = void 0;
|
|
49563
|
+
}
|
|
49564
|
+
for (const s6 of skel.doc.slots) if (s6.clip) masks.set(s6.id, s6.clip);
|
|
49565
|
+
return masks;
|
|
49566
|
+
}
|
|
49534
49567
|
var cnum = (skel, kind, id, prop, rest) => num(skel.pose, pk(kind, id, prop), typeof rest === "boolean" ? rest ? 1 : 0 : rest);
|
|
49535
49568
|
var IDENTITY = { a: 1, b: 0, c: 0, d: 1, tx: 0, ty: 0 };
|
|
49536
49569
|
var wrapRad = (r5) => r5 > Math.PI ? r5 - 2 * Math.PI : r5 < -Math.PI ? r5 + 2 * Math.PI : r5;
|
|
@@ -49740,7 +49773,8 @@ function applyTransformConstraint(skel, c5) {
|
|
|
49740
49773
|
if (!mixRotate && !mixX && !mixY && !mixScaleX && !mixScaleY && !mixShearY) return;
|
|
49741
49774
|
c5 = { ...c5, ...Object.fromEntries(["x", "y", "rotation", "scaleX", "scaleY", "shearY"].map((p6) => [p6, cnum(skel, "transform", c5.id, p6, c5[p6])])) };
|
|
49742
49775
|
const target = skel.bones.get(c5.target), tw = target.world;
|
|
49743
|
-
const
|
|
49776
|
+
const handedness = tw.a * tw.d - tw.b * tw.c < 0 ? -1 : 1;
|
|
49777
|
+
const offsetRotation = c5.rotation * DEG2 * (c5.local ? 1 : handedness), offsetShearY = c5.shearY * DEG2 * (c5.local ? 1 : handedness);
|
|
49744
49778
|
for (const id of c5.bones) {
|
|
49745
49779
|
const b4 = boneOf(skel.doc, id), st = skel.bones.get(id);
|
|
49746
49780
|
if (c5.local) {
|
|
@@ -49902,6 +49936,7 @@ function applyPathConstraint(skel, c5) {
|
|
|
49902
49936
|
const position = cnum(skel, "path", c5.id, "position", c5.position), spacing = cnum(skel, "path", c5.id, "spacing", c5.spacing);
|
|
49903
49937
|
const tangents = c5.rotateMode === "tangent", scale = c5.rotateMode === "chainScale";
|
|
49904
49938
|
const bones = c5.bones.map((id) => ({ b: boneOf(skel.doc, id), st: skel.bones.get(id) }));
|
|
49939
|
+
const inputWorlds = bones.map(({ st }) => ({ ...st.world }));
|
|
49905
49940
|
const spacesCount = tangents ? bones.length : bones.length + 1;
|
|
49906
49941
|
const spaces = new Array(spacesCount).fill(0), worldLengths = [];
|
|
49907
49942
|
let sum = 0;
|
|
@@ -49936,9 +49971,10 @@ function applyPathConstraint(skel, c5) {
|
|
|
49936
49971
|
if (tw.a * tw.d - tw.b * tw.c < 0) offsetRotation = -offsetRotation;
|
|
49937
49972
|
}
|
|
49938
49973
|
let boneX = samples[0].x, boneY = samples[0].y;
|
|
49974
|
+
const solvedWorlds = /* @__PURE__ */ new Map();
|
|
49939
49975
|
for (let i6 = 0; i6 < bones.length; i6++) {
|
|
49940
49976
|
const { b: b4, st } = bones[i6];
|
|
49941
|
-
let w5 = { ...
|
|
49977
|
+
let w5 = { ...inputWorlds[i6] };
|
|
49942
49978
|
w5.tx += (boneX - w5.tx) * mixX;
|
|
49943
49979
|
w5.ty += (boneY - w5.ty) * mixY;
|
|
49944
49980
|
const next = samples[i6 + 1] ?? { x: 0, y: 0, angle: 0 };
|
|
@@ -49968,7 +50004,11 @@ function applyPathConstraint(skel, c5) {
|
|
|
49968
50004
|
const cos = Math.cos(r5), sin = Math.sin(r5);
|
|
49969
50005
|
w5 = { a: cos * a5 - sin * bb, b: sin * a5 + cos * bb, c: cos * cc - sin * d6, d: sin * cc + cos * d6, tx: w5.tx, ty: w5.ty };
|
|
49970
50006
|
}
|
|
49971
|
-
|
|
50007
|
+
solvedWorlds.set(b4.id, w5);
|
|
50008
|
+
}
|
|
50009
|
+
for (const b4 of boneOrder(skel.doc)) {
|
|
50010
|
+
const world = solvedWorlds.get(b4.id);
|
|
50011
|
+
if (world) setEffectiveWorld(skel, b4, skel.bones.get(b4.id), world);
|
|
49972
50012
|
}
|
|
49973
50013
|
}
|
|
49974
50014
|
function measuredPathLength(points, closed, lengths, constantSpeed) {
|
|
@@ -49997,9 +50037,14 @@ function inheritedParentLinear(bone, parent) {
|
|
|
49997
50037
|
case "onlyTranslation":
|
|
49998
50038
|
return { a: 1, b: 0, c: 0, d: 1, tx: 0, ty: 0 };
|
|
49999
50039
|
case "noRotationOrReflection": {
|
|
50000
|
-
const lenX = Math.hypot(parent.a, parent.b)
|
|
50040
|
+
const lenX = Math.hypot(parent.a, parent.b);
|
|
50001
50041
|
const det = parent.a * parent.d - parent.b * parent.c;
|
|
50002
|
-
|
|
50042
|
+
if (lenX * lenX <= 1e-4) {
|
|
50043
|
+
const lenY = Math.hypot(parent.c, parent.d), ux3 = lenY ? parent.c / lenY : 0, uy3 = lenY ? parent.d / lenY : 0;
|
|
50044
|
+
return { a: lenY * ux3 * ux3, b: lenY * ux3 * uy3, c: lenY * ux3 * uy3, d: lenY * uy3 * uy3, tx: 0, ty: 0 };
|
|
50045
|
+
}
|
|
50046
|
+
const ux2 = parent.a / lenX, uy2 = parent.b / lenX, sy = Math.abs(det) / lenX;
|
|
50047
|
+
return { a: lenX * ux2 * ux2 + sy * uy2 * uy2, b: (lenX - sy) * ux2 * uy2, c: (lenX - sy) * ux2 * uy2, d: lenX * uy2 * uy2 + sy * ux2 * ux2, tx: 0, ty: 0 };
|
|
50003
50048
|
}
|
|
50004
50049
|
case "noScale":
|
|
50005
50050
|
case "noScaleOrReflection": {
|
|
@@ -50025,7 +50070,16 @@ function setEffectiveWorld(skel, bone, st, world) {
|
|
|
50025
50070
|
st.scaleY = Math.hypot(lin.c, lin.d);
|
|
50026
50071
|
st.shearX = 0;
|
|
50027
50072
|
st.shearY = shortest(Math.atan2(lin.d, lin.c) / DEG2 - st.rotation - 90);
|
|
50028
|
-
|
|
50073
|
+
if (parent && (bone.inherit === "noScale" || bone.inherit === "noScaleOrReflection")) {
|
|
50074
|
+
const direction = mul(invert({ ...parent, tx: 0, ty: 0 }), { ...world, tx: 0, ty: 0 });
|
|
50075
|
+
st.rotation = Math.atan2(direction.b, direction.a) / DEG2;
|
|
50076
|
+
st.scaleX = Math.hypot(world.a, world.b);
|
|
50077
|
+
st.scaleY = Math.hypot(world.c, world.d);
|
|
50078
|
+
const sign = bone.inherit === "noScale" && parent.a * parent.d - parent.b * parent.c < 0 ? -1 : 1;
|
|
50079
|
+
st.shearY = shortest((Math.atan2(world.d, world.c) - Math.atan2(world.b, world.a)) / DEG2 * sign - 90);
|
|
50080
|
+
}
|
|
50081
|
+
st.world = world;
|
|
50082
|
+
updateSubtree(skel, bone.id, false);
|
|
50029
50083
|
}
|
|
50030
50084
|
function attachmentWorldVertices(skel, slotId, att) {
|
|
50031
50085
|
const deform = skel.pose.get(pk("deform", att.id, "vertices"));
|
|
@@ -58172,7 +58226,7 @@ var RigPlayer = class {
|
|
|
58172
58226
|
this.clipPoints.clear();
|
|
58173
58227
|
const skel = this.skel = evaluate(this.doc, pose2, this.beforePose);
|
|
58174
58228
|
pose2 = skel.pose;
|
|
58175
|
-
const clips = new Set(
|
|
58229
|
+
const masks = slotClips(skel), clips = new Set(masks.values());
|
|
58176
58230
|
for (const [id, g4] of this.clipGraphics) if (!clips.has(id)) {
|
|
58177
58231
|
g4.destroy();
|
|
58178
58232
|
this.clipGraphics.delete(id);
|
|
@@ -58199,7 +58253,8 @@ var RigPlayer = class {
|
|
|
58199
58253
|
if (Array.isArray(orderV)) orderV.forEach((id, i6) => order.set(id, i6));
|
|
58200
58254
|
for (const it of this.items) {
|
|
58201
58255
|
const s6 = it.slot, shown2 = skel.attachment.get(s6.id);
|
|
58202
|
-
|
|
58256
|
+
const mask = masks.get(s6.id);
|
|
58257
|
+
it.node.mask = mask ? this.clipGraphics.get(mask) ?? null : null;
|
|
58203
58258
|
if (shown2 !== it.att || this.hidden.has(s6.id)) {
|
|
58204
58259
|
it.node.visible = false;
|
|
58205
58260
|
continue;
|