@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 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 u6 = ease(a5.ease)((t5 - a5.t) / (b4.t - a5.t)), av = a5.v, bv = b4.v;
49376
- return av.map((v6, k4) => v6 + ((bv[k4] ?? v6) - v6) * u6);
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 offsetRotation = c5.rotation * DEG2, offsetShearY = c5.shearY * DEG2;
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 = { ...st.world };
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
- setEffectiveWorld(skel, b4, st, w5);
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) || 1;
50040
+ const lenX = Math.hypot(parent.a, parent.b);
50001
50041
  const det = parent.a * parent.d - parent.b * parent.c;
50002
- return { a: lenX, b: 0, c: 0, d: Math.abs(det) / lenX, tx: 0, ty: 0 };
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
- updateSubtree(skel, bone.id);
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(this.doc.slots.flatMap((s6) => s6.clip ? [s6.clip] : []));
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
- it.node.mask = s6.clip ? this.clipGraphics.get(s6.clip) ?? null : null;
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;