@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.
@@ -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
- if (c2.bones.length > 2) errs.push(`ik "${c2.id}": must have 1 or 2 bones`);
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 sample(track, t2) {
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
- const out2 = new Map(to);
49895
- for (const [k2, f2] of from) {
49896
- const cur = to.get(k2);
49897
- if (typeof f2 === "number") {
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 === void 0 && w2 < 1) out2.set(k2, f2);
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) throw new Error(`unknown animation "${id}"`);
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
- this.base = id === void 0 ? void 0 : this.channel(id);
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 this.overlays.set(name, this.channel(id));
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
- /** `mix`: default crossfade seconds for state transitions (setBase, completion → base); play() passes its own */
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 ?? 0,
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
- it.node.alpha = numOf(pose, pk("slot", s2.id, "alpha"), s2.alpha) * color[3];
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
- const frame = grab(1);
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
+ }>;
@@ -3,6 +3,8 @@ export interface RenderOptions {
3
3
  times: number[];
4
4
  overlayBones?: boolean;
5
5
  background?: string;
6
+ transparent?: boolean;
7
+ weightBone?: string;
6
8
  scale?: number;
7
9
  sheetColumns?: number;
8
10
  onionSkin?: boolean;
@@ -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[];
@@ -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;
@@ -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;