@ikijs/editor 0.32.1 → 0.33.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
@@ -2937,6 +2937,7 @@ function mouthAnchor(byRole) {
2937
2937
  }
2938
2938
 
2939
2939
  // src/auto-rig/head.ts
2940
+ var EAR_EDGE = 3;
2940
2941
  function outerEdge(runs, axisX, side) {
2941
2942
  if (runs.length === 0) return void 0;
2942
2943
  const x = side > 0 ? runs[runs.length - 1] : runs[0];
@@ -2970,7 +2971,11 @@ function findEars(raw, wMax, firstRow, lastRow, minRows, maxRows) {
2970
2971
  if (raw[t] - raw[t - EAR_ROWS] < step) continue;
2971
2972
  for (let b = t + minRows; b <= Math.min(lastRow, t + maxRows); b++) {
2972
2973
  if (raw[b - EAR_ROWS] - raw[b] >= step) {
2973
- return { top: t - EAR_ROWS, bottom: b };
2974
+ let end = b;
2975
+ for (let k = b + 1; k <= Math.min(lastRow, b + minRows); k++) {
2976
+ if (raw[k - EAR_ROWS] - raw[k] >= step) end = k;
2977
+ }
2978
+ return { top: t - EAR_ROWS, bottom: end };
2974
2979
  }
2975
2980
  }
2976
2981
  return void 0;
@@ -3048,6 +3053,7 @@ function buildHeadFrame(layers, opts) {
3048
3053
  return rows[i] + (rows[j] - rows[i]) * clamp(r - i, 0, 1);
3049
3054
  };
3050
3055
  const rawRows = raw;
3056
+ let earRows;
3051
3057
  const earless = (chin) => {
3052
3058
  const rowOf = (y) => Math.round(canvasH / 2 - y - faceLayer.bbox.y - 0.5);
3053
3059
  const span3 = Math.max(1, eyeY - chin);
@@ -3070,8 +3076,10 @@ function buildHeadFrame(layers, opts) {
3070
3076
  top: rowY(top) + 0.5,
3071
3077
  bottom: rowY(bottom) - 0.5,
3072
3078
  attachAt: (y) => attach(clamp(canvasH / 2 - y - faceLayer.bbox.y - 0.5, top, bottom)),
3073
- outer
3079
+ outer,
3080
+ edgeAt: (y) => attach(clamp(canvasH / 2 - y - faceLayer.bbox.y - 0.5, top, bottom)) + EAR_EDGE
3074
3081
  };
3082
+ earRows = { top, bottom };
3075
3083
  return smoothProfile(
3076
3084
  rawRows.map((v, r) => r > top && r < bottom ? attach(r) : v)
3077
3085
  );
@@ -3193,9 +3201,13 @@ function buildHeadFrame(layers, opts) {
3193
3201
  headHalfAt = readRows(head);
3194
3202
  headMax = Math.max(...head);
3195
3203
  if (ears !== void 0) {
3204
+ const band = earRows;
3205
+ const edge = head.map(
3206
+ (w, r) => r >= band.top && r <= band.bottom ? w + EAR_EDGE : w
3207
+ );
3196
3208
  const bottomOf = (d) => {
3197
- for (let r = head.length - 1; r >= 0; r--) {
3198
- if (head[r] >= d) return rowY(r) - 0.5;
3209
+ for (let r = edge.length - 1; r >= 0; r--) {
3210
+ if (edge[r] >= d) return rowY(r) - 0.5;
3199
3211
  }
3200
3212
  return face.y1;
3201
3213
  };
@@ -3395,7 +3407,7 @@ function earField(m) {
3395
3407
  const attach = ears.attachAt(y);
3396
3408
  const span3 = Math.max(1, ears.outer - attach);
3397
3409
  if (sideOf(m, x, s) === "far") {
3398
- const rim2 = TURN.earFar * m.face;
3410
+ const rim2 = m.earFar * m.face;
3399
3411
  const root2 = earRoot(m, face, y, s);
3400
3412
  const lead = Math.max(
3401
3413
  0,
@@ -3420,7 +3432,7 @@ function farEarCovered(m) {
3420
3432
  const ears = m.frame.ears;
3421
3433
  if (ears === void 0) return true;
3422
3434
  const face = faceField(m);
3423
- const rim = TURN.earFar * m.face;
3435
+ const rim = m.earFar * m.face;
3424
3436
  const y0 = ears.bottom - 2;
3425
3437
  const y1 = ears.top + 2;
3426
3438
  const n = Math.max(1, Math.ceil(y1 - y0));
@@ -4261,6 +4273,7 @@ function fitTurn(ctx, targets, plan, build) {
4261
4273
  lead: plan.turn.featureLead,
4262
4274
  follow: plan.hair.follow,
4263
4275
  outline: plan.hair.outlineFollow,
4276
+ earFar: plan.turn.farEar,
4264
4277
  fore: targets.farEyeRatio === void 0 ? plan.turn.eyeForeshorten : 1,
4265
4278
  shell: 1,
4266
4279
  noseLead: targets.noseShift !== void 0 || plan.turn.noseLead === 1 ? void 0 : plan.turn.noseLead
@@ -4497,6 +4510,7 @@ function buildTurn(frame, landmarks, q, turn) {
4497
4510
  hairOuter: q.outline * face,
4498
4511
  hairBack: A * hh * turn.hairBack,
4499
4512
  shellScale: q.shell,
4513
+ earFar: q.earFar,
4500
4514
  earFarScale: 1 + A * (turn.earFarScale - 1),
4501
4515
  ...landmarks
4502
4516
  };
@@ -4584,6 +4598,23 @@ var RIG_PLAN_FIELDS = [
4584
4598
  migratedFrom: null,
4585
4599
  what: "how far the nose leads the face plate on the turn \u2014 turn only, the nod keeps the profile's nose depth"
4586
4600
  },
4601
+ {
4602
+ path: "turn.farEar",
4603
+ group: "turn",
4604
+ key: "farEar",
4605
+ kind: "absolute",
4606
+ unit: "share of the face plate's slide",
4607
+ default: PROFILE.TURN.earFar,
4608
+ range: [0, 1],
4609
+ recommended: [0, 0.5],
4610
+ step: 0.1,
4611
+ poses: TURN_POSES,
4612
+ cue: null,
4613
+ fitBy: null,
4614
+ provisional: false,
4615
+ migratedFrom: null,
4616
+ what: "how far the far ear's outer edge follows the face on the turn (0: it holds and the head slides over it, about half of a default-sized ear going under the face; 1: it rides with the face and stays whole)"
4617
+ },
4587
4618
  {
4588
4619
  path: "hair.follow",
4589
4620
  group: "hair",
@@ -6682,6 +6713,11 @@ function buildFaceMesh(frame, b, head, neckCells) {
6682
6713
  frame.axisX - frame.headMax + 0.5,
6683
6714
  frame.axisX + frame.headMax - 0.5
6684
6715
  ];
6716
+ const ears2 = frame.ears;
6717
+ if (ears2 !== void 0 && neck2 === void 0) {
6718
+ const d = ears2.edgeAt(ears2.bottom);
6719
+ at.push(frame.axisX - d, frame.axisX + d);
6720
+ }
6685
6721
  if (neck2 !== void 0) {
6686
6722
  at.push(
6687
6723
  frame.axisX,