@ikijs/editor 0.19.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,
@@ -2707,40 +2714,56 @@ var ARM_IDS = {
2707
2714
  forearmDeformer: "forearmDeformer_R"
2708
2715
  }
2709
2716
  };
2710
- function armGeometry(layer, bodyAxisX) {
2717
+ function endPivot(layer, end) {
2711
2718
  const { bbox, canvasW, canvasH, rowRuns } = layer;
2712
- const b = boxOfLayer(layer);
2713
- 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) => {
2714
2746
  const runs = rowRuns?.[k] ?? [];
2715
2747
  if (runs.length === 0) return [bbox.x, bbox.x + bbox.w];
2716
- let [s2, e2] = [runs[0], runs[1]];
2748
+ let [s, e] = [runs[0], runs[1]];
2717
2749
  for (let i = 2; i < runs.length; i += 2) {
2718
- 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]];
2719
2751
  }
2720
- return [s2, e2];
2752
+ return [s, e];
2721
2753
  };
2754
+ }
2755
+ function armGeometry(layer, bodyAxisX) {
2756
+ const { bbox, canvasW, canvasH, rowRuns } = layer;
2757
+ const b = boxOfLayer(layer);
2758
+ const span = runSpan(layer);
2722
2759
  const centreX = (k) => {
2723
2760
  const [s2, e2] = span(k);
2724
2761
  return (s2 + e2) / 2 - canvasW / 2;
2725
2762
  };
2726
2763
  const rowY = (k) => canvasH / 2 - (bbox.y + k + 0.5);
2727
2764
  const painted = rowRuns?.flatMap((runs, k) => runs.length > 0 ? [k] : []) ?? [];
2728
- const first = painted[0] ?? 0;
2729
2765
  const last = painted.at(-1) ?? bbox.h - 1;
2730
- const lo = Math.ceil(CAP_ROWS[0] * bbox.h);
2731
- const hi = Math.max(lo, Math.floor(CAP_ROWS[1] * bbox.h));
2732
- const widths = [];
2733
- for (let k = lo; k <= hi; k++) {
2734
- const [s2, e2] = span(k);
2735
- widths.push(e2 - s2);
2736
- }
2737
- widths.sort((p, q) => p - q);
2738
- const ru = widths[widths.length >> 1] / 2;
2739
- const ks = first + ru;
2740
- const shoulder = {
2741
- x: roundTo(centreX(Math.round(ks)), 0.01),
2742
- y: roundTo(rowY(ks), 0.01)
2743
- };
2766
+ const { point: shoulder, radius: ru, row: ks } = endPivot(layer, "top");
2744
2767
  const ke = Math.round(ks + ELBOW_AT * (last - ks));
2745
2768
  if (ke <= ks) {
2746
2769
  throw new LayerGeometryError(
@@ -2788,7 +2811,7 @@ function meshOf(vertices, indices) {
2788
2811
  );
2789
2812
  return { vertices, uvs, indices };
2790
2813
  }
2791
- function armParts(role, layer, g, order) {
2814
+ function armParts(role, layer, g, order, poseSwitch) {
2792
2815
  const b = boxOfLayer(layer);
2793
2816
  const lx = (x) => roundTo((x - cx(b)) / bw(b), 1e-5);
2794
2817
  const ly = (y) => roundTo((y - cy(b)) / bh(b), 1e-5);
@@ -2841,7 +2864,7 @@ function armParts(role, layer, g, order) {
2841
2864
  const [p, q] = [ringInner + j, ringOuter + j];
2842
2865
  ring.push(p, q, q + 1, p, q + 1, p + 1);
2843
2866
  }
2844
- const part = (id, deformer, z, mesh) => ({
2867
+ const part = (id, deformer, z, mesh, swaps = false) => ({
2845
2868
  id,
2846
2869
  color: [1, 1, 1, 1],
2847
2870
  width: bw(b),
@@ -2849,49 +2872,123 @@ function armParts(role, layer, g, order) {
2849
2872
  transform: { x: cx(b), y: cy(b) },
2850
2873
  order: z,
2851
2874
  deformer,
2852
- 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
+ } : {}
2853
2886
  });
2854
2887
  const ids = ARM_IDS[role];
2855
2888
  return [
2856
- part(ids.cap, ids.forearmDeformer, order, {
2857
- vertices,
2858
- uvs,
2859
- indices: [...fan, ...ring]
2860
- }),
2889
+ part(
2890
+ ids.cap,
2891
+ ids.forearmDeformer,
2892
+ order,
2893
+ {
2894
+ vertices,
2895
+ uvs,
2896
+ indices: [...fan, ...ring]
2897
+ },
2898
+ true
2899
+ ),
2861
2900
  part(role, ids.armDeformer, order + 1, meshOf(band(0.5, seam), quad)),
2862
2901
  part(
2863
2902
  ids.forearm,
2864
2903
  ids.forearmDeformer,
2865
2904
  order + 2,
2866
- meshOf(band(seam, -0.5), quad)
2905
+ meshOf(band(seam, -0.5), quad),
2906
+ true
2867
2907
  )
2868
2908
  ];
2869
2909
  }
2910
+ var rotateBinding = (parameter, range, sign) => ({
2911
+ parameter,
2912
+ channel: "rotate",
2913
+ from: sign * range[0],
2914
+ to: sign * range[1]
2915
+ });
2870
2916
  function armDeformers(role, g) {
2871
2917
  const ids = ARM_IDS[role];
2872
2918
  const params = ARM_PARAMS[role];
2873
- const rotate = (parameter, range, sign) => ({
2874
- parameter,
2875
- channel: "rotate",
2876
- from: sign * range[0],
2877
- to: sign * range[1]
2878
- });
2879
2919
  return [
2880
2920
  {
2881
2921
  id: ids.armDeformer,
2882
2922
  parent: BODY_WARP_ID,
2883
2923
  pivot: { ...g.shoulder },
2884
- bindings: [rotate(params.arm, ARM_RANGE, g.side)]
2924
+ bindings: [rotateBinding(params.arm, ARM_RANGE, g.side)]
2885
2925
  },
2886
2926
  {
2887
2927
  id: ids.forearmDeformer,
2888
2928
  parent: ids.armDeformer,
2889
2929
  pivot: { ...g.elbow },
2890
- bindings: [rotate(params.elbow, ELBOW_RANGE, -g.side)]
2930
+ bindings: [rotateBinding(params.elbow, ELBOW_RANGE, -g.side)]
2891
2931
  }
2892
2932
  ];
2893
2933
  }
2894
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
+
2895
2992
  // src/auto-rig/roles.ts
2896
2993
  var ROLE_TABLE = [
2897
2994
  { role: "hair_back", family: "hair_back" },
@@ -2918,7 +3015,10 @@ var ROLE_TABLE = [
2918
3015
  { role: "lash_R", family: "eye_R" },
2919
3016
  { role: "brow_L", family: "brow_L" },
2920
3017
  { role: "brow_R", family: "brow_R" },
2921
- { 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" }
2922
3022
  ];
2923
3023
  var REQUIRED_ROLES = ROLE_TABLE.filter(
2924
3024
  (r) => r.required
@@ -2958,6 +3058,16 @@ function checkArmsHaveBody(roles) {
2958
3058
  );
2959
3059
  }
2960
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
+ }
2961
3071
  function parseLayerRoles(fileNames) {
2962
3072
  const seen = /* @__PURE__ */ new Map();
2963
3073
  const out = [];
@@ -2984,42 +3094,43 @@ function parseLayerRoles(fileNames) {
2984
3094
  );
2985
3095
  }
2986
3096
  checkArmsHaveBody(seen.keys());
3097
+ checkPosesHaveArms(seen.keys());
2987
3098
  return out;
2988
3099
  }
2989
3100
 
2990
3101
  // src/auto-rig/generate.ts
2991
3102
  import {
2992
3103
  IKI_FORMAT_VERSION as IKI_FORMAT_VERSION2,
2993
- StandardParameter as P6,
3104
+ StandardParameter as P7,
2994
3105
  parseIkiModel as parseIkiModel3
2995
3106
  } from "@ikijs/format";
2996
3107
 
2997
3108
  // src/auto-rig/animations.ts
2998
3109
  import {
2999
- StandardParameter as P4
3110
+ StandardParameter as P5
3000
3111
  } from "@ikijs/format";
3001
3112
  function browsY(y) {
3002
3113
  return [
3003
- { parameter: P4.BrowLeftY, value: y, blend: "add" },
3004
- { parameter: P4.BrowRightY, value: y, blend: "add" }
3114
+ { parameter: P5.BrowLeftY, value: y, blend: "add" },
3115
+ { parameter: P5.BrowRightY, value: y, blend: "add" }
3005
3116
  ];
3006
3117
  }
3007
3118
  function browsTilt(innerDown) {
3008
3119
  return [
3009
- { parameter: P4.BrowLeftAngle, value: innerDown, blend: "add" },
3010
- { parameter: P4.BrowRightAngle, value: -innerDown, blend: "add" }
3120
+ { parameter: P5.BrowLeftAngle, value: innerDown, blend: "add" },
3121
+ { parameter: P5.BrowRightAngle, value: -innerDown, blend: "add" }
3011
3122
  ];
3012
3123
  }
3013
3124
  function cheek(value) {
3014
- return { parameter: P4.Cheek, value, blend: "add" };
3125
+ return { parameter: P5.Cheek, value, blend: "add" };
3015
3126
  }
3016
- var BROWS = [P4.BrowLeftY, P4.BrowRightY, P4.BrowLeftAngle, P4.BrowRightAngle];
3127
+ var BROWS = [P5.BrowLeftY, P5.BrowRightY, P5.BrowLeftAngle, P5.BrowRightAngle];
3017
3128
  var EXPRESSIONS = [
3018
3129
  {
3019
3130
  id: "smile",
3020
3131
  description: "Smiling and pleased: warm, friendly, content. For greetings, thanks, agreement and gentle happiness.",
3021
3132
  parameters: [
3022
- { parameter: P4.MouthForm, value: 1, blend: "add" },
3133
+ { parameter: P5.MouthForm, value: 1, blend: "add" },
3023
3134
  ...browsY(0.25),
3024
3135
  cheek(0.4)
3025
3136
  ]
@@ -3028,10 +3139,10 @@ var EXPRESSIONS = [
3028
3139
  id: "laugh",
3029
3140
  description: "Laughing, eyes shut and mouth open: delighted, amused. For jokes, playfulness and big joy.",
3030
3141
  parameters: [
3031
- { parameter: P4.EyeOpenLeft, value: 0, blend: "multiply" },
3032
- { parameter: P4.EyeOpenRight, value: 0, blend: "multiply" },
3033
- { parameter: P4.MouthForm, value: 1, blend: "add" },
3034
- { 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" },
3035
3146
  ...browsY(0.4),
3036
3147
  cheek(0.65)
3037
3148
  ]
@@ -3043,7 +3154,7 @@ var EXPRESSIONS = [
3043
3154
  parameters: [
3044
3155
  ...browsY(-0.5),
3045
3156
  ...browsTilt(0.8),
3046
- { parameter: P4.MouthForm, value: -0.9, blend: "add" }
3157
+ { parameter: P5.MouthForm, value: -0.9, blend: "add" }
3047
3158
  ]
3048
3159
  },
3049
3160
  {
@@ -3053,7 +3164,7 @@ var EXPRESSIONS = [
3053
3164
  parameters: [
3054
3165
  ...browsY(0.35),
3055
3166
  ...browsTilt(-0.8),
3056
- { parameter: P4.MouthForm, value: -0.8, blend: "add" }
3167
+ { parameter: P5.MouthForm, value: -0.8, blend: "add" }
3057
3168
  ]
3058
3169
  },
3059
3170
  {
@@ -3061,18 +3172,18 @@ var EXPRESSIONS = [
3061
3172
  description: "Surprised, mouth dropped open: startled, amazed. For shock, sudden news or disbelief.",
3062
3173
  parameters: [
3063
3174
  ...browsY(0.8),
3064
- { parameter: P4.MouthOpen, value: 0.9, blend: "overwrite" }
3175
+ { parameter: P5.MouthOpen, value: 0.9, blend: "overwrite" }
3065
3176
  ]
3066
3177
  },
3067
3178
  {
3068
3179
  id: "shy",
3069
3180
  description: "Shy or embarrassed: bashful, flustered. For being praised, teased or caught off guard.",
3070
- needs: [P4.Cheek],
3181
+ needs: [P5.Cheek],
3071
3182
  parameters: [
3072
3183
  cheek(1),
3073
3184
  // Gaze down.
3074
- { parameter: P4.EyeballY, value: -0.65, blend: "add" },
3075
- { 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" },
3076
3187
  ...browsTilt(-0.25)
3077
3188
  ]
3078
3189
  }
@@ -3085,14 +3196,14 @@ function defaultExpressions(declared) {
3085
3196
  parameters: e.parameters.filter((t) => declared.has(t.parameter))
3086
3197
  }));
3087
3198
  }
3088
- var DEFAULT_MOTIONS = {
3199
+ var HEAD_MOTIONS = {
3089
3200
  Nod: [
3090
3201
  {
3091
3202
  description: "Nods yes: agreement, acknowledgement, understanding.",
3092
3203
  duration: 1,
3093
3204
  curves: [
3094
3205
  {
3095
- parameter: P4.AngleY,
3206
+ parameter: P5.AngleY,
3096
3207
  keys: [
3097
3208
  [0, 0],
3098
3209
  [0.3, -18],
@@ -3113,7 +3224,7 @@ var DEFAULT_MOTIONS = {
3113
3224
  fadeIn: 0.15,
3114
3225
  curves: [
3115
3226
  {
3116
- parameter: P4.AngleX,
3227
+ parameter: P5.AngleX,
3117
3228
  keys: [
3118
3229
  [0, 0],
3119
3230
  [0.2, -18],
@@ -3132,7 +3243,7 @@ var DEFAULT_MOTIONS = {
3132
3243
  duration: 1.4,
3133
3244
  curves: [
3134
3245
  {
3135
- parameter: P4.AngleZ,
3246
+ parameter: P5.AngleZ,
3136
3247
  keys: [
3137
3248
  [0, 0],
3138
3249
  [0.35, 20],
@@ -3144,6 +3255,55 @@ var DEFAULT_MOTIONS = {
3144
3255
  }
3145
3256
  ]
3146
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
+ }
3147
3307
 
3148
3308
  // src/auto-rig/checks.ts
3149
3309
  var finite = (v) => typeof v === "number" && Number.isFinite(v);
@@ -3249,6 +3409,7 @@ function checkLayers(layers, canvas) {
3249
3409
  throw new Error(`auto-rig: missing required role(s) ${missing.join(", ")}`);
3250
3410
  }
3251
3411
  checkArmsHaveBody(seen);
3412
+ checkPosesHaveArms(seen);
3252
3413
  }
3253
3414
  function checkHeadEdges(edges, byRole) {
3254
3415
  if (edges === void 0) return;
@@ -3366,7 +3527,7 @@ function checkStrandEdges(strand, irises, hair) {
3366
3527
  }
3367
3528
 
3368
3529
  // src/auto-rig/context.ts
3369
- import { StandardParameter as P5 } from "@ikijs/format";
3530
+ import { StandardParameter as P6 } from "@ikijs/format";
3370
3531
 
3371
3532
  // src/auto-rig/head.ts
3372
3533
  function outerEdge(runs, axisX, side) {
@@ -4398,6 +4559,7 @@ function familyField(m, family) {
4398
4559
  return hairField(m, m.hairBack, NOD.hairBack);
4399
4560
  case "body":
4400
4561
  case "arm":
4562
+ case "forearm_pose":
4401
4563
  return () => [0, 0];
4402
4564
  }
4403
4565
  }
@@ -4497,7 +4659,7 @@ function renderedLander(turn, parts, grids, from = /* @__PURE__ */ new Map()) {
4497
4659
  const family = roleSpec(role).family;
4498
4660
  const part = parts.find((p) => p.id === role);
4499
4661
  const g = grids.get(family);
4500
- const warp = part?.warps?.find((w) => w.parameter === P5.AngleX);
4662
+ const warp = part?.warps?.find((w) => w.parameter === P6.AngleX);
4501
4663
  if (part === void 0 || part.mesh === void 0 || !g && !warp) {
4502
4664
  return x + field(role)(x, y, ax, 0)[0];
4503
4665
  }
@@ -5233,6 +5395,7 @@ var WARP_ID = {
5233
5395
  };
5234
5396
  var isGridFamily = (f) => f in WARP_ID;
5235
5397
  var isArm = (spec) => spec.family === "arm";
5398
+ var isPose = (spec) => spec.family === "forearm_pose";
5236
5399
  var GRID_PAD = 6;
5237
5400
  function generateIkiFromLayerSet(layers, canvas, options = {}) {
5238
5401
  checkLayers(layers, canvas);
@@ -5301,11 +5464,22 @@ function generateIkiFromLayerSet(layers, canvas, options = {}) {
5301
5464
  const layer = byRole.get(spec.role);
5302
5465
  if (layer === void 0) continue;
5303
5466
  if (isArm(spec)) {
5467
+ const pose = POSE_OF_ARM[spec.role];
5304
5468
  parts.push(
5305
- ...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
+ )
5306
5476
  );
5307
5477
  continue;
5308
5478
  }
5479
+ if (isPose(spec)) {
5480
+ parts.push(forearmPosePart(spec.role, layer, parts.length));
5481
+ continue;
5482
+ }
5309
5483
  const built = buildPart(
5310
5484
  spec,
5311
5485
  boxOfLayer(layer),
@@ -5358,7 +5532,7 @@ function generateIkiFromLayerSet(layers, canvas, options = {}) {
5358
5532
  part.mesh = bodyWarp.mesh;
5359
5533
  continue;
5360
5534
  }
5361
- if (family === "arm") continue;
5535
+ if (family === "arm" || family === "forearm_pose") continue;
5362
5536
  part.deformer = "headDeformer";
5363
5537
  if (part.mesh === void 0) continue;
5364
5538
  const own = familyField(turn, family);
@@ -5375,10 +5549,10 @@ function generateIkiFromLayerSet(layers, canvas, options = {}) {
5375
5549
  canvas: { width: canvas.width, height: canvas.height },
5376
5550
  parameters,
5377
5551
  parts,
5378
- deformers: deformers(frame, grids, bodyWarp?.deformer, arms),
5552
+ deformers: deformers(frame, grids, bodyWarp?.deformer, arms, byRole),
5379
5553
  ...has("hair_front") ? { physics: hairPhysics() } : {},
5380
5554
  expressions: defaultExpressions(new Set(parameters.map((p) => p.id))),
5381
- motions: DEFAULT_MOTIONS
5555
+ motions: defaultMotions(new Set(parameters.map((p) => p.id)))
5382
5556
  };
5383
5557
  const valid = parseIkiModel3(model);
5384
5558
  if (hasNose && options.onTurnSolved) {
@@ -5395,14 +5569,14 @@ function generateIkiFromLayerSet(layers, canvas, options = {}) {
5395
5569
  function turnWarps(mesh, b, fieldOf) {
5396
5570
  return [
5397
5571
  localWarp(
5398
- P6.AngleX,
5572
+ P7.AngleX,
5399
5573
  mesh,
5400
5574
  b,
5401
5575
  X_STOPS,
5402
5576
  ([x, y], v, i) => v === 0 ? [0, 0] : [fieldOf(i)(x, y, v, 0)[0], 0]
5403
5577
  ),
5404
5578
  localWarp(
5405
- P6.AngleY,
5579
+ P7.AngleY,
5406
5580
  mesh,
5407
5581
  b,
5408
5582
  Y_STOPS,
@@ -5450,7 +5624,7 @@ function buildPart(spec, b, order, frame, byRole, style, frontGrid) {
5450
5624
  }
5451
5625
  if (mesh !== void 0) part.mesh = mesh;
5452
5626
  const side = spec.role.endsWith("_L") ? "L" : "R";
5453
- const eyeOpen = side === "L" ? P6.EyeOpenLeft : P6.EyeOpenRight;
5627
+ const eyeOpen = side === "L" ? P7.EyeOpenLeft : P7.EyeOpenRight;
5454
5628
  const hh = frame.hh;
5455
5629
  switch (spec.role) {
5456
5630
  case "face":
@@ -5465,7 +5639,7 @@ function buildPart(spec, b, order, frame, byRole, style, frontGrid) {
5465
5639
  headOwnRoll(byRole.has("body"))
5466
5640
  ),
5467
5641
  localWarp(
5468
- P6.Breath,
5642
+ P7.Breath,
5469
5643
  mesh,
5470
5644
  b,
5471
5645
  [0, 1],
@@ -5512,8 +5686,8 @@ function buildPart(spec, b, order, frame, byRole, style, frontGrid) {
5512
5686
  if (byRole.has("mouth_open")) {
5513
5687
  warps.push(mouthWiden(mesh, b));
5514
5688
  bindings.push(
5515
- { parameter: P6.MouthOpen, channel: "opacity", from: 1, to: 0 },
5516
- { 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 }
5517
5691
  );
5518
5692
  extra = [(AMPLITUDE.mouthOpenWidth - 1) * (bw(b) / 2), 0];
5519
5693
  } else {
@@ -5523,19 +5697,19 @@ function buildPart(spec, b, order, frame, byRole, style, frontGrid) {
5523
5697
  );
5524
5698
  bindings.push(
5525
5699
  {
5526
- parameter: P6.MouthOpen,
5700
+ parameter: P7.MouthOpen,
5527
5701
  channel: "scaleY",
5528
5702
  from: 0,
5529
5703
  to: roundTo(grow, 0.01)
5530
5704
  },
5531
5705
  {
5532
- parameter: P6.MouthOpen,
5706
+ parameter: P7.MouthOpen,
5533
5707
  channel: "scaleX",
5534
5708
  from: 0,
5535
5709
  to: roundTo(AMPLITUDE.mouthOpenWidth - 1, 0.01)
5536
5710
  },
5537
5711
  {
5538
- parameter: P6.MouthOpen,
5712
+ parameter: P7.MouthOpen,
5539
5713
  channel: "translateY",
5540
5714
  from: 0,
5541
5715
  to: -roundTo(grow * bh(b) / 2, 0.01)
@@ -5547,7 +5721,7 @@ function buildPart(spec, b, order, frame, byRole, style, frontGrid) {
5547
5721
  case "mouth_open":
5548
5722
  warps.push(mouthForm(mesh, b), mouthOpenGrow(mesh, b));
5549
5723
  bindings.push({
5550
- parameter: P6.MouthOpen,
5724
+ parameter: P7.MouthOpen,
5551
5725
  channel: "opacity",
5552
5726
  from: 0,
5553
5727
  to: 1
@@ -5557,7 +5731,7 @@ function buildPart(spec, b, order, frame, byRole, style, frontGrid) {
5557
5731
  case "blush_L":
5558
5732
  case "blush_R":
5559
5733
  bindings.push({
5560
- parameter: P6.Cheek,
5734
+ parameter: P7.Cheek,
5561
5735
  channel: "opacity",
5562
5736
  from: AMPLITUDE.blushRest,
5563
5737
  to: 1
@@ -5568,8 +5742,8 @@ function buildPart(spec, b, order, frame, byRole, style, frontGrid) {
5568
5742
  if (byRole.has("hair_front")) {
5569
5743
  const amp = style.sway * AMPLITUDE.sway * hh;
5570
5744
  warps.push(
5571
- hairSway(P6.HairSwayX, mesh, b, amp),
5572
- hairSway(P6.HairSwayZ, mesh, b, amp)
5745
+ hairSway(P7.HairSwayX, mesh, b, amp),
5746
+ hairSway(P7.HairSwayZ, mesh, b, amp)
5573
5747
  );
5574
5748
  }
5575
5749
  warps.push(tiltHang(mesh, b, pivot, hairHangWeight(frame.chinY, b.y0)));
@@ -5642,10 +5816,14 @@ function meshScale(role, frame) {
5642
5816
  return Infinity;
5643
5817
  }
5644
5818
  }
5645
- function deformers(frame, grids, bodyWarp, arms) {
5819
+ function deformers(frame, grids, bodyWarp, arms, byRole) {
5646
5820
  const out = [];
5647
5821
  if (bodyWarp !== void 0) out.push(bodyWarp);
5648
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
+ }
5649
5827
  const hasBody = bodyWarp !== void 0;
5650
5828
  const roll = headOwnRoll(hasBody);
5651
5829
  out.push({
@@ -5654,9 +5832,9 @@ function deformers(frame, grids, bodyWarp, arms) {
5654
5832
  pivot: { x: roundTo(frame.axisX, 0.01), y: roundTo(frame.chinY, 0.01) },
5655
5833
  bindings: [
5656
5834
  // AngleZ is clockwise-positive (Live2D's); a rotation is CCW-positive.
5657
- { parameter: P6.AngleZ, channel: "rotate", from: roll, to: -roll },
5835
+ { parameter: P7.AngleZ, channel: "rotate", from: roll, to: -roll },
5658
5836
  {
5659
- parameter: P6.Breath,
5837
+ parameter: P7.Breath,
5660
5838
  channel: "translateY",
5661
5839
  from: 0,
5662
5840
  to: roundTo(headOwnBreath(hasBody) * frame.hh, 0.01)
@@ -5674,8 +5852,8 @@ function deformers(frame, grids, bodyWarp, arms) {
5674
5852
  points: g.lattice.points
5675
5853
  },
5676
5854
  warp2d: {
5677
- parameter: P6.AngleX,
5678
- parameterY: P6.AngleY,
5855
+ parameter: P7.AngleX,
5856
+ parameterY: P7.AngleY,
5679
5857
  valuesX: X_STOPS,
5680
5858
  valuesY: Y_STOPS,
5681
5859
  keyforms2d: g.keyforms.map((offsets) => ({ offsets }))
@@ -5688,16 +5866,16 @@ function hairPhysics() {
5688
5866
  return [
5689
5867
  {
5690
5868
  id: "hairSway",
5691
- input: { parameter: P6.AngleX, weight: 1 },
5692
- output: { parameter: P6.HairSwayX, scale: 5 },
5869
+ input: { parameter: P7.AngleX, weight: 1 },
5870
+ output: { parameter: P7.HairSwayX, scale: 5 },
5693
5871
  mass: 1,
5694
5872
  stiffness: 30,
5695
5873
  damping: 3
5696
5874
  },
5697
5875
  {
5698
5876
  id: "hairTilt",
5699
- input: { parameter: P6.AngleZ, weight: 1 },
5700
- output: { parameter: P6.HairSwayZ, scale: 8 },
5877
+ input: { parameter: P7.AngleZ, weight: 1 },
5878
+ output: { parameter: P7.HairSwayZ, scale: 8 },
5701
5879
  mass: 1,
5702
5880
  stiffness: 25,
5703
5881
  damping: 2.5
@@ -5707,47 +5885,55 @@ function hairPhysics() {
5707
5885
  function declareParameters(roles) {
5708
5886
  const out = [];
5709
5887
  const add = (id, name, min, max, def) => out.push({ id, name, min, max, default: def });
5710
- add(P6.AngleX, "Head Angle", -30, 30, 0);
5711
- add(P6.AngleY, "Head Angle Y", -30, 30, 0);
5712
- 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);
5713
5891
  if (roles.has("body")) {
5714
- add(P6.BodyAngleX, "Body Angle X", -10, 10, 0);
5715
- add(P6.BodyAngleY, "Body Angle Y", -10, 10, 0);
5716
- 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);
5717
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
+ };
5718
5900
  const [armMin, armMax] = ARM_RANGE;
5719
5901
  const [elbowMin, elbowMax] = ELBOW_RANGE;
5720
5902
  if (roles.has("arm_L")) {
5721
- add(P6.ArmLeft, "Arm L", armMin, armMax, 0);
5722
- add(P6.ElbowLeft, "Elbow L", elbowMin, elbowMax, 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);
5723
5907
  }
5724
5908
  if (roles.has("arm_R")) {
5725
- add(P6.ArmRight, "Arm R", armMin, armMax, 0);
5726
- add(P6.ElbowRight, "Elbow R", elbowMin, elbowMax, 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);
5727
5913
  }
5728
- add(P6.EyeOpenLeft, "Eye L", 0, 1, 1);
5729
- 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);
5730
5916
  if ([...roles].some((r) => /^(iris|pupil|highlight)_/.test(r))) {
5731
- add(P6.EyeballX, "Gaze X", -1, 1, 0);
5732
- 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);
5733
5919
  }
5734
- add(P6.MouthOpen, "Mouth Open", 0, 1, 0);
5735
- 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);
5736
5922
  if (roles.has("brow_L")) {
5737
- add(P6.BrowLeftY, "Brow L Y", -1, 1, 0);
5738
- 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);
5739
5925
  }
5740
5926
  if (roles.has("brow_R")) {
5741
- add(P6.BrowRightY, "Brow R Y", -1, 1, 0);
5742
- 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);
5743
5929
  }
5744
5930
  if (roles.has("blush_L") || roles.has("blush_R")) {
5745
- add(P6.Cheek, "Cheek", 0, 1, 0);
5931
+ add(P7.Cheek, "Cheek", 0, 1, 0);
5746
5932
  }
5747
- add(P6.Breath, "Breath", 0, 1, 0);
5933
+ add(P7.Breath, "Breath", 0, 1, 0);
5748
5934
  if (roles.has("hair_front")) {
5749
- add(P6.HairSwayX, "Hair Sway X", -SWAY_RANGE, SWAY_RANGE, 0);
5750
- 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);
5751
5937
  }
5752
5938
  return out;
5753
5939
  }
@@ -5794,6 +5980,7 @@ export {
5794
5980
  createLayerSetMeasurer,
5795
5981
  denseCoreOf,
5796
5982
  detectAlphaBbox,
5983
+ forearmPoseGeometry,
5797
5984
  foregroundSpan,
5798
5985
  generateIkiFromLayerSet,
5799
5986
  headHalfOf,