@ikijs/editor 0.18.0 → 0.20.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.mjs CHANGED
@@ -1565,7 +1565,13 @@ var ROW_RUN_ROLES = /* @__PURE__ */ new Set([
1565
1565
  "hair_back",
1566
1566
  "body",
1567
1567
  "arm_L",
1568
- "arm_R"
1568
+ "arm_R",
1569
+ "forearm_pose_L",
1570
+ "forearm_pose_R"
1571
+ ]);
1572
+ var REST_HIDDEN_ROLES = /* @__PURE__ */ new Set([
1573
+ "forearm_pose_L",
1574
+ "forearm_pose_R"
1569
1575
  ]);
1570
1576
  function createLayerSetMeasurer(canvas) {
1571
1577
  for (const field of ["width", "height"]) {
@@ -1596,6 +1602,7 @@ function createLayerSetMeasurer(canvas) {
1596
1602
  if (bbox === null) return null;
1597
1603
  const rowLeft = new Int32Array(canvasH).fill(canvasW);
1598
1604
  const rowRight = new Int32Array(canvasH).fill(-1);
1605
+ const hidden = REST_HIDDEN_ROLES.has(role);
1599
1606
  const rowRuns = ROW_RUN_ROLES.has(role) ? [] : void 0;
1600
1607
  for (let y = 0; y < canvasH; y++) {
1601
1608
  let runs;
@@ -1611,14 +1618,14 @@ function createLayerSetMeasurer(canvas) {
1611
1618
  runs.push(x);
1612
1619
  inRun = on;
1613
1620
  }
1614
- if (!on) continue;
1621
+ if (!on || hidden) continue;
1615
1622
  opaque[p] = 1;
1616
1623
  if (x < rowLeft[y]) rowLeft[y] = x;
1617
1624
  if (x > rowRight[y]) rowRight[y] = x;
1618
1625
  }
1619
1626
  if (runs !== void 0 && inRun) runs.push(canvasW);
1620
1627
  }
1621
- rowSpansByRole.push({ role, rowLeft, rowRight });
1628
+ if (!hidden) rowSpansByRole.push({ role, rowLeft, rowRight });
1622
1629
  const layer = {
1623
1630
  role,
1624
1631
  fileName,
@@ -2684,7 +2691,11 @@ function headOwnBreath(hasBody) {
2684
2691
  var ELBOW_AT = 0.42;
2685
2692
  var CAP_ROWS = [0.1, 0.3];
2686
2693
  var CAP_SEGMENTS = 12;
2687
- var ARM_RANGE = [-30, 150];
2694
+ var CAP_INSET = 3;
2695
+ var capInset = (i) => Math.round(CAP_INSET * Math.min(1, Math.min(i, CAP_SEGMENTS - i) / 2));
2696
+ var CAP_RIM = 0.25;
2697
+ var ARM_RANGE = [-8, 32];
2698
+ var ELBOW_RANGE = [-10, 90];
2688
2699
  var ARM_PARAMS = {
2689
2700
  arm_L: { arm: P3.ArmLeft, elbow: P3.ElbowLeft },
2690
2701
  arm_R: { arm: P3.ArmRight, elbow: P3.ElbowRight }
@@ -2692,49 +2703,67 @@ var ARM_PARAMS = {
2692
2703
  var ARM_IDS = {
2693
2704
  arm_L: {
2694
2705
  forearm: "forearm_L",
2706
+ cap: "elbow_L",
2695
2707
  armDeformer: "armDeformer_L",
2696
2708
  forearmDeformer: "forearmDeformer_L"
2697
2709
  },
2698
2710
  arm_R: {
2699
2711
  forearm: "forearm_R",
2712
+ cap: "elbow_R",
2700
2713
  armDeformer: "armDeformer_R",
2701
2714
  forearmDeformer: "forearmDeformer_R"
2702
2715
  }
2703
2716
  };
2704
- function armGeometry(layer, bodyAxisX) {
2717
+ function endPivot(layer, end) {
2705
2718
  const { bbox, canvasW, canvasH, rowRuns } = layer;
2706
- const b = boxOfLayer(layer);
2707
- const span = (k) => {
2719
+ const span = runSpan(layer);
2720
+ const from = (k) => end === "top" ? k : bbox.h - 1 - k;
2721
+ const painted = rowRuns?.flatMap((runs, k) => runs.length > 0 ? [k] : []) ?? [];
2722
+ const firstK = end === "top" ? painted[0] ?? 0 : painted.at(-1) ?? bbox.h - 1;
2723
+ const lo = Math.ceil(CAP_ROWS[0] * bbox.h);
2724
+ const hi = Math.max(lo, Math.floor(CAP_ROWS[1] * bbox.h));
2725
+ const widths = [];
2726
+ for (let k = lo; k <= hi; k++) {
2727
+ const [s2, e2] = span(from(k));
2728
+ widths.push(e2 - s2);
2729
+ }
2730
+ widths.sort((p, q) => p - q);
2731
+ const radius = widths[widths.length >> 1] / 2;
2732
+ const ks = end === "top" ? firstK + radius : firstK - radius;
2733
+ const [s, e] = span(Math.round(ks));
2734
+ return {
2735
+ point: {
2736
+ x: roundTo((s + e) / 2 - canvasW / 2, 0.01),
2737
+ y: roundTo(canvasH / 2 - (bbox.y + ks + 0.5), 0.01)
2738
+ },
2739
+ radius,
2740
+ row: ks
2741
+ };
2742
+ }
2743
+ function runSpan(layer) {
2744
+ const { bbox, rowRuns } = layer;
2745
+ return (k) => {
2708
2746
  const runs = rowRuns?.[k] ?? [];
2709
2747
  if (runs.length === 0) return [bbox.x, bbox.x + bbox.w];
2710
- let [s2, e2] = [runs[0], runs[1]];
2748
+ let [s, e] = [runs[0], runs[1]];
2711
2749
  for (let i = 2; i < runs.length; i += 2) {
2712
- if (runs[i + 1] - runs[i] > e2 - s2) [s2, e2] = [runs[i], runs[i + 1]];
2750
+ if (runs[i + 1] - runs[i] > e - s) [s, e] = [runs[i], runs[i + 1]];
2713
2751
  }
2714
- return [s2, e2];
2752
+ return [s, e];
2715
2753
  };
2754
+ }
2755
+ function armGeometry(layer, bodyAxisX) {
2756
+ const { bbox, canvasW, canvasH, rowRuns } = layer;
2757
+ const b = boxOfLayer(layer);
2758
+ const span = runSpan(layer);
2716
2759
  const centreX = (k) => {
2717
2760
  const [s2, e2] = span(k);
2718
2761
  return (s2 + e2) / 2 - canvasW / 2;
2719
2762
  };
2720
2763
  const rowY = (k) => canvasH / 2 - (bbox.y + k + 0.5);
2721
2764
  const painted = rowRuns?.flatMap((runs, k) => runs.length > 0 ? [k] : []) ?? [];
2722
- const first = painted[0] ?? 0;
2723
2765
  const last = painted.at(-1) ?? bbox.h - 1;
2724
- const lo = Math.ceil(CAP_ROWS[0] * bbox.h);
2725
- const hi = Math.max(lo, Math.floor(CAP_ROWS[1] * bbox.h));
2726
- const widths = [];
2727
- for (let k = lo; k <= hi; k++) {
2728
- const [s2, e2] = span(k);
2729
- widths.push(e2 - s2);
2730
- }
2731
- widths.sort((p, q) => p - q);
2732
- const ru = widths[widths.length >> 1] / 2;
2733
- const ks = first + ru;
2734
- const shoulder = {
2735
- x: roundTo(centreX(Math.round(ks)), 0.01),
2736
- y: roundTo(rowY(ks), 0.01)
2737
- };
2766
+ const { point: shoulder, radius: ru, row: ks } = endPivot(layer, "top");
2738
2767
  const ke = Math.round(ks + ELBOW_AT * (last - ks));
2739
2768
  if (ke <= ks) {
2740
2769
  throw new LayerGeometryError(
@@ -2749,11 +2778,30 @@ function armGeometry(layer, bodyAxisX) {
2749
2778
  b.x1 - elbow.x,
2750
2779
  b.y1 - elbow.y
2751
2780
  );
2781
+ const inside = (x, y) => {
2782
+ const k = Math.floor(y) - bbox.y;
2783
+ if (k < 0 || k >= bbox.h) return false;
2784
+ const [rs, re] = span(k);
2785
+ return rs < x && x < re;
2786
+ };
2787
+ const capRays = Array.from({ length: CAP_SEGMENTS + 1 }, (_, i) => {
2788
+ const a = Math.PI * i / CAP_SEGMENTS;
2789
+ let t = 1;
2790
+ while (t <= capRadius + CAP_INSET && inside(
2791
+ elbow.x + canvasW / 2 + t * Math.cos(a),
2792
+ canvasH / 2 - elbow.y - t * Math.sin(a)
2793
+ )) {
2794
+ t++;
2795
+ }
2796
+ return Math.min(capRadius, Math.max(1, t - capInset(i)));
2797
+ });
2752
2798
  return {
2753
2799
  shoulder,
2754
2800
  shoulderRadius: ru,
2755
2801
  elbow,
2756
2802
  capRadius,
2803
+ capRays,
2804
+ seamRun: [s - bbox.x, e - bbox.x],
2757
2805
  side: shoulder.x > bodyAxisX ? 1 : -1
2758
2806
  };
2759
2807
  }
@@ -2763,84 +2811,190 @@ function meshOf(vertices, indices) {
2763
2811
  );
2764
2812
  return { vertices, uvs, indices };
2765
2813
  }
2766
- function armParts(role, layer, g, order) {
2814
+ function armParts(role, layer, g, order, poseSwitch) {
2767
2815
  const b = boxOfLayer(layer);
2768
2816
  const lx = (x) => roundTo((x - cx(b)) / bw(b), 1e-5);
2769
2817
  const ly = (y) => roundTo((y - cy(b)) / bh(b), 1e-5);
2770
2818
  const seam = ly(g.elbow.y);
2771
- const band = (top, bottom) => [
2819
+ const band = (top2, bottom) => [
2772
2820
  -0.5,
2773
- top,
2821
+ top2,
2774
2822
  0.5,
2775
- top,
2823
+ top2,
2776
2824
  -0.5,
2777
2825
  bottom,
2778
2826
  0.5,
2779
2827
  bottom
2780
2828
  ];
2781
2829
  const quad = [2, 3, 0, 0, 3, 1];
2782
- const cap = [lx(g.elbow.x), seam];
2830
+ const rays = g.capRays.length;
2831
+ const top = (rays - 1) / 2;
2832
+ const at = (i, r) => {
2833
+ const a = Math.PI * i / (rays - 1);
2834
+ return [lx(g.elbow.x + r * Math.cos(a)), ly(g.elbow.y + r * Math.sin(a))];
2835
+ };
2836
+ const width = (i) => Math.min(CAP_RIM * g.capRadius, g.capRays[i] / 2);
2837
+ const ringRays = Array.from({ length: rays }, (_, i) => i);
2838
+ ringRays.splice(top, 0, top);
2839
+ const vertices = [lx(g.elbow.x), seam];
2840
+ for (let i = 0; i < rays; i++) {
2841
+ vertices.push(...at(i, g.capRays[i] - width(i)));
2842
+ }
2843
+ const ringInner = vertices.length / 2;
2844
+ for (const i of ringRays) vertices.push(...at(i, g.capRays[i] - width(i)));
2845
+ const ringOuter = vertices.length / 2;
2846
+ for (const i of ringRays) vertices.push(...at(i, g.capRays[i]));
2847
+ const { uvs } = meshOf(vertices, []);
2848
+ const col = (c) => roundTo(Math.min(Math.max(c + 0.5, 0.5), bw(b) - 0.5) / bw(b), 1e-5);
2849
+ const v = roundTo(0.5 - seam, 1e-5);
2850
+ ringRays.forEach((i, j) => {
2851
+ const right = j <= top;
2852
+ const outer = right ? g.seamRun[1] : g.seamRun[0] - 1;
2853
+ const step = right ? -width(i) : width(i);
2854
+ uvs[(ringInner + j) * 2] = col(outer + step);
2855
+ uvs[(ringInner + j) * 2 + 1] = v;
2856
+ uvs[(ringOuter + j) * 2] = col(outer);
2857
+ uvs[(ringOuter + j) * 2 + 1] = v;
2858
+ });
2783
2859
  const fan = [];
2784
- for (let i = 0; i <= CAP_SEGMENTS; i++) {
2785
- const a = Math.PI * i / CAP_SEGMENTS;
2786
- cap.push(
2787
- lx(g.elbow.x + g.capRadius * Math.cos(a)),
2788
- ly(g.elbow.y + g.capRadius * Math.sin(a))
2789
- );
2790
- if (i > 0) fan.push(4, 4 + i, 5 + i);
2791
- }
2792
- const part = (id, deformer, at, mesh) => ({
2860
+ for (let i = 0; i < rays - 1; i++) fan.push(0, 1 + i, 2 + i);
2861
+ const ring = [];
2862
+ for (let j = 0; j < ringRays.length - 1; j++) {
2863
+ if (j === top) continue;
2864
+ const [p, q] = [ringInner + j, ringOuter + j];
2865
+ ring.push(p, q, q + 1, p, q + 1, p + 1);
2866
+ }
2867
+ const part = (id, deformer, z, mesh, swaps = false) => ({
2793
2868
  id,
2794
2869
  color: [1, 1, 1, 1],
2795
2870
  width: bw(b),
2796
2871
  height: bh(b),
2797
2872
  transform: { x: cx(b), y: cy(b) },
2798
- order: at,
2873
+ order: z,
2799
2874
  deformer,
2800
- mesh
2875
+ mesh,
2876
+ ...swaps && poseSwitch !== void 0 ? {
2877
+ bindings: [
2878
+ {
2879
+ parameter: poseSwitch,
2880
+ channel: "opacity",
2881
+ from: 1,
2882
+ to: 0
2883
+ }
2884
+ ]
2885
+ } : {}
2801
2886
  });
2802
2887
  const ids = ARM_IDS[role];
2803
2888
  return [
2804
- part(role, ids.armDeformer, order, meshOf(band(0.5, seam), quad)),
2889
+ part(
2890
+ ids.cap,
2891
+ ids.forearmDeformer,
2892
+ order,
2893
+ {
2894
+ vertices,
2895
+ uvs,
2896
+ indices: [...fan, ...ring]
2897
+ },
2898
+ true
2899
+ ),
2900
+ part(role, ids.armDeformer, order + 1, meshOf(band(0.5, seam), quad)),
2805
2901
  part(
2806
2902
  ids.forearm,
2807
2903
  ids.forearmDeformer,
2808
- order + 1,
2809
- meshOf([...band(seam, -0.5), ...cap], [...quad, ...fan])
2904
+ order + 2,
2905
+ meshOf(band(seam, -0.5), quad),
2906
+ true
2810
2907
  )
2811
2908
  ];
2812
2909
  }
2910
+ var rotateBinding = (parameter, range, sign) => ({
2911
+ parameter,
2912
+ channel: "rotate",
2913
+ from: sign * range[0],
2914
+ to: sign * range[1]
2915
+ });
2813
2916
  function armDeformers(role, g) {
2814
2917
  const ids = ARM_IDS[role];
2815
2918
  const params = ARM_PARAMS[role];
2816
- const rotate = (parameter) => ({
2817
- parameter,
2818
- channel: "rotate",
2819
- from: g.side * ARM_RANGE[0],
2820
- to: g.side * ARM_RANGE[1]
2821
- });
2822
2919
  return [
2823
2920
  {
2824
2921
  id: ids.armDeformer,
2825
2922
  parent: BODY_WARP_ID,
2826
2923
  pivot: { ...g.shoulder },
2827
- bindings: [rotate(params.arm)]
2924
+ bindings: [rotateBinding(params.arm, ARM_RANGE, g.side)]
2828
2925
  },
2829
2926
  {
2830
2927
  id: ids.forearmDeformer,
2831
2928
  parent: ids.armDeformer,
2832
2929
  pivot: { ...g.elbow },
2833
- bindings: [rotate(params.elbow)]
2930
+ bindings: [rotateBinding(params.elbow, ELBOW_RANGE, -g.side)]
2834
2931
  }
2835
2932
  ];
2836
2933
  }
2837
2934
 
2935
+ // src/auto-rig/forearm-pose.ts
2936
+ import {
2937
+ StandardParameter as P4
2938
+ } from "@ikijs/format";
2939
+ var ARM_OF_POSE = {
2940
+ forearm_pose_L: "arm_L",
2941
+ forearm_pose_R: "arm_R"
2942
+ };
2943
+ var POSE_OF_ARM = {
2944
+ arm_L: "forearm_pose_L",
2945
+ arm_R: "forearm_pose_R"
2946
+ };
2947
+ var POSE_PARAMS = {
2948
+ forearm_pose_L: { pose: P4.ArmPoseLeft, angle: P4.ArmPoseAngleLeft },
2949
+ forearm_pose_R: { pose: P4.ArmPoseRight, angle: P4.ArmPoseAngleRight }
2950
+ };
2951
+ var POSE_IDS = {
2952
+ forearm_pose_L: "armPoseDeformer_L",
2953
+ forearm_pose_R: "armPoseDeformer_R"
2954
+ };
2955
+ var POSE_ANGLE_RANGE = [-15, 15];
2956
+ function forearmPoseGeometry(layer) {
2957
+ const { point, radius } = endPivot(layer, "bottom");
2958
+ const first = layer.rowRuns?.findIndex((runs) => runs.length > 0) ?? -1;
2959
+ const k = first < 0 ? 0 : first;
2960
+ return {
2961
+ pivot: point,
2962
+ radius,
2963
+ top: roundTo(layer.canvasH / 2 - (layer.bbox.y + k + 0.5), 0.01)
2964
+ };
2965
+ }
2966
+ function forearmPosePart(role, layer, order) {
2967
+ const b = boxOfLayer(layer);
2968
+ return {
2969
+ id: role,
2970
+ color: [1, 1, 1, 1],
2971
+ width: bw(b),
2972
+ height: bh(b),
2973
+ transform: { x: cx(b), y: cy(b) },
2974
+ order,
2975
+ deformer: POSE_IDS[role],
2976
+ bindings: [
2977
+ { parameter: POSE_PARAMS[role].pose, channel: "opacity", from: 0, to: 1 }
2978
+ ]
2979
+ };
2980
+ }
2981
+ function forearmPoseDeformer(role, arm) {
2982
+ return {
2983
+ id: POSE_IDS[role],
2984
+ parent: ARM_IDS[ARM_OF_POSE[role]].armDeformer,
2985
+ pivot: { ...arm.elbow },
2986
+ bindings: [
2987
+ rotateBinding(POSE_PARAMS[role].angle, POSE_ANGLE_RANGE, -arm.side)
2988
+ ]
2989
+ };
2990
+ }
2991
+
2838
2992
  // src/auto-rig/roles.ts
2839
2993
  var ROLE_TABLE = [
2840
2994
  { role: "hair_back", family: "hair_back" },
2841
2995
  { role: "body", family: "body" },
2842
- { role: "arm_L", family: "arm", parts: ["arm_L", "forearm_L"] },
2843
- { role: "arm_R", family: "arm", parts: ["arm_R", "forearm_R"] },
2996
+ { role: "arm_L", family: "arm", parts: ["elbow_L", "arm_L", "forearm_L"] },
2997
+ { role: "arm_R", family: "arm", parts: ["elbow_R", "arm_R", "forearm_R"] },
2844
2998
  { role: "face", family: "face", required: true },
2845
2999
  { role: "blush_L", family: "face" },
2846
3000
  { role: "blush_R", family: "face" },
@@ -2861,7 +3015,10 @@ var ROLE_TABLE = [
2861
3015
  { role: "lash_R", family: "eye_R" },
2862
3016
  { role: "brow_L", family: "brow_L" },
2863
3017
  { role: "brow_R", family: "brow_R" },
2864
- { role: "hair_front", family: "hair_front" }
3018
+ { role: "hair_front", family: "hair_front" },
3019
+ // Drawn above the face and the hair: a raised hand passes them.
3020
+ { role: "forearm_pose_L", family: "forearm_pose" },
3021
+ { role: "forearm_pose_R", family: "forearm_pose" }
2865
3022
  ];
2866
3023
  var REQUIRED_ROLES = ROLE_TABLE.filter(
2867
3024
  (r) => r.required
@@ -2901,6 +3058,16 @@ function checkArmsHaveBody(roles) {
2901
3058
  );
2902
3059
  }
2903
3060
  }
3061
+ function checkPosesHaveArms(roles) {
3062
+ const has = new Set(roles);
3063
+ for (const side of ["L", "R"]) {
3064
+ if (has.has(`forearm_pose_${side}`) && !has.has(`arm_${side}`)) {
3065
+ throw new Error(
3066
+ `auto-rig: forearm_pose_${side} needs an arm_${side} layer (a pose forearm hangs from its arm's elbow)`
3067
+ );
3068
+ }
3069
+ }
3070
+ }
2904
3071
  function parseLayerRoles(fileNames) {
2905
3072
  const seen = /* @__PURE__ */ new Map();
2906
3073
  const out = [];
@@ -2927,42 +3094,43 @@ function parseLayerRoles(fileNames) {
2927
3094
  );
2928
3095
  }
2929
3096
  checkArmsHaveBody(seen.keys());
3097
+ checkPosesHaveArms(seen.keys());
2930
3098
  return out;
2931
3099
  }
2932
3100
 
2933
3101
  // src/auto-rig/generate.ts
2934
3102
  import {
2935
3103
  IKI_FORMAT_VERSION as IKI_FORMAT_VERSION2,
2936
- StandardParameter as P6,
3104
+ StandardParameter as P7,
2937
3105
  parseIkiModel as parseIkiModel3
2938
3106
  } from "@ikijs/format";
2939
3107
 
2940
3108
  // src/auto-rig/animations.ts
2941
3109
  import {
2942
- StandardParameter as P4
3110
+ StandardParameter as P5
2943
3111
  } from "@ikijs/format";
2944
3112
  function browsY(y) {
2945
3113
  return [
2946
- { parameter: P4.BrowLeftY, value: y, blend: "add" },
2947
- { parameter: P4.BrowRightY, value: y, blend: "add" }
3114
+ { parameter: P5.BrowLeftY, value: y, blend: "add" },
3115
+ { parameter: P5.BrowRightY, value: y, blend: "add" }
2948
3116
  ];
2949
3117
  }
2950
3118
  function browsTilt(innerDown) {
2951
3119
  return [
2952
- { parameter: P4.BrowLeftAngle, value: innerDown, blend: "add" },
2953
- { parameter: P4.BrowRightAngle, value: -innerDown, blend: "add" }
3120
+ { parameter: P5.BrowLeftAngle, value: innerDown, blend: "add" },
3121
+ { parameter: P5.BrowRightAngle, value: -innerDown, blend: "add" }
2954
3122
  ];
2955
3123
  }
2956
3124
  function cheek(value) {
2957
- return { parameter: P4.Cheek, value, blend: "add" };
3125
+ return { parameter: P5.Cheek, value, blend: "add" };
2958
3126
  }
2959
- var BROWS = [P4.BrowLeftY, P4.BrowRightY, P4.BrowLeftAngle, P4.BrowRightAngle];
3127
+ var BROWS = [P5.BrowLeftY, P5.BrowRightY, P5.BrowLeftAngle, P5.BrowRightAngle];
2960
3128
  var EXPRESSIONS = [
2961
3129
  {
2962
3130
  id: "smile",
2963
3131
  description: "Smiling and pleased: warm, friendly, content. For greetings, thanks, agreement and gentle happiness.",
2964
3132
  parameters: [
2965
- { parameter: P4.MouthForm, value: 1, blend: "add" },
3133
+ { parameter: P5.MouthForm, value: 1, blend: "add" },
2966
3134
  ...browsY(0.25),
2967
3135
  cheek(0.4)
2968
3136
  ]
@@ -2971,10 +3139,10 @@ var EXPRESSIONS = [
2971
3139
  id: "laugh",
2972
3140
  description: "Laughing, eyes shut and mouth open: delighted, amused. For jokes, playfulness and big joy.",
2973
3141
  parameters: [
2974
- { parameter: P4.EyeOpenLeft, value: 0, blend: "multiply" },
2975
- { parameter: P4.EyeOpenRight, value: 0, blend: "multiply" },
2976
- { parameter: P4.MouthForm, value: 1, blend: "add" },
2977
- { parameter: P4.MouthOpen, value: 0.8, blend: "overwrite" },
3142
+ { parameter: P5.EyeOpenLeft, value: 0, blend: "multiply" },
3143
+ { parameter: P5.EyeOpenRight, value: 0, blend: "multiply" },
3144
+ { parameter: P5.MouthForm, value: 1, blend: "add" },
3145
+ { parameter: P5.MouthOpen, value: 0.8, blend: "overwrite" },
2978
3146
  ...browsY(0.4),
2979
3147
  cheek(0.65)
2980
3148
  ]
@@ -2986,7 +3154,7 @@ var EXPRESSIONS = [
2986
3154
  parameters: [
2987
3155
  ...browsY(-0.5),
2988
3156
  ...browsTilt(0.8),
2989
- { parameter: P4.MouthForm, value: -0.9, blend: "add" }
3157
+ { parameter: P5.MouthForm, value: -0.9, blend: "add" }
2990
3158
  ]
2991
3159
  },
2992
3160
  {
@@ -2996,7 +3164,7 @@ var EXPRESSIONS = [
2996
3164
  parameters: [
2997
3165
  ...browsY(0.35),
2998
3166
  ...browsTilt(-0.8),
2999
- { parameter: P4.MouthForm, value: -0.8, blend: "add" }
3167
+ { parameter: P5.MouthForm, value: -0.8, blend: "add" }
3000
3168
  ]
3001
3169
  },
3002
3170
  {
@@ -3004,18 +3172,18 @@ var EXPRESSIONS = [
3004
3172
  description: "Surprised, mouth dropped open: startled, amazed. For shock, sudden news or disbelief.",
3005
3173
  parameters: [
3006
3174
  ...browsY(0.8),
3007
- { parameter: P4.MouthOpen, value: 0.9, blend: "overwrite" }
3175
+ { parameter: P5.MouthOpen, value: 0.9, blend: "overwrite" }
3008
3176
  ]
3009
3177
  },
3010
3178
  {
3011
3179
  id: "shy",
3012
3180
  description: "Shy or embarrassed: bashful, flustered. For being praised, teased or caught off guard.",
3013
- needs: [P4.Cheek],
3181
+ needs: [P5.Cheek],
3014
3182
  parameters: [
3015
3183
  cheek(1),
3016
3184
  // Gaze down.
3017
- { parameter: P4.EyeballY, value: -0.65, blend: "add" },
3018
- { parameter: P4.MouthForm, value: 0.4, blend: "add" },
3185
+ { parameter: P5.EyeballY, value: -0.65, blend: "add" },
3186
+ { parameter: P5.MouthForm, value: 0.4, blend: "add" },
3019
3187
  ...browsTilt(-0.25)
3020
3188
  ]
3021
3189
  }
@@ -3028,14 +3196,14 @@ function defaultExpressions(declared) {
3028
3196
  parameters: e.parameters.filter((t) => declared.has(t.parameter))
3029
3197
  }));
3030
3198
  }
3031
- var DEFAULT_MOTIONS = {
3199
+ var HEAD_MOTIONS = {
3032
3200
  Nod: [
3033
3201
  {
3034
3202
  description: "Nods yes: agreement, acknowledgement, understanding.",
3035
3203
  duration: 1,
3036
3204
  curves: [
3037
3205
  {
3038
- parameter: P4.AngleY,
3206
+ parameter: P5.AngleY,
3039
3207
  keys: [
3040
3208
  [0, 0],
3041
3209
  [0.3, -18],
@@ -3056,7 +3224,7 @@ var DEFAULT_MOTIONS = {
3056
3224
  fadeIn: 0.15,
3057
3225
  curves: [
3058
3226
  {
3059
- parameter: P4.AngleX,
3227
+ parameter: P5.AngleX,
3060
3228
  keys: [
3061
3229
  [0, 0],
3062
3230
  [0.2, -18],
@@ -3075,7 +3243,7 @@ var DEFAULT_MOTIONS = {
3075
3243
  duration: 1.4,
3076
3244
  curves: [
3077
3245
  {
3078
- parameter: P4.AngleZ,
3246
+ parameter: P5.AngleZ,
3079
3247
  keys: [
3080
3248
  [0, 0],
3081
3249
  [0.35, 20],
@@ -3087,6 +3255,55 @@ var DEFAULT_MOTIONS = {
3087
3255
  }
3088
3256
  ]
3089
3257
  };
3258
+ var WAVE = {
3259
+ Wave: [
3260
+ {
3261
+ description: "Waves hello with the right hand: greeting, goodbye, getting attention.",
3262
+ duration: 2.2,
3263
+ fadeIn: 0.15,
3264
+ fadeOut: 0.15,
3265
+ curves: [
3266
+ {
3267
+ parameter: P5.ArmPoseRight,
3268
+ interpolation: "linear",
3269
+ keys: [
3270
+ [0, 0],
3271
+ [0.15, 1],
3272
+ [2.05, 1],
3273
+ [2.2, 0]
3274
+ ]
3275
+ },
3276
+ {
3277
+ parameter: P5.ArmRight,
3278
+ keys: [
3279
+ [0, 0],
3280
+ [0.4, 10],
3281
+ [1.8, 10],
3282
+ [2.2, 0]
3283
+ ]
3284
+ },
3285
+ {
3286
+ parameter: P5.ArmPoseAngleRight,
3287
+ keys: [
3288
+ [0, 0],
3289
+ [0.5, 0],
3290
+ [0.7, 12],
3291
+ [1, -12],
3292
+ [1.3, 12],
3293
+ [1.7, 0],
3294
+ [2.2, 0]
3295
+ ]
3296
+ }
3297
+ ]
3298
+ }
3299
+ ]
3300
+ };
3301
+ function defaultMotions(declared) {
3302
+ const waves = [P5.ArmRight, P5.ArmPoseRight, P5.ArmPoseAngleRight].every(
3303
+ (id) => declared.has(id)
3304
+ );
3305
+ return waves ? { ...HEAD_MOTIONS, ...WAVE } : HEAD_MOTIONS;
3306
+ }
3090
3307
 
3091
3308
  // src/auto-rig/checks.ts
3092
3309
  var finite = (v) => typeof v === "number" && Number.isFinite(v);
@@ -3192,6 +3409,7 @@ function checkLayers(layers, canvas) {
3192
3409
  throw new Error(`auto-rig: missing required role(s) ${missing.join(", ")}`);
3193
3410
  }
3194
3411
  checkArmsHaveBody(seen);
3412
+ checkPosesHaveArms(seen);
3195
3413
  }
3196
3414
  function checkHeadEdges(edges, byRole) {
3197
3415
  if (edges === void 0) return;
@@ -3309,7 +3527,7 @@ function checkStrandEdges(strand, irises, hair) {
3309
3527
  }
3310
3528
 
3311
3529
  // src/auto-rig/context.ts
3312
- import { StandardParameter as P5 } from "@ikijs/format";
3530
+ import { StandardParameter as P6 } from "@ikijs/format";
3313
3531
 
3314
3532
  // src/auto-rig/head.ts
3315
3533
  function outerEdge(runs, axisX, side) {
@@ -4341,6 +4559,7 @@ function familyField(m, family) {
4341
4559
  return hairField(m, m.hairBack, NOD.hairBack);
4342
4560
  case "body":
4343
4561
  case "arm":
4562
+ case "forearm_pose":
4344
4563
  return () => [0, 0];
4345
4564
  }
4346
4565
  }
@@ -4440,7 +4659,7 @@ function renderedLander(turn, parts, grids, from = /* @__PURE__ */ new Map()) {
4440
4659
  const family = roleSpec(role).family;
4441
4660
  const part = parts.find((p) => p.id === role);
4442
4661
  const g = grids.get(family);
4443
- const warp = part?.warps?.find((w) => w.parameter === P5.AngleX);
4662
+ const warp = part?.warps?.find((w) => w.parameter === P6.AngleX);
4444
4663
  if (part === void 0 || part.mesh === void 0 || !g && !warp) {
4445
4664
  return x + field(role)(x, y, ax, 0)[0];
4446
4665
  }
@@ -5176,6 +5395,7 @@ var WARP_ID = {
5176
5395
  };
5177
5396
  var isGridFamily = (f) => f in WARP_ID;
5178
5397
  var isArm = (spec) => spec.family === "arm";
5398
+ var isPose = (spec) => spec.family === "forearm_pose";
5179
5399
  var GRID_PAD = 6;
5180
5400
  function generateIkiFromLayerSet(layers, canvas, options = {}) {
5181
5401
  checkLayers(layers, canvas);
@@ -5244,11 +5464,22 @@ function generateIkiFromLayerSet(layers, canvas, options = {}) {
5244
5464
  const layer = byRole.get(spec.role);
5245
5465
  if (layer === void 0) continue;
5246
5466
  if (isArm(spec)) {
5467
+ const pose = POSE_OF_ARM[spec.role];
5247
5468
  parts.push(
5248
- ...armParts(spec.role, layer, arms.get(spec.role), parts.length)
5469
+ ...armParts(
5470
+ spec.role,
5471
+ layer,
5472
+ arms.get(spec.role),
5473
+ parts.length,
5474
+ has(pose) ? POSE_PARAMS[pose].pose : void 0
5475
+ )
5249
5476
  );
5250
5477
  continue;
5251
5478
  }
5479
+ if (isPose(spec)) {
5480
+ parts.push(forearmPosePart(spec.role, layer, parts.length));
5481
+ continue;
5482
+ }
5252
5483
  const built = buildPart(
5253
5484
  spec,
5254
5485
  boxOfLayer(layer),
@@ -5301,7 +5532,7 @@ function generateIkiFromLayerSet(layers, canvas, options = {}) {
5301
5532
  part.mesh = bodyWarp.mesh;
5302
5533
  continue;
5303
5534
  }
5304
- if (family === "arm") continue;
5535
+ if (family === "arm" || family === "forearm_pose") continue;
5305
5536
  part.deformer = "headDeformer";
5306
5537
  if (part.mesh === void 0) continue;
5307
5538
  const own = familyField(turn, family);
@@ -5318,10 +5549,10 @@ function generateIkiFromLayerSet(layers, canvas, options = {}) {
5318
5549
  canvas: { width: canvas.width, height: canvas.height },
5319
5550
  parameters,
5320
5551
  parts,
5321
- deformers: deformers(frame, grids, bodyWarp?.deformer, arms),
5552
+ deformers: deformers(frame, grids, bodyWarp?.deformer, arms, byRole),
5322
5553
  ...has("hair_front") ? { physics: hairPhysics() } : {},
5323
5554
  expressions: defaultExpressions(new Set(parameters.map((p) => p.id))),
5324
- motions: DEFAULT_MOTIONS
5555
+ motions: defaultMotions(new Set(parameters.map((p) => p.id)))
5325
5556
  };
5326
5557
  const valid = parseIkiModel3(model);
5327
5558
  if (hasNose && options.onTurnSolved) {
@@ -5338,14 +5569,14 @@ function generateIkiFromLayerSet(layers, canvas, options = {}) {
5338
5569
  function turnWarps(mesh, b, fieldOf) {
5339
5570
  return [
5340
5571
  localWarp(
5341
- P6.AngleX,
5572
+ P7.AngleX,
5342
5573
  mesh,
5343
5574
  b,
5344
5575
  X_STOPS,
5345
5576
  ([x, y], v, i) => v === 0 ? [0, 0] : [fieldOf(i)(x, y, v, 0)[0], 0]
5346
5577
  ),
5347
5578
  localWarp(
5348
- P6.AngleY,
5579
+ P7.AngleY,
5349
5580
  mesh,
5350
5581
  b,
5351
5582
  Y_STOPS,
@@ -5393,7 +5624,7 @@ function buildPart(spec, b, order, frame, byRole, style, frontGrid) {
5393
5624
  }
5394
5625
  if (mesh !== void 0) part.mesh = mesh;
5395
5626
  const side = spec.role.endsWith("_L") ? "L" : "R";
5396
- const eyeOpen = side === "L" ? P6.EyeOpenLeft : P6.EyeOpenRight;
5627
+ const eyeOpen = side === "L" ? P7.EyeOpenLeft : P7.EyeOpenRight;
5397
5628
  const hh = frame.hh;
5398
5629
  switch (spec.role) {
5399
5630
  case "face":
@@ -5408,7 +5639,7 @@ function buildPart(spec, b, order, frame, byRole, style, frontGrid) {
5408
5639
  headOwnRoll(byRole.has("body"))
5409
5640
  ),
5410
5641
  localWarp(
5411
- P6.Breath,
5642
+ P7.Breath,
5412
5643
  mesh,
5413
5644
  b,
5414
5645
  [0, 1],
@@ -5455,8 +5686,8 @@ function buildPart(spec, b, order, frame, byRole, style, frontGrid) {
5455
5686
  if (byRole.has("mouth_open")) {
5456
5687
  warps.push(mouthWiden(mesh, b));
5457
5688
  bindings.push(
5458
- { parameter: P6.MouthOpen, channel: "opacity", from: 1, to: 0 },
5459
- { parameter: P6.MouthOpen, channel: "opacity", from: 1, to: 0 }
5689
+ { parameter: P7.MouthOpen, channel: "opacity", from: 1, to: 0 },
5690
+ { parameter: P7.MouthOpen, channel: "opacity", from: 1, to: 0 }
5460
5691
  );
5461
5692
  extra = [(AMPLITUDE.mouthOpenWidth - 1) * (bw(b) / 2), 0];
5462
5693
  } else {
@@ -5466,19 +5697,19 @@ function buildPart(spec, b, order, frame, byRole, style, frontGrid) {
5466
5697
  );
5467
5698
  bindings.push(
5468
5699
  {
5469
- parameter: P6.MouthOpen,
5700
+ parameter: P7.MouthOpen,
5470
5701
  channel: "scaleY",
5471
5702
  from: 0,
5472
5703
  to: roundTo(grow, 0.01)
5473
5704
  },
5474
5705
  {
5475
- parameter: P6.MouthOpen,
5706
+ parameter: P7.MouthOpen,
5476
5707
  channel: "scaleX",
5477
5708
  from: 0,
5478
5709
  to: roundTo(AMPLITUDE.mouthOpenWidth - 1, 0.01)
5479
5710
  },
5480
5711
  {
5481
- parameter: P6.MouthOpen,
5712
+ parameter: P7.MouthOpen,
5482
5713
  channel: "translateY",
5483
5714
  from: 0,
5484
5715
  to: -roundTo(grow * bh(b) / 2, 0.01)
@@ -5490,7 +5721,7 @@ function buildPart(spec, b, order, frame, byRole, style, frontGrid) {
5490
5721
  case "mouth_open":
5491
5722
  warps.push(mouthForm(mesh, b), mouthOpenGrow(mesh, b));
5492
5723
  bindings.push({
5493
- parameter: P6.MouthOpen,
5724
+ parameter: P7.MouthOpen,
5494
5725
  channel: "opacity",
5495
5726
  from: 0,
5496
5727
  to: 1
@@ -5500,7 +5731,7 @@ function buildPart(spec, b, order, frame, byRole, style, frontGrid) {
5500
5731
  case "blush_L":
5501
5732
  case "blush_R":
5502
5733
  bindings.push({
5503
- parameter: P6.Cheek,
5734
+ parameter: P7.Cheek,
5504
5735
  channel: "opacity",
5505
5736
  from: AMPLITUDE.blushRest,
5506
5737
  to: 1
@@ -5511,8 +5742,8 @@ function buildPart(spec, b, order, frame, byRole, style, frontGrid) {
5511
5742
  if (byRole.has("hair_front")) {
5512
5743
  const amp = style.sway * AMPLITUDE.sway * hh;
5513
5744
  warps.push(
5514
- hairSway(P6.HairSwayX, mesh, b, amp),
5515
- hairSway(P6.HairSwayZ, mesh, b, amp)
5745
+ hairSway(P7.HairSwayX, mesh, b, amp),
5746
+ hairSway(P7.HairSwayZ, mesh, b, amp)
5516
5747
  );
5517
5748
  }
5518
5749
  warps.push(tiltHang(mesh, b, pivot, hairHangWeight(frame.chinY, b.y0)));
@@ -5585,10 +5816,14 @@ function meshScale(role, frame) {
5585
5816
  return Infinity;
5586
5817
  }
5587
5818
  }
5588
- function deformers(frame, grids, bodyWarp, arms) {
5819
+ function deformers(frame, grids, bodyWarp, arms, byRole) {
5589
5820
  const out = [];
5590
5821
  if (bodyWarp !== void 0) out.push(bodyWarp);
5591
5822
  for (const [role, g] of arms) out.push(...armDeformers(role, g));
5823
+ for (const [role, arm] of arms) {
5824
+ const pose = POSE_OF_ARM[role];
5825
+ if (byRole.has(pose)) out.push(forearmPoseDeformer(pose, arm));
5826
+ }
5592
5827
  const hasBody = bodyWarp !== void 0;
5593
5828
  const roll = headOwnRoll(hasBody);
5594
5829
  out.push({
@@ -5597,9 +5832,9 @@ function deformers(frame, grids, bodyWarp, arms) {
5597
5832
  pivot: { x: roundTo(frame.axisX, 0.01), y: roundTo(frame.chinY, 0.01) },
5598
5833
  bindings: [
5599
5834
  // AngleZ is clockwise-positive (Live2D's); a rotation is CCW-positive.
5600
- { parameter: P6.AngleZ, channel: "rotate", from: roll, to: -roll },
5835
+ { parameter: P7.AngleZ, channel: "rotate", from: roll, to: -roll },
5601
5836
  {
5602
- parameter: P6.Breath,
5837
+ parameter: P7.Breath,
5603
5838
  channel: "translateY",
5604
5839
  from: 0,
5605
5840
  to: roundTo(headOwnBreath(hasBody) * frame.hh, 0.01)
@@ -5617,8 +5852,8 @@ function deformers(frame, grids, bodyWarp, arms) {
5617
5852
  points: g.lattice.points
5618
5853
  },
5619
5854
  warp2d: {
5620
- parameter: P6.AngleX,
5621
- parameterY: P6.AngleY,
5855
+ parameter: P7.AngleX,
5856
+ parameterY: P7.AngleY,
5622
5857
  valuesX: X_STOPS,
5623
5858
  valuesY: Y_STOPS,
5624
5859
  keyforms2d: g.keyforms.map((offsets) => ({ offsets }))
@@ -5631,16 +5866,16 @@ function hairPhysics() {
5631
5866
  return [
5632
5867
  {
5633
5868
  id: "hairSway",
5634
- input: { parameter: P6.AngleX, weight: 1 },
5635
- output: { parameter: P6.HairSwayX, scale: 5 },
5869
+ input: { parameter: P7.AngleX, weight: 1 },
5870
+ output: { parameter: P7.HairSwayX, scale: 5 },
5636
5871
  mass: 1,
5637
5872
  stiffness: 30,
5638
5873
  damping: 3
5639
5874
  },
5640
5875
  {
5641
5876
  id: "hairTilt",
5642
- input: { parameter: P6.AngleZ, weight: 1 },
5643
- output: { parameter: P6.HairSwayZ, scale: 8 },
5877
+ input: { parameter: P7.AngleZ, weight: 1 },
5878
+ output: { parameter: P7.HairSwayZ, scale: 8 },
5644
5879
  mass: 1,
5645
5880
  stiffness: 25,
5646
5881
  damping: 2.5
@@ -5650,46 +5885,55 @@ function hairPhysics() {
5650
5885
  function declareParameters(roles) {
5651
5886
  const out = [];
5652
5887
  const add = (id, name, min, max, def) => out.push({ id, name, min, max, default: def });
5653
- add(P6.AngleX, "Head Angle", -30, 30, 0);
5654
- add(P6.AngleY, "Head Angle Y", -30, 30, 0);
5655
- add(P6.AngleZ, "Head Angle Z", -30, 30, 0);
5888
+ add(P7.AngleX, "Head Angle", -30, 30, 0);
5889
+ add(P7.AngleY, "Head Angle Y", -30, 30, 0);
5890
+ add(P7.AngleZ, "Head Angle Z", -30, 30, 0);
5656
5891
  if (roles.has("body")) {
5657
- add(P6.BodyAngleX, "Body Angle X", -10, 10, 0);
5658
- add(P6.BodyAngleY, "Body Angle Y", -10, 10, 0);
5659
- add(P6.BodyAngleZ, "Body Angle Z", -10, 10, 0);
5892
+ add(P7.BodyAngleX, "Body Angle X", -10, 10, 0);
5893
+ add(P7.BodyAngleY, "Body Angle Y", -10, 10, 0);
5894
+ add(P7.BodyAngleZ, "Body Angle Z", -10, 10, 0);
5660
5895
  }
5896
+ const addPose = (side, pose, angle) => {
5897
+ add(pose, `Arm Pose ${side}`, 0, 1, 0);
5898
+ add(angle, `Arm Pose Angle ${side}`, ...POSE_ANGLE_RANGE, 0);
5899
+ };
5661
5900
  const [armMin, armMax] = ARM_RANGE;
5901
+ const [elbowMin, elbowMax] = ELBOW_RANGE;
5662
5902
  if (roles.has("arm_L")) {
5663
- add(P6.ArmLeft, "Arm L", armMin, armMax, 0);
5664
- add(P6.ElbowLeft, "Elbow L", armMin, armMax, 0);
5903
+ add(P7.ArmLeft, "Arm L", armMin, armMax, 0);
5904
+ add(P7.ElbowLeft, "Elbow L", elbowMin, elbowMax, 0);
5905
+ if (roles.has("forearm_pose_L"))
5906
+ addPose("L", P7.ArmPoseLeft, P7.ArmPoseAngleLeft);
5665
5907
  }
5666
5908
  if (roles.has("arm_R")) {
5667
- add(P6.ArmRight, "Arm R", armMin, armMax, 0);
5668
- add(P6.ElbowRight, "Elbow R", armMin, armMax, 0);
5909
+ add(P7.ArmRight, "Arm R", armMin, armMax, 0);
5910
+ add(P7.ElbowRight, "Elbow R", elbowMin, elbowMax, 0);
5911
+ if (roles.has("forearm_pose_R"))
5912
+ addPose("R", P7.ArmPoseRight, P7.ArmPoseAngleRight);
5669
5913
  }
5670
- add(P6.EyeOpenLeft, "Eye L", 0, 1, 1);
5671
- add(P6.EyeOpenRight, "Eye R", 0, 1, 1);
5914
+ add(P7.EyeOpenLeft, "Eye L", 0, 1, 1);
5915
+ add(P7.EyeOpenRight, "Eye R", 0, 1, 1);
5672
5916
  if ([...roles].some((r) => /^(iris|pupil|highlight)_/.test(r))) {
5673
- add(P6.EyeballX, "Gaze X", -1, 1, 0);
5674
- add(P6.EyeballY, "Gaze Y", -1, 1, 0);
5917
+ add(P7.EyeballX, "Gaze X", -1, 1, 0);
5918
+ add(P7.EyeballY, "Gaze Y", -1, 1, 0);
5675
5919
  }
5676
- add(P6.MouthOpen, "Mouth Open", 0, 1, 0);
5677
- add(P6.MouthForm, "Mouth Form", -1, 1, 0);
5920
+ add(P7.MouthOpen, "Mouth Open", 0, 1, 0);
5921
+ add(P7.MouthForm, "Mouth Form", -1, 1, 0);
5678
5922
  if (roles.has("brow_L")) {
5679
- add(P6.BrowLeftY, "Brow L Y", -1, 1, 0);
5680
- add(P6.BrowLeftAngle, "Brow L Angle", -1, 1, 0);
5923
+ add(P7.BrowLeftY, "Brow L Y", -1, 1, 0);
5924
+ add(P7.BrowLeftAngle, "Brow L Angle", -1, 1, 0);
5681
5925
  }
5682
5926
  if (roles.has("brow_R")) {
5683
- add(P6.BrowRightY, "Brow R Y", -1, 1, 0);
5684
- add(P6.BrowRightAngle, "Brow R Angle", -1, 1, 0);
5927
+ add(P7.BrowRightY, "Brow R Y", -1, 1, 0);
5928
+ add(P7.BrowRightAngle, "Brow R Angle", -1, 1, 0);
5685
5929
  }
5686
5930
  if (roles.has("blush_L") || roles.has("blush_R")) {
5687
- add(P6.Cheek, "Cheek", 0, 1, 0);
5931
+ add(P7.Cheek, "Cheek", 0, 1, 0);
5688
5932
  }
5689
- add(P6.Breath, "Breath", 0, 1, 0);
5933
+ add(P7.Breath, "Breath", 0, 1, 0);
5690
5934
  if (roles.has("hair_front")) {
5691
- add(P6.HairSwayX, "Hair Sway X", -SWAY_RANGE, SWAY_RANGE, 0);
5692
- add(P6.HairSwayZ, "Hair Sway Z", -SWAY_RANGE, SWAY_RANGE, 0);
5935
+ add(P7.HairSwayX, "Hair Sway X", -SWAY_RANGE, SWAY_RANGE, 0);
5936
+ add(P7.HairSwayZ, "Hair Sway Z", -SWAY_RANGE, SWAY_RANGE, 0);
5693
5937
  }
5694
5938
  return out;
5695
5939
  }
@@ -5736,6 +5980,7 @@ export {
5736
5980
  createLayerSetMeasurer,
5737
5981
  denseCoreOf,
5738
5982
  detectAlphaBbox,
5983
+ forearmPoseGeometry,
5739
5984
  foregroundSpan,
5740
5985
  generateIkiFromLayerSet,
5741
5986
  headHalfOf,