@ikijs/editor 0.24.1 → 0.25.1

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
@@ -36,6 +36,8 @@ __export(index_exports, {
36
36
  LIP_INSIDE_ROLES: () => LIP_INSIDE_ROLES,
37
37
  LIP_ROLES: () => LIP_ROLES,
38
38
  LayerGeometryError: () => LayerGeometryError,
39
+ PatchError: () => PatchError,
40
+ SHUT_BY: () => SHUT_BY,
39
41
  SPECK_CORE_FRACTION: () => SPECK_CORE_FRACTION,
40
42
  SetDeformerBindings: () => SetDeformerBindings,
41
43
  SetDeformerParent: () => SetDeformerParent,
@@ -1560,8 +1562,8 @@ function foregroundSpan(mask, width, height, rowLo, rowHi) {
1560
1562
  }
1561
1563
  return { left, right };
1562
1564
  }
1563
- function headHalfOf(span) {
1564
- return (span.right - span.left + 1) / 2;
1565
+ function headHalfOf(span2) {
1566
+ return (span2.right - span2.left + 1) / 2;
1565
1567
  }
1566
1568
  var DARK_SHARE = 0.55;
1567
1569
  function jawRowsOf(rgba, canvasW, bbox, rowLeft, rowRight) {
@@ -1571,9 +1573,9 @@ function jawRowsOf(rgba, canvasW, bbox, rowLeft, rowRight) {
1571
1573
  let widest = bbox.y;
1572
1574
  let widestSpan = -1;
1573
1575
  for (let y = bbox.y; y < bbox.y + bbox.h; y++) {
1574
- const span = rowRight[y] - rowLeft[y];
1575
- if (span >= widestSpan) {
1576
- widestSpan = span;
1576
+ const span2 = rowRight[y] - rowLeft[y];
1577
+ if (span2 >= widestSpan) {
1578
+ widestSpan = span2;
1577
1579
  widest = y;
1578
1580
  }
1579
1581
  for (let x = bbox.x; x < bbox.x + bbox.w; x++) {
@@ -1742,14 +1744,14 @@ function createLayerSetMeasurer(canvas) {
1742
1744
  }
1743
1745
  function finish() {
1744
1746
  const eyeRow = eyeBandRowOf(layerInputs);
1745
- const span = foregroundSpan(
1747
+ const span2 = foregroundSpan(
1746
1748
  opaque,
1747
1749
  canvasW,
1748
1750
  canvasH,
1749
1751
  eyeRow - HEAD_BAND,
1750
1752
  eyeRow + HEAD_BAND
1751
1753
  );
1752
- const headHalfWidth = span.right > span.left ? headHalfOf(span) : void 0;
1754
+ const headHalfWidth = span2.right > span2.left ? headHalfOf(span2) : void 0;
1753
1755
  const headHalfWidthApplied = headHalfWidth !== void 0 && headHalfWidth > facePlateHalfOf(layerInputs);
1754
1756
  let headEdges;
1755
1757
  if (headHalfWidthApplied) {
@@ -1777,9 +1779,9 @@ function createLayerSetMeasurer(canvas) {
1777
1779
  const centre = iris.bbox.x + iris.bbox.w / 2;
1778
1780
  const otherX = other.bbox.x + other.bbox.w / 2 - half;
1779
1781
  const c = side < 0 ? Math.ceil(centre) - 1 : Math.floor(centre);
1780
- const span2 = rowSpansByRole.find((s) => s.role === iris.role);
1781
- if (span2.rowRight[r] < span2.rowLeft[r]) return void 0;
1782
- const minRun = STRAND_MIN_RUN_FRACTION * (span2.rowRight[r] + 1 - span2.rowLeft[r]);
1782
+ const span3 = rowSpansByRole.find((s) => s.role === iris.role);
1783
+ if (span3.rowRight[r] < span3.rowLeft[r]) return void 0;
1784
+ const minRun = STRAND_MIN_RUN_FRACTION * (span3.rowRight[r] + 1 - span3.rowLeft[r]);
1783
1785
  const strand = new Uint8Array(canvasW);
1784
1786
  const runs = r >= hairTop && r < hairTop + hairRuns.length ? hairRuns[r - hairTop] : [];
1785
1787
  for (let k = 0; k < runs.length; k += 2) {
@@ -1804,8 +1806,8 @@ function createLayerSetMeasurer(canvas) {
1804
1806
  while (strandAt(x)) x += side;
1805
1807
  return {
1806
1808
  y: canvasH / 2 - (r + 0.5),
1807
- irisOuter: (side < 0 ? span2.rowLeft[r] : span2.rowRight[r] + 1) - half,
1808
- irisInner: (side < 0 ? span2.rowRight[r] + 1 : span2.rowLeft[r]) - half,
1809
+ irisOuter: (side < 0 ? span3.rowLeft[r] : span3.rowRight[r] + 1) - half,
1810
+ irisInner: (side < 0 ? span3.rowRight[r] + 1 : span3.rowLeft[r]) - half,
1809
1811
  runOuter: outerBoundary(x - side),
1810
1812
  runFace
1811
1813
  };
@@ -2209,6 +2211,12 @@ var DEFAULT_TURN_TARGETS = Object.freeze({
2209
2211
  farEyeRatio: round2(TURN.eyeFarScale / TURN.eyeNearScale),
2210
2212
  silhouetteRatio: 1
2211
2213
  });
2214
+ var PatchError = class extends Error {
2215
+ constructor(message) {
2216
+ super(message);
2217
+ this.name = "PatchError";
2218
+ }
2219
+ };
2212
2220
 
2213
2221
  // src/auto-rig/arms.ts
2214
2222
  var import_format5 = require("@ikijs/format");
@@ -2279,15 +2287,16 @@ function gridMesh(cols, rows) {
2279
2287
  return { vertices, uvs, indices };
2280
2288
  }
2281
2289
  function columnMesh(b, grid) {
2282
- const { xs } = grid;
2283
- const rows = grid.ys.length - 1;
2290
+ const { xs, ys } = grid;
2291
+ const rows = ys.length - 1;
2284
2292
  const vertices = [];
2285
2293
  const uvs = [];
2286
2294
  for (let r = 0; r <= rows; r++) {
2295
+ const v = (ys[0] - ys[r]) / (ys[0] - ys[rows]);
2287
2296
  for (const x of xs) {
2288
2297
  const u = (x - b.x0) / bw(b);
2289
- vertices.push(roundTo(u - 0.5, 1e-5), roundTo(0.5 - r / rows, 1e-5));
2290
- uvs.push(roundTo(u, 1e-5), roundTo(r / rows, 1e-5));
2298
+ vertices.push(roundTo(u - 0.5, 1e-5), roundTo(0.5 - v, 1e-5));
2299
+ uvs.push(roundTo(u, 1e-5), roundTo(v, 1e-5));
2291
2300
  }
2292
2301
  }
2293
2302
  const n = xs.length;
@@ -2834,7 +2843,7 @@ var ARM_IDS = {
2834
2843
  };
2835
2844
  function endPivot(layer, end) {
2836
2845
  const { bbox, canvasW, canvasH, rowRuns } = layer;
2837
- const span = runSpan(layer);
2846
+ const span2 = runSpan(layer);
2838
2847
  const from = (k) => end === "top" ? k : bbox.h - 1 - k;
2839
2848
  const painted = rowRuns?.flatMap((runs, k) => runs.length > 0 ? [k] : []) ?? [];
2840
2849
  const firstK = end === "top" ? painted[0] ?? 0 : painted.at(-1) ?? bbox.h - 1;
@@ -2842,13 +2851,13 @@ function endPivot(layer, end) {
2842
2851
  const hi = Math.max(lo, Math.floor(CAP_ROWS[1] * bbox.h));
2843
2852
  const widths = [];
2844
2853
  for (let k = lo; k <= hi; k++) {
2845
- const [s2, e2] = span(from(k));
2854
+ const [s2, e2] = span2(from(k));
2846
2855
  widths.push(e2 - s2);
2847
2856
  }
2848
2857
  widths.sort((p, q) => p - q);
2849
2858
  const radius = widths[widths.length >> 1] / 2;
2850
2859
  const ks = end === "top" ? firstK + radius : firstK - radius;
2851
- const [s, e] = span(Math.round(ks));
2860
+ const [s, e] = span2(Math.round(ks));
2852
2861
  return {
2853
2862
  point: {
2854
2863
  x: roundTo((s + e) / 2 - canvasW / 2, 0.01),
@@ -2873,9 +2882,9 @@ function runSpan(layer) {
2873
2882
  function armGeometry(layer, bodyAxisX) {
2874
2883
  const { bbox, canvasW, canvasH, rowRuns } = layer;
2875
2884
  const b = boxOfLayer(layer);
2876
- const span = runSpan(layer);
2885
+ const span2 = runSpan(layer);
2877
2886
  const centreX = (k) => {
2878
- const [s2, e2] = span(k);
2887
+ const [s2, e2] = span2(k);
2879
2888
  return (s2 + e2) / 2 - canvasW / 2;
2880
2889
  };
2881
2890
  const rowY = (k) => canvasH / 2 - (bbox.y + k + 0.5);
@@ -2889,7 +2898,7 @@ function armGeometry(layer, bodyAxisX) {
2889
2898
  );
2890
2899
  }
2891
2900
  const elbow = { x: roundTo(centreX(ke), 0.01), y: roundTo(rowY(ke), 0.01) };
2892
- const [s, e] = span(ke);
2901
+ const [s, e] = span2(ke);
2893
2902
  const capRadius = Math.min(
2894
2903
  (e - s) / 2 + 1,
2895
2904
  elbow.x - b.x0,
@@ -2899,7 +2908,7 @@ function armGeometry(layer, bodyAxisX) {
2899
2908
  const inside = (x, y) => {
2900
2909
  const k = Math.floor(y) - bbox.y;
2901
2910
  if (k < 0 || k >= bbox.h) return false;
2902
- const [rs, re] = span(k);
2911
+ const [rs, re] = span2(k);
2903
2912
  return rs < x && x < re;
2904
2913
  };
2905
2914
  const capRays = Array.from({ length: CAP_SEGMENTS + 1 }, (_, i) => {
@@ -3106,13 +3115,24 @@ function forearmPoseDeformer(role, arm) {
3106
3115
  }
3107
3116
 
3108
3117
  // src/auto-rig/mouth.ts
3109
- var import_format7 = require("@ikijs/format");
3118
+ var import_format8 = require("@ikijs/format");
3110
3119
 
3111
3120
  // src/auto-rig/roles.ts
3121
+ var import_format7 = require("@ikijs/format");
3112
3122
  var LIP_ROLES = ["mouth_inner", "lip_lower", "lip_upper"];
3113
3123
  var isLipRole = (role) => LIP_ROLES.includes(role);
3114
3124
  var LIP_INSIDE_ROLES = ["mouth_tongue", "mouth_teeth"];
3115
3125
  var isLipInsideRole = (role) => LIP_INSIDE_ROLES.includes(role);
3126
+ var SHUT_BY = new Map([
3127
+ ["eye_L", import_format7.StandardParameter.EyeOpenLeft],
3128
+ ["lash_lower_L", import_format7.StandardParameter.EyeOpenLeft],
3129
+ ["eye_R", import_format7.StandardParameter.EyeOpenRight],
3130
+ ["lash_lower_R", import_format7.StandardParameter.EyeOpenRight],
3131
+ ...[...LIP_ROLES, ...LIP_INSIDE_ROLES].map((id) => [
3132
+ id,
3133
+ import_format7.StandardParameter.MouthOpen
3134
+ ])
3135
+ ]);
3116
3136
  var ROLE_TABLE = [
3117
3137
  { role: "hair_back", family: "hair_back" },
3118
3138
  { role: "body", family: "body" },
@@ -3457,7 +3477,7 @@ function columnOfKnot(knot, opening) {
3457
3477
  function mouthFoldWarp(role, mesh, xs, box, opening) {
3458
3478
  const fold = mouthFold(role, opening);
3459
3479
  return localWarp(
3460
- import_format7.StandardParameter.MouthOpen,
3480
+ import_format8.StandardParameter.MouthOpen,
3461
3481
  mesh,
3462
3482
  box,
3463
3483
  [0, 1],
@@ -3466,13 +3486,13 @@ function mouthFoldWarp(role, mesh, xs, box, opening) {
3466
3486
  }
3467
3487
  function mouthRestShift(byRole) {
3468
3488
  const rig = buildMouthRig(byRole);
3469
- const fields = /* @__PURE__ */ new Map();
3489
+ const fields2 = /* @__PURE__ */ new Map();
3470
3490
  for (const role of ["lip_upper", "lip_lower"]) {
3471
3491
  const box = boxOfLayer(byRole.get(role));
3472
3492
  const { mesh, xs } = mouthMesh(box, rig.knots);
3473
3493
  const warp = mouthFoldWarp(role, mesh, xs, box, rig.opening);
3474
3494
  const offsets = warp.keyforms[0].offsets;
3475
- fields.set(role, {
3495
+ fields2.set(role, {
3476
3496
  box,
3477
3497
  xs,
3478
3498
  rows: offsets.length / 2 / xs.length - 1,
@@ -3481,7 +3501,7 @@ function mouthRestShift(byRole) {
3481
3501
  }
3482
3502
  const { canvasW, canvasH } = byRole.get("lip_upper");
3483
3503
  return (role, col, row) => {
3484
- const { box, xs, rows, dy } = fields.get(role);
3504
+ const { box, xs, rows, dy } = fields2.get(role);
3485
3505
  const x = col + 0.5 - canvasW / 2;
3486
3506
  const y = canvasH / 2 - (row + 0.5);
3487
3507
  if (x < box.x0 || x > box.x1 || y < box.y0 || y > box.y1) return void 0;
@@ -3570,32 +3590,32 @@ function mouthAnchor(byRole) {
3570
3590
  }
3571
3591
 
3572
3592
  // src/auto-rig/generate.ts
3573
- var import_format10 = require("@ikijs/format");
3593
+ var import_format12 = require("@ikijs/format");
3574
3594
 
3575
3595
  // src/auto-rig/animations.ts
3576
- var import_format8 = require("@ikijs/format");
3596
+ var import_format9 = require("@ikijs/format");
3577
3597
  function browsY(y) {
3578
3598
  return [
3579
- { parameter: import_format8.StandardParameter.BrowLeftY, value: y, blend: "add" },
3580
- { parameter: import_format8.StandardParameter.BrowRightY, value: y, blend: "add" }
3599
+ { parameter: import_format9.StandardParameter.BrowLeftY, value: y, blend: "add" },
3600
+ { parameter: import_format9.StandardParameter.BrowRightY, value: y, blend: "add" }
3581
3601
  ];
3582
3602
  }
3583
3603
  function browsTilt(innerDown) {
3584
3604
  return [
3585
- { parameter: import_format8.StandardParameter.BrowLeftAngle, value: innerDown, blend: "add" },
3586
- { parameter: import_format8.StandardParameter.BrowRightAngle, value: -innerDown, blend: "add" }
3605
+ { parameter: import_format9.StandardParameter.BrowLeftAngle, value: innerDown, blend: "add" },
3606
+ { parameter: import_format9.StandardParameter.BrowRightAngle, value: -innerDown, blend: "add" }
3587
3607
  ];
3588
3608
  }
3589
3609
  function cheek(value) {
3590
- return { parameter: import_format8.StandardParameter.Cheek, value, blend: "add" };
3610
+ return { parameter: import_format9.StandardParameter.Cheek, value, blend: "add" };
3591
3611
  }
3592
- var BROWS = [import_format8.StandardParameter.BrowLeftY, import_format8.StandardParameter.BrowRightY, import_format8.StandardParameter.BrowLeftAngle, import_format8.StandardParameter.BrowRightAngle];
3612
+ var BROWS = [import_format9.StandardParameter.BrowLeftY, import_format9.StandardParameter.BrowRightY, import_format9.StandardParameter.BrowLeftAngle, import_format9.StandardParameter.BrowRightAngle];
3593
3613
  var EXPRESSIONS = [
3594
3614
  {
3595
3615
  id: "smile",
3596
3616
  description: "Smiling and pleased: warm, friendly, content. For greetings, thanks, agreement and gentle happiness.",
3597
3617
  parameters: [
3598
- { parameter: import_format8.StandardParameter.MouthForm, value: 1, blend: "add" },
3618
+ { parameter: import_format9.StandardParameter.MouthForm, value: 1, blend: "add" },
3599
3619
  ...browsY(0.25),
3600
3620
  cheek(0.4)
3601
3621
  ]
@@ -3604,10 +3624,10 @@ var EXPRESSIONS = [
3604
3624
  id: "laugh",
3605
3625
  description: "Laughing, eyes shut and mouth open: delighted, amused. For jokes, playfulness and big joy.",
3606
3626
  parameters: [
3607
- { parameter: import_format8.StandardParameter.EyeOpenLeft, value: 0, blend: "multiply" },
3608
- { parameter: import_format8.StandardParameter.EyeOpenRight, value: 0, blend: "multiply" },
3609
- { parameter: import_format8.StandardParameter.MouthForm, value: 1, blend: "add" },
3610
- { parameter: import_format8.StandardParameter.MouthOpen, value: 0.8, blend: "overwrite" },
3627
+ { parameter: import_format9.StandardParameter.EyeOpenLeft, value: 0, blend: "multiply" },
3628
+ { parameter: import_format9.StandardParameter.EyeOpenRight, value: 0, blend: "multiply" },
3629
+ { parameter: import_format9.StandardParameter.MouthForm, value: 1, blend: "add" },
3630
+ { parameter: import_format9.StandardParameter.MouthOpen, value: 0.8, blend: "overwrite" },
3611
3631
  ...browsY(0.4),
3612
3632
  cheek(0.65)
3613
3633
  ]
@@ -3619,7 +3639,7 @@ var EXPRESSIONS = [
3619
3639
  parameters: [
3620
3640
  ...browsY(-0.5),
3621
3641
  ...browsTilt(0.8),
3622
- { parameter: import_format8.StandardParameter.MouthForm, value: -0.9, blend: "add" }
3642
+ { parameter: import_format9.StandardParameter.MouthForm, value: -0.9, blend: "add" }
3623
3643
  ]
3624
3644
  },
3625
3645
  {
@@ -3629,7 +3649,7 @@ var EXPRESSIONS = [
3629
3649
  parameters: [
3630
3650
  ...browsY(0.35),
3631
3651
  ...browsTilt(-0.8),
3632
- { parameter: import_format8.StandardParameter.MouthForm, value: -0.8, blend: "add" }
3652
+ { parameter: import_format9.StandardParameter.MouthForm, value: -0.8, blend: "add" }
3633
3653
  ]
3634
3654
  },
3635
3655
  {
@@ -3637,18 +3657,18 @@ var EXPRESSIONS = [
3637
3657
  description: "Surprised, mouth dropped open: startled, amazed. For shock, sudden news or disbelief.",
3638
3658
  parameters: [
3639
3659
  ...browsY(0.8),
3640
- { parameter: import_format8.StandardParameter.MouthOpen, value: 0.9, blend: "overwrite" }
3660
+ { parameter: import_format9.StandardParameter.MouthOpen, value: 0.9, blend: "overwrite" }
3641
3661
  ]
3642
3662
  },
3643
3663
  {
3644
3664
  id: "shy",
3645
3665
  description: "Shy or embarrassed: bashful, flustered. For being praised, teased or caught off guard.",
3646
- needs: [import_format8.StandardParameter.Cheek],
3666
+ needs: [import_format9.StandardParameter.Cheek],
3647
3667
  parameters: [
3648
3668
  cheek(1),
3649
3669
  // Gaze down.
3650
- { parameter: import_format8.StandardParameter.EyeballY, value: -0.65, blend: "add" },
3651
- { parameter: import_format8.StandardParameter.MouthForm, value: 0.4, blend: "add" },
3670
+ { parameter: import_format9.StandardParameter.EyeballY, value: -0.65, blend: "add" },
3671
+ { parameter: import_format9.StandardParameter.MouthForm, value: 0.4, blend: "add" },
3652
3672
  ...browsTilt(-0.25)
3653
3673
  ]
3654
3674
  }
@@ -3668,7 +3688,7 @@ var HEAD_MOTIONS = {
3668
3688
  duration: 1,
3669
3689
  curves: [
3670
3690
  {
3671
- parameter: import_format8.StandardParameter.AngleY,
3691
+ parameter: import_format9.StandardParameter.AngleY,
3672
3692
  keys: [
3673
3693
  [0, 0],
3674
3694
  [0.3, -18],
@@ -3689,7 +3709,7 @@ var HEAD_MOTIONS = {
3689
3709
  fadeIn: 0.15,
3690
3710
  curves: [
3691
3711
  {
3692
- parameter: import_format8.StandardParameter.AngleX,
3712
+ parameter: import_format9.StandardParameter.AngleX,
3693
3713
  keys: [
3694
3714
  [0, 0],
3695
3715
  [0.2, -18],
@@ -3708,7 +3728,7 @@ var HEAD_MOTIONS = {
3708
3728
  duration: 1.4,
3709
3729
  curves: [
3710
3730
  {
3711
- parameter: import_format8.StandardParameter.AngleZ,
3731
+ parameter: import_format9.StandardParameter.AngleZ,
3712
3732
  keys: [
3713
3733
  [0, 0],
3714
3734
  [0.35, 20],
@@ -3729,7 +3749,7 @@ var WAVE = {
3729
3749
  fadeOut: 0.15,
3730
3750
  curves: [
3731
3751
  {
3732
- parameter: import_format8.StandardParameter.ArmPoseRight,
3752
+ parameter: import_format9.StandardParameter.ArmPoseRight,
3733
3753
  interpolation: "linear",
3734
3754
  keys: [
3735
3755
  [0, 0],
@@ -3739,7 +3759,7 @@ var WAVE = {
3739
3759
  ]
3740
3760
  },
3741
3761
  {
3742
- parameter: import_format8.StandardParameter.ArmRight,
3762
+ parameter: import_format9.StandardParameter.ArmRight,
3743
3763
  keys: [
3744
3764
  [0, 0],
3745
3765
  [0.4, 10],
@@ -3748,7 +3768,7 @@ var WAVE = {
3748
3768
  ]
3749
3769
  },
3750
3770
  {
3751
- parameter: import_format8.StandardParameter.ArmPoseAngleRight,
3771
+ parameter: import_format9.StandardParameter.ArmPoseAngleRight,
3752
3772
  keys: [
3753
3773
  [0, 0],
3754
3774
  [0.5, 0],
@@ -3764,7 +3784,7 @@ var WAVE = {
3764
3784
  ]
3765
3785
  };
3766
3786
  function defaultMotions(declared) {
3767
- const waves = [import_format8.StandardParameter.ArmRight, import_format8.StandardParameter.ArmPoseRight, import_format8.StandardParameter.ArmPoseAngleRight].every(
3787
+ const waves = [import_format9.StandardParameter.ArmRight, import_format9.StandardParameter.ArmPoseRight, import_format9.StandardParameter.ArmPoseAngleRight].every(
3768
3788
  (id) => declared.has(id)
3769
3789
  );
3770
3790
  return waves ? { ...HEAD_MOTIONS, ...WAVE } : HEAD_MOTIONS;
@@ -4000,7 +4020,7 @@ function checkStrandEdges(strand, irises, hair) {
4000
4020
  }
4001
4021
 
4002
4022
  // src/auto-rig/context.ts
4003
- var import_format9 = require("@ikijs/format");
4023
+ var import_format10 = require("@ikijs/format");
4004
4024
 
4005
4025
  // src/auto-rig/head.ts
4006
4026
  function outerEdge(runs, axisX, side) {
@@ -4109,14 +4129,14 @@ function buildHeadFrame(layers, opts) {
4109
4129
  const rawRows = raw;
4110
4130
  const earless = (chin) => {
4111
4131
  const rowOf = (y) => Math.round(canvasH / 2 - y - faceLayer.bbox.y - 0.5);
4112
- const span = Math.max(1, eyeY - chin);
4132
+ const span2 = Math.max(1, eyeY - chin);
4113
4133
  const found = findEars(
4114
4134
  rawRows,
4115
4135
  wMax,
4116
4136
  Math.round(0.2 * rawRows.length),
4117
4137
  Math.min(rawRows.length - 1, rowOf(chin)),
4118
- Math.round(0.1 * span),
4119
- Math.round(0.8 * span)
4138
+ Math.round(0.1 * span2),
4139
+ Math.round(0.8 * span2)
4120
4140
  );
4121
4141
  if (found === void 0) return profile;
4122
4142
  const { top, bottom } = found;
@@ -4150,16 +4170,16 @@ function buildHeadFrame(layers, opts) {
4150
4170
  }
4151
4171
  }
4152
4172
  if (neckRow >= 0 && n - neckRow >= 0.12 * n) {
4153
- let span = 0;
4173
+ let span2 = 0;
4154
4174
  let waist = Infinity;
4155
4175
  for (let r = neckRow; r < n; r++) {
4156
- span = Math.max(span, profile[r]);
4176
+ span2 = Math.max(span2, profile[r]);
4157
4177
  if (r < n - tail) waist = Math.min(waist, profile[r]);
4158
4178
  }
4159
4179
  neck = {
4160
4180
  topY: rowY(neckRow),
4161
4181
  half: profile[neckRow],
4162
- span,
4182
+ span: span2,
4163
4183
  waist: Math.min(waist, profile[neckRow])
4164
4184
  };
4165
4185
  }
@@ -4359,22 +4379,22 @@ function earField(m) {
4359
4379
  const dy = face(x, y, 0, ay)[1];
4360
4380
  const d = Math.abs(x - f.axisX);
4361
4381
  const attach = ears.attachAt(y);
4362
- const span = Math.max(1, ears.outer - attach);
4382
+ const span2 = Math.max(1, ears.outer - attach);
4363
4383
  if (sideOf(m, x, s) === "far") {
4364
4384
  const rim2 = TURN.earFar * m.face;
4365
4385
  const root2 = earRoot(m, face, y, s);
4366
4386
  const lead = Math.max(
4367
4387
  0,
4368
- Math.min((1 - m.earFarScale) * span, root2 - rim2)
4388
+ Math.min((1 - m.earFarScale) * span2, root2 - rim2)
4369
4389
  );
4370
- let along = rim2 + lead * (ears.outer - d) / span;
4390
+ let along = rim2 + lead * (ears.outer - d) / span2;
4371
4391
  if (d < attach) along = Math.min(along, s * face(x, y, 30 * s, 0)[0]);
4372
4392
  return [s * a * along, dy];
4373
4393
  }
4374
4394
  if (d <= attach) return [face(x, y, ax, 0)[0], dy];
4375
4395
  const root = face(f.axisX + Math.sign(x - f.axisX) * attach, y, ax, 0)[0];
4376
4396
  const rim = s * a * TURN.earNear * m.face;
4377
- const t = Math.min(1, (d - attach) / span);
4397
+ const t = Math.min(1, (d - attach) / span2);
4378
4398
  return [root + (rim - root) * t, dy];
4379
4399
  };
4380
4400
  }
@@ -4538,7 +4558,17 @@ function hairFrontField(m) {
4538
4558
  );
4539
4559
  return core + (m.hairOuter - core) * out;
4540
4560
  };
4541
- const ride = outerRide(m, inner, coreAt, followAt, eyeY + 1.5 * eyeH);
4561
+ const ride = outerRide(
4562
+ m,
4563
+ inner,
4564
+ coreAt,
4565
+ followAt,
4566
+ eyeY + 1.5 * eyeH,
4567
+ eyeY + eyeH / 2,
4568
+ Math.max(
4569
+ ...[m.boxes.iris_L, m.boxes.iris_R].map((b) => b?.y1 ?? -Infinity)
4570
+ )
4571
+ );
4542
4572
  const followOf = (x, y, s) => followAt(x, y, s) + (ride === void 0 ? 0 : ride.lift(x, y, s));
4543
4573
  const drift = Math.abs(m.hairBack);
4544
4574
  const crown = crownRide(m, cap, (x, y, s) => followOf(x, y, s) + drift);
@@ -4565,9 +4595,12 @@ function crownBlend(m, y) {
4565
4595
  }
4566
4596
  function gridRow(ys, y, snap = false) {
4567
4597
  const n = ys.length;
4568
- let v = clamp((ys[0] - y) / (ys[0] - ys[n - 1]) * (n - 1), 0, n - 1);
4598
+ const yc = clamp(y, ys[n - 1], ys[0]);
4599
+ let r = 0;
4600
+ while (r < n - 2 && ys[r + 1] >= yc) r++;
4601
+ let v = r + (ys[r] - yc) / (ys[r] - ys[r + 1]);
4569
4602
  if (snap && Math.abs(v - Math.round(v)) < 1e-3) v = Math.round(v);
4570
- const r = Math.min(n - 2, Math.floor(v));
4603
+ r = Math.min(n - 2, Math.floor(v));
4571
4604
  return { r, t: v - r };
4572
4605
  }
4573
4606
  function gridCol(xs, x) {
@@ -4657,7 +4690,7 @@ function paints(runs, px) {
4657
4690
  }
4658
4691
  return false;
4659
4692
  }
4660
- function outerRide(m, inner, coreAt, followAt, capLimit) {
4693
+ function outerRide(m, inner, coreAt, followAt, capLimit, eyeTop, irisTop) {
4661
4694
  const f = m.frame;
4662
4695
  const grid = m.hairFrontGrid;
4663
4696
  if (grid === void 0) return void 0;
@@ -4844,10 +4877,25 @@ function outerRide(m, inner, coreAt, followAt, capLimit) {
4844
4877
  }
4845
4878
  for (let j = 0; j < 2; j++) {
4846
4879
  for (let r = n - 2; r >= 0; r--) {
4847
- if (wr[r] < 1)
4880
+ if (r <= last || wr[r] < 1)
4848
4881
  blend[r * 2 + j] = Math.max(blend[r * 2 + j], blend[(r + 1) * 2 + j]);
4849
4882
  }
4850
4883
  }
4884
+ let zero = last + 1;
4885
+ while (zero < n && ys[zero] > eyeTop) zero++;
4886
+ while (zero > last + 1 && zero < n && ys[zero] < irisTop - 0.5) zero--;
4887
+ const endY = zero < n ? ys[zero] : Math.min(Math.max(eyeTop, irisTop - 0.5), ys[n - 1] - 1);
4888
+ for (let j = 0; j < 2 && last < n - 1; j++) {
4889
+ for (let r = 1; r < zero; r++) {
4890
+ for (let q = 0; q < r && q <= last; q++) {
4891
+ if (wr[q] < 1) continue;
4892
+ blend[r * 2 + j] = Math.max(
4893
+ blend[r * 2 + j],
4894
+ blend[q * 2 + j] * (ys[r] - endY) / (ys[q] - endY)
4895
+ );
4896
+ }
4897
+ }
4898
+ }
4851
4899
  const cutRow = (r, j, x) => r > last ? 0 : along(cuts.subarray((r * 2 + j) * nc, (r * 2 + j + 1) * nc), x);
4852
4900
  return {
4853
4901
  lift: liftAt,
@@ -5130,7 +5178,7 @@ function renderedLander(turn, parts, grids, from = /* @__PURE__ */ new Map()) {
5130
5178
  const family = roleSpec(role).family;
5131
5179
  const part = parts.find((p) => p.id === role);
5132
5180
  const g = grids.get(family);
5133
- const warp = part?.warps?.find((w) => w.parameter === import_format9.StandardParameter.AngleX);
5181
+ const warp = part?.warps?.find((w) => w.parameter === import_format10.StandardParameter.AngleX);
5134
5182
  if (part === void 0 || part.mesh === void 0 || !g && !warp) {
5135
5183
  return x + field(role)(x, y, ax, 0)[0];
5136
5184
  }
@@ -5269,11 +5317,11 @@ function buildFaceMesh(frame, b, head, neckCells) {
5269
5317
  const indices = [];
5270
5318
  const neck = frame.neck;
5271
5319
  if (neck !== void 0) {
5272
- const span = neck.span + pad;
5273
- const nc = cellsFor(2 * span, neckCells.px, neckCells.min, neckCells.max);
5320
+ const span2 = neck.span + pad;
5321
+ const nc = cellsFor(2 * span2, neckCells.px, neckCells.min, neckCells.max);
5274
5322
  const xs2 = columns(
5275
- Math.max(b.x0, frame.axisX - span),
5276
- Math.min(b.x1, frame.axisX + span),
5323
+ Math.max(b.x0, frame.axisX - span2),
5324
+ Math.min(b.x1, frame.axisX + span2),
5277
5325
  nc
5278
5326
  );
5279
5327
  const sampleBelow = (x) => Math.max(
@@ -5409,6 +5457,9 @@ function buildFaceMesh(frame, b, head, neckCells) {
5409
5457
  return { mesh: { vertices, uvs, indices }, region, headStart };
5410
5458
  }
5411
5459
 
5460
+ // src/auto-rig/patches.ts
5461
+ var import_format11 = require("@ikijs/format");
5462
+
5412
5463
  // src/auto-rig/solve.ts
5413
5464
  var RANGES = {
5414
5465
  // [lo, hi, lo exclusive]
@@ -5839,6 +5890,235 @@ function buildTurn(frame, landmarks, q) {
5839
5890
  };
5840
5891
  }
5841
5892
 
5893
+ // src/auto-rig/patches.ts
5894
+ var OP_KEYS = ["target", "at", "region", "by", "falloff", "why"];
5895
+ var REGION_KEYS = ["role", "x", "y"];
5896
+ var BY_KEYS = ["dx", "dy"];
5897
+ var ROLLS = /* @__PURE__ */ new Set([import_format11.StandardParameter.AngleZ, import_format11.StandardParameter.BodyAngleZ]);
5898
+ var REST_IS_ART = "rest is the drawn art, which no patch changes (change it with the layout or by regenerating the layer)";
5899
+ function applyPatches(patches, ctx) {
5900
+ if (!Array.isArray(patches)) {
5901
+ throw new PatchError(
5902
+ `patches must be an array, got ${patches === null ? "null" : typeof patches}`
5903
+ );
5904
+ }
5905
+ const sums = /* @__PURE__ */ new Map();
5906
+ const reports = patches.map((op, i) => replay(op, i, ctx, sums));
5907
+ for (const [offsets, s] of sums) {
5908
+ s.terms.forEach((t, k) => {
5909
+ if (t.length === 0) return;
5910
+ const d = t.sort((a, b) => a - b).reduce((a, b) => a + b, 0);
5911
+ offsets[k] = roundTo(offsets[k] + d / s.scale[k % 2], s.step);
5912
+ });
5913
+ }
5914
+ return reports;
5915
+ }
5916
+ function replay(op, i, ctx, sums) {
5917
+ const name = `patches[${i}]`;
5918
+ const o = fields(name, op, OP_KEYS);
5919
+ const target = text(`${name}.target`, o.target);
5920
+ const at = numbers(`${name}.at`, o.at);
5921
+ const region = fields(`${name}.region`, o.region, REGION_KEYS);
5922
+ const role = text(`${name}.region.role`, region.role);
5923
+ const x = span(`${name}.region.x`, region.x);
5924
+ const y = span(`${name}.region.y`, region.y);
5925
+ const by = fields(`${name}.by`, o.by, BY_KEYS);
5926
+ const dx = finite2(`${name}.by.dx`, by.dx);
5927
+ const dy = finite2(`${name}.by.dy`, by.dy);
5928
+ const falloff = o.falloff === void 0 ? 0 : finite2(`${name}.falloff`, o.falloff, 0);
5929
+ if (o.why !== void 0) text(`${name}.why`, o.why);
5930
+ const box = ctx.boxOf(role);
5931
+ if (box === void 0) {
5932
+ const roles = [...new Set(ctx.parts.map((p) => roleOfPart(p.id)))].filter(
5933
+ (r) => ctx.boxOf(r) !== void 0
5934
+ );
5935
+ throw new PatchError(
5936
+ `${name}.region.role "${role}" is not a layer of this set (roles: ${roles.join(", ")})`
5937
+ );
5938
+ }
5939
+ const stop = resolveStop(name, target, at, ctx);
5940
+ const { points } = stop;
5941
+ const weights = regionWeights(points, box, x, y, falloff * ctx.faceWidth);
5942
+ const hit = weights.flatMap((w, k) => w > 0 ? [k] : []);
5943
+ if (hit.length === 0) {
5944
+ const fx = points.map(([px]) => (px - box.x0) / bw(box));
5945
+ const fy = points.map(([, py]) => (box.y1 - py) / bh(box));
5946
+ const range = (v) => `[${roundTo(Math.min(...v), 0.01)}, ${roundTo(Math.max(...v), 0.01)}]`;
5947
+ throw new PatchError(
5948
+ `${name}.region (falloff ${falloff}) catches none of ${target}'s ${points.length} points; at rest they span x ${range(fx)}, y ${range(fy)} of ${role}'s box`
5949
+ );
5950
+ }
5951
+ let sum = sums.get(stop.offsets);
5952
+ if (sum === void 0) {
5953
+ sum = {
5954
+ terms: stop.offsets.map(() => []),
5955
+ scale: stop.scale,
5956
+ step: stop.step
5957
+ };
5958
+ sums.set(stop.offsets, sum);
5959
+ }
5960
+ const ddx = dx * ctx.faceWidth;
5961
+ const ddy = -dy * ctx.faceWidth;
5962
+ for (const k of hit) {
5963
+ sum.terms[2 * k].push(ddx * weights[k]);
5964
+ sum.terms[2 * k + 1].push(ddy * weights[k]);
5965
+ }
5966
+ const maxW = Math.max(...hit.map((k) => weights[k]));
5967
+ const report = {
5968
+ index: i,
5969
+ target,
5970
+ at,
5971
+ moved: hit.length,
5972
+ of: points.length,
5973
+ maxPx: roundTo(maxW * Math.hypot(dx, dy) * ctx.faceWidth, 0.1)
5974
+ };
5975
+ if (stop.stride !== void 0) {
5976
+ const { stride } = stop;
5977
+ const cols = hit.map((k) => k % stride);
5978
+ const rows = hit.map((k) => Math.floor(k / stride));
5979
+ report.cols = [Math.min(...cols), Math.max(...cols)];
5980
+ report.rows = [Math.min(...rows), Math.max(...rows)];
5981
+ }
5982
+ return report;
5983
+ }
5984
+ function resolveStop(name, target, at, ctx) {
5985
+ const grid = ctx.grids.get(target);
5986
+ if (grid !== void 0) {
5987
+ const l = grid.lattice.points;
5988
+ const points = [];
5989
+ for (let k = 0; k < l.length; k += 2) points.push([l[k], l[k + 1]]);
5990
+ return {
5991
+ offsets: gridStop(name, at, target, grid.keyforms),
5992
+ points,
5993
+ scale: [1, 1],
5994
+ step: 0.1,
5995
+ stride: grid.lattice.cols + 1
5996
+ };
5997
+ }
5998
+ const part = ctx.parts.find((p) => p.id === target);
5999
+ const b = ctx.boxOf(target);
6000
+ if (part?.mesh === void 0 || part.warps === void 0 || b === void 0) {
6001
+ const parts = ctx.parts.filter(
6002
+ (p) => p.mesh !== void 0 && p.warps !== void 0 && ctx.boxOf(p.id) !== void 0
6003
+ );
6004
+ throw new PatchError(
6005
+ `${name}.target "${target}" is neither a feature grid (${[...ctx.grids.keys()].join(", ")}) nor a mesh part with warps (${parts.map((p) => p.id).join(", ")}); bodyWarp and the matrix deformers are not patchable`
6006
+ );
6007
+ }
6008
+ return {
6009
+ offsets: partStop(name, at, part, part.warps, ctx),
6010
+ points: meshPoints(part.mesh, b),
6011
+ scale: [bw(b), bh(b)],
6012
+ step: 1e-4
6013
+ };
6014
+ }
6015
+ function gridStop(name, at, id, keyforms) {
6016
+ const i = X_STOPS.indexOf(at[import_format11.StandardParameter.AngleX]);
6017
+ const j = Y_STOPS.indexOf(at[import_format11.StandardParameter.AngleY]);
6018
+ if (Object.keys(at).length !== 2 || i < 0 || j < 0) {
6019
+ throw new PatchError(
6020
+ `${name}.at must name ${import_format11.StandardParameter.AngleX} in [${X_STOPS.join(", ")}] and ${import_format11.StandardParameter.AngleY} in [${Y_STOPS.join(", ")}] for the grid ${id}, got ${JSON.stringify(at)}`
6021
+ );
6022
+ }
6023
+ if (X_STOPS[i] === 0 && Y_STOPS[j] === 0) {
6024
+ throw new PatchError(
6025
+ `${name}.at ${import_format11.StandardParameter.AngleX}=0 ${import_format11.StandardParameter.AngleY}=0 is ${id}'s rest stop: ${REST_IS_ART}; patch a stop with ${import_format11.StandardParameter.AngleX} or ${import_format11.StandardParameter.AngleY} at \xB130 instead`
6026
+ );
6027
+ }
6028
+ return keyforms[j * X_STOPS.length + i];
6029
+ }
6030
+ function partStop(name, at, part, warps, ctx) {
6031
+ const keys = Object.keys(at);
6032
+ const [param] = keys;
6033
+ const value = at[param];
6034
+ const keyform = keys.length === 1 ? warps.filter((w) => w.parameter === param).flatMap((w) => w.keyforms).find((k) => k.value === value) : void 0;
6035
+ if (keyform === void 0) {
6036
+ throw new PatchError(
6037
+ `${name}.at must name one parameter at one of ${part.id}'s warp stops (${listStops(warps, () => true)}), got ${JSON.stringify(at)}`
6038
+ );
6039
+ }
6040
+ if (ROLLS.has(param)) {
6041
+ throw new PatchError(
6042
+ `${name}.at ${param}: that parameter rolls ${part.id}'s deformer chain, so a displacement at its stop is not on screen axes; patch another of its stops (${listStops(warps, (p) => !ROLLS.has(p))}) or its feature grid instead`
6043
+ );
6044
+ }
6045
+ const isRest = (p, v) => v === ctx.parameters.find((q) => q.id === p)?.default;
6046
+ const isShut = (p, v) => SHUT_BY.get(part.id) === p && v === 0;
6047
+ if (!isRest(param, value) && !isShut(param, value)) return keyform.offsets;
6048
+ const open = listStops(warps, (p, v) => !isRest(p, v) && !isShut(p, v));
6049
+ const grid = part.deformer !== void 0 && ctx.grids.has(part.deformer) ? `its feature grid ${part.deformer} at a ${import_format11.StandardParameter.AngleX} \xD7 ${import_format11.StandardParameter.AngleY} stop` : "";
6050
+ const instead = [open && `another of its stops (${open})`, grid].filter((s) => s !== "").join(" or ");
6051
+ const hint = instead === "" ? "" : `; patch ${instead} instead`;
6052
+ throw new PatchError(
6053
+ isRest(param, value) ? `${name}.at ${param}=${value} is that parameter's default, ${part.id}'s rest stop: ${REST_IS_ART}${hint}` : `${name}.at ${param}=0 is where the rig shuts ${part.id} on purpose, a closure the fold check exempts, so a fold a patch made there would never be reported${hint}`
6054
+ );
6055
+ }
6056
+ function listStops(warps, keep) {
6057
+ const stops = /* @__PURE__ */ new Map();
6058
+ for (const w of warps) {
6059
+ for (const k of w.keyforms) {
6060
+ if (!keep(w.parameter, k.value)) continue;
6061
+ const s = stops.get(w.parameter) ?? /* @__PURE__ */ new Set();
6062
+ s.add(k.value);
6063
+ stops.set(w.parameter, s);
6064
+ }
6065
+ }
6066
+ return [...stops].map(([p, s]) => `${p}@${[...s].sort((a, b) => a - b).join(",")}`).join(" ");
6067
+ }
6068
+ function regionWeights(points, box, x, y, fall) {
6069
+ const x0 = box.x0 + x[0] * bw(box);
6070
+ const x1 = box.x0 + x[1] * bw(box);
6071
+ const top = box.y1 - y[0] * bh(box);
6072
+ const bottom = box.y1 - y[1] * bh(box);
6073
+ return points.map(([px, py]) => {
6074
+ const d = Math.hypot(
6075
+ Math.max(x0 - px, 0, px - x1),
6076
+ Math.max(bottom - py, 0, py - top)
6077
+ );
6078
+ return d === 0 ? 1 : d < fall ? smoothstep(fall, 0, d) : 0;
6079
+ });
6080
+ }
6081
+ function asPatch(check) {
6082
+ try {
6083
+ return check();
6084
+ } catch (e) {
6085
+ throw e instanceof TurnTargetError ? new PatchError(e.message) : e;
6086
+ }
6087
+ }
6088
+ function fields(name, v, known) {
6089
+ if (v === void 0) throw new PatchError(`${name} is missing`);
6090
+ asPatch(() => checkedKeys(name, v, known));
6091
+ return v;
6092
+ }
6093
+ function numbers(name, v) {
6094
+ const o = fields(name, v, Object.keys(v ?? {}));
6095
+ return Object.fromEntries(
6096
+ Object.entries(o).map(([k, n]) => [k, finite2(`${name}.${k}`, n)])
6097
+ );
6098
+ }
6099
+ var finite2 = (name, v, lo = -Infinity) => asPatch(() => checkedNumber(name, v, lo, Infinity));
6100
+ function text(name, v) {
6101
+ if (typeof v !== "string") {
6102
+ throw new PatchError(`${name} must be a string, got ${String(v)}`);
6103
+ }
6104
+ return v;
6105
+ }
6106
+ function span(name, v) {
6107
+ if (!Array.isArray(v) || v.length !== 2) {
6108
+ throw new PatchError(
6109
+ `${name} must be [from, to], got ${JSON.stringify(v)}`
6110
+ );
6111
+ }
6112
+ const lo = finite2(`${name}[0]`, v[0]);
6113
+ const hi = finite2(`${name}[1]`, v[1]);
6114
+ if (lo > hi) {
6115
+ throw new PatchError(
6116
+ `${name} [${lo}, ${hi}] runs backwards: ${name}[0] must not exceed ${name}[1]`
6117
+ );
6118
+ }
6119
+ return [lo, hi];
6120
+ }
6121
+
5842
6122
  // src/auto-rig/generate.ts
5843
6123
  var MESH_CELLS = {
5844
6124
  face: { px: 28, min: 4, max: 24 },
@@ -5896,7 +6176,7 @@ function generateIkiFromLayerSet(layers, canvas, options = {}) {
5896
6176
  const solveFrame = buildHeadFrame(layers, frameOptions);
5897
6177
  const ctx = solveContext(solveFrame, byRole, turnOptions, hasNose);
5898
6178
  const boxes = Object.fromEntries(
5899
- ["eye_L", "eye_R", "brow_L", "brow_R"].filter(has).map((r) => [r, box(r)])
6179
+ ["eye_L", "eye_R", "iris_L", "iris_R", "brow_L", "brow_R"].filter(has).map((r) => [r, box(r)])
5900
6180
  );
5901
6181
  const landmarks = {
5902
6182
  boxes,
@@ -6016,8 +6296,17 @@ function generateIkiFromLayerSet(layers, canvas, options = {}) {
6016
6296
  ];
6017
6297
  }
6018
6298
  const parameters = declareParameters(new Set(byRole.keys()));
6299
+ const reports = options.patches === void 0 ? void 0 : applyPatches(options.patches, {
6300
+ grids: new Map(
6301
+ [...grids].map(([family, g]) => [WARP_ID[family], g])
6302
+ ),
6303
+ parts,
6304
+ boxOf: (role) => has(role) ? box(role) : void 0,
6305
+ faceWidth: byRole.get("face").cropW,
6306
+ parameters
6307
+ });
6019
6308
  const model = {
6020
- version: import_format10.IKI_FORMAT_VERSION,
6309
+ version: import_format12.IKI_FORMAT_VERSION,
6021
6310
  name: "auto-rigged",
6022
6311
  canvas: { width: canvas.width, height: canvas.height },
6023
6312
  parameters,
@@ -6027,7 +6316,8 @@ function generateIkiFromLayerSet(layers, canvas, options = {}) {
6027
6316
  expressions: defaultExpressions(new Set(parameters.map((p) => p.id))),
6028
6317
  motions: defaultMotions(new Set(parameters.map((p) => p.id)))
6029
6318
  };
6030
- const valid = (0, import_format10.parseIkiModel)(model);
6319
+ const valid = (0, import_format12.parseIkiModel)(model);
6320
+ if (reports !== void 0) options.onPatchesApplied?.(reports);
6031
6321
  if (hasNose && options.onTurnSolved) {
6032
6322
  options.onTurnSolved(
6033
6323
  turnReport(
@@ -6042,14 +6332,14 @@ function generateIkiFromLayerSet(layers, canvas, options = {}) {
6042
6332
  function turnWarps(mesh, b, fieldOf) {
6043
6333
  return [
6044
6334
  localWarp(
6045
- import_format10.StandardParameter.AngleX,
6335
+ import_format12.StandardParameter.AngleX,
6046
6336
  mesh,
6047
6337
  b,
6048
6338
  X_STOPS,
6049
6339
  ([x, y], v, i) => v === 0 ? [0, 0] : [fieldOf(i)(x, y, v, 0)[0], 0]
6050
6340
  ),
6051
6341
  localWarp(
6052
- import_format10.StandardParameter.AngleY,
6342
+ import_format12.StandardParameter.AngleY,
6053
6343
  mesh,
6054
6344
  b,
6055
6345
  Y_STOPS,
@@ -6100,7 +6390,7 @@ function buildPart(spec, b, order, frame, byRole, style, frontGrid, lips) {
6100
6390
  }
6101
6391
  if (mesh !== void 0) part.mesh = mesh;
6102
6392
  const side = spec.role.endsWith("_L") ? "L" : "R";
6103
- const eyeOpen = side === "L" ? import_format10.StandardParameter.EyeOpenLeft : import_format10.StandardParameter.EyeOpenRight;
6393
+ const eyeOpen = side === "L" ? import_format12.StandardParameter.EyeOpenLeft : import_format12.StandardParameter.EyeOpenRight;
6104
6394
  const hh = frame.hh;
6105
6395
  switch (spec.role) {
6106
6396
  case "face":
@@ -6115,7 +6405,7 @@ function buildPart(spec, b, order, frame, byRole, style, frontGrid, lips) {
6115
6405
  headOwnRoll(byRole.has("body"))
6116
6406
  ),
6117
6407
  localWarp(
6118
- import_format10.StandardParameter.Breath,
6408
+ import_format12.StandardParameter.Breath,
6119
6409
  mesh,
6120
6410
  b,
6121
6411
  [0, 1],
@@ -6162,8 +6452,8 @@ function buildPart(spec, b, order, frame, byRole, style, frontGrid, lips) {
6162
6452
  if (byRole.has("mouth_open")) {
6163
6453
  warps.push(mouthWiden(mesh, b));
6164
6454
  bindings.push(
6165
- { parameter: import_format10.StandardParameter.MouthOpen, channel: "opacity", from: 1, to: 0 },
6166
- { parameter: import_format10.StandardParameter.MouthOpen, channel: "opacity", from: 1, to: 0 }
6455
+ { parameter: import_format12.StandardParameter.MouthOpen, channel: "opacity", from: 1, to: 0 },
6456
+ { parameter: import_format12.StandardParameter.MouthOpen, channel: "opacity", from: 1, to: 0 }
6167
6457
  );
6168
6458
  extra = [(AMPLITUDE.mouthOpenWidth - 1) * (bw(b) / 2), 0];
6169
6459
  } else {
@@ -6173,19 +6463,19 @@ function buildPart(spec, b, order, frame, byRole, style, frontGrid, lips) {
6173
6463
  );
6174
6464
  bindings.push(
6175
6465
  {
6176
- parameter: import_format10.StandardParameter.MouthOpen,
6466
+ parameter: import_format12.StandardParameter.MouthOpen,
6177
6467
  channel: "scaleY",
6178
6468
  from: 0,
6179
6469
  to: roundTo(grow, 0.01)
6180
6470
  },
6181
6471
  {
6182
- parameter: import_format10.StandardParameter.MouthOpen,
6472
+ parameter: import_format12.StandardParameter.MouthOpen,
6183
6473
  channel: "scaleX",
6184
6474
  from: 0,
6185
6475
  to: roundTo(AMPLITUDE.mouthOpenWidth - 1, 0.01)
6186
6476
  },
6187
6477
  {
6188
- parameter: import_format10.StandardParameter.MouthOpen,
6478
+ parameter: import_format12.StandardParameter.MouthOpen,
6189
6479
  channel: "translateY",
6190
6480
  from: 0,
6191
6481
  to: -roundTo(grow * bh(b) / 2, 0.01)
@@ -6197,7 +6487,7 @@ function buildPart(spec, b, order, frame, byRole, style, frontGrid, lips) {
6197
6487
  case "mouth_open":
6198
6488
  warps.push(mouthForm(mesh, b), mouthOpenGrow(mesh, b));
6199
6489
  bindings.push({
6200
- parameter: import_format10.StandardParameter.MouthOpen,
6490
+ parameter: import_format12.StandardParameter.MouthOpen,
6201
6491
  channel: "opacity",
6202
6492
  from: 0,
6203
6493
  to: 1
@@ -6219,7 +6509,7 @@ function buildPart(spec, b, order, frame, byRole, style, frontGrid, lips) {
6219
6509
  case "blush_L":
6220
6510
  case "blush_R":
6221
6511
  bindings.push({
6222
- parameter: import_format10.StandardParameter.Cheek,
6512
+ parameter: import_format12.StandardParameter.Cheek,
6223
6513
  channel: "opacity",
6224
6514
  from: AMPLITUDE.blushRest,
6225
6515
  to: 1
@@ -6230,8 +6520,8 @@ function buildPart(spec, b, order, frame, byRole, style, frontGrid, lips) {
6230
6520
  if (byRole.has("hair_front")) {
6231
6521
  const amp = style.sway * AMPLITUDE.sway * hh;
6232
6522
  warps.push(
6233
- hairSway(import_format10.StandardParameter.HairSwayX, mesh, b, amp),
6234
- hairSway(import_format10.StandardParameter.HairSwayZ, mesh, b, amp)
6523
+ hairSway(import_format12.StandardParameter.HairSwayX, mesh, b, amp),
6524
+ hairSway(import_format12.StandardParameter.HairSwayZ, mesh, b, amp)
6235
6525
  );
6236
6526
  }
6237
6527
  warps.push(tiltHang(mesh, b, pivot, hairHangWeight(frame.chinY, b.y0)));
@@ -6246,6 +6536,8 @@ function buildPart(spec, b, order, frame, byRole, style, frontGrid, lips) {
6246
6536
  headStart
6247
6537
  };
6248
6538
  }
6539
+ var RAMP_ROWS = 3;
6540
+ var MIN_RAMP_CELL = 4;
6249
6541
  function hairFrontGrid(b, frame, boxes) {
6250
6542
  const cells = MESH_CELLS.hair_front;
6251
6543
  const px = Math.min(cells.px, meshScale("hair_front", frame));
@@ -6270,6 +6562,24 @@ function hairFrontGrid(b, frame, boxes) {
6270
6562
  { length: rows + 1 },
6271
6563
  (_, r) => b.y1 - bh(b) * r / rows
6272
6564
  );
6565
+ const eyes = [boxes.eye_L, boxes.eye_R].filter((e) => e !== void 0);
6566
+ const irises = [boxes.iris_L, boxes.iris_R].filter((e) => e !== void 0);
6567
+ if (eyes.length > 0 && irises.length > 0) {
6568
+ const eyeY = eyes.reduce((t, e) => t + (e.y0 + e.y1) / 2, 0) / eyes.length;
6569
+ const eyeH = eyes.reduce((t, e) => t + (e.y1 - e.y0), 0) / eyes.length;
6570
+ const hi = Math.min(eyeY + 1.5 * eyeH, b.y1);
6571
+ const lo = Math.max(Math.max(...irises.map((e) => e.y1)) - 0.5, b.y0);
6572
+ for (; ; ) {
6573
+ const edges = [hi, ...ys.filter((y) => y < hi && y > lo), lo];
6574
+ if (edges.length - 2 >= RAMP_ROWS) break;
6575
+ let gap = 0;
6576
+ for (let i = 1; i + 1 < edges.length; i++)
6577
+ if (edges[i] - edges[i + 1] > edges[gap] - edges[gap + 1]) gap = i;
6578
+ if (edges[gap] - edges[gap + 1] < 2 * MIN_RAMP_CELL) break;
6579
+ ys.push((edges[gap] + edges[gap + 1]) / 2);
6580
+ ys.sort((p, q) => q - p);
6581
+ }
6582
+ }
6273
6583
  return { xs, ys };
6274
6584
  }
6275
6585
  function meshScale(role, frame) {
@@ -6297,9 +6607,9 @@ function deformers(frame, grids, bodyWarp, arms, byRole) {
6297
6607
  pivot: { x: roundTo(frame.axisX, 0.01), y: roundTo(frame.chinY, 0.01) },
6298
6608
  bindings: [
6299
6609
  // AngleZ is clockwise-positive (Live2D's); a rotation is CCW-positive.
6300
- { parameter: import_format10.StandardParameter.AngleZ, channel: "rotate", from: roll, to: -roll },
6610
+ { parameter: import_format12.StandardParameter.AngleZ, channel: "rotate", from: roll, to: -roll },
6301
6611
  {
6302
- parameter: import_format10.StandardParameter.Breath,
6612
+ parameter: import_format12.StandardParameter.Breath,
6303
6613
  channel: "translateY",
6304
6614
  from: 0,
6305
6615
  to: roundTo(headOwnBreath(hasBody) * frame.hh, 0.01)
@@ -6317,8 +6627,8 @@ function deformers(frame, grids, bodyWarp, arms, byRole) {
6317
6627
  points: g.lattice.points
6318
6628
  },
6319
6629
  warp2d: {
6320
- parameter: import_format10.StandardParameter.AngleX,
6321
- parameterY: import_format10.StandardParameter.AngleY,
6630
+ parameter: import_format12.StandardParameter.AngleX,
6631
+ parameterY: import_format12.StandardParameter.AngleY,
6322
6632
  valuesX: X_STOPS,
6323
6633
  valuesY: Y_STOPS,
6324
6634
  keyforms2d: g.keyforms.map((offsets) => ({ offsets }))
@@ -6331,16 +6641,16 @@ function hairPhysics() {
6331
6641
  return [
6332
6642
  {
6333
6643
  id: "hairSway",
6334
- input: { parameter: import_format10.StandardParameter.AngleX, weight: 1 },
6335
- output: { parameter: import_format10.StandardParameter.HairSwayX, scale: 5 },
6644
+ input: { parameter: import_format12.StandardParameter.AngleX, weight: 1 },
6645
+ output: { parameter: import_format12.StandardParameter.HairSwayX, scale: 5 },
6336
6646
  mass: 1,
6337
6647
  stiffness: 30,
6338
6648
  damping: 3
6339
6649
  },
6340
6650
  {
6341
6651
  id: "hairTilt",
6342
- input: { parameter: import_format10.StandardParameter.AngleZ, weight: 1 },
6343
- output: { parameter: import_format10.StandardParameter.HairSwayZ, scale: 8 },
6652
+ input: { parameter: import_format12.StandardParameter.AngleZ, weight: 1 },
6653
+ output: { parameter: import_format12.StandardParameter.HairSwayZ, scale: 8 },
6344
6654
  mass: 1,
6345
6655
  stiffness: 25,
6346
6656
  damping: 2.5
@@ -6350,13 +6660,13 @@ function hairPhysics() {
6350
6660
  function declareParameters(roles) {
6351
6661
  const out = [];
6352
6662
  const add = (id, name, min, max, def) => out.push({ id, name, min, max, default: def });
6353
- add(import_format10.StandardParameter.AngleX, "Head Angle", -30, 30, 0);
6354
- add(import_format10.StandardParameter.AngleY, "Head Angle Y", -30, 30, 0);
6355
- add(import_format10.StandardParameter.AngleZ, "Head Angle Z", -30, 30, 0);
6663
+ add(import_format12.StandardParameter.AngleX, "Head Angle", -30, 30, 0);
6664
+ add(import_format12.StandardParameter.AngleY, "Head Angle Y", -30, 30, 0);
6665
+ add(import_format12.StandardParameter.AngleZ, "Head Angle Z", -30, 30, 0);
6356
6666
  if (roles.has("body")) {
6357
- add(import_format10.StandardParameter.BodyAngleX, "Body Angle X", -10, 10, 0);
6358
- add(import_format10.StandardParameter.BodyAngleY, "Body Angle Y", -10, 10, 0);
6359
- add(import_format10.StandardParameter.BodyAngleZ, "Body Angle Z", -10, 10, 0);
6667
+ add(import_format12.StandardParameter.BodyAngleX, "Body Angle X", -10, 10, 0);
6668
+ add(import_format12.StandardParameter.BodyAngleY, "Body Angle Y", -10, 10, 0);
6669
+ add(import_format12.StandardParameter.BodyAngleZ, "Body Angle Z", -10, 10, 0);
6360
6670
  }
6361
6671
  const addPose = (side, pose, angle) => {
6362
6672
  add(pose, `Arm Pose ${side}`, 0, 1, 0);
@@ -6365,40 +6675,40 @@ function declareParameters(roles) {
6365
6675
  const [armMin, armMax] = ARM_RANGE;
6366
6676
  const [elbowMin, elbowMax] = ELBOW_RANGE;
6367
6677
  if (roles.has("arm_L")) {
6368
- add(import_format10.StandardParameter.ArmLeft, "Arm L", armMin, armMax, 0);
6369
- add(import_format10.StandardParameter.ElbowLeft, "Elbow L", elbowMin, elbowMax, 0);
6678
+ add(import_format12.StandardParameter.ArmLeft, "Arm L", armMin, armMax, 0);
6679
+ add(import_format12.StandardParameter.ElbowLeft, "Elbow L", elbowMin, elbowMax, 0);
6370
6680
  if (roles.has("forearm_pose_L"))
6371
- addPose("L", import_format10.StandardParameter.ArmPoseLeft, import_format10.StandardParameter.ArmPoseAngleLeft);
6681
+ addPose("L", import_format12.StandardParameter.ArmPoseLeft, import_format12.StandardParameter.ArmPoseAngleLeft);
6372
6682
  }
6373
6683
  if (roles.has("arm_R")) {
6374
- add(import_format10.StandardParameter.ArmRight, "Arm R", armMin, armMax, 0);
6375
- add(import_format10.StandardParameter.ElbowRight, "Elbow R", elbowMin, elbowMax, 0);
6684
+ add(import_format12.StandardParameter.ArmRight, "Arm R", armMin, armMax, 0);
6685
+ add(import_format12.StandardParameter.ElbowRight, "Elbow R", elbowMin, elbowMax, 0);
6376
6686
  if (roles.has("forearm_pose_R"))
6377
- addPose("R", import_format10.StandardParameter.ArmPoseRight, import_format10.StandardParameter.ArmPoseAngleRight);
6687
+ addPose("R", import_format12.StandardParameter.ArmPoseRight, import_format12.StandardParameter.ArmPoseAngleRight);
6378
6688
  }
6379
- add(import_format10.StandardParameter.EyeOpenLeft, "Eye L", 0, 1, 1);
6380
- add(import_format10.StandardParameter.EyeOpenRight, "Eye R", 0, 1, 1);
6689
+ add(import_format12.StandardParameter.EyeOpenLeft, "Eye L", 0, 1, 1);
6690
+ add(import_format12.StandardParameter.EyeOpenRight, "Eye R", 0, 1, 1);
6381
6691
  if ([...roles].some((r) => /^(iris|pupil|highlight)_/.test(r))) {
6382
- add(import_format10.StandardParameter.EyeballX, "Gaze X", -1, 1, 0);
6383
- add(import_format10.StandardParameter.EyeballY, "Gaze Y", -1, 1, 0);
6692
+ add(import_format12.StandardParameter.EyeballX, "Gaze X", -1, 1, 0);
6693
+ add(import_format12.StandardParameter.EyeballY, "Gaze Y", -1, 1, 0);
6384
6694
  }
6385
- add(import_format10.StandardParameter.MouthOpen, "Mouth Open", 0, 1, 0);
6386
- add(import_format10.StandardParameter.MouthForm, "Mouth Form", -1, 1, 0);
6695
+ add(import_format12.StandardParameter.MouthOpen, "Mouth Open", 0, 1, 0);
6696
+ add(import_format12.StandardParameter.MouthForm, "Mouth Form", -1, 1, 0);
6387
6697
  if (roles.has("brow_L")) {
6388
- add(import_format10.StandardParameter.BrowLeftY, "Brow L Y", -1, 1, 0);
6389
- add(import_format10.StandardParameter.BrowLeftAngle, "Brow L Angle", -1, 1, 0);
6698
+ add(import_format12.StandardParameter.BrowLeftY, "Brow L Y", -1, 1, 0);
6699
+ add(import_format12.StandardParameter.BrowLeftAngle, "Brow L Angle", -1, 1, 0);
6390
6700
  }
6391
6701
  if (roles.has("brow_R")) {
6392
- add(import_format10.StandardParameter.BrowRightY, "Brow R Y", -1, 1, 0);
6393
- add(import_format10.StandardParameter.BrowRightAngle, "Brow R Angle", -1, 1, 0);
6702
+ add(import_format12.StandardParameter.BrowRightY, "Brow R Y", -1, 1, 0);
6703
+ add(import_format12.StandardParameter.BrowRightAngle, "Brow R Angle", -1, 1, 0);
6394
6704
  }
6395
6705
  if (roles.has("blush_L") || roles.has("blush_R")) {
6396
- add(import_format10.StandardParameter.Cheek, "Cheek", 0, 1, 0);
6706
+ add(import_format12.StandardParameter.Cheek, "Cheek", 0, 1, 0);
6397
6707
  }
6398
- add(import_format10.StandardParameter.Breath, "Breath", 0, 1, 0);
6708
+ add(import_format12.StandardParameter.Breath, "Breath", 0, 1, 0);
6399
6709
  if (roles.has("hair_front")) {
6400
- add(import_format10.StandardParameter.HairSwayX, "Hair Sway X", -SWAY_RANGE, SWAY_RANGE, 0);
6401
- add(import_format10.StandardParameter.HairSwayZ, "Hair Sway Z", -SWAY_RANGE, SWAY_RANGE, 0);
6710
+ add(import_format12.StandardParameter.HairSwayX, "Hair Sway X", -SWAY_RANGE, SWAY_RANGE, 0);
6711
+ add(import_format12.StandardParameter.HairSwayZ, "Hair Sway Z", -SWAY_RANGE, SWAY_RANGE, 0);
6402
6712
  }
6403
6713
  return out;
6404
6714
  }
@@ -6420,6 +6730,8 @@ function declareParameters(roles) {
6420
6730
  LIP_INSIDE_ROLES,
6421
6731
  LIP_ROLES,
6422
6732
  LayerGeometryError,
6733
+ PatchError,
6734
+ SHUT_BY,
6423
6735
  SPECK_CORE_FRACTION,
6424
6736
  SetDeformerBindings,
6425
6737
  SetDeformerParent,