@ikijs/editor 0.20.0 → 0.21.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/dist/index.js CHANGED
@@ -1644,7 +1644,10 @@ var ROW_RUN_ROLES = /* @__PURE__ */ new Set([
1644
1644
  "arm_L",
1645
1645
  "arm_R",
1646
1646
  "forearm_pose_L",
1647
- "forearm_pose_R"
1647
+ "forearm_pose_R",
1648
+ "mouth_inner",
1649
+ "lip_lower",
1650
+ "lip_upper"
1648
1651
  ]);
1649
1652
  var REST_HIDDEN_ROLES = /* @__PURE__ */ new Set([
1650
1653
  "forearm_pose_L",
@@ -2257,6 +2260,29 @@ function gridMesh(cols, rows) {
2257
2260
  }
2258
2261
  return { vertices, uvs, indices };
2259
2262
  }
2263
+ function columnMesh(b, grid) {
2264
+ const { xs } = grid;
2265
+ const rows = grid.ys.length - 1;
2266
+ const vertices = [];
2267
+ const uvs = [];
2268
+ for (let r = 0; r <= rows; r++) {
2269
+ for (const x of xs) {
2270
+ const u = (x - b.x0) / bw(b);
2271
+ vertices.push(roundTo(u - 0.5, 1e-5), roundTo(0.5 - r / rows, 1e-5));
2272
+ uvs.push(roundTo(u, 1e-5), roundTo(r / rows, 1e-5));
2273
+ }
2274
+ }
2275
+ const n = xs.length;
2276
+ const indices = [];
2277
+ for (let r = 0; r < rows; r++) {
2278
+ for (let c = 0; c + 1 < n; c++) {
2279
+ const tl = r * n + c;
2280
+ const bl = tl + n;
2281
+ indices.push(bl, bl + 1, tl, tl, bl + 1, tl + 1);
2282
+ }
2283
+ }
2284
+ return { vertices, uvs, indices };
2285
+ }
2260
2286
  function meshPoints(mesh, box) {
2261
2287
  const out = [];
2262
2288
  for (let i = 0; i < mesh.vertices.length; i += 2) {
@@ -2380,10 +2406,13 @@ function browBindings(side, reach) {
2380
2406
  ];
2381
2407
  }
2382
2408
  var BROW_TILT = 15;
2383
- function mouthForm(mesh, box) {
2384
- const a = 0.3 * bh(box);
2385
- const c = cx(box);
2386
- const half = bw(box) / 2;
2409
+ var mouthFrameOf = (box) => ({
2410
+ c: cx(box),
2411
+ half: bw(box) / 2,
2412
+ amp: 0.3 * bh(box)
2413
+ });
2414
+ function mouthForm(mesh, box, frame = mouthFrameOf(box)) {
2415
+ const { c, half, amp: a } = frame;
2387
2416
  return localWarp(import_format3.StandardParameter.MouthForm, mesh, box, [-1, 0, 1], ([x], v) => {
2388
2417
  if (v === 0) return [0, 0];
2389
2418
  const u = (x - c) / half;
@@ -2401,8 +2430,8 @@ function mouthOpenGrow(mesh, box) {
2401
2430
  ([x, y], v) => v === 1 ? [(AMPLITUDE.mouthOpenWidth - 1) * (x - c), 0] : [0, box.y1 + (y - box.y1) * 0.2 - y]
2402
2431
  );
2403
2432
  }
2404
- function mouthWiden(mesh, box) {
2405
- const c = cx(box);
2433
+ function mouthWiden(mesh, box, frame = mouthFrameOf(box)) {
2434
+ const c = frame.c;
2406
2435
  return localWarp(import_format3.StandardParameter.MouthOpen, mesh, box, [0, 1], ([x], v) => [
2407
2436
  v * (AMPLITUDE.mouthOpenWidth - 1) * (x - c),
2408
2437
  0
@@ -3059,6 +3088,8 @@ function forearmPoseDeformer(role, arm) {
3059
3088
  }
3060
3089
 
3061
3090
  // src/auto-rig/roles.ts
3091
+ var LIP_ROLES = ["mouth_inner", "lip_lower", "lip_upper"];
3092
+ var isLipRole = (role) => LIP_ROLES.includes(role);
3062
3093
  var ROLE_TABLE = [
3063
3094
  { role: "hair_back", family: "hair_back" },
3064
3095
  { role: "body", family: "body" },
@@ -3068,8 +3099,14 @@ var ROLE_TABLE = [
3068
3099
  { role: "blush_L", family: "face" },
3069
3100
  { role: "blush_R", family: "face" },
3070
3101
  { role: "nose", family: "nose" },
3071
- { role: "mouth", family: "mouth", required: true },
3102
+ { role: "mouth", family: "mouth" },
3072
3103
  { role: "mouth_open", family: "mouth" },
3104
+ // The lip set, back to front: the interior, the lower lip's skin, the upper
3105
+ // lip and its line over both. It folds open instead of crossfading
3106
+ // (`mouth.ts`), so it stands in for `mouth`, never beside it.
3107
+ { role: "mouth_inner", family: "mouth" },
3108
+ { role: "lip_lower", family: "mouth" },
3109
+ { role: "lip_upper", family: "mouth" },
3073
3110
  { role: "eye_L", family: "eye_L", required: true },
3074
3111
  { role: "eye_R", family: "eye_R", required: true },
3075
3112
  { role: "iris_L", family: "eye_L" },
@@ -3127,6 +3164,29 @@ function checkArmsHaveBody(roles) {
3127
3164
  );
3128
3165
  }
3129
3166
  }
3167
+ function checkMouth(roles) {
3168
+ const has = new Set(roles);
3169
+ const present = LIP_ROLES.filter((r) => has.has(r));
3170
+ if (present.length === 0) {
3171
+ if (!has.has("mouth")) {
3172
+ throw new Error(
3173
+ `auto-rig: missing required role mouth (or the lip set ${LIP_ROLES.join(", ")})`
3174
+ );
3175
+ }
3176
+ return;
3177
+ }
3178
+ if (present.length < LIP_ROLES.length) {
3179
+ throw new Error(
3180
+ `auto-rig: the lip set is partial: missing ${LIP_ROLES.filter((r) => !has.has(r)).join(", ")}`
3181
+ );
3182
+ }
3183
+ const mixed = ["mouth", "mouth_open"].filter((r) => has.has(r));
3184
+ if (mixed.length > 0) {
3185
+ throw new Error(
3186
+ `auto-rig: the lip set cannot be mixed with ${mixed.join(", ")} (the lip set replaces the mouth drawings; use one or the other)`
3187
+ );
3188
+ }
3189
+ }
3130
3190
  function checkPosesHaveArms(roles) {
3131
3191
  const has = new Set(roles);
3132
3192
  for (const side of ["L", "R"]) {
@@ -3162,13 +3222,14 @@ function parseLayerRoles(fileNames) {
3162
3222
  `auto-rig: missing required role${missing.length > 1 ? "s" : ""} ${missing.join(", ")}`
3163
3223
  );
3164
3224
  }
3225
+ checkMouth(seen.keys());
3165
3226
  checkArmsHaveBody(seen.keys());
3166
3227
  checkPosesHaveArms(seen.keys());
3167
3228
  return out;
3168
3229
  }
3169
3230
 
3170
3231
  // src/auto-rig/generate.ts
3171
- var import_format9 = require("@ikijs/format");
3232
+ var import_format10 = require("@ikijs/format");
3172
3233
 
3173
3234
  // src/auto-rig/animations.ts
3174
3235
  var import_format7 = require("@ikijs/format");
@@ -3471,6 +3532,14 @@ function checkLayers(layers, canvas) {
3471
3532
  if (missing.length > 0) {
3472
3533
  throw new Error(`auto-rig: missing required role(s) ${missing.join(", ")}`);
3473
3534
  }
3535
+ checkMouth(seen);
3536
+ for (const l of layers) {
3537
+ if (l.rowRuns === void 0 && isLipRole(l.role)) {
3538
+ throw new Error(
3539
+ `auto-rig: layer "${l.fileName}": the lip set needs rowRuns (the fold reads the opening's rows off them; measure with createLayerSetMeasurer)`
3540
+ );
3541
+ }
3542
+ }
3474
3543
  checkArmsHaveBody(seen);
3475
3544
  checkPosesHaveArms(seen);
3476
3545
  }
@@ -3590,7 +3659,180 @@ function checkStrandEdges(strand, irises, hair) {
3590
3659
  }
3591
3660
 
3592
3661
  // src/auto-rig/context.ts
3662
+ var import_format9 = require("@ikijs/format");
3663
+
3664
+ // src/auto-rig/mouth.ts
3593
3665
  var import_format8 = require("@ikijs/format");
3666
+ var UPPER_SHARE = 0.3;
3667
+ var MOUTH_KNOT_PX = 4;
3668
+ var isLipSet = (byRole) => LIP_ROLES.every((r) => byRole.has(r));
3669
+ function columnRuns(layer) {
3670
+ const cols = /* @__PURE__ */ new Map();
3671
+ (layer.rowRuns ?? []).forEach((runs, k) => {
3672
+ for (let i = 0; i < runs.length; i += 2) {
3673
+ for (let c = runs[i]; c < runs[i + 1]; c++) {
3674
+ const list = cols.get(c) ?? [];
3675
+ const last = list[list.length - 1];
3676
+ if (last !== void 0 && last[1] === k) last[1] = k + 1;
3677
+ else list.push([k, k + 1]);
3678
+ cols.set(c, list);
3679
+ }
3680
+ }
3681
+ });
3682
+ return cols;
3683
+ }
3684
+ var edgesOf = (layer, run) => ({
3685
+ top: layer.canvasH / 2 - (layer.bbox.y + run[0]),
3686
+ bottom: layer.canvasH / 2 - (layer.bbox.y + run[1])
3687
+ });
3688
+ function mouthOpening(byRole) {
3689
+ const inner = byRole.get("mouth_inner");
3690
+ const upper = byRole.get("lip_upper");
3691
+ const lower = byRole.get("lip_lower");
3692
+ const innerCols = columnRuns(inner);
3693
+ if (innerCols.size === 0) {
3694
+ throw new LayerGeometryError(
3695
+ `auto-rig: layer "${inner.fileName}": mouth_inner has no opaque pixel (alpha \u2265 128) to read the opening off`
3696
+ );
3697
+ }
3698
+ const upperCols = columnRuns(upper);
3699
+ const lowerCols = columnRuns(lower);
3700
+ const cols = [...innerCols.keys()];
3701
+ const x0 = Math.min(...cols);
3702
+ const x1 = Math.max(...cols);
3703
+ const read = (col) => {
3704
+ const runs = innerCols.get(col);
3705
+ if (runs === void 0) return void 0;
3706
+ const T = edgesOf(inner, runs[0]).top;
3707
+ const Bb = edgesOf(inner, runs[runs.length - 1]).bottom;
3708
+ const line = [...upperCols.get(col) ?? []].reverse().map((r) => edgesOf(upper, r)).find((e) => e.top >= T);
3709
+ const Tu = line?.bottom ?? T;
3710
+ const lineTop = line?.top ?? T;
3711
+ const skin = lowerCols.get(col)?.[0];
3712
+ return {
3713
+ T,
3714
+ Bb,
3715
+ Tu,
3716
+ lineTop,
3717
+ lineH: lineTop - Tu,
3718
+ Bl: skin === void 0 ? Bb : edgesOf(lower, skin).top,
3719
+ H: T - Bb,
3720
+ seam: T - UPPER_SHARE * (T - Bb)
3721
+ };
3722
+ };
3723
+ const reads = [];
3724
+ for (let c = x0; c <= x1; c++) {
3725
+ let r;
3726
+ for (let d = 0; (r = read(c - d) ?? read(c + d)) === void 0; d++) ;
3727
+ reads.push(r);
3728
+ }
3729
+ const base = (col) => reads[Math.min(x1, Math.max(x0, col)) - x0];
3730
+ const centre = Math.round((x0 + x1) / 2);
3731
+ const w = base(centre).lineH;
3732
+ return {
3733
+ x0,
3734
+ x1,
3735
+ centre,
3736
+ canvasW: inner.canvasW,
3737
+ w,
3738
+ at(col) {
3739
+ const b = base(col);
3740
+ return { ...b, overlap: Math.max(0, Math.min(w, b.lineH)) };
3741
+ }
3742
+ };
3743
+ }
3744
+ function mouthFold(role, opening) {
3745
+ return (col, y, v) => {
3746
+ if (v === 1) return 0;
3747
+ if (role !== "mouth_inner" && (col < opening.x0 || col > opening.x1))
3748
+ return 0;
3749
+ const c = opening.at(col);
3750
+ const k = 1 - v;
3751
+ switch (role) {
3752
+ case "lip_upper":
3753
+ return (c.seam - c.Tu) * k;
3754
+ case "lip_lower":
3755
+ return (c.seam + c.overlap - c.Bl) * k;
3756
+ case "mouth_inner":
3757
+ return (c.seam + c.overlap - y) * k;
3758
+ }
3759
+ };
3760
+ }
3761
+ function columnOfKnot(knot, opening, role) {
3762
+ const boundary = Math.round(knot + opening.canvasW / 2);
3763
+ const col = boundary === opening.x1 + 1 ? opening.x1 : boundary;
3764
+ if (col >= opening.x0 && col <= opening.x1) return col;
3765
+ return role === "mouth_inner" ? Math.min(opening.x1, Math.max(opening.x0, col)) : void 0;
3766
+ }
3767
+ function mouthFoldWarp(role, mesh, xs, box, opening) {
3768
+ const fold = mouthFold(role, opening);
3769
+ return localWarp(import_format8.StandardParameter.MouthOpen, mesh, box, [0, 1], (p, v, i) => {
3770
+ const col = columnOfKnot(xs[i % xs.length], opening, role);
3771
+ return [0, col === void 0 ? 0 : fold(col, p[1], v)];
3772
+ });
3773
+ }
3774
+ function mouthKnots(union, opening) {
3775
+ const half = opening.canvasW / 2;
3776
+ const step = MOUTH_KNOT_PX;
3777
+ const xs = [union.x0];
3778
+ while (xs[xs.length - 1] + step < union.x1 - 0.3 * step)
3779
+ xs.push(xs[xs.length - 1] + step);
3780
+ xs.push(union.x1);
3781
+ const fixed = /* @__PURE__ */ new Set([union.x0, union.x1]);
3782
+ for (const x of [opening.x0 - half, opening.x1 + 1 - half]) {
3783
+ fixed.add(x);
3784
+ if (xs.includes(x)) continue;
3785
+ const near = xs.findIndex((v) => Math.abs(v - x) < 0.3 * step);
3786
+ if (near >= 0 && !fixed.has(xs[near])) xs[near] = x;
3787
+ else xs.push(x);
3788
+ }
3789
+ xs.sort((p, q) => p - q);
3790
+ for (const x of xs) {
3791
+ if (!Number.isInteger(x + half)) {
3792
+ throw new Error(
3793
+ `auto-rig: mouth knot ${x} is not an integer canvas boundary`
3794
+ );
3795
+ }
3796
+ }
3797
+ return xs;
3798
+ }
3799
+ function mouthMesh(box, knots) {
3800
+ const xs = [box.x0, ...knots.filter((k) => k > box.x0 && k < box.x1), box.x1];
3801
+ const rows = cellsFor(bh(box), MOUTH_KNOT_PX, 2, 40);
3802
+ const ys = Array.from(
3803
+ { length: rows + 1 },
3804
+ (_, r) => box.y1 - bh(box) * r / rows
3805
+ );
3806
+ return { mesh: columnMesh(box, { xs, ys }), xs };
3807
+ }
3808
+ function buildMouthRig(byRole) {
3809
+ const opening = mouthOpening(byRole);
3810
+ const union = unionBoxes(LIP_ROLES.map((r) => boxOfLayer(byRole.get(r))));
3811
+ return {
3812
+ opening,
3813
+ frame: mouthFrameOf(union),
3814
+ knots: mouthKnots(union, opening),
3815
+ union
3816
+ };
3817
+ }
3818
+ function mouthAnchor(byRole) {
3819
+ const box = (role) => boxOfLayer(byRole.get(role));
3820
+ if (isLipSet(byRole)) {
3821
+ const opening = mouthOpening(byRole);
3822
+ return {
3823
+ at: {
3824
+ x: cx(box("mouth_inner")),
3825
+ y: opening.at(opening.centre).seam
3826
+ },
3827
+ box: unionBoxes(LIP_ROLES.map(box))
3828
+ };
3829
+ }
3830
+ const m = box("mouth");
3831
+ return {
3832
+ at: { x: cx(m), y: cy(m) },
3833
+ box: byRole.has("mouth_open") ? unionBoxes([m, box("mouth_open")]) : m
3834
+ };
3835
+ }
3594
3836
 
3595
3837
  // src/auto-rig/head.ts
3596
3838
  function outerEdge(runs, axisX, side) {
@@ -3670,7 +3912,7 @@ function buildHeadFrame(layers, opts) {
3670
3912
  return ya === void 0 || yb === void 0 ? void 0 : (ya + yb) / 2;
3671
3913
  };
3672
3914
  const eyeY = pair("iris_L", "iris_R") ?? pair("eye_L", "eye_R");
3673
- const mouthY = centreY("mouth");
3915
+ const mouthY = mouthAnchor(byRole).at.y;
3674
3916
  const rowY = (r) => canvasH / 2 - (faceLayer.bbox.y + r + 0.5);
3675
3917
  const raw = filledProfile(faceLayer.rowHalfWidths);
3676
3918
  const profile = raw === null ? null : smoothProfile(raw);
@@ -4643,7 +4885,6 @@ function sideIrises(byRole) {
4643
4885
  return pair === void 0 ? {} : { left: pair.left.box, right: pair.right.box };
4644
4886
  }
4645
4887
  function solveContext(frame, byRole, options, hasNose) {
4646
- const box = (role) => boxOfLayer(byRole.get(role));
4647
4888
  const row = (s, strand2, sideSign) => {
4648
4889
  if (strand2 !== void 0) {
4649
4890
  const lo = sideSign < 0 ? strand2.irisOuter : strand2.irisInner;
@@ -4692,8 +4933,7 @@ function solveContext(frame, byRole, options, hasNose) {
4692
4933
  noseBox = nose.denseCore ? boxOfPixels(nose.denseCore, nose.canvasW, nose.canvasH) : boxOfLayer(nose);
4693
4934
  noseAt = { x: cx(noseBox), y: cy(noseBox) };
4694
4935
  }
4695
- const mouthBox = byRole.has("mouth_open") ? unionBoxes([box("mouth"), box("mouth_open")]) : box("mouth");
4696
- const m = box("mouth");
4936
+ const mouth = mouthAnchor(byRole);
4697
4937
  return {
4698
4938
  frame,
4699
4939
  left,
@@ -4705,8 +4945,8 @@ function solveContext(frame, byRole, options, hasNose) {
4705
4945
  eyeBoxes: { left: eyes.left.box, right: eyes.right.box },
4706
4946
  noseAt,
4707
4947
  noseBox,
4708
- mouthAt: { x: cx(m), y: cy(m) },
4709
- mouthBox
4948
+ mouthAt: mouth.at,
4949
+ mouthBox: mouth.box
4710
4950
  };
4711
4951
  }
4712
4952
  function renderedLander(turn, parts, grids, from = /* @__PURE__ */ new Map()) {
@@ -4722,7 +4962,7 @@ function renderedLander(turn, parts, grids, from = /* @__PURE__ */ new Map()) {
4722
4962
  const family = roleSpec(role).family;
4723
4963
  const part = parts.find((p) => p.id === role);
4724
4964
  const g = grids.get(family);
4725
- const warp = part?.warps?.find((w) => w.parameter === import_format8.StandardParameter.AngleX);
4965
+ const warp = part?.warps?.find((w) => w.parameter === import_format9.StandardParameter.AngleX);
4726
4966
  if (part === void 0 || part.mesh === void 0 || !g && !warp) {
4727
4967
  return x + field(role)(x, y, ax, 0)[0];
4728
4968
  }
@@ -5519,6 +5759,7 @@ function generateIkiFromLayerSet(layers, canvas, options = {}) {
5519
5759
  hh: frame.hh,
5520
5760
  pivots: [...arms.values()].map((g) => g.shoulder)
5521
5761
  }) : void 0;
5762
+ const lips = isLipSet(byRole) ? buildMouthRig(byRole) : void 0;
5522
5763
  const parts = [];
5523
5764
  const reach = /* @__PURE__ */ new Map();
5524
5765
  let regionOf = [];
@@ -5550,7 +5791,8 @@ function generateIkiFromLayerSet(layers, canvas, options = {}) {
5550
5791
  frame,
5551
5792
  byRole,
5552
5793
  style,
5553
- landmarks.hairFrontGrid
5794
+ landmarks.hairFrontGrid,
5795
+ lips
5554
5796
  );
5555
5797
  if (built.region !== void 0) {
5556
5798
  regionOf = built.region;
@@ -5607,7 +5849,7 @@ function generateIkiFromLayerSet(layers, canvas, options = {}) {
5607
5849
  }
5608
5850
  const parameters = declareParameters(new Set(byRole.keys()));
5609
5851
  const model = {
5610
- version: import_format9.IKI_FORMAT_VERSION,
5852
+ version: import_format10.IKI_FORMAT_VERSION,
5611
5853
  name: "auto-rigged",
5612
5854
  canvas: { width: canvas.width, height: canvas.height },
5613
5855
  parameters,
@@ -5617,7 +5859,7 @@ function generateIkiFromLayerSet(layers, canvas, options = {}) {
5617
5859
  expressions: defaultExpressions(new Set(parameters.map((p) => p.id))),
5618
5860
  motions: defaultMotions(new Set(parameters.map((p) => p.id)))
5619
5861
  };
5620
- const valid = (0, import_format9.parseIkiModel)(model);
5862
+ const valid = (0, import_format10.parseIkiModel)(model);
5621
5863
  if (hasNose && options.onTurnSolved) {
5622
5864
  options.onTurnSolved(
5623
5865
  turnReport(
@@ -5632,14 +5874,14 @@ function generateIkiFromLayerSet(layers, canvas, options = {}) {
5632
5874
  function turnWarps(mesh, b, fieldOf) {
5633
5875
  return [
5634
5876
  localWarp(
5635
- import_format9.StandardParameter.AngleX,
5877
+ import_format10.StandardParameter.AngleX,
5636
5878
  mesh,
5637
5879
  b,
5638
5880
  X_STOPS,
5639
5881
  ([x, y], v, i) => v === 0 ? [0, 0] : [fieldOf(i)(x, y, v, 0)[0], 0]
5640
5882
  ),
5641
5883
  localWarp(
5642
- import_format9.StandardParameter.AngleY,
5884
+ import_format10.StandardParameter.AngleY,
5643
5885
  mesh,
5644
5886
  b,
5645
5887
  Y_STOPS,
@@ -5652,7 +5894,7 @@ function plateFields(turn, region, head) {
5652
5894
  const neck = neckField(turn);
5653
5895
  return (i) => region[i] === "ear" ? ear : region[i] === "neck" ? neck : head;
5654
5896
  }
5655
- function buildPart(spec, b, order, frame, byRole, style, frontGrid) {
5897
+ function buildPart(spec, b, order, frame, byRole, style, frontGrid, lips) {
5656
5898
  const box = (role) => boxOfLayer(byRole.get(role));
5657
5899
  const part = {
5658
5900
  id: spec.role,
@@ -5667,6 +5909,7 @@ function buildPart(spec, b, order, frame, byRole, style, frontGrid) {
5667
5909
  const bindings = [];
5668
5910
  let extra = [0, 0];
5669
5911
  let mesh;
5912
+ let knotXs;
5670
5913
  let region;
5671
5914
  let headStart = 0;
5672
5915
  if (spec.role === "face") {
@@ -5677,6 +5920,8 @@ function buildPart(spec, b, order, frame, byRole, style, frontGrid) {
5677
5920
  headStart = fm.headStart;
5678
5921
  } else if (spec.role === "hair_front") {
5679
5922
  mesh = columnMesh(b, frontGrid);
5923
+ } else if (lips !== void 0 && isLipRole(spec.role)) {
5924
+ ({ mesh, xs: knotXs } = mouthMesh(b, lips.knots));
5680
5925
  } else if (spec.family !== "body") {
5681
5926
  const cells = MESH_CELLS[spec.role] ?? FEATURE_MESH_CELLS;
5682
5927
  const px = Math.min(cells.px, meshScale(spec.role, frame));
@@ -5687,7 +5932,7 @@ function buildPart(spec, b, order, frame, byRole, style, frontGrid) {
5687
5932
  }
5688
5933
  if (mesh !== void 0) part.mesh = mesh;
5689
5934
  const side = spec.role.endsWith("_L") ? "L" : "R";
5690
- const eyeOpen = side === "L" ? import_format9.StandardParameter.EyeOpenLeft : import_format9.StandardParameter.EyeOpenRight;
5935
+ const eyeOpen = side === "L" ? import_format10.StandardParameter.EyeOpenLeft : import_format10.StandardParameter.EyeOpenRight;
5691
5936
  const hh = frame.hh;
5692
5937
  switch (spec.role) {
5693
5938
  case "face":
@@ -5702,7 +5947,7 @@ function buildPart(spec, b, order, frame, byRole, style, frontGrid) {
5702
5947
  headOwnRoll(byRole.has("body"))
5703
5948
  ),
5704
5949
  localWarp(
5705
- import_format9.StandardParameter.Breath,
5950
+ import_format10.StandardParameter.Breath,
5706
5951
  mesh,
5707
5952
  b,
5708
5953
  [0, 1],
@@ -5749,8 +5994,8 @@ function buildPart(spec, b, order, frame, byRole, style, frontGrid) {
5749
5994
  if (byRole.has("mouth_open")) {
5750
5995
  warps.push(mouthWiden(mesh, b));
5751
5996
  bindings.push(
5752
- { parameter: import_format9.StandardParameter.MouthOpen, channel: "opacity", from: 1, to: 0 },
5753
- { parameter: import_format9.StandardParameter.MouthOpen, channel: "opacity", from: 1, to: 0 }
5997
+ { parameter: import_format10.StandardParameter.MouthOpen, channel: "opacity", from: 1, to: 0 },
5998
+ { parameter: import_format10.StandardParameter.MouthOpen, channel: "opacity", from: 1, to: 0 }
5754
5999
  );
5755
6000
  extra = [(AMPLITUDE.mouthOpenWidth - 1) * (bw(b) / 2), 0];
5756
6001
  } else {
@@ -5760,19 +6005,19 @@ function buildPart(spec, b, order, frame, byRole, style, frontGrid) {
5760
6005
  );
5761
6006
  bindings.push(
5762
6007
  {
5763
- parameter: import_format9.StandardParameter.MouthOpen,
6008
+ parameter: import_format10.StandardParameter.MouthOpen,
5764
6009
  channel: "scaleY",
5765
6010
  from: 0,
5766
6011
  to: roundTo(grow, 0.01)
5767
6012
  },
5768
6013
  {
5769
- parameter: import_format9.StandardParameter.MouthOpen,
6014
+ parameter: import_format10.StandardParameter.MouthOpen,
5770
6015
  channel: "scaleX",
5771
6016
  from: 0,
5772
6017
  to: roundTo(AMPLITUDE.mouthOpenWidth - 1, 0.01)
5773
6018
  },
5774
6019
  {
5775
- parameter: import_format9.StandardParameter.MouthOpen,
6020
+ parameter: import_format10.StandardParameter.MouthOpen,
5776
6021
  channel: "translateY",
5777
6022
  from: 0,
5778
6023
  to: -roundTo(grow * bh(b) / 2, 0.01)
@@ -5784,17 +6029,26 @@ function buildPart(spec, b, order, frame, byRole, style, frontGrid) {
5784
6029
  case "mouth_open":
5785
6030
  warps.push(mouthForm(mesh, b), mouthOpenGrow(mesh, b));
5786
6031
  bindings.push({
5787
- parameter: import_format9.StandardParameter.MouthOpen,
6032
+ parameter: import_format10.StandardParameter.MouthOpen,
5788
6033
  channel: "opacity",
5789
6034
  from: 0,
5790
6035
  to: 1
5791
6036
  });
5792
6037
  extra = [(AMPLITUDE.mouthOpenWidth - 1) * (bw(b) / 2), 0];
5793
6038
  break;
6039
+ case "mouth_inner":
6040
+ case "lip_lower":
6041
+ case "lip_upper":
6042
+ warps.push(
6043
+ mouthFoldWarp(spec.role, mesh, knotXs, b, lips.opening),
6044
+ mouthForm(mesh, b, lips.frame),
6045
+ mouthWiden(mesh, b, lips.frame)
6046
+ );
6047
+ break;
5794
6048
  case "blush_L":
5795
6049
  case "blush_R":
5796
6050
  bindings.push({
5797
- parameter: import_format9.StandardParameter.Cheek,
6051
+ parameter: import_format10.StandardParameter.Cheek,
5798
6052
  channel: "opacity",
5799
6053
  from: AMPLITUDE.blushRest,
5800
6054
  to: 1
@@ -5805,8 +6059,8 @@ function buildPart(spec, b, order, frame, byRole, style, frontGrid) {
5805
6059
  if (byRole.has("hair_front")) {
5806
6060
  const amp = style.sway * AMPLITUDE.sway * hh;
5807
6061
  warps.push(
5808
- hairSway(import_format9.StandardParameter.HairSwayX, mesh, b, amp),
5809
- hairSway(import_format9.StandardParameter.HairSwayZ, mesh, b, amp)
6062
+ hairSway(import_format10.StandardParameter.HairSwayX, mesh, b, amp),
6063
+ hairSway(import_format10.StandardParameter.HairSwayZ, mesh, b, amp)
5810
6064
  );
5811
6065
  }
5812
6066
  warps.push(tiltHang(mesh, b, pivot, hairHangWeight(frame.chinY, b.y0)));
@@ -5847,29 +6101,6 @@ function hairFrontGrid(b, frame, boxes) {
5847
6101
  );
5848
6102
  return { xs, ys };
5849
6103
  }
5850
- function columnMesh(b, grid) {
5851
- const { xs } = grid;
5852
- const rows = grid.ys.length - 1;
5853
- const vertices = [];
5854
- const uvs = [];
5855
- for (let r = 0; r <= rows; r++) {
5856
- for (const x of xs) {
5857
- const u = (x - b.x0) / bw(b);
5858
- vertices.push(roundTo(u - 0.5, 1e-5), roundTo(0.5 - r / rows, 1e-5));
5859
- uvs.push(roundTo(u, 1e-5), roundTo(r / rows, 1e-5));
5860
- }
5861
- }
5862
- const n = xs.length;
5863
- const indices = [];
5864
- for (let r = 0; r < rows; r++) {
5865
- for (let c = 0; c + 1 < n; c++) {
5866
- const tl = r * n + c;
5867
- const bl = tl + n;
5868
- indices.push(bl, bl + 1, tl, tl, bl + 1, tl + 1);
5869
- }
5870
- }
5871
- return { vertices, uvs, indices };
5872
- }
5873
6104
  function meshScale(role, frame) {
5874
6105
  switch (role) {
5875
6106
  case "hair_front":
@@ -5895,9 +6126,9 @@ function deformers(frame, grids, bodyWarp, arms, byRole) {
5895
6126
  pivot: { x: roundTo(frame.axisX, 0.01), y: roundTo(frame.chinY, 0.01) },
5896
6127
  bindings: [
5897
6128
  // AngleZ is clockwise-positive (Live2D's); a rotation is CCW-positive.
5898
- { parameter: import_format9.StandardParameter.AngleZ, channel: "rotate", from: roll, to: -roll },
6129
+ { parameter: import_format10.StandardParameter.AngleZ, channel: "rotate", from: roll, to: -roll },
5899
6130
  {
5900
- parameter: import_format9.StandardParameter.Breath,
6131
+ parameter: import_format10.StandardParameter.Breath,
5901
6132
  channel: "translateY",
5902
6133
  from: 0,
5903
6134
  to: roundTo(headOwnBreath(hasBody) * frame.hh, 0.01)
@@ -5915,8 +6146,8 @@ function deformers(frame, grids, bodyWarp, arms, byRole) {
5915
6146
  points: g.lattice.points
5916
6147
  },
5917
6148
  warp2d: {
5918
- parameter: import_format9.StandardParameter.AngleX,
5919
- parameterY: import_format9.StandardParameter.AngleY,
6149
+ parameter: import_format10.StandardParameter.AngleX,
6150
+ parameterY: import_format10.StandardParameter.AngleY,
5920
6151
  valuesX: X_STOPS,
5921
6152
  valuesY: Y_STOPS,
5922
6153
  keyforms2d: g.keyforms.map((offsets) => ({ offsets }))
@@ -5929,16 +6160,16 @@ function hairPhysics() {
5929
6160
  return [
5930
6161
  {
5931
6162
  id: "hairSway",
5932
- input: { parameter: import_format9.StandardParameter.AngleX, weight: 1 },
5933
- output: { parameter: import_format9.StandardParameter.HairSwayX, scale: 5 },
6163
+ input: { parameter: import_format10.StandardParameter.AngleX, weight: 1 },
6164
+ output: { parameter: import_format10.StandardParameter.HairSwayX, scale: 5 },
5934
6165
  mass: 1,
5935
6166
  stiffness: 30,
5936
6167
  damping: 3
5937
6168
  },
5938
6169
  {
5939
6170
  id: "hairTilt",
5940
- input: { parameter: import_format9.StandardParameter.AngleZ, weight: 1 },
5941
- output: { parameter: import_format9.StandardParameter.HairSwayZ, scale: 8 },
6171
+ input: { parameter: import_format10.StandardParameter.AngleZ, weight: 1 },
6172
+ output: { parameter: import_format10.StandardParameter.HairSwayZ, scale: 8 },
5942
6173
  mass: 1,
5943
6174
  stiffness: 25,
5944
6175
  damping: 2.5
@@ -5948,13 +6179,13 @@ function hairPhysics() {
5948
6179
  function declareParameters(roles) {
5949
6180
  const out = [];
5950
6181
  const add = (id, name, min, max, def) => out.push({ id, name, min, max, default: def });
5951
- add(import_format9.StandardParameter.AngleX, "Head Angle", -30, 30, 0);
5952
- add(import_format9.StandardParameter.AngleY, "Head Angle Y", -30, 30, 0);
5953
- add(import_format9.StandardParameter.AngleZ, "Head Angle Z", -30, 30, 0);
6182
+ add(import_format10.StandardParameter.AngleX, "Head Angle", -30, 30, 0);
6183
+ add(import_format10.StandardParameter.AngleY, "Head Angle Y", -30, 30, 0);
6184
+ add(import_format10.StandardParameter.AngleZ, "Head Angle Z", -30, 30, 0);
5954
6185
  if (roles.has("body")) {
5955
- add(import_format9.StandardParameter.BodyAngleX, "Body Angle X", -10, 10, 0);
5956
- add(import_format9.StandardParameter.BodyAngleY, "Body Angle Y", -10, 10, 0);
5957
- add(import_format9.StandardParameter.BodyAngleZ, "Body Angle Z", -10, 10, 0);
6186
+ add(import_format10.StandardParameter.BodyAngleX, "Body Angle X", -10, 10, 0);
6187
+ add(import_format10.StandardParameter.BodyAngleY, "Body Angle Y", -10, 10, 0);
6188
+ add(import_format10.StandardParameter.BodyAngleZ, "Body Angle Z", -10, 10, 0);
5958
6189
  }
5959
6190
  const addPose = (side, pose, angle) => {
5960
6191
  add(pose, `Arm Pose ${side}`, 0, 1, 0);
@@ -5963,40 +6194,40 @@ function declareParameters(roles) {
5963
6194
  const [armMin, armMax] = ARM_RANGE;
5964
6195
  const [elbowMin, elbowMax] = ELBOW_RANGE;
5965
6196
  if (roles.has("arm_L")) {
5966
- add(import_format9.StandardParameter.ArmLeft, "Arm L", armMin, armMax, 0);
5967
- add(import_format9.StandardParameter.ElbowLeft, "Elbow L", elbowMin, elbowMax, 0);
6197
+ add(import_format10.StandardParameter.ArmLeft, "Arm L", armMin, armMax, 0);
6198
+ add(import_format10.StandardParameter.ElbowLeft, "Elbow L", elbowMin, elbowMax, 0);
5968
6199
  if (roles.has("forearm_pose_L"))
5969
- addPose("L", import_format9.StandardParameter.ArmPoseLeft, import_format9.StandardParameter.ArmPoseAngleLeft);
6200
+ addPose("L", import_format10.StandardParameter.ArmPoseLeft, import_format10.StandardParameter.ArmPoseAngleLeft);
5970
6201
  }
5971
6202
  if (roles.has("arm_R")) {
5972
- add(import_format9.StandardParameter.ArmRight, "Arm R", armMin, armMax, 0);
5973
- add(import_format9.StandardParameter.ElbowRight, "Elbow R", elbowMin, elbowMax, 0);
6203
+ add(import_format10.StandardParameter.ArmRight, "Arm R", armMin, armMax, 0);
6204
+ add(import_format10.StandardParameter.ElbowRight, "Elbow R", elbowMin, elbowMax, 0);
5974
6205
  if (roles.has("forearm_pose_R"))
5975
- addPose("R", import_format9.StandardParameter.ArmPoseRight, import_format9.StandardParameter.ArmPoseAngleRight);
6206
+ addPose("R", import_format10.StandardParameter.ArmPoseRight, import_format10.StandardParameter.ArmPoseAngleRight);
5976
6207
  }
5977
- add(import_format9.StandardParameter.EyeOpenLeft, "Eye L", 0, 1, 1);
5978
- add(import_format9.StandardParameter.EyeOpenRight, "Eye R", 0, 1, 1);
6208
+ add(import_format10.StandardParameter.EyeOpenLeft, "Eye L", 0, 1, 1);
6209
+ add(import_format10.StandardParameter.EyeOpenRight, "Eye R", 0, 1, 1);
5979
6210
  if ([...roles].some((r) => /^(iris|pupil|highlight)_/.test(r))) {
5980
- add(import_format9.StandardParameter.EyeballX, "Gaze X", -1, 1, 0);
5981
- add(import_format9.StandardParameter.EyeballY, "Gaze Y", -1, 1, 0);
6211
+ add(import_format10.StandardParameter.EyeballX, "Gaze X", -1, 1, 0);
6212
+ add(import_format10.StandardParameter.EyeballY, "Gaze Y", -1, 1, 0);
5982
6213
  }
5983
- add(import_format9.StandardParameter.MouthOpen, "Mouth Open", 0, 1, 0);
5984
- add(import_format9.StandardParameter.MouthForm, "Mouth Form", -1, 1, 0);
6214
+ add(import_format10.StandardParameter.MouthOpen, "Mouth Open", 0, 1, 0);
6215
+ add(import_format10.StandardParameter.MouthForm, "Mouth Form", -1, 1, 0);
5985
6216
  if (roles.has("brow_L")) {
5986
- add(import_format9.StandardParameter.BrowLeftY, "Brow L Y", -1, 1, 0);
5987
- add(import_format9.StandardParameter.BrowLeftAngle, "Brow L Angle", -1, 1, 0);
6217
+ add(import_format10.StandardParameter.BrowLeftY, "Brow L Y", -1, 1, 0);
6218
+ add(import_format10.StandardParameter.BrowLeftAngle, "Brow L Angle", -1, 1, 0);
5988
6219
  }
5989
6220
  if (roles.has("brow_R")) {
5990
- add(import_format9.StandardParameter.BrowRightY, "Brow R Y", -1, 1, 0);
5991
- add(import_format9.StandardParameter.BrowRightAngle, "Brow R Angle", -1, 1, 0);
6221
+ add(import_format10.StandardParameter.BrowRightY, "Brow R Y", -1, 1, 0);
6222
+ add(import_format10.StandardParameter.BrowRightAngle, "Brow R Angle", -1, 1, 0);
5992
6223
  }
5993
6224
  if (roles.has("blush_L") || roles.has("blush_R")) {
5994
- add(import_format9.StandardParameter.Cheek, "Cheek", 0, 1, 0);
6225
+ add(import_format10.StandardParameter.Cheek, "Cheek", 0, 1, 0);
5995
6226
  }
5996
- add(import_format9.StandardParameter.Breath, "Breath", 0, 1, 0);
6227
+ add(import_format10.StandardParameter.Breath, "Breath", 0, 1, 0);
5997
6228
  if (roles.has("hair_front")) {
5998
- add(import_format9.StandardParameter.HairSwayX, "Hair Sway X", -SWAY_RANGE, SWAY_RANGE, 0);
5999
- add(import_format9.StandardParameter.HairSwayZ, "Hair Sway Z", -SWAY_RANGE, SWAY_RANGE, 0);
6229
+ add(import_format10.StandardParameter.HairSwayX, "Hair Sway X", -SWAY_RANGE, SWAY_RANGE, 0);
6230
+ add(import_format10.StandardParameter.HairSwayZ, "Hair Sway Z", -SWAY_RANGE, SWAY_RANGE, 0);
6000
6231
  }
6001
6232
  return out;
6002
6233
  }