@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/editor.js
CHANGED
|
@@ -2177,8 +2177,8 @@ var init_Matrix = __esm({
|
|
|
2177
2177
|
const pivot = transform.pivot;
|
|
2178
2178
|
const skewX = -Math.atan2(-c5, d5);
|
|
2179
2179
|
const skewY = Math.atan2(b4, a5);
|
|
2180
|
-
const
|
|
2181
|
-
if (
|
|
2180
|
+
const delta2 = Math.abs(skewX + skewY);
|
|
2181
|
+
if (delta2 < 1e-5 || Math.abs(PI_2 - delta2) < 1e-5) {
|
|
2182
2182
|
transform.rotation = skewY;
|
|
2183
2183
|
transform.skew.x = transform.skew.y = 0;
|
|
2184
2184
|
} else {
|
|
@@ -8294,11 +8294,11 @@ var init_Ticker = __esm({
|
|
|
8294
8294
|
}
|
|
8295
8295
|
elapsedMS *= this.speed;
|
|
8296
8296
|
if (this._minElapsedMS) {
|
|
8297
|
-
const
|
|
8298
|
-
if (
|
|
8297
|
+
const delta2 = currentTime - this._lastFrame | 0;
|
|
8298
|
+
if (delta2 < this._minElapsedMS) {
|
|
8299
8299
|
return;
|
|
8300
8300
|
}
|
|
8301
|
-
this._lastFrame = currentTime -
|
|
8301
|
+
this._lastFrame = currentTime - delta2 % this._minElapsedMS;
|
|
8302
8302
|
}
|
|
8303
8303
|
this.deltaMS = elapsedMS;
|
|
8304
8304
|
this.deltaTime = this.deltaMS * _Ticker2.targetFPMS;
|
|
@@ -35314,19 +35314,19 @@ function parse2(path2) {
|
|
|
35314
35314
|
path2.replace(segment, (_4, cmd, args) => {
|
|
35315
35315
|
let type = cmd.toLowerCase();
|
|
35316
35316
|
let command = cmd;
|
|
35317
|
-
const
|
|
35318
|
-
if (type === "m" &&
|
|
35319
|
-
data.push([command, ...
|
|
35317
|
+
const values2 = parseValues(args);
|
|
35318
|
+
if (type === "m" && values2.length > 2) {
|
|
35319
|
+
data.push([command, ...values2.splice(0, 2)]);
|
|
35320
35320
|
type = "l";
|
|
35321
35321
|
command = command === "m" ? "l" : "L";
|
|
35322
35322
|
}
|
|
35323
35323
|
while (true) {
|
|
35324
|
-
if (
|
|
35325
|
-
data.push([command, ...
|
|
35324
|
+
if (values2.length === length[type]) {
|
|
35325
|
+
data.push([command, ...values2]);
|
|
35326
35326
|
return "";
|
|
35327
35327
|
}
|
|
35328
|
-
if (
|
|
35329
|
-
data.push([command, ...
|
|
35328
|
+
if (values2.length < length[type]) throw new Error("malformed path data");
|
|
35329
|
+
data.push([command, ...values2.splice(0, length[type])]);
|
|
35330
35330
|
}
|
|
35331
35331
|
});
|
|
35332
35332
|
return data;
|
|
@@ -36596,10 +36596,10 @@ var init_ShapePath = __esm({
|
|
|
36596
36596
|
regularPoly(x4, y4, radius, sides, rotation = 0, transform) {
|
|
36597
36597
|
sides = Math.max(sides | 0, 3);
|
|
36598
36598
|
const startAngle = -1 * Math.PI / 2 + rotation;
|
|
36599
|
-
const
|
|
36599
|
+
const delta2 = Math.PI * 2 / sides;
|
|
36600
36600
|
const polygon = [];
|
|
36601
36601
|
for (let i6 = 0; i6 < sides; i6++) {
|
|
36602
|
-
const angle = startAngle - i6 *
|
|
36602
|
+
const angle = startAngle - i6 * delta2;
|
|
36603
36603
|
polygon.push(
|
|
36604
36604
|
x4 + radius * Math.cos(angle),
|
|
36605
36605
|
y4 + radius * Math.sin(angle)
|
|
@@ -36628,10 +36628,10 @@ var init_ShapePath = __esm({
|
|
|
36628
36628
|
const sideLength = radius * Math.sin(Math.PI / sides) - 1e-3;
|
|
36629
36629
|
corner = Math.min(corner, sideLength);
|
|
36630
36630
|
const startAngle = -1 * Math.PI / 2 + rotation;
|
|
36631
|
-
const
|
|
36631
|
+
const delta2 = Math.PI * 2 / sides;
|
|
36632
36632
|
const internalAngle = (sides - 2) * Math.PI / sides / 2;
|
|
36633
36633
|
for (let i6 = 0; i6 < sides; i6++) {
|
|
36634
|
-
const angle = i6 *
|
|
36634
|
+
const angle = i6 * delta2 + startAngle;
|
|
36635
36635
|
const x0 = x4 + radius * Math.cos(angle);
|
|
36636
36636
|
const y0 = y4 + radius * Math.sin(angle);
|
|
36637
36637
|
const a1 = angle + Math.PI + internalAngle;
|
|
@@ -37114,11 +37114,11 @@ var init_GraphicsPath = __esm({
|
|
|
37114
37114
|
innerRadius || (innerRadius = radius / 2);
|
|
37115
37115
|
const startAngle = -1 * Math.PI / 2 + rotation;
|
|
37116
37116
|
const len = points * 2;
|
|
37117
|
-
const
|
|
37117
|
+
const delta2 = Math.PI * 2 / len;
|
|
37118
37118
|
const polygon = [];
|
|
37119
37119
|
for (let i6 = 0; i6 < len; i6++) {
|
|
37120
37120
|
const r5 = i6 % 2 ? innerRadius : radius;
|
|
37121
|
-
const angle = i6 *
|
|
37121
|
+
const angle = i6 * delta2 + startAngle;
|
|
37122
37122
|
polygon.push(
|
|
37123
37123
|
x4 + r5 * Math.cos(angle),
|
|
37124
37124
|
y4 + r5 * Math.sin(angle)
|
|
@@ -42707,10 +42707,10 @@ var init_TextStyle = __esm({
|
|
|
42707
42707
|
* @param values - Partial style options to assign
|
|
42708
42708
|
* @returns This TextStyle instance for chaining
|
|
42709
42709
|
*/
|
|
42710
|
-
assign(
|
|
42711
|
-
for (const key in
|
|
42710
|
+
assign(values2) {
|
|
42711
|
+
for (const key in values2) {
|
|
42712
42712
|
const thisKey = key;
|
|
42713
|
-
this[thisKey] =
|
|
42713
|
+
this[thisKey] = values2[key];
|
|
42714
42714
|
}
|
|
42715
42715
|
return this;
|
|
42716
42716
|
}
|
|
@@ -48027,9 +48027,9 @@ ZodLiteral.create = (value, params2) => {
|
|
|
48027
48027
|
...processCreateParams(params2)
|
|
48028
48028
|
});
|
|
48029
48029
|
};
|
|
48030
|
-
function createZodEnum(
|
|
48030
|
+
function createZodEnum(values2, params2) {
|
|
48031
48031
|
return new ZodEnum({
|
|
48032
|
-
values,
|
|
48032
|
+
values: values2,
|
|
48033
48033
|
typeName: ZodFirstPartyTypeKind.ZodEnum,
|
|
48034
48034
|
...processCreateParams(params2)
|
|
48035
48035
|
});
|
|
@@ -48085,14 +48085,14 @@ var ZodEnum = class _ZodEnum extends ZodType {
|
|
|
48085
48085
|
}
|
|
48086
48086
|
return enumValues;
|
|
48087
48087
|
}
|
|
48088
|
-
extract(
|
|
48089
|
-
return _ZodEnum.create(
|
|
48088
|
+
extract(values2, newDef = this._def) {
|
|
48089
|
+
return _ZodEnum.create(values2, {
|
|
48090
48090
|
...this._def,
|
|
48091
48091
|
...newDef
|
|
48092
48092
|
});
|
|
48093
48093
|
}
|
|
48094
|
-
exclude(
|
|
48095
|
-
return _ZodEnum.create(this.options.filter((opt) => !
|
|
48094
|
+
exclude(values2, newDef = this._def) {
|
|
48095
|
+
return _ZodEnum.create(this.options.filter((opt) => !values2.includes(opt)), {
|
|
48096
48096
|
...this._def,
|
|
48097
48097
|
...newDef
|
|
48098
48098
|
});
|
|
@@ -48130,9 +48130,9 @@ var ZodNativeEnum = class extends ZodType {
|
|
|
48130
48130
|
return this._def.values;
|
|
48131
48131
|
}
|
|
48132
48132
|
};
|
|
48133
|
-
ZodNativeEnum.create = (
|
|
48133
|
+
ZodNativeEnum.create = (values2, params2) => {
|
|
48134
48134
|
return new ZodNativeEnum({
|
|
48135
|
-
values,
|
|
48135
|
+
values: values2,
|
|
48136
48136
|
typeName: ZodFirstPartyTypeKind.ZodNativeEnum,
|
|
48137
48137
|
...processCreateParams(params2)
|
|
48138
48138
|
});
|
|
@@ -48719,7 +48719,7 @@ function minJacobian(offsets, w5, h6, samples = 17) {
|
|
|
48719
48719
|
var ID_RE = /^[a-z][a-z0-9_]*$/;
|
|
48720
48720
|
var Id = external_exports.string().regex(ID_RE, "snake_case id");
|
|
48721
48721
|
var Ease = external_exports.union([
|
|
48722
|
-
external_exports.enum(["linear", "step", "ease_in", "ease_out", "ease_in_out", "back_out", "elastic_out"]),
|
|
48722
|
+
external_exports.enum(["linear", "step", "ease_in", "ease_out", "ease_in_out", "minimum_jerk", "back_out", "elastic_out"]),
|
|
48723
48723
|
// cubic-bezier control points, same semantics as CSS
|
|
48724
48724
|
external_exports.object({ bezier: external_exports.tuple([external_exports.number(), external_exports.number(), external_exports.number(), external_exports.number()]) })
|
|
48725
48725
|
]);
|
|
@@ -48751,6 +48751,7 @@ var Asset = external_exports.object({
|
|
|
48751
48751
|
/** where this layer came from — provenance for agents */
|
|
48752
48752
|
origin: external_exports.object({
|
|
48753
48753
|
sourceImage: external_exports.string().optional(),
|
|
48754
|
+
bboxSpace: external_exports.enum(["source", "document"]).optional(),
|
|
48754
48755
|
/** bbox in source image: [x, y, w, h] */
|
|
48755
48756
|
bbox: external_exports.tuple([external_exports.number(), external_exports.number(), external_exports.number(), external_exports.number()]).optional(),
|
|
48756
48757
|
inpainted: external_exports.boolean().default(false),
|
|
@@ -48773,6 +48774,8 @@ var Bone = external_exports.object({
|
|
|
48773
48774
|
var Slot = external_exports.object({
|
|
48774
48775
|
id: Id,
|
|
48775
48776
|
bone: Id,
|
|
48777
|
+
/** Closed path attachment used as a moving clipping mask. */
|
|
48778
|
+
clip: Id.optional(),
|
|
48776
48779
|
/** active attachment id; undefined = hidden */
|
|
48777
48780
|
attachment: Id.optional(),
|
|
48778
48781
|
alpha: external_exports.number().min(0).max(1).default(1),
|
|
@@ -48835,6 +48838,8 @@ var IkConstraint = external_exports.object({
|
|
|
48835
48838
|
order: external_exports.number().int().default(0),
|
|
48836
48839
|
bones: external_exports.array(Id).min(1),
|
|
48837
48840
|
target: Id,
|
|
48841
|
+
iterations: external_exports.number().int().min(1).max(1e3).optional(),
|
|
48842
|
+
tolerance: external_exports.number().positive().optional(),
|
|
48838
48843
|
mix: external_exports.number().default(1),
|
|
48839
48844
|
softness: external_exports.number().default(0),
|
|
48840
48845
|
bendPositive: external_exports.boolean().default(true),
|
|
@@ -48887,7 +48892,7 @@ var BoneProp = external_exports.enum(["x", "y", "rotation", "scaleX", "scaleY",
|
|
|
48887
48892
|
var SlotProp = external_exports.enum(["alpha", "attachment", "z", "color"]);
|
|
48888
48893
|
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");
|
|
48889
48894
|
var IkProp = external_exports.enum(["mix", "softness", "bendPositive", "compress", "stretch"]);
|
|
48890
|
-
var TransformProp = external_exports.enum(["mixRotate", "mixX", "mixY", "mixScaleX", "mixScaleY", "mixShearY"]);
|
|
48895
|
+
var TransformProp = external_exports.enum(["mixRotate", "mixX", "mixY", "mixScaleX", "mixScaleY", "mixShearY", "x", "y", "rotation", "scaleX", "scaleY", "shearY"]);
|
|
48891
48896
|
var PathProp = external_exports.enum(["position", "spacing", "mixRotate", "mixX", "mixY"]);
|
|
48892
48897
|
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())]);
|
|
48893
48898
|
var Key = external_exports.object({
|
|
@@ -48896,14 +48901,15 @@ var Key = external_exports.object({
|
|
|
48896
48901
|
/** easing INTO the next key */
|
|
48897
48902
|
ease: Ease.default("ease_in_out")
|
|
48898
48903
|
});
|
|
48904
|
+
var Interpolation = { interpolation: external_exports.enum(["segment", "pchip"]).optional() };
|
|
48899
48905
|
var Track = external_exports.discriminatedUnion("target", [
|
|
48900
|
-
external_exports.object({ target: external_exports.literal("bone"), id: Id, prop: BoneProp, keys: external_exports.array(Key).min(1) }),
|
|
48901
|
-
external_exports.object({ target: external_exports.literal("slot"), id: Id, prop: SlotProp, keys: external_exports.array(Key).min(1) }),
|
|
48902
|
-
external_exports.object({ target: external_exports.literal("deform"), id: Id, prop: DeformProp, keys: external_exports.array(Key).min(1) }),
|
|
48903
|
-
external_exports.object({ target: external_exports.literal("ik"), id: Id, prop: IkProp, keys: external_exports.array(Key).min(1) }),
|
|
48904
|
-
external_exports.object({ target: external_exports.literal("transform"), id: Id, prop: TransformProp, keys: external_exports.array(Key).min(1) }),
|
|
48905
|
-
external_exports.object({ target: external_exports.literal("path"), id: Id, prop: PathProp, keys: external_exports.array(Key).min(1) }),
|
|
48906
|
-
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) })
|
|
48906
|
+
external_exports.object({ target: external_exports.literal("bone"), id: Id, prop: BoneProp, ...Interpolation, keys: external_exports.array(Key).min(1) }),
|
|
48907
|
+
external_exports.object({ target: external_exports.literal("slot"), id: Id, prop: SlotProp, ...Interpolation, keys: external_exports.array(Key).min(1) }),
|
|
48908
|
+
external_exports.object({ target: external_exports.literal("deform"), id: Id, prop: DeformProp, ...Interpolation, keys: external_exports.array(Key).min(1) }),
|
|
48909
|
+
external_exports.object({ target: external_exports.literal("ik"), id: Id, prop: IkProp, ...Interpolation, keys: external_exports.array(Key).min(1) }),
|
|
48910
|
+
external_exports.object({ target: external_exports.literal("transform"), id: Id, prop: TransformProp, ...Interpolation, keys: external_exports.array(Key).min(1) }),
|
|
48911
|
+
external_exports.object({ target: external_exports.literal("path"), id: Id, prop: PathProp, ...Interpolation, keys: external_exports.array(Key).min(1) }),
|
|
48912
|
+
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) })
|
|
48907
48913
|
]);
|
|
48908
48914
|
var Event2 = external_exports.object({
|
|
48909
48915
|
t: external_exports.number().nonnegative(),
|
|
@@ -49045,6 +49051,10 @@ function validate(doc2) {
|
|
|
49045
49051
|
if (a5.frame && !a5.trim && !a5.rotate && (a5.frame[2] !== a5.width || a5.frame[3] !== a5.height)) errs.push(`asset "${a5.id}": frame size must equal width\xD7height (or declare trim/rotate)`);
|
|
49046
49052
|
}
|
|
49047
49053
|
for (const s6 of doc2.slots) {
|
|
49054
|
+
if (s6.clip) {
|
|
49055
|
+
const mask = attachments.get(s6.clip);
|
|
49056
|
+
if (mask?.type !== "path" || !mask.closed) errs.push(`slot "${s6.id}": clip must name a closed path attachment`);
|
|
49057
|
+
}
|
|
49048
49058
|
if (!bones.has(s6.bone)) errs.push(`slot "${s6.id}": unknown bone "${s6.bone}"`);
|
|
49049
49059
|
if (s6.attachment) {
|
|
49050
49060
|
const a5 = attachments.get(s6.attachment);
|
|
@@ -49076,7 +49086,8 @@ function validate(doc2) {
|
|
|
49076
49086
|
}
|
|
49077
49087
|
}
|
|
49078
49088
|
for (const c5 of doc2.ik) {
|
|
49079
|
-
|
|
49089
|
+
for (let i6 = 1; i6 < c5.bones.length; i6++) if (doc2.bones.find((b4) => b4.id === c5.bones[i6])?.parent !== c5.bones[i6 - 1])
|
|
49090
|
+
errs.push(`ik "${c5.id}": bones must form a contiguous parent-to-child chain`);
|
|
49080
49091
|
for (const b4 of c5.bones) if (!bones.has(b4)) errs.push(`ik "${c5.id}": unknown bone "${b4}"`);
|
|
49081
49092
|
if (!bones.has(c5.target)) errs.push(`ik "${c5.id}": unknown target bone "${c5.target}"`);
|
|
49082
49093
|
}
|
|
@@ -49148,6 +49159,7 @@ var named = {
|
|
|
49148
49159
|
ease_in: (t5) => t5 * t5,
|
|
49149
49160
|
ease_out: (t5) => 1 - (1 - t5) * (1 - t5),
|
|
49150
49161
|
ease_in_out: (t5) => t5 < 0.5 ? 2 * t5 * t5 : 1 - (-2 * t5 + 2) ** 2 / 2,
|
|
49162
|
+
minimum_jerk: (t5) => t5 * t5 * t5 * (10 + t5 * (-15 + 6 * t5)),
|
|
49151
49163
|
back_out: (t5) => 1 + 2.70158 * (t5 - 1) ** 3 + 1.70158 * (t5 - 1) ** 2,
|
|
49152
49164
|
elastic_out: (t5) => t5 === 0 ? 0 : t5 === 1 ? 1 : 2 ** (-10 * t5) * Math.sin((t5 * 10 - 0.75) * (2 * Math.PI / 3)) + 1
|
|
49153
49165
|
};
|
|
@@ -49179,7 +49191,25 @@ function ease(spec) {
|
|
|
49179
49191
|
return f6;
|
|
49180
49192
|
}
|
|
49181
49193
|
var pk = (target, id, prop) => `${target}:${id}:${prop}`;
|
|
49182
|
-
function
|
|
49194
|
+
function smoothValue(track, i6, t5, component, loop2) {
|
|
49195
|
+
const k4 = track.keys, n4 = k4.length;
|
|
49196
|
+
const v6 = (j5) => component === void 0 ? k4[j5].v : k4[j5].v[component];
|
|
49197
|
+
const h6 = (j5) => k4[j5 + 1].t - k4[j5].t;
|
|
49198
|
+
const d5 = (j5) => (v6(j5 + 1) - v6(j5)) / h6(j5);
|
|
49199
|
+
const harmonic = (a5, b4, h0, h1) => a5 * b4 <= 0 ? 0 : 3 * (h0 + h1) / ((2 * h1 + h0) / a5 + (h1 + 2 * h0) / b4);
|
|
49200
|
+
const tangent = (j5) => {
|
|
49201
|
+
if (j5 === 0 || j5 === n4 - 1) {
|
|
49202
|
+
if (loop2 && n4 > 2 && v6(0) === v6(n4 - 1) && k4[0].ease !== "step" && k4[n4 - 2].ease !== "step")
|
|
49203
|
+
return harmonic(d5(n4 - 2), d5(0), h6(n4 - 2), h6(0));
|
|
49204
|
+
return 0;
|
|
49205
|
+
}
|
|
49206
|
+
if (k4[j5 - 1].ease === "step" || k4[j5].ease === "step") return 0;
|
|
49207
|
+
return harmonic(d5(j5 - 1), d5(j5), h6(j5 - 1), h6(j5));
|
|
49208
|
+
};
|
|
49209
|
+
const u6 = (t5 - k4[i6].t) / h6(i6), u22 = u6 * u6, u32 = u22 * u6;
|
|
49210
|
+
return (2 * u32 - 3 * u22 + 1) * v6(i6) + (u32 - 2 * u22 + u6) * h6(i6) * tangent(i6) + (-2 * u32 + 3 * u22) * v6(i6 + 1) + (u32 - u22) * h6(i6) * tangent(i6 + 1);
|
|
49211
|
+
}
|
|
49212
|
+
function sample(track, t5, loop2 = false) {
|
|
49183
49213
|
const keys = track.keys;
|
|
49184
49214
|
if (t5 <= keys[0].t) return keys[0].v;
|
|
49185
49215
|
const last = keys[keys.length - 1];
|
|
@@ -49187,31 +49217,38 @@ function sample(track, t5) {
|
|
|
49187
49217
|
let i6 = 0;
|
|
49188
49218
|
while (keys[i6 + 1].t <= t5) i6++;
|
|
49189
49219
|
const a5 = keys[i6], b4 = keys[i6 + 1];
|
|
49220
|
+
if (a5.ease === "step") return a5.v;
|
|
49190
49221
|
if (typeof a5.v === "number" && typeof b4.v === "number") {
|
|
49222
|
+
if (track.interpolation === "pchip") return smoothValue(track, i6, t5, void 0, loop2);
|
|
49191
49223
|
const u6 = ease(a5.ease)((t5 - a5.t) / (b4.t - a5.t));
|
|
49192
49224
|
return a5.v + (b4.v - a5.v) * u6;
|
|
49193
49225
|
}
|
|
49194
49226
|
if (Array.isArray(a5.v) && Array.isArray(b4.v) && typeof a5.v[0] === "number") {
|
|
49227
|
+
if (track.interpolation === "pchip") return a5.v.map((_4, j5) => smoothValue(track, i6, t5, j5, loop2));
|
|
49195
49228
|
const u6 = ease(a5.ease)((t5 - a5.t) / (b4.t - a5.t)), av = a5.v, bv = b4.v;
|
|
49196
49229
|
return av.map((v6, k4) => v6 + ((bv[k4] ?? v6) - v6) * u6);
|
|
49197
49230
|
}
|
|
49198
49231
|
return a5.v;
|
|
49199
49232
|
}
|
|
49200
49233
|
function poseOf(anim, t5, out2 = /* @__PURE__ */ new Map()) {
|
|
49201
|
-
for (const tr of anim.tracks) out2.set(pk(tr.target, tr.id, tr.prop), sample(tr, t5));
|
|
49234
|
+
for (const tr of anim.tracks) out2.set(pk(tr.target, tr.id, tr.prop), sample(tr, t5, anim.loop && tr.keys[0].t === 0 && tr.keys.at(-1).t === anim.duration));
|
|
49202
49235
|
return out2;
|
|
49203
49236
|
}
|
|
49204
49237
|
function blendPoses(from, to, w5, restOf) {
|
|
49205
|
-
|
|
49206
|
-
|
|
49207
|
-
|
|
49208
|
-
|
|
49238
|
+
if (w5 >= 1) return new Map(to);
|
|
49239
|
+
const out2 = /* @__PURE__ */ new Map();
|
|
49240
|
+
for (const k4 of /* @__PURE__ */ new Set([...from.keys(), ...to.keys()])) {
|
|
49241
|
+
const f6 = from.has(k4) ? from.get(k4) : restOf(k4);
|
|
49242
|
+
const cur = to.has(k4) ? to.get(k4) : restOf(k4);
|
|
49243
|
+
if (typeof f6 === "number" && !Array.isArray(cur)) {
|
|
49209
49244
|
const target = typeof cur === "number" ? cur : restOf(k4);
|
|
49210
49245
|
out2.set(k4, f6 + (target - f6) * w5);
|
|
49211
49246
|
} else if (Array.isArray(f6) && typeof f6[0] === "number") {
|
|
49212
49247
|
const fv = f6, tv = Array.isArray(cur) && typeof cur[0] === "number" ? cur : fv.map(() => 0);
|
|
49213
49248
|
out2.set(k4, fv.map((v6, i6) => v6 + ((tv[i6] ?? 0) - v6) * w5));
|
|
49214
|
-
} else if (cur
|
|
49249
|
+
} else if (Array.isArray(cur) && typeof cur[0] === "number" && typeof f6 !== "string") {
|
|
49250
|
+
out2.set(k4, cur.map((v6) => v6 * w5));
|
|
49251
|
+
} else out2.set(k4, f6 ?? null);
|
|
49215
49252
|
}
|
|
49216
49253
|
return out2;
|
|
49217
49254
|
}
|
|
@@ -49380,10 +49417,43 @@ function aimAt(skel, boneId, tx, ty, mix, compress = false, stretch = false, uni
|
|
|
49380
49417
|
updateSubtree(skel, boneId);
|
|
49381
49418
|
}
|
|
49382
49419
|
var shortest = (deg) => ((deg + 180) % 360 + 360) % 360 - 180;
|
|
49420
|
+
function applyLongIk(skel, c5, tx, ty, mix) {
|
|
49421
|
+
const initial = c5.bones.map((id) => ({ ...skel.bones.get(id) }));
|
|
49422
|
+
const last = c5.bones.at(-1), len = boneOf(skel.doc, last).length;
|
|
49423
|
+
const end = () => apply(skel.bones.get(last).world, len, 0);
|
|
49424
|
+
const tolerance = c5.tolerance ?? 0.05;
|
|
49425
|
+
const root2 = skel.bones.get(c5.bones[0]).world, tip = end();
|
|
49426
|
+
if (Math.abs((tip[0] - root2.tx) * (ty - root2.ty) - (tip[1] - root2.ty) * (tx - root2.tx)) < 1e-7 && Math.hypot(tx - root2.tx, ty - root2.ty) < Math.hypot(tip[0] - root2.tx, tip[1] - root2.ty) - tolerance) {
|
|
49427
|
+
skel.bones.get(last).rotation += cnum(skel, "ik", c5.id, "bendPositive", c5.bendPositive) > 0.5 ? 5 : -5;
|
|
49428
|
+
updateSubtree(skel, last);
|
|
49429
|
+
}
|
|
49430
|
+
for (let iter = 0; iter < (c5.iterations ?? 100); iter++) {
|
|
49431
|
+
const e5 = end();
|
|
49432
|
+
if (Math.hypot(e5[0] - tx, e5[1] - ty) <= tolerance) break;
|
|
49433
|
+
for (let i6 = c5.bones.length - 1; i6 >= 0; i6--) {
|
|
49434
|
+
const id = c5.bones[i6], b4 = boneOf(skel.doc, id), st = skel.bones.get(id), w5 = st.world, e6 = end();
|
|
49435
|
+
const angle = wrapRad(Math.atan2(ty - w5.ty, tx - w5.tx) - Math.atan2(e6[1] - w5.ty, e6[0] - w5.tx));
|
|
49436
|
+
const cos = Math.cos(angle), sin = Math.sin(angle);
|
|
49437
|
+
setEffectiveWorld(skel, b4, st, { ...w5, a: cos * w5.a - sin * w5.b, b: sin * w5.a + cos * w5.b, c: cos * w5.c - sin * w5.d, d: sin * w5.c + cos * w5.d });
|
|
49438
|
+
}
|
|
49439
|
+
}
|
|
49440
|
+
if (mix < 1) {
|
|
49441
|
+
c5.bones.forEach((id, i6) => {
|
|
49442
|
+
const st = skel.bones.get(id), from = initial[i6];
|
|
49443
|
+
for (const p6 of ["rotation", "shearX", "shearY"]) st[p6] = from[p6] + shortest(st[p6] - from[p6]) * mix;
|
|
49444
|
+
for (const p6 of ["x", "y", "scaleX", "scaleY"]) st[p6] = from[p6] + (st[p6] - from[p6]) * mix;
|
|
49445
|
+
});
|
|
49446
|
+
updateSubtree(skel, c5.bones[0]);
|
|
49447
|
+
}
|
|
49448
|
+
}
|
|
49383
49449
|
function applyIk(skel, c5) {
|
|
49384
49450
|
const mix = cnum(skel, "ik", c5.id, "mix", c5.mix);
|
|
49385
49451
|
if (mix <= 0) return;
|
|
49386
49452
|
const target = skel.bones.get(c5.target).world;
|
|
49453
|
+
if (c5.bones.length > 2) {
|
|
49454
|
+
applyLongIk(skel, c5, target.tx, target.ty, mix);
|
|
49455
|
+
return;
|
|
49456
|
+
}
|
|
49387
49457
|
const bendDir = cnum(skel, "ik", c5.id, "bendPositive", c5.bendPositive) > 0.5 ? 1 : -1;
|
|
49388
49458
|
const compress = cnum(skel, "ik", c5.id, "compress", c5.compress) > 0.5, stretch = cnum(skel, "ik", c5.id, "stretch", c5.stretch) > 0.5;
|
|
49389
49459
|
let softness = cnum(skel, "ik", c5.id, "softness", c5.softness);
|
|
@@ -49518,6 +49588,7 @@ function applyTransformConstraint(skel, c5) {
|
|
|
49518
49588
|
const mixRotate = cnum(skel, "transform", c5.id, "mixRotate", c5.mixRotate), mixX = cnum(skel, "transform", c5.id, "mixX", c5.mixX), mixY = cnum(skel, "transform", c5.id, "mixY", c5.mixY);
|
|
49519
49589
|
const mixScaleX = cnum(skel, "transform", c5.id, "mixScaleX", c5.mixScaleX), mixScaleY = cnum(skel, "transform", c5.id, "mixScaleY", c5.mixScaleY), mixShearY = cnum(skel, "transform", c5.id, "mixShearY", c5.mixShearY);
|
|
49520
49590
|
if (!mixRotate && !mixX && !mixY && !mixScaleX && !mixScaleY && !mixShearY) return;
|
|
49591
|
+
c5 = { ...c5, ...Object.fromEntries(["x", "y", "rotation", "scaleX", "scaleY", "shearY"].map((p6) => [p6, cnum(skel, "transform", c5.id, p6, c5[p6])])) };
|
|
49521
49592
|
const target = skel.bones.get(c5.target), tw = target.world;
|
|
49522
49593
|
const offsetRotation = c5.rotation * DEG2, offsetShearY = c5.shearY * DEG2;
|
|
49523
49594
|
for (const id of c5.bones) {
|
|
@@ -49955,6 +50026,116 @@ function paintWeights(skel, att, world, opts) {
|
|
|
49955
50026
|
return { ...rest, weights };
|
|
49956
50027
|
}
|
|
49957
50028
|
|
|
50029
|
+
// src/rig-quality.ts
|
|
50030
|
+
var values = (v6) => typeof v6 === "number" ? [v6] : Array.isArray(v6) && v6.every((n4) => typeof n4 === "number") ? v6 : void 0;
|
|
50031
|
+
var delta = (a5, b4) => a5.map((v6, i6) => v6 - b4[i6]);
|
|
50032
|
+
var magnitude = (a5) => Math.max(0, ...a5.map(Math.abs));
|
|
50033
|
+
function motionQuality(doc2, anim, opts = {}) {
|
|
50034
|
+
const out2 = [], D2 = anim.duration;
|
|
50035
|
+
const report = (code, subject, message, time2, level = "warn") => out2.push({ level, code, subject, animation: anim.id, time: time2, message });
|
|
50036
|
+
for (const tr of anim.tracks) {
|
|
50037
|
+
const id = `${tr.target}.${tr.id}.${tr.prop}`, looping = anim.loop && tr.keys[0].t === 0 && tr.keys.at(-1).t === D2;
|
|
50038
|
+
const at = (t5) => sample(tr, t5, looping), numeric = tr.keys.every((k4) => values(k4.v) !== void 0);
|
|
50039
|
+
if (anim.loop) {
|
|
50040
|
+
const first = at(0), last = at(D2), av = values(first), bv = values(last);
|
|
50041
|
+
const diff = av && bv && av.length === bv.length ? magnitude(delta(av, bv)) : first === last || JSON.stringify(first) === JSON.stringify(last) ? 0 : Infinity;
|
|
50042
|
+
if (diff > 1e-3 && typeof first !== "number") report("loop_seam", id, `loop endpoint values differ (${Number.isFinite(diff) ? `max delta ${diff.toFixed(4)}` : "discrete state"}); all channels must close`);
|
|
50043
|
+
if (numeric && diff <= 1e-3 && tr.keys.length > 1) {
|
|
50044
|
+
const h6 = Math.min(D2 / 1e4, (tr.keys[1].t - tr.keys[0].t) / 100, (tr.keys.at(-1).t - tr.keys.at(-2).t) / 100);
|
|
50045
|
+
const va = delta(values(at(h6)), values(at(0))).map((v6) => v6 / h6), vb = delta(values(at(D2)), values(at(D2 - h6))).map((v6) => v6 / h6);
|
|
50046
|
+
const dv = magnitude(delta(va, vb));
|
|
50047
|
+
if (dv > Math.max(0.05, 0.05 * Math.max(magnitude(va), magnitude(vb)))) report("loop_velocity", id, `loop velocity changes by ${dv.toFixed(3)} units/s at the wrap`);
|
|
50048
|
+
}
|
|
50049
|
+
}
|
|
50050
|
+
if (!numeric) continue;
|
|
50051
|
+
for (let i6 = 1; i6 < tr.keys.length - 1; i6++) {
|
|
50052
|
+
const p6 = tr.keys[i6 - 1], k4 = tr.keys[i6], n4 = tr.keys[i6 + 1];
|
|
50053
|
+
if (p6.ease === "step" || k4.ease === "step") continue;
|
|
50054
|
+
const h6 = Math.min(k4.t - p6.t, n4.t - k4.t) / 1e3;
|
|
50055
|
+
const left = delta(values(at(k4.t)), values(at(k4.t - h6))).map((v6) => v6 / h6), right = delta(values(at(k4.t + h6)), values(at(k4.t))).map((v6) => v6 / h6);
|
|
50056
|
+
const before = delta(values(k4.v), values(p6.v)), after = delta(values(n4.v), values(k4.v));
|
|
50057
|
+
const pass = before.some((v6, j5) => v6 * after[j5] > 1e-6);
|
|
50058
|
+
const speed = Math.max(magnitude(before) / (k4.t - p6.t), magnitude(after) / (n4.t - k4.t));
|
|
50059
|
+
if (pass && Math.max(magnitude(left), magnitude(right)) < speed * 0.02) report("pass_key_stop", id, `motion stops at a pass-through key; use track interpolation pchip or matched tangents`, k4.t);
|
|
50060
|
+
const jump = magnitude(delta(left, right));
|
|
50061
|
+
if (jump > Math.max(0.1, speed * 0.1)) report("velocity_jump", id, `velocity jumps ${jump.toFixed(3)} units/s across this key`, k4.t);
|
|
50062
|
+
const acceleration = (a5, b4, c5) => values(at(b4)).map((v6, j5) => (values(at(c5))[j5] - 2 * v6 + values(at(a5))[j5]) / (h6 * h6));
|
|
50063
|
+
const al = acceleration(k4.t - 2 * h6, k4.t - h6, k4.t), ar = acceleration(k4.t, k4.t + h6, k4.t + 2 * h6);
|
|
50064
|
+
const aj = magnitude(delta(al, ar));
|
|
50065
|
+
if (aj > Math.max(1, speed / Math.min(k4.t - p6.t, n4.t - k4.t) * 2)) report("acceleration_jump", id, `acceleration changes by ${aj.toFixed(2)} units/s\xB2 at this key; PCHIP guarantees velocity, not acceleration continuity`, k4.t, "info");
|
|
50066
|
+
}
|
|
50067
|
+
}
|
|
50068
|
+
const steps = Math.max(2, Math.ceil(D2 * 60));
|
|
50069
|
+
const times3 = new Set(Array.from({ length: steps + 1 }, (_4, i6) => i6 * D2 / steps));
|
|
50070
|
+
for (const tr of anim.tracks) for (const k4 of tr.keys) {
|
|
50071
|
+
times3.add(k4.t);
|
|
50072
|
+
if (k4.t > 0) times3.add(Math.max(0, k4.t - 1e-5));
|
|
50073
|
+
}
|
|
50074
|
+
const samples = [...times3].sort((a5, b4) => a5 - b4).map((t5) => ({ t: t5, pose: poseOf(anim, t5) }));
|
|
50075
|
+
for (const bone of opts.secondaryBones ?? []) {
|
|
50076
|
+
if (!doc2.bones.some((b4) => b4.id === bone)) throw new Error(`unknown secondary bone "${bone}"`);
|
|
50077
|
+
const local = samples.map((s6) => evaluate(doc2, s6.pose).bones.get(bone));
|
|
50078
|
+
const curves = ["rotation", "x", "y"].map((prop) => local.map((b4) => b4[prop]));
|
|
50079
|
+
if (curves.every((v6) => Math.max(...v6) - Math.min(...v6) < 1e-3)) report("secondary_frozen", bone, `declared secondary bone has no local motion in this animation`);
|
|
50080
|
+
}
|
|
50081
|
+
const reported = /* @__PURE__ */ new Set();
|
|
50082
|
+
for (const [front, back] of opts.frontOf ?? []) {
|
|
50083
|
+
if (![front, back].every((id) => doc2.slots.some((s6) => s6.id === id))) throw new Error(`unknown frontOf slots: ${front}, ${back}`);
|
|
50084
|
+
for (const { t: t5, pose: pose2 } of samples) {
|
|
50085
|
+
const skel = evaluate(doc2, pose2);
|
|
50086
|
+
if (!skel.attachment.get(front) || !skel.attachment.get(back)) continue;
|
|
50087
|
+
const visible = (id) => {
|
|
50088
|
+
const slot = doc2.slots.find((s6) => s6.id === id);
|
|
50089
|
+
const color = pose2.get(pk("slot", id, "color")) ?? colorToArray(slot.color);
|
|
50090
|
+
return Number(pose2.get(pk("slot", id, "alpha")) ?? slot.alpha) * color[3] > 0;
|
|
50091
|
+
};
|
|
50092
|
+
if (!visible(front) || !visible(back)) continue;
|
|
50093
|
+
const order = pose2.get(pk("drawOrder", "order", "order"));
|
|
50094
|
+
const z3 = (id) => order ? order.indexOf(id) : Number(pose2.get(pk("slot", id, "z")) ?? doc2.slots.find((s6) => s6.id === id).z);
|
|
50095
|
+
const pair = JSON.stringify([front, back]);
|
|
50096
|
+
if (z3(front) <= z3(back) && !reported.has(pair)) {
|
|
50097
|
+
reported.add(pair);
|
|
50098
|
+
report("draw_order_rule", front, `"${front}" must stay explicitly in front of "${back}" according to the brief (equal z is ambiguous)`, t5);
|
|
50099
|
+
}
|
|
50100
|
+
}
|
|
50101
|
+
}
|
|
50102
|
+
for (const tr of anim.tracks.filter((t5) => t5.target === "slot" && t5.prop === "attachment")) for (const key of tr.keys.filter((k4) => k4.t > 0)) {
|
|
50103
|
+
const a5 = evaluate(doc2, poseOf(anim, Math.max(0, key.t - 1e-6))), b4 = evaluate(doc2, poseOf(anim, key.t));
|
|
50104
|
+
const before = a5.attachment.get(tr.id), after = b4.attachment.get(tr.id);
|
|
50105
|
+
if (!before || !after || before.id === after.id) continue;
|
|
50106
|
+
const landmarksA = opts.landmarks?.[before.id], landmarksB = opts.landmarks?.[after.id];
|
|
50107
|
+
if (!landmarksA && !landmarksB) continue;
|
|
50108
|
+
if (!landmarksA || !landmarksB || !Object.keys(landmarksA).some((name) => landmarksB[name])) {
|
|
50109
|
+
report("landmark_unmeasured", tr.id, `swap ${before.id} \u2192 ${after.id} has no shared declared landmarks`, key.t);
|
|
50110
|
+
continue;
|
|
50111
|
+
}
|
|
50112
|
+
const point = (skel, att, uv) => {
|
|
50113
|
+
if (att.type === "region") {
|
|
50114
|
+
const [w5, h6] = regionSize(doc2, att);
|
|
50115
|
+
return apply(regionMatrix(skel, att.slot, att), (uv[0] - 0.5) * w5, (uv[1] - 0.5) * h6);
|
|
50116
|
+
}
|
|
50117
|
+
if (att.type !== "mesh") return;
|
|
50118
|
+
const v6 = attachmentWorldVertices(skel, att.slot, att);
|
|
50119
|
+
for (let i6 = 0; i6 < att.triangles.length; i6 += 3) {
|
|
50120
|
+
const [ia, ib, ic] = att.triangles.slice(i6, i6 + 3), ux2 = att.uvs[ia * 2], uy2 = att.uvs[ia * 2 + 1], bx = att.uvs[ib * 2] - ux2, by = att.uvs[ib * 2 + 1] - uy2, cx = att.uvs[ic * 2] - ux2, cy = att.uvs[ic * 2 + 1] - uy2, det = bx * cy - by * cx;
|
|
50121
|
+
if (Math.abs(det) < 1e-12) continue;
|
|
50122
|
+
const s6 = ((uv[0] - ux2) * cy - (uv[1] - uy2) * cx) / det, r5 = (bx * (uv[1] - uy2) - by * (uv[0] - ux2)) / det;
|
|
50123
|
+
if (s6 >= -1e-6 && r5 >= -1e-6 && s6 + r5 <= 1 + 1e-6) return [v6[ia * 2] * (1 - s6 - r5) + v6[ib * 2] * s6 + v6[ic * 2] * r5, v6[ia * 2 + 1] * (1 - s6 - r5) + v6[ib * 2 + 1] * s6 + v6[ic * 2 + 1] * r5];
|
|
50124
|
+
}
|
|
50125
|
+
};
|
|
50126
|
+
for (const [name, uv] of Object.entries(landmarksA)) if (landmarksB[name]) {
|
|
50127
|
+
const p6 = point(a5, before, uv), q2 = point(b4, after, landmarksB[name]);
|
|
50128
|
+
if (!p6 || !q2) {
|
|
50129
|
+
report("landmark_unmeasured", tr.id, `landmark "${name}" is outside attachment geometry`, key.t);
|
|
50130
|
+
continue;
|
|
50131
|
+
}
|
|
50132
|
+
const distance = Math.hypot(p6[0] - q2[0], p6[1] - q2[1]);
|
|
50133
|
+
if (distance > (opts.landmarkTolerance ?? 2)) report("attachment_landmark_jump", tr.id, `"${name}" moves ${distance.toFixed(2)}px when ${before.id} changes to ${after.id}`, key.t);
|
|
50134
|
+
}
|
|
50135
|
+
}
|
|
50136
|
+
return out2;
|
|
50137
|
+
}
|
|
50138
|
+
|
|
49958
50139
|
// src/rig-path.ts
|
|
49959
50140
|
var flatten = (pts) => pts.flatMap((p6) => [p6[0], p6[1]]);
|
|
49960
50141
|
var anchorCount = (vertexCount2) => Math.floor(vertexCount2 / 3);
|
|
@@ -50119,6 +50300,43 @@ function pathFromPoints(skel, slotId, points, closed) {
|
|
|
50119
50300
|
}
|
|
50120
50301
|
return { vertices, closed, lengths: curveLengths(flatten(ctrl.flat()), closed) };
|
|
50121
50302
|
}
|
|
50303
|
+
function constraintDependencies(doc2) {
|
|
50304
|
+
const parent = new Map(doc2.bones.map((b4) => [b4.id, b4.parent]));
|
|
50305
|
+
const ancestors = (id) => {
|
|
50306
|
+
const out2 = /* @__PURE__ */ new Set();
|
|
50307
|
+
for (let b4 = id; b4 && !out2.has(b4); b4 = parent.get(b4)) out2.add(b4);
|
|
50308
|
+
return out2;
|
|
50309
|
+
};
|
|
50310
|
+
const all = [
|
|
50311
|
+
...doc2.ik.map((c5) => ({ kind: "ik", ...c5 })),
|
|
50312
|
+
...doc2.transform.map((c5) => ({ kind: "transform", ...c5 })),
|
|
50313
|
+
...doc2.path.map((c5) => ({ kind: "path", ...c5 }))
|
|
50314
|
+
].sort((a5, b4) => a5.order - b4.order);
|
|
50315
|
+
const active = (c5) => MIX_PROPS[c5.kind].some((p6) => c5[p6] !== 0 || doc2.animations.some((a5) => a5.tracks.some((t5) => t5.target === c5.kind && t5.id === c5.id && t5.prop === p6 && t5.keys.some((k4) => typeof k4.v === "number" && k4.v !== 0))));
|
|
50316
|
+
const edges = [];
|
|
50317
|
+
all.forEach((reader, ri) => {
|
|
50318
|
+
if (!active(reader)) return;
|
|
50319
|
+
const reads = /* @__PURE__ */ new Set();
|
|
50320
|
+
const add = (b4) => ancestors(b4).forEach((x4) => reads.add(x4));
|
|
50321
|
+
if (reader.kind === "path") {
|
|
50322
|
+
const slot = doc2.slots.find((s6) => s6.id === reader.target);
|
|
50323
|
+
if (slot) add(slot.bone);
|
|
50324
|
+
for (const a5 of doc2.attachments) if (a5.slot === reader.target && a5.type === "path") {
|
|
50325
|
+
for (const list of a5.weights ?? []) for (const inf of list) if (inf.w !== 0) add(inf.bone);
|
|
50326
|
+
}
|
|
50327
|
+
} else add(reader.target);
|
|
50328
|
+
for (const b4 of reader.bones) {
|
|
50329
|
+
const p6 = parent.get(b4);
|
|
50330
|
+
if (p6 && !reader.bones.includes(p6)) add(p6);
|
|
50331
|
+
}
|
|
50332
|
+
all.forEach((writer, wi) => {
|
|
50333
|
+
if (ri === wi || !active(writer)) return;
|
|
50334
|
+
const bone = writer.bones.find((b4) => reads.has(b4));
|
|
50335
|
+
if (bone) edges.push({ reader: reader.id, readerKind: reader.kind, writer: writer.id, writerKind: writer.kind, bone, late: wi > ri });
|
|
50336
|
+
});
|
|
50337
|
+
});
|
|
50338
|
+
return { all, edges };
|
|
50339
|
+
}
|
|
50122
50340
|
|
|
50123
50341
|
// src/rig-check.ts
|
|
50124
50342
|
var FOLD_ERROR_RATIO = 0.05;
|
|
@@ -50128,15 +50346,22 @@ var isNumeric = (t5) => t5.target !== "slot" || t5.prop !== "attachment";
|
|
|
50128
50346
|
var trackId2 = (t5) => `${t5.target}.${t5.id}.${t5.prop}`;
|
|
50129
50347
|
var fmt = (n4) => String(Math.round(n4 * 1e3) / 1e3);
|
|
50130
50348
|
var times = (a5, fps) => {
|
|
50131
|
-
const n4 = Math.max(1, Math.
|
|
50132
|
-
for (let i6 = 0; i6 <= n4; i6++) out2.push(
|
|
50349
|
+
const n4 = Math.max(1, Math.ceil(a5.duration * fps)), out2 = [];
|
|
50350
|
+
for (let i6 = 0; i6 <= n4; i6++) out2.push(a5.duration * i6 / n4);
|
|
50133
50351
|
return out2;
|
|
50134
50352
|
};
|
|
50135
50353
|
function checkRig(doc2, opts = {}) {
|
|
50136
50354
|
const fps = opts.fps ?? 30, dfps = opts.deformFps ?? 60, overlapMin = opts.overlap ?? 0.6, margin = opts.margin ?? 1;
|
|
50355
|
+
if (![fps, dfps].every((v6) => Number.isFinite(v6) && v6 > 0)) throw new Error("check sampling rates must be positive");
|
|
50137
50356
|
const anims = opts.animation === void 0 ? doc2.animations : doc2.animations.filter((a5) => a5.id === opts.animation);
|
|
50138
50357
|
if (opts.animation !== void 0 && !anims.length) throw new Error(`unknown animation "${opts.animation}"`);
|
|
50139
50358
|
const out2 = [];
|
|
50359
|
+
for (const e5 of constraintDependencies(doc2).edges.filter((e6) => e6.late)) out2.push({
|
|
50360
|
+
level: "warn",
|
|
50361
|
+
code: "constraint_order",
|
|
50362
|
+
subject: e5.reader,
|
|
50363
|
+
message: `constraint "${e5.reader}" reads bone "${e5.bone}" before later constraint "${e5.writer}" moves it; use rig_order_constraints or set explicit order`
|
|
50364
|
+
});
|
|
50140
50365
|
const W = doc2.meta.width, H3 = doc2.meta.height;
|
|
50141
50366
|
const overflowOf = (pose2, slotId, skel) => {
|
|
50142
50367
|
const pts = slotOutline(doc2, pose2, slotId, 4, skel);
|
|
@@ -50308,6 +50533,7 @@ function checkRig(doc2, opts = {}) {
|
|
|
50308
50533
|
return rest;
|
|
50309
50534
|
};
|
|
50310
50535
|
for (const a5 of anims) {
|
|
50536
|
+
out2.push(...motionQuality(doc2, a5, opts));
|
|
50311
50537
|
for (const t5 of a5.tracks) {
|
|
50312
50538
|
if (!isNumeric(t5)) continue;
|
|
50313
50539
|
const vals = t5.keys.map((k4) => k4.v);
|
|
@@ -50415,7 +50641,9 @@ function checkRig(doc2, opts = {}) {
|
|
|
50415
50641
|
if (!sorted.length) continue;
|
|
50416
50642
|
const typical = sorted[Math.floor(sorted.length / 2)];
|
|
50417
50643
|
const worst2 = ds.reduce((m5, x4) => x4.d > m5.d ? x4 : m5, ds[0]);
|
|
50418
|
-
|
|
50644
|
+
const index = ds.indexOf(worst2);
|
|
50645
|
+
const neighbours = Math.max(ds[index - 1]?.d ?? 0, ds[index + 1]?.d ?? 0);
|
|
50646
|
+
if (worst2.d > Math.max(6, 5 * typical, 3 * neighbours)) {
|
|
50419
50647
|
const stepped = stepTimes.some((st) => st > worst2.t - 1 / dfps - 1e-9 && st <= worst2.t + 1e-9);
|
|
50420
50648
|
out2.push({
|
|
50421
50649
|
level: stepped ? "info" : "warn",
|
|
@@ -50466,7 +50694,7 @@ function checkRig(doc2, opts = {}) {
|
|
|
50466
50694
|
for (const att of doc2.attachments) {
|
|
50467
50695
|
if (att.type !== "region") continue;
|
|
50468
50696
|
const asset = doc2.assets.find((a5) => a5.id === att.asset), bbox = asset?.origin?.bbox;
|
|
50469
|
-
if (!asset || !bbox || !doc2.slots.some((s6) => s6.id === att.slot)) continue;
|
|
50697
|
+
if (!asset || !bbox || asset.origin?.bboxSpace === "source" || !doc2.slots.some((s6) => s6.id === att.slot)) continue;
|
|
50470
50698
|
const [cx, cy] = apply(regionMatrix(restWorld, att.slot, att), 0, 0);
|
|
50471
50699
|
const ex2 = bbox[0] + bbox[2] / 2, ey2 = bbox[1] + bbox[3] / 2, dx = cx - ex2, dy = cy - ey2;
|
|
50472
50700
|
const dist = Math.hypot(dx, dy);
|
|
@@ -50511,7 +50739,8 @@ function checkRig(doc2, opts = {}) {
|
|
|
50511
50739
|
for (const att of doc2.attachments) if (att.type !== "path" && shownAttachments.has(att.id)) used.add(att.asset);
|
|
50512
50740
|
for (const asset of doc2.assets) if (!used.has(asset.id))
|
|
50513
50741
|
out2.push({ level: "info", code: "unused_asset", subject: asset.id, message: `asset "${asset.id}" is not shown by any attachment at rest or in any animation` });
|
|
50514
|
-
|
|
50742
|
+
const maskSlots = new Set(doc2.slots.flatMap((s6) => s6.clip ? [doc2.attachments.find((a5) => a5.id === s6.clip)?.slot] : []));
|
|
50743
|
+
for (const s6 of doc2.slots) if (!s6.attachment && !swappedIn.has(s6.id) && !maskSlots.has(s6.id))
|
|
50515
50744
|
out2.push({ level: "warn", code: "never_visible", subject: s6.id, message: `slot "${s6.id}" has no attachment and no animation ever shows one` });
|
|
50516
50745
|
return out2;
|
|
50517
50746
|
}
|
|
@@ -50913,10 +51142,11 @@ function setPathAttachment(doc2, id, slot, geom) {
|
|
|
50913
51142
|
function setKeys(doc2, animId2, track, keys, duration, opts = {}) {
|
|
50914
51143
|
const anim = doc2.animations.find((a5) => a5.id === animId2) ?? { id: animId2, duration: duration ?? 1, loop: false, tracks: [], events: [] };
|
|
50915
51144
|
const same2 = (t5) => t5.target === track.target && t5.id === track.id && t5.prop === track.prop;
|
|
51145
|
+
const old = anim.tracks.find(same2);
|
|
50916
51146
|
const given = keys.map((k4) => ({ ...k4, ease: k4.ease ?? "ease_in_out" }));
|
|
50917
51147
|
const kept = opts.merge ? (anim.tracks.find(same2)?.keys ?? []).filter((k4) => !given.some((g4) => Math.abs(g4.t - k4.t) < 1e-9)) : [];
|
|
50918
51148
|
const sorted = [...kept, ...given].sort((a5, b4) => a5.t - b4.t);
|
|
50919
|
-
const edited = { ...track, keys: sorted };
|
|
51149
|
+
const edited = { ...old, ...track, interpolation: track.interpolation ?? old?.interpolation, keys: sorted };
|
|
50920
51150
|
const tracks = anim.tracks.some(same2) ? anim.tracks.map((t5) => same2(t5) ? edited : t5) : [...anim.tracks, edited];
|
|
50921
51151
|
const dur = duration ?? Math.max(anim.duration, ...sorted.map((k4) => k4.t));
|
|
50922
51152
|
const next = { ...anim, tracks, duration: dur, preset: void 0 };
|
|
@@ -51796,7 +52026,7 @@ function KeyDiamond({ track, value }) {
|
|
|
51796
52026
|
void commit("rig_remove_keys", { animation: anim.id, ...track, times: [t5] });
|
|
51797
52027
|
return;
|
|
51798
52028
|
}
|
|
51799
|
-
const v6 = tr ? sample(tr, t5) : value;
|
|
52029
|
+
const v6 = tr ? sample(tr, t5, anim.loop && tr.keys[0].t === 0 && tr.keys.at(-1).t === anim.duration) : value;
|
|
51800
52030
|
const ease2 = track.prop === "attachment" ? { ease: "step" } : {};
|
|
51801
52031
|
void commit("rig_set_keys", { animation: anim.id, ...track, merge: true, keys: [{ t: t5, v: v6, ...ease2 }] });
|
|
51802
52032
|
};
|
|
@@ -51846,6 +52076,16 @@ function BoneForm({ d: d5, id }) {
|
|
|
51846
52076
|
/* @__PURE__ */ u4(Num, { label: "length", value: b4.length, onCommit: (v6) => setField({ length: v6 }) }),
|
|
51847
52077
|
/* @__PURE__ */ u4(Sel, { label: "inherit", value: b4.inherit, options: INHERIT.map((v6) => [v6, v6]), onCommit: (v6) => setField({ inherit: v6 }) }),
|
|
51848
52078
|
/* @__PURE__ */ u4(Txt, { label: "color", value: b4.color ?? "", onCommit: (v6) => setField({ color: v6 }) }),
|
|
52079
|
+
anim && /* @__PURE__ */ u4(S, { children: [
|
|
52080
|
+
/* @__PURE__ */ u4("button", { onClick: () => {
|
|
52081
|
+
const target = prompt("Follow bone at the current time (world pose is preserved)");
|
|
52082
|
+
if (target) void commit("rig_attach_to_bone", { animation: anim.id, bone: id, target, t: time.value });
|
|
52083
|
+
}, children: "attach at contact" }),
|
|
52084
|
+
/* @__PURE__ */ u4("button", { onClick: () => {
|
|
52085
|
+
const driver = prompt("Driver bone for baked secondary rotation", b4.parent ?? "root");
|
|
52086
|
+
if (driver) void commit("rig_bake_secondary", { animation: anim.id, driver, bones: [id] });
|
|
52087
|
+
}, children: "bake secondary motion" })
|
|
52088
|
+
] }),
|
|
51849
52089
|
/* @__PURE__ */ u4("h4", { children: [
|
|
51850
52090
|
"transform",
|
|
51851
52091
|
anim ? " (posed)" : ""
|
|
@@ -51912,6 +52152,15 @@ function SlotForm({ d: d5, id }) {
|
|
|
51912
52152
|
}
|
|
51913
52153
|
),
|
|
51914
52154
|
/* @__PURE__ */ u4(Num, { label: "z", value: z3, track: track("z"), onCommit: (v6) => anim ? setKey("z", Math.round(v6)) : setSlot({ z: Math.round(v6) }) }),
|
|
52155
|
+
/* @__PURE__ */ u4(
|
|
52156
|
+
Sel,
|
|
52157
|
+
{
|
|
52158
|
+
label: "clip mask",
|
|
52159
|
+
value: s6.clip ?? "",
|
|
52160
|
+
options: [["", "none"], ...d5.attachments.filter((a5) => a5.type === "path" && a5.closed).map((a5) => [a5.id, a5.id])],
|
|
52161
|
+
onCommit: (v6) => setSlot({ clip: v6 || null })
|
|
52162
|
+
}
|
|
52163
|
+
),
|
|
51915
52164
|
/* @__PURE__ */ u4(Sel, { label: "blend", value: s6.blend, options: BLEND.map((v6) => [v6, v6]), onCommit: (v6) => setSlot({ blend: v6 }) }),
|
|
51916
52165
|
/* @__PURE__ */ u4(Txt, { label: "color", value: s6.color, onCommit: (v6) => setSlot({ color: v6 }) })
|
|
51917
52166
|
] });
|
|
@@ -52053,7 +52302,15 @@ function WeightsPanel({ d: d5, a: a5 }) {
|
|
|
52053
52302
|
/* @__PURE__ */ u4("input", { type: "checkbox", checked: candidates.includes(x4.id), onChange: (e5) => toggle(x4.id, e5.currentTarget.checked) }),
|
|
52054
52303
|
x4.id
|
|
52055
52304
|
] }, x4.id)),
|
|
52056
|
-
/* @__PURE__ */ u4("button", { class: "auto-weights", title: "re-skin this mesh to the checked bones by distance", onClick: autoWeights, children: "auto weights" })
|
|
52305
|
+
/* @__PURE__ */ u4("button", { class: "auto-weights", title: "re-skin this mesh to the checked bones by distance", onClick: autoWeights, children: "auto weights" }),
|
|
52306
|
+
/* @__PURE__ */ u4("button", { disabled: candidates.length < 2, onClick: () => void commit("rig_gradient_weights", { id: a5.id, bones: candidates }), children: "chain gradient" }),
|
|
52307
|
+
/* @__PURE__ */ u4("p", { class: "hint", children: [
|
|
52308
|
+
"Gradient order: ",
|
|
52309
|
+
candidates.join(" \u2192 "),
|
|
52310
|
+
". Uncheck and select bones in chain order."
|
|
52311
|
+
] }),
|
|
52312
|
+
/* @__PURE__ */ u4("button", { disabled: !a5.weights, onClick: () => void commit("rig_smooth_weights", { id: a5.id, locked: selectedVertices.value }), children: "smooth (keep selected fixed)" }),
|
|
52313
|
+
/* @__PURE__ */ u4("button", { disabled: !selectedVertices.value.length, onClick: () => void commit("rig_pin_weights", { id: a5.id, bone: b4.bone, vertices: selectedVertices.value }), children: "bind selected to brush bone" })
|
|
52057
52314
|
] });
|
|
52058
52315
|
}
|
|
52059
52316
|
function VertexForm({ d: d5, id, index }) {
|
|
@@ -52153,8 +52410,6 @@ function ConstraintHead({ d: d5, kind, c: c5, set }) {
|
|
|
52153
52410
|
const unknown = bones.filter((b4) => !d5.bones.some((x4) => x4.id === b4));
|
|
52154
52411
|
if (!bones.length) setStatus(`${c5.id}: a constraint drives at least one bone`, "warn");
|
|
52155
52412
|
else if (unknown.length) setStatus(`${c5.id}: no bone "${unknown.join('", "')}" in this rig`, "warn");
|
|
52156
|
-
else if (kind === "ik" && bones.length > 2)
|
|
52157
|
-
setStatus(`${c5.id}: an IK chain is one or two bones (the format has no longer ones), got ${bones.length}`, "warn");
|
|
52158
52413
|
else if (bones.join(",") !== c5.bones.join(",")) set({ bones });
|
|
52159
52414
|
};
|
|
52160
52415
|
const options = kind === "path" ? d5.slots.filter((s6) => d5.attachments.some((a5) => a5.slot === s6.id && a5.type === "path")).map((s6) => [s6.id, s6.id]) : d5.bones.map((b4) => [b4.id, b4.id]);
|
|
@@ -52167,15 +52422,23 @@ function ConstraintHead({ d: d5, kind, c: c5, set }) {
|
|
|
52167
52422
|
/* @__PURE__ */ u4(Sel, { label: "target", name: "target", value: c5.target, options, onCommit: (v6) => set({ target: v6 }) })
|
|
52168
52423
|
] });
|
|
52169
52424
|
}
|
|
52170
|
-
var Order = ({ value, set }) => /* @__PURE__ */ u4(
|
|
52425
|
+
var Order = ({ value, set }) => /* @__PURE__ */ u4(S, { children: [
|
|
52426
|
+
/* @__PURE__ */ u4(Num, { label: "order", name: "order", value, onCommit: (v6) => set({ order: Math.round(v6) }) }),
|
|
52427
|
+
/* @__PURE__ */ u4("button", { onClick: () => void commit("rig_order_constraints", {}), children: "order by dependencies" })
|
|
52428
|
+
] });
|
|
52171
52429
|
function IkForm({ d: d5, c: c5 }) {
|
|
52172
52430
|
const o5 = constraintOps("ik", c5.id);
|
|
52173
52431
|
return /* @__PURE__ */ u4(S, { children: [
|
|
52174
52432
|
/* @__PURE__ */ u4("h4", { children: "ik constraint" }),
|
|
52175
52433
|
/* @__PURE__ */ u4(ConstraintHead, { d: d5, kind: "ik", c: c5, set: o5.set }),
|
|
52176
52434
|
/* @__PURE__ */ u4(Num, { label: "mix", name: "mix", step: 0.05, value: o5.value("mix", c5.mix), track: o5.track("mix"), onCommit: (v6) => o5.num("mix", v6) }),
|
|
52177
|
-
/* @__PURE__ */ u4(
|
|
52178
|
-
|
|
52435
|
+
c5.bones.length > 2 && /* @__PURE__ */ u4(S, { children: [
|
|
52436
|
+
/* @__PURE__ */ u4(Num, { label: "iterations", value: c5.iterations ?? 100, onCommit: (v6) => o5.set({ iterations: Math.round(v6) }) }),
|
|
52437
|
+
/* @__PURE__ */ u4(Num, { label: "tolerance", value: c5.tolerance ?? 0.05, step: 0.01, onCommit: (v6) => o5.set({ tolerance: v6 }) })
|
|
52438
|
+
] }),
|
|
52439
|
+
c5.bones.length <= 2 && /* @__PURE__ */ u4(Num, { label: "softness", name: "softness", value: o5.value("softness", c5.softness), track: o5.track("softness"), onCommit: (v6) => o5.num("softness", v6) }),
|
|
52440
|
+
IK_FLAGS.filter((f6) => c5.bones.length <= 2 || f6 === "bendPositive").map((f6) => /* @__PURE__ */ u4(Chk, { label: f6, name: f6, value: c5[f6], onCommit: (v6) => o5.set({ [f6]: v6 }) }, f6)),
|
|
52441
|
+
c5.bones.length > 2 && /* @__PURE__ */ u4("p", { class: "hint", children: "Long chains bend without stretching. Use a path to control the shape between the root and tip." }),
|
|
52179
52442
|
/* @__PURE__ */ u4(Order, { value: c5.order, set: o5.set })
|
|
52180
52443
|
] });
|
|
52181
52444
|
}
|
|
@@ -52186,7 +52449,7 @@ function TransformForm({ d: d5, c: c5 }) {
|
|
|
52186
52449
|
/* @__PURE__ */ u4(ConstraintHead, { d: d5, kind: "transform", c: c5, set: o5.set }),
|
|
52187
52450
|
TRANSFORM_FLAGS.map((f6) => /* @__PURE__ */ u4(Chk, { label: f6, name: f6, value: c5[f6], onCommit: (v6) => o5.set({ [f6]: v6 }) }, f6)),
|
|
52188
52451
|
/* @__PURE__ */ u4("h4", { children: "offset" }),
|
|
52189
|
-
TRANSFORM_OFFSETS.map((p6) => /* @__PURE__ */ u4(Num, { label: p6, name: p6, value: c5[p6], step: p6.startsWith("scale") ? 0.05 : 1, onCommit: (v6) => o5.
|
|
52452
|
+
TRANSFORM_OFFSETS.map((p6) => /* @__PURE__ */ u4(Num, { label: p6, name: p6, value: o5.value(p6, c5[p6]), track: o5.track(p6), step: p6.startsWith("scale") ? 0.05 : 1, onCommit: (v6) => o5.num(p6, v6) }, p6)),
|
|
52190
52453
|
/* @__PURE__ */ u4("h4", { children: "mix" }),
|
|
52191
52454
|
MIX_PROPS.transform.map((p6) => /* @__PURE__ */ u4(Num, { label: p6, name: p6, step: 0.05, value: o5.value(p6, c5[p6]), track: o5.track(p6), onCommit: (v6) => o5.num(p6, v6) }, p6)),
|
|
52192
52455
|
/* @__PURE__ */ u4(Order, { value: c5.order, set: o5.set })
|
|
@@ -52197,13 +52460,13 @@ function PathForm({ d: d5, c: c5 }) {
|
|
|
52197
52460
|
return /* @__PURE__ */ u4(S, { children: [
|
|
52198
52461
|
/* @__PURE__ */ u4("h4", { children: "path constraint" }),
|
|
52199
52462
|
/* @__PURE__ */ u4(ConstraintHead, { d: d5, kind: "path", c: c5, set: o5.set }),
|
|
52200
|
-
PATH_MODES.map(([p6,
|
|
52463
|
+
PATH_MODES.map(([p6, values2]) => /* @__PURE__ */ u4(
|
|
52201
52464
|
Sel,
|
|
52202
52465
|
{
|
|
52203
52466
|
label: p6,
|
|
52204
52467
|
name: p6,
|
|
52205
52468
|
value: c5[p6],
|
|
52206
|
-
options:
|
|
52469
|
+
options: values2.map((v6) => [v6, v6]),
|
|
52207
52470
|
onCommit: (v6) => o5.set({ [p6]: v6 })
|
|
52208
52471
|
},
|
|
52209
52472
|
p6
|
|
@@ -52508,7 +52771,25 @@ function TrackSliders() {
|
|
|
52508
52771
|
h2(() => () => cancelReleases(group), [group]);
|
|
52509
52772
|
if (!a5 || !ref || !d5) return null;
|
|
52510
52773
|
const tr = a5.tracks.find((t5) => t5.target === ref.target && t5.id === ref.id && t5.prop === ref.prop);
|
|
52511
|
-
if (!tr || tr.keys.some((k4) => typeof k4.v !== "number")) return null;
|
|
52774
|
+
if (!tr || tr.keys.some((k4) => typeof k4.v !== "number" && !(Array.isArray(k4.v) && k4.v.every((v6) => typeof v6 === "number")))) return null;
|
|
52775
|
+
const interpolation = /* @__PURE__ */ u4(
|
|
52776
|
+
Sel,
|
|
52777
|
+
{
|
|
52778
|
+
label: "interpolation",
|
|
52779
|
+
value: tr.interpolation ?? "segment",
|
|
52780
|
+
options: [["segment", "per-key easing"], ["pchip", "smooth through keys"]],
|
|
52781
|
+
onCommit: (v6) => void commit("rig_set_keys", { animation: a5.id, ...ref, interpolation: v6, keys: tr.keys })
|
|
52782
|
+
}
|
|
52783
|
+
);
|
|
52784
|
+
if (tr.keys.some((k4) => typeof k4.v !== "number")) return /* @__PURE__ */ u4("div", { class: "track-adjust", children: [
|
|
52785
|
+
/* @__PURE__ */ u4("h4", { children: [
|
|
52786
|
+
"track \xB7 ",
|
|
52787
|
+
ref.id,
|
|
52788
|
+
".",
|
|
52789
|
+
ref.prop
|
|
52790
|
+
] }),
|
|
52791
|
+
interpolation
|
|
52792
|
+
] });
|
|
52512
52793
|
const cur = adjust.value.key === key ? adjust.value : { key, ...NEUTRAL };
|
|
52513
52794
|
const vs = tr.keys.map((k4) => k4.v);
|
|
52514
52795
|
const R2 = Math.max(1, Math.max(...vs) - Math.min(...vs));
|
|
@@ -52543,6 +52824,7 @@ function TrackSliders() {
|
|
|
52543
52824
|
".",
|
|
52544
52825
|
ref.prop
|
|
52545
52826
|
] }),
|
|
52827
|
+
interpolation,
|
|
52546
52828
|
Object.keys(NEUTRAL).map((k4) => /* @__PURE__ */ u4(
|
|
52547
52829
|
Slider,
|
|
52548
52830
|
{
|
|
@@ -52559,7 +52841,7 @@ function TrackSliders() {
|
|
|
52559
52841
|
))
|
|
52560
52842
|
] });
|
|
52561
52843
|
}
|
|
52562
|
-
var EASES = ["linear", "step", "ease_in", "ease_out", "ease_in_out", "back_out", "elastic_out", "bezier"];
|
|
52844
|
+
var EASES = ["linear", "step", "ease_in", "ease_out", "ease_in_out", "minimum_jerk", "back_out", "elastic_out", "bezier"];
|
|
52563
52845
|
var BEZ_DEFAULT = [0.25, 0.1, 0.25, 1];
|
|
52564
52846
|
var EW = 100;
|
|
52565
52847
|
var EPAD = 15;
|
|
@@ -52619,6 +52901,26 @@ function KeyEase() {
|
|
|
52619
52901
|
const tr = a5.tracks.find((k4) => k4.target === track.target && k4.id === track.id && k4.prop === track.prop);
|
|
52620
52902
|
const key = tr?.keys.find((k4) => Math.abs(k4.t - times3[0]) < 1e-6);
|
|
52621
52903
|
if (!key) return null;
|
|
52904
|
+
if (tr?.interpolation === "pchip") return /* @__PURE__ */ u4(S, { children: [
|
|
52905
|
+
/* @__PURE__ */ u4("h4", { children: [
|
|
52906
|
+
"key ",
|
|
52907
|
+
fmt2(key.t),
|
|
52908
|
+
"s \xB7 ",
|
|
52909
|
+
track.id,
|
|
52910
|
+
".",
|
|
52911
|
+
track.prop
|
|
52912
|
+
] }),
|
|
52913
|
+
/* @__PURE__ */ u4("p", { children: "Velocity is matched through neighbouring keys. Select per-key easing above to edit segment curves." }),
|
|
52914
|
+
/* @__PURE__ */ u4(
|
|
52915
|
+
Sel,
|
|
52916
|
+
{
|
|
52917
|
+
label: "segment",
|
|
52918
|
+
value: key.ease === "step" ? "step" : "smooth",
|
|
52919
|
+
options: [["smooth", "smooth"], ["step", "hold until next key"]],
|
|
52920
|
+
onCommit: (v6) => void commit("rig_set_keys", { animation: a5.id, ...track, merge: true, keys: [{ t: key.t, v: key.v, ease: v6 === "step" ? "step" : "linear" }] })
|
|
52921
|
+
}
|
|
52922
|
+
)
|
|
52923
|
+
] });
|
|
52622
52924
|
const bez = typeof key.ease === "string" ? BEZ_DEFAULT : key.ease.bezier;
|
|
52623
52925
|
const set = (ease2) => void commit("rig_set_keys", {
|
|
52624
52926
|
animation: a5.id,
|
|
@@ -53117,7 +53419,7 @@ var BoneGizmo = class {
|
|
|
53117
53419
|
begin(handle, p6) {
|
|
53118
53420
|
const skel = this.ctx.skel(), doc2 = this.ctx.shown(), st = skel.bones.get(this.boneId);
|
|
53119
53421
|
if (!st) return;
|
|
53120
|
-
const
|
|
53422
|
+
const values2 = { x: st.x, y: st.y, rotation: st.rotation, scaleX: st.scaleX, scaleY: st.scaleY };
|
|
53121
53423
|
const parent = parentWorld(skel, doc2, this.boneId);
|
|
53122
53424
|
const [lx, ly] = parent ? toLocal(parent, p6[0], p6[1]) : p6;
|
|
53123
53425
|
this.patch = void 0;
|
|
@@ -53127,11 +53429,11 @@ var BoneGizmo = class {
|
|
|
53127
53429
|
joint: [st.world.tx, st.world.ty],
|
|
53128
53430
|
doc: doc2,
|
|
53129
53431
|
skel,
|
|
53130
|
-
values,
|
|
53432
|
+
values: values2,
|
|
53131
53433
|
time: this.ctx.time(),
|
|
53132
53434
|
// parent-local, so the scale ratio ignores the parent's scale; the axis is whichever grip was grabbed (x or y)
|
|
53133
|
-
axis: unit(lx -
|
|
53134
|
-
along: Math.hypot(lx -
|
|
53435
|
+
axis: unit(lx - values2.x, ly - values2.y),
|
|
53436
|
+
along: Math.hypot(lx - values2.x, ly - values2.y)
|
|
53135
53437
|
};
|
|
53136
53438
|
}
|
|
53137
53439
|
drag(p6, mods) {
|
|
@@ -53337,20 +53639,20 @@ var VertexGizmo = class {
|
|
|
53337
53639
|
const anim = doc2.animations.find((a5) => a5.id === this.ctx.animId());
|
|
53338
53640
|
const tr = anim?.tracks.find((x4) => x4.target === "deform" && x4.id === this.attachmentId && x4.prop === "vertices");
|
|
53339
53641
|
if (!tr || !tr.keys.length) return void 0;
|
|
53340
|
-
const v6 = sample(tr, t5);
|
|
53642
|
+
const v6 = sample(tr, t5, !!anim?.loop && tr.keys[0].t === 0 && tr.keys.at(-1).t === anim.duration);
|
|
53341
53643
|
return Array.isArray(v6) && v6.length === deformLength(att) && typeof v6[0] === "number" ? v6.slice() : void 0;
|
|
53342
53644
|
}
|
|
53343
53645
|
drag(p6, _mods) {
|
|
53344
53646
|
const s6 = this.start;
|
|
53345
53647
|
if (!s6) return this.ctx.shown();
|
|
53346
|
-
const
|
|
53347
|
-
this.delta =
|
|
53648
|
+
const delta2 = [p6[0] - s6.p[0], p6[1] - s6.p[1]];
|
|
53649
|
+
this.delta = delta2;
|
|
53348
53650
|
if (this.ctx.mode() !== "animate") {
|
|
53349
|
-
const patch = this.shift(s6, this.localDeltas(s6,
|
|
53651
|
+
const patch = this.shift(s6, this.localDeltas(s6, delta2));
|
|
53350
53652
|
this.patch = patch;
|
|
53351
53653
|
return s6.att.type === "path" ? setPathAttachment(s6.doc, this.attachmentId, s6.att.slot, { ...patch, closed: s6.att.closed }).doc : setMesh(s6.doc, this.attachmentId, patch).doc;
|
|
53352
53654
|
}
|
|
53353
|
-
this.offsets = this.localDeltas(s6,
|
|
53655
|
+
this.offsets = this.localDeltas(s6, delta2, s6.base?.slice()).map(round6);
|
|
53354
53656
|
const anim = this.ctx.animId();
|
|
53355
53657
|
if (!anim) return s6.doc;
|
|
53356
53658
|
return setKeys(
|
|
@@ -53368,9 +53670,9 @@ var VertexGizmo = class {
|
|
|
53368
53670
|
* rather than handed over, so no future in-place writer downstream can spoil the next move's baseline.
|
|
53369
53671
|
* Rebuilt from that base every move, so a gesture never accumulates.
|
|
53370
53672
|
*/
|
|
53371
|
-
localDeltas(s6,
|
|
53673
|
+
localDeltas(s6, delta2, base) {
|
|
53372
53674
|
let out2 = base;
|
|
53373
|
-
for (const i6 of s6.indices) out2 = deformOffsetsFor(s6.skel, s6.att, out2, i6,
|
|
53675
|
+
for (const i6 of s6.indices) out2 = deformOffsetsFor(s6.skel, s6.att, out2, i6, delta2);
|
|
53374
53676
|
return out2 ?? [];
|
|
53375
53677
|
}
|
|
53376
53678
|
/**
|
|
@@ -53400,12 +53702,12 @@ var VertexGizmo = class {
|
|
|
53400
53702
|
return { vertices: s6.att.vertices.map((v6, k4) => moved.has(k4 >> 1) ? round6(v6 + off[k4]) : v6) };
|
|
53401
53703
|
}
|
|
53402
53704
|
commit() {
|
|
53403
|
-
const s6 = this.start,
|
|
53705
|
+
const s6 = this.start, delta2 = this.delta, patch = this.patch, offsets = this.offsets;
|
|
53404
53706
|
this.start = void 0;
|
|
53405
53707
|
this.delta = void 0;
|
|
53406
53708
|
this.patch = void 0;
|
|
53407
53709
|
this.offsets = void 0;
|
|
53408
|
-
if (!s6 || !
|
|
53710
|
+
if (!s6 || !delta2 || Math.hypot(delta2[0], delta2[1]) <= 1e-6) return void 0;
|
|
53409
53711
|
if (this.ctx.mode() !== "animate") {
|
|
53410
53712
|
if (!patch) return void 0;
|
|
53411
53713
|
return s6.att.type === "path" ? { tool: "rig_set_path_attachment", args: { id: this.attachmentId, slot: s6.att.slot, ...patch, closed: s6.att.closed } } : { tool: "rig_set_mesh", args: { id: this.attachmentId, ...patch } };
|
|
@@ -57351,6 +57653,7 @@ var import_gifenc = __toESM(require_gifenc(), 1);
|
|
|
57351
57653
|
var AnimationState = class {
|
|
57352
57654
|
constructor(sink, opts = {}) {
|
|
57353
57655
|
this.sink = sink;
|
|
57656
|
+
this.opts = opts;
|
|
57354
57657
|
this.defaultMix = opts.mix ?? 0;
|
|
57355
57658
|
}
|
|
57356
57659
|
onEvent;
|
|
@@ -57375,11 +57678,18 @@ var AnimationState = class {
|
|
|
57375
57678
|
}
|
|
57376
57679
|
channel(id, loop2, priority = 0) {
|
|
57377
57680
|
const anim = this.sink.doc.animations.find((a5) => a5.id === id);
|
|
57378
|
-
if (!anim)
|
|
57681
|
+
if (!anim) {
|
|
57682
|
+
const message = `unknown animation "${id}"; available: ${this.sink.doc.animations.map((a5) => a5.id).join(", ")}`;
|
|
57683
|
+
if (this.opts.missingAnimation === "throw") throw new Error(message);
|
|
57684
|
+
(this.opts.onWarning ?? console.warn)(message);
|
|
57685
|
+
return void 0;
|
|
57686
|
+
}
|
|
57379
57687
|
return { anim, time: 0, loop: loop2 ?? anim.loop, done: false, priority, fresh: true };
|
|
57380
57688
|
}
|
|
57381
57689
|
setBase(id, opts = {}) {
|
|
57382
|
-
|
|
57690
|
+
const next = id === void 0 ? void 0 : this.channel(id);
|
|
57691
|
+
if (id !== void 0 && !next) return;
|
|
57692
|
+
this.base = next;
|
|
57383
57693
|
if (!this.main || this.main.done) {
|
|
57384
57694
|
this.main = void 0;
|
|
57385
57695
|
this.beginMix(opts.mix);
|
|
@@ -57388,6 +57698,16 @@ var AnimationState = class {
|
|
|
57388
57698
|
}
|
|
57389
57699
|
play(id, opts = {}) {
|
|
57390
57700
|
const ch = this.channel(id, opts.loop, opts.priority ?? 0);
|
|
57701
|
+
if (!ch) {
|
|
57702
|
+
this.main?.settle?.("interrupted");
|
|
57703
|
+
for (const queued of this.queue) queued.settle?.("dropped");
|
|
57704
|
+
this.main = void 0;
|
|
57705
|
+
this.queue = [];
|
|
57706
|
+
this.beginMix(opts.mix);
|
|
57707
|
+
this.update(0);
|
|
57708
|
+
opts.onComplete?.("dropped");
|
|
57709
|
+
return Promise.resolve("dropped");
|
|
57710
|
+
}
|
|
57391
57711
|
const promise2 = new Promise((resolve) => {
|
|
57392
57712
|
ch.settle = (r5) => {
|
|
57393
57713
|
ch.settle = void 0;
|
|
@@ -57408,7 +57728,10 @@ var AnimationState = class {
|
|
|
57408
57728
|
/** Independent loop on top of the shown pose; `undefined` removes it. */
|
|
57409
57729
|
setOverlay(name, id) {
|
|
57410
57730
|
if (id === void 0) this.overlays.delete(name);
|
|
57411
|
-
else
|
|
57731
|
+
else {
|
|
57732
|
+
const ch = this.channel(id);
|
|
57733
|
+
if (ch) this.overlays.set(name, ch);
|
|
57734
|
+
}
|
|
57412
57735
|
}
|
|
57413
57736
|
/** Clear everything and show the setup pose. Pending plays resolve as "stopped". */
|
|
57414
57737
|
stop() {
|
|
@@ -57493,6 +57816,7 @@ function hit(t5, prev, cur, d5, loop2) {
|
|
|
57493
57816
|
// src/rig-runtime.ts
|
|
57494
57817
|
var DEG4 = Math.PI / 180;
|
|
57495
57818
|
var MESH_GRID = 9;
|
|
57819
|
+
var GHOST_ALPHA = 0.5;
|
|
57496
57820
|
async function createTextures(doc2, loadSource) {
|
|
57497
57821
|
const bySrc = /* @__PURE__ */ new Map(), out2 = /* @__PURE__ */ new Map();
|
|
57498
57822
|
for (const a5 of doc2.assets) {
|
|
@@ -57532,7 +57856,7 @@ function atlasUV(asset, u6, v6, sourceW, sourceH) {
|
|
|
57532
57856
|
var rgbOf = (c5) => Math.round(c5[0] * 255) << 16 | Math.round(c5[1] * 255) << 8 | Math.round(c5[2] * 255);
|
|
57533
57857
|
var sameNumbers = (a5, b4) => a5 === b4 || a5.length === b4.length && a5.every((v6, i6) => v6 === b4[i6]);
|
|
57534
57858
|
var RigPlayer = class {
|
|
57535
|
-
/**
|
|
57859
|
+
/** Default crossfade is 0.18 seconds; explicit mix: 0 requests a hard cut. */
|
|
57536
57860
|
constructor(doc2, textures, opts = {}) {
|
|
57537
57861
|
this.doc = doc2;
|
|
57538
57862
|
parse(doc2);
|
|
@@ -57571,7 +57895,7 @@ var RigPlayer = class {
|
|
|
57571
57895
|
this.items.push(item);
|
|
57572
57896
|
this.view.addChild(item.node);
|
|
57573
57897
|
}
|
|
57574
|
-
this.state = new AnimationState(this, { mix: opts.mix ?? 0 });
|
|
57898
|
+
this.state = new AnimationState(this, { ...opts, mix: opts.mix ?? 0.18 });
|
|
57575
57899
|
this.setPose(/* @__PURE__ */ new Map());
|
|
57576
57900
|
}
|
|
57577
57901
|
view = new Container();
|
|
@@ -57582,6 +57906,9 @@ var RigPlayer = class {
|
|
|
57582
57906
|
pages = /* @__PURE__ */ new Map();
|
|
57583
57907
|
/** slots the editor hides; honoured by setPose */
|
|
57584
57908
|
hidden = /* @__PURE__ */ new Set();
|
|
57909
|
+
/** slots the editor has selected; setPose draws them at GHOST_ALPHA at least */
|
|
57910
|
+
ghosts = /* @__PURE__ */ new Set();
|
|
57911
|
+
clipGraphics = /* @__PURE__ */ new Map();
|
|
57585
57912
|
/** whole atlas page as a plain texture (identity uv mapping) for skinned meshes */
|
|
57586
57913
|
pageTexture(tex) {
|
|
57587
57914
|
let page = this.pages.get(tex.source);
|
|
@@ -57593,7 +57920,7 @@ var RigPlayer = class {
|
|
|
57593
57920
|
this.state.onEvent = opts.onEvent;
|
|
57594
57921
|
return this.state.play(animId2, {
|
|
57595
57922
|
loop: opts.loop,
|
|
57596
|
-
mix: opts.mix
|
|
57923
|
+
mix: opts.mix,
|
|
57597
57924
|
ifBusy: "interrupt",
|
|
57598
57925
|
onComplete: (r5) => {
|
|
57599
57926
|
if (r5 === "completed") opts.onComplete?.();
|
|
@@ -57622,6 +57949,10 @@ var RigPlayer = class {
|
|
|
57622
57949
|
if (visible) this.hidden.delete(slotId);
|
|
57623
57950
|
else this.hidden.add(slotId);
|
|
57624
57951
|
}
|
|
57952
|
+
/** Editor-only: draw these slots at GHOST_ALPHA at least, whatever the pose says (an empty list clears it). */
|
|
57953
|
+
setGhostSlots(slotIds) {
|
|
57954
|
+
this.ghosts = new Set(slotIds);
|
|
57955
|
+
}
|
|
57625
57956
|
/** Stop playback and immediately restore the complete setup pose. */
|
|
57626
57957
|
stop() {
|
|
57627
57958
|
this.state.stop();
|
|
@@ -57633,12 +57964,32 @@ var RigPlayer = class {
|
|
|
57633
57964
|
/** Apply a pose (rest where silent) to the display objects. */
|
|
57634
57965
|
setPose(pose2) {
|
|
57635
57966
|
const skel = this.skel = evaluate(this.doc, pose2);
|
|
57967
|
+
const clips = new Set(this.doc.slots.flatMap((s6) => s6.clip ? [s6.clip] : []));
|
|
57968
|
+
for (const [id, g4] of this.clipGraphics) if (!clips.has(id)) {
|
|
57969
|
+
g4.destroy();
|
|
57970
|
+
this.clipGraphics.delete(id);
|
|
57971
|
+
}
|
|
57972
|
+
for (const id of clips) {
|
|
57973
|
+
const att = this.doc.attachments.find((a5) => a5.id === id);
|
|
57974
|
+
if (att?.type !== "path" || !att.closed) continue;
|
|
57975
|
+
let g4 = this.clipGraphics.get(id);
|
|
57976
|
+
if (!g4) {
|
|
57977
|
+
g4 = new Graphics();
|
|
57978
|
+
this.clipGraphics.set(id, g4);
|
|
57979
|
+
this.view.addChild(g4);
|
|
57980
|
+
}
|
|
57981
|
+
const vertices = attachmentWorldVertices(skel, att.slot, att);
|
|
57982
|
+
const n4 = Math.max(32, vertices.length * 4);
|
|
57983
|
+
const points = samplePath(vertices, true, att.lengths, true, Array.from({ length: n4 }, (_4, i6) => i6 / n4), true);
|
|
57984
|
+
g4.clear().poly(points.flatMap((p6) => [p6.x, p6.y]), true).fill(16777215);
|
|
57985
|
+
}
|
|
57636
57986
|
const m5 = new Matrix();
|
|
57637
57987
|
const orderV = pose2.get(pk("drawOrder", "order", "order"));
|
|
57638
57988
|
const order = /* @__PURE__ */ new Map();
|
|
57639
57989
|
if (Array.isArray(orderV)) orderV.forEach((id, i6) => order.set(id, i6));
|
|
57640
57990
|
for (const it of this.items) {
|
|
57641
57991
|
const s6 = it.slot, shown2 = skel.attachment.get(s6.id);
|
|
57992
|
+
it.node.mask = s6.clip ? this.clipGraphics.get(s6.clip) ?? null : null;
|
|
57642
57993
|
if (shown2 !== it.att || this.hidden.has(s6.id)) {
|
|
57643
57994
|
it.node.visible = false;
|
|
57644
57995
|
continue;
|
|
@@ -57646,7 +57997,8 @@ var RigPlayer = class {
|
|
|
57646
57997
|
it.node.visible = true;
|
|
57647
57998
|
const colorV = pose2.get(pk("slot", s6.id, "color"));
|
|
57648
57999
|
const color = Array.isArray(colorV) && typeof colorV[0] === "number" ? colorV : colorToArray(s6.color);
|
|
57649
|
-
|
|
58000
|
+
const alpha = numOf(pose2, pk("slot", s6.id, "alpha"), s6.alpha) * color[3];
|
|
58001
|
+
it.node.alpha = this.ghosts.has(s6.id) ? Math.max(alpha, GHOST_ALPHA) : alpha;
|
|
57650
58002
|
it.node.tint = rgbOf(color);
|
|
57651
58003
|
it.node.zIndex = order.size ? order.get(s6.id) ?? 0 : numOf(pose2, pk("slot", s6.id, "z"), s6.z);
|
|
57652
58004
|
if (it.skinned) {
|
|
@@ -57979,6 +58331,7 @@ var Stage = class {
|
|
|
57979
58331
|
}));
|
|
57980
58332
|
this.dispose.push(j3(() => {
|
|
57981
58333
|
hiddenSlots.value;
|
|
58334
|
+
selection.value;
|
|
57982
58335
|
f3(() => this.applyPose());
|
|
57983
58336
|
}));
|
|
57984
58337
|
this.dispose.push(j3(() => {
|
|
@@ -58088,7 +58441,7 @@ var Stage = class {
|
|
|
58088
58441
|
for (const l6 of this.labelPool.values()) l6.destroy();
|
|
58089
58442
|
this.labelPool.clear();
|
|
58090
58443
|
this.labels.removeChildren();
|
|
58091
|
-
this.player = new RigPlayer(d5, this.textures);
|
|
58444
|
+
this.player = new RigPlayer(d5, this.textures, { mix: 0 });
|
|
58092
58445
|
this.structKey = structureKeyOf(d5);
|
|
58093
58446
|
this.player.view.visible = !this.atlasOn;
|
|
58094
58447
|
this.camera.addChildAt(this.player.view, 1);
|
|
@@ -58113,6 +58466,7 @@ var Stage = class {
|
|
|
58113
58466
|
if (!p6 || !d5) return;
|
|
58114
58467
|
const hidden = hiddenSlots.value;
|
|
58115
58468
|
for (const s6 of d5.slots) p6.setSlotVisible(s6.id, !hidden.has(s6.id));
|
|
58469
|
+
p6.setGhostSlots(playing.peek() ? [] : this.selectedSlots(d5));
|
|
58116
58470
|
const anim = currentAnim.value;
|
|
58117
58471
|
if (mode.value === "animate" && anim) {
|
|
58118
58472
|
void p6.play(anim.id, { loop: loop.value });
|
|
@@ -58126,6 +58480,21 @@ var Stage = class {
|
|
|
58126
58480
|
}
|
|
58127
58481
|
this.drawOverlay();
|
|
58128
58482
|
}
|
|
58483
|
+
/**
|
|
58484
|
+
* The slots the selection is about — a slot, the one an attachment (or a vertex of it) hangs in, or every slot on
|
|
58485
|
+
* a selected bone. The player draws them half-visible at least, so a part faded out at rest (runes that only light
|
|
58486
|
+
* up in `win`) is not an invisible box of grips. Not while playing: the animation's own fade is what is watched.
|
|
58487
|
+
*/
|
|
58488
|
+
selectedSlots(d5) {
|
|
58489
|
+
const sel = selection.peek();
|
|
58490
|
+
if (sel?.kind === "slot") return [sel.id];
|
|
58491
|
+
if (sel?.kind === "attachment" || sel?.kind === "vertex") {
|
|
58492
|
+
const a5 = d5.attachments.find((a6) => a6.id === sel.id);
|
|
58493
|
+
return a5 ? [a5.slot] : [];
|
|
58494
|
+
}
|
|
58495
|
+
if (sel?.kind === "bone") return d5.slots.filter((s6) => s6.bone === sel.id).map((s6) => s6.id);
|
|
58496
|
+
return [];
|
|
58497
|
+
}
|
|
58129
58498
|
// ---------- reference underlay ----------
|
|
58130
58499
|
reference;
|
|
58131
58500
|
referenceSrc;
|
|
@@ -58204,6 +58573,25 @@ var Stage = class {
|
|
|
58204
58573
|
* is on screen. The overlay (bones, labels, gizmos) is switched off for the duration, so nothing of the editor
|
|
58205
58574
|
* ends up in it; the paper stays, which is what makes every pixel opaque — GIF has no partial transparency.
|
|
58206
58575
|
*/
|
|
58576
|
+
async exportPng() {
|
|
58577
|
+
if (!this.player || this.exporting) return;
|
|
58578
|
+
const p6 = this.player, d5 = shown.peek();
|
|
58579
|
+
if (!d5) return;
|
|
58580
|
+
this.exporting = true;
|
|
58581
|
+
const wasPlaying = playing.peek();
|
|
58582
|
+
playing.value = false;
|
|
58583
|
+
p6.setGhostSlots([]);
|
|
58584
|
+
p6.setPose(pose.peek());
|
|
58585
|
+
try {
|
|
58586
|
+
return await this.app.renderer.extract.base64({ target: p6.view, frame: new Rectangle(0, 0, d5.meta.width, d5.meta.height), clearColor: [0, 0, 0, 0] });
|
|
58587
|
+
} finally {
|
|
58588
|
+
this.exporting = false;
|
|
58589
|
+
if (!this.destroyed) {
|
|
58590
|
+
playing.value = wasPlaying;
|
|
58591
|
+
this.applyPose();
|
|
58592
|
+
}
|
|
58593
|
+
}
|
|
58594
|
+
}
|
|
58207
58595
|
async exportGif(fps = FPS) {
|
|
58208
58596
|
const d5 = shown.peek(), anim = currentAnim.peek(), p6 = this.player;
|
|
58209
58597
|
if (!p6 || !d5 || !anim || mode.peek() !== "animate") {
|
|
@@ -58215,12 +58603,13 @@ var Stage = class {
|
|
|
58215
58603
|
const wasPlaying = playing.peek();
|
|
58216
58604
|
playing.value = false;
|
|
58217
58605
|
this.overlay.visible = false;
|
|
58606
|
+
p6.setGhostSlots([]);
|
|
58218
58607
|
try {
|
|
58219
58608
|
const w5 = d5.meta.width, h6 = d5.meta.height, n4 = Math.max(1, Math.round(anim.duration * fps));
|
|
58220
58609
|
const frame = new Rectangle(0, 0, w5, h6), gif = (0, import_gifenc.GIFEncoder)();
|
|
58221
58610
|
for (let i6 = 0; i6 < n4; i6++) {
|
|
58222
58611
|
if (this.destroyed) return;
|
|
58223
|
-
void p6.play(anim.id, { loop: false });
|
|
58612
|
+
void p6.play(anim.id, { loop: false, mix: 0 });
|
|
58224
58613
|
p6.tick(i6 / fps);
|
|
58225
58614
|
const { pixels } = this.app.renderer.extract.pixels({ target: this.camera, frame });
|
|
58226
58615
|
const palette = (0, import_gifenc.quantize)(pixels, 256);
|
|
@@ -59380,7 +59769,7 @@ var Timeline = class {
|
|
|
59380
59769
|
selectedTrack.value = ref;
|
|
59381
59770
|
selectedKeys.value = [t5];
|
|
59382
59771
|
});
|
|
59383
|
-
void commit("rig_set_keys", { animation: id, ...ref, keys: [{ t: t5, v: sample(tr, t5), ease: prev?.ease }], merge: true });
|
|
59772
|
+
void commit("rig_set_keys", { animation: id, ...ref, keys: [{ t: t5, v: sample(tr, t5, anim.loop && tr.keys[0].t === 0 && tr.keys.at(-1).t === anim.duration), ease: prev?.ease }], merge: true });
|
|
59384
59773
|
}
|
|
59385
59774
|
bindPointer() {
|
|
59386
59775
|
const c5 = this.canvas;
|
|
@@ -59546,6 +59935,30 @@ async function exportGif() {
|
|
|
59546
59935
|
a5.click();
|
|
59547
59936
|
setTimeout(() => URL.revokeObjectURL(url), 1e4);
|
|
59548
59937
|
}
|
|
59938
|
+
async function exportMp4() {
|
|
59939
|
+
const animation = animId.peek();
|
|
59940
|
+
if (!animation) {
|
|
59941
|
+
setStatus("Select an animation first", "warn");
|
|
59942
|
+
return;
|
|
59943
|
+
}
|
|
59944
|
+
const outFile = prompt("Save MP4 to (path relative to this rig, or absolute)", `../previews/${shown.peek()?.meta.name ?? "rig"}-${animation}.mp4`);
|
|
59945
|
+
if (!outFile) return;
|
|
59946
|
+
setStatus("Rendering MP4 at 60 fps\u2026");
|
|
59947
|
+
try {
|
|
59948
|
+
const r5 = await callTool(rigPath, "rig_export_video", { animation, outFile, fps: 60 });
|
|
59949
|
+
setStatus(r5.text, r5.isError ? "warn" : "info");
|
|
59950
|
+
} catch (e5) {
|
|
59951
|
+
setStatus(String(e5), "warn");
|
|
59952
|
+
}
|
|
59953
|
+
}
|
|
59954
|
+
async function exportPng() {
|
|
59955
|
+
const url = await stage?.exportPng();
|
|
59956
|
+
if (!url) return;
|
|
59957
|
+
const a5 = document.createElement("a");
|
|
59958
|
+
a5.href = url;
|
|
59959
|
+
a5.download = `${shown.peek()?.meta.name ?? "rig"}.png`;
|
|
59960
|
+
a5.click();
|
|
59961
|
+
}
|
|
59549
59962
|
function setMode(m5) {
|
|
59550
59963
|
if (m5 === "animate" && !shown.value?.animations.length) {
|
|
59551
59964
|
setStatus("no animations \u2014 create one in the properties panel", "warn");
|
|
@@ -59772,6 +60185,8 @@ function TopBar() {
|
|
|
59772
60185
|
children: "gif"
|
|
59773
60186
|
}
|
|
59774
60187
|
),
|
|
60188
|
+
/* @__PURE__ */ u4("button", { class: "mp4", onClick: click(() => void exportMp4()), children: "mp4 \xB7 60 fps" }),
|
|
60189
|
+
/* @__PURE__ */ u4("button", { class: "png", title: "transparent PNG of the current pose", onClick: click(() => void exportPng()), children: "png" }),
|
|
59775
60190
|
/* @__PURE__ */ u4("button", { class: "fit", title: "fit the document to the viewport (F)", onClick: click(() => stage?.fit()), children: "fit" }),
|
|
59776
60191
|
/* @__PURE__ */ u4(
|
|
59777
60192
|
"button",
|