@ikijs/editor 0.33.1 → 0.34.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
@@ -1971,7 +1971,7 @@ var PROFILE_VALUES = {
1971
1971
  noseDepth: 2,
1972
1972
  hairFollow: 1,
1973
1973
  fore: 0.2,
1974
- backShare: 0.1,
1974
+ backShare: 0.3,
1975
1975
  earFar: 0,
1976
1976
  earFore: 0.1,
1977
1977
  nodDown: 0.18,
@@ -2023,7 +2023,8 @@ function deriveProfile(v) {
2023
2023
  * takes it only down to its root, `hairSideField`) rides the face at this
2024
2024
  * share of its translation. */
2025
2025
  hairFollow: v.hairFollow,
2026
- /** The head's outline at the eye row holds: where the front hair draws
2026
+ /** The head's outline at the eye row turns only as the back hair does
2027
+ * (`hairBack`, which this share never undercuts): where the front hair draws
2027
2028
  * it, its outer edge follows the face this much on a row where no back
2028
2029
  * hair paints behind that edge. Where back hair does, the edge rides
2029
2030
  * further, as far as that back hair stays behind it through the turn and
@@ -2032,8 +2033,8 @@ function deriveProfile(v) {
2032
2033
  * rows above the eyes) it follows less where the turn would otherwise
2033
2034
  * carry it further outside its back hair than the nod alone does. */
2034
2035
  outlineFollow: 0,
2035
- /** The back hair's slight counter-motion. */
2036
- hairBack: -v.backShare * v.slide,
2036
+ /** The back hair turns with the head, a share of the plate's slide. */
2037
+ hairBack: v.backShare * v.slide,
2037
2038
  /** The ears lag the plate: the far one's outer edge moves this share of
2038
2039
  * its slide; the near one rides the head. */
2039
2040
  earFar: v.earFar,
@@ -3680,7 +3681,7 @@ function hairFrontField(m) {
3680
3681
  )
3681
3682
  );
3682
3683
  const followOf = (x, y, s) => followAt(x, y, s) + (ride === void 0 ? 0 : ride.lift(x, y, s));
3683
- const drift = Math.abs(m.hairBack);
3684
+ const drift = -m.hairBack;
3684
3685
  const boundOf = (x, y, s) => {
3685
3686
  if (ride === void 0) return followOf(x, y, s);
3686
3687
  const b = ride.blend(x, y, s);
@@ -3847,7 +3848,7 @@ function outerRide(m, inner, coreAt, followAt, capLimit, eyeTop, irisTop) {
3847
3848
  if (f.hairRuns("hair_front", top) === void 0 || f.hairRuns("hair_back", top) === void 0) {
3848
3849
  return void 0;
3849
3850
  }
3850
- const drift = Math.abs(m.hairBack);
3851
+ const drift = -m.hairBack;
3851
3852
  const sides = [-1, 1];
3852
3853
  const n = ys.length;
3853
3854
  const count = Math.round(top - ys[n - 1]);
@@ -3978,6 +3979,7 @@ function outerRide(m, inner, coreAt, followAt, capLimit, eyeTop, irisTop) {
3978
3979
  const cuts = new Float64Array(n * 2 * nc);
3979
3980
  const blend = new Float64Array(n * 2 * nc);
3980
3981
  const motion = new Array(n * 2);
3982
+ const held = new Array(n * 2);
3981
3983
  const outward = sides.map((side) => {
3982
3984
  const cols = xs.map((_, c) => c).filter((c) => side * (xs[c] - f.axisX) >= 0);
3983
3985
  return side < 0 ? cols.reverse() : cols;
@@ -4034,22 +4036,24 @@ function outerRide(m, inner, coreAt, followAt, capLimit, eyeTop, irisTop) {
4034
4036
  }
4035
4037
  }
4036
4038
  motion[r * 2 + j] = after.map((v) => v + drift);
4039
+ held[r * 2 + j] = today.map((v) => v + drift);
4037
4040
  for (let c = 0; c < nc; c++)
4038
4041
  cuts[(r * 2 + j) * nc + c] = after[c] - today[c];
4039
4042
  });
4040
4043
  }
4044
+ const at = (r, j, c) => (r * 2 + j) * nc + c;
4045
+ let zero = last + 1;
4046
+ while (zero < n && ys[zero] > eyeTop) zero++;
4047
+ while (zero > last + 1 && zero < n && ys[zero] < irisTop - 0.5) zero--;
4048
+ const endY = zero < n ? ys[zero] : Math.min(Math.max(eyeTop, irisTop - 0.5), ys[n - 1] - 1);
4041
4049
  for (let r = 0; r < n; r++) {
4042
4050
  sides.forEach((s, j) => {
4043
4051
  motion[r * 2 + j] ??= xs.map(
4044
4052
  (x) => followAt(x, ys[r], s) + liftAt(x, ys[r], s) + drift
4045
4053
  );
4054
+ held[r * 2 + j] ??= motion[r * 2 + j];
4046
4055
  });
4047
4056
  }
4048
- const at = (r, j, c) => (r * 2 + j) * nc + c;
4049
- let zero = last + 1;
4050
- while (zero < n && ys[zero] > eyeTop) zero++;
4051
- while (zero > last + 1 && zero < n && ys[zero] < irisTop - 0.5) zero--;
4052
- const endY = zero < n ? ys[zero] : Math.min(Math.max(eyeTop, irisTop - 0.5), ys[n - 1] - 1);
4053
4057
  for (let pass = 0; pass < 8; pass++) {
4054
4058
  const was = blend.slice();
4055
4059
  for (let j = 0; j < 2; j++) {
@@ -4067,11 +4071,15 @@ function outerRide(m, inner, coreAt, followAt, capLimit, eyeTop, irisTop) {
4067
4071
  for (let q = 0; q < r && q <= last; q++) {
4068
4072
  if (wr[q] < 1) continue;
4069
4073
  const k = (ys[r] - endY) / (ys[q] - endY);
4070
- for (let c = 0; c < nc; c++)
4071
- blend[at(r, j, c)] = Math.max(
4072
- blend[at(r, j, c)],
4073
- blend[at(q, j, c)] * k
4074
- );
4074
+ const [mq, hq] = [motion[q * 2 + j], held[q * 2 + j]];
4075
+ const [mr, hr] = [motion[r * 2 + j], held[r * 2 + j]];
4076
+ for (let c = 0; c < nc; c++) {
4077
+ const bq = blend[at(q, j, c)];
4078
+ const ease = hq[c] > 0 ? clamp(1 - (1 - bq) * mq[c] / hq[c], 0, 1) : bq;
4079
+ const want = ease * k;
4080
+ const br = mr[c] > 0 && hr[c] > 0 ? 1 - (1 - want) * hr[c] / mr[c] : want;
4081
+ blend[at(r, j, c)] = Math.max(blend[at(r, j, c)], br);
4082
+ }
4075
4083
  }
4076
4084
  }
4077
4085
  }
@@ -4088,7 +4096,7 @@ function outerRide(m, inner, coreAt, followAt, capLimit, eyeTop, irisTop) {
4088
4096
  }
4089
4097
  if (blend.every((v, i) => v === was[i])) break;
4090
4098
  }
4091
- const cutRow = (r, j, x) => r > last ? 0 : along(cuts.subarray((r * 2 + j) * nc, (r * 2 + j + 1) * nc), x);
4099
+ const cutRow = (r, j, x) => r > last ? 0 : along(cuts.subarray(at(r, j, 0), at(r, j, nc)), x);
4092
4100
  return {
4093
4101
  lift: liftAt,
4094
4102
  // Between vertex rows, as the render interpolates; a vertex reads its own
@@ -4572,6 +4580,7 @@ function buildTurn(frame, landmarks, q, turn) {
4572
4580
  const eyeHalf = eyes.reduce((sum, e) => sum + (e.x1 - e.x0) / 2, 0) / Math.max(1, eyes.length);
4573
4581
  const farScale = 1 + q.fore * A * (turn.eyeFarScale - 1);
4574
4582
  const farCorner = lead(turn.eyeFar) - (1 - farScale) * eyeHalf;
4583
+ const shell = A * hh * turn.hairBack;
4575
4584
  return {
4576
4585
  frame,
4577
4586
  face,
@@ -4589,10 +4598,13 @@ function buildTurn(frame, landmarks, q, turn) {
4589
4598
  mouth: q.mouth ?? lead(turn.mouth),
4590
4599
  mouthWidth: 1 + A * (turn.mouthWidth - 1),
4591
4600
  tilt: A,
4592
- hairFront: q.follow * face,
4593
- hairFar: Math.max(q.follow * face, farCorner),
4594
- hairOuter: q.outline * face,
4595
- hairBack: A * hh * turn.hairBack,
4601
+ // The front hair lies on the head in front of its shell: neither over
4602
+ // the face nor at the outline it draws does it turn less than the back
4603
+ // hair behind it.
4604
+ hairFront: Math.max(q.follow * face, shell),
4605
+ hairFar: Math.max(q.follow * face, shell, farCorner),
4606
+ hairOuter: Math.max(q.outline * face, shell),
4607
+ hairBack: shell,
4596
4608
  shellScale: q.shell,
4597
4609
  earFar: q.earFar,
4598
4610
  earFarScale: 1 + A * (turn.earFarScale - 1),
@@ -4714,7 +4726,7 @@ var RIG_PLAN_FIELDS = [
4714
4726
  fitBy: null,
4715
4727
  provisional: false,
4716
4728
  migratedFrom: "hairFollow",
4717
- what: "the front hair's share of the face's turn"
4729
+ what: "the front hair's share of the face's turn (never less than the back hair's own turn, 0.3 of the face's: a share under that has no effect)"
4718
4730
  },
4719
4731
  {
4720
4732
  path: "hair.outlineFollow",
@@ -4731,7 +4743,7 @@ var RIG_PLAN_FIELDS = [
4731
4743
  fitBy: null,
4732
4744
  provisional: false,
4733
4745
  migratedFrom: "outlineFollow",
4734
- what: "the front hair's outer edge's share where it draws the head's outline (0: the outline holds, though where back hair paints behind it, it may ride further, as far as that back hair reaches; at hair.follow the front hair's side strands ride whole)"
4746
+ what: "the front hair's outer edge's share where it draws the head's outline (never less than the back hair's own turn: at 0 the outline turns only as the back hair does, though where back hair paints behind it, it may ride further, as far as that back hair reaches; at hair.follow the front hair's side strands ride whole)"
4735
4747
  },
4736
4748
  {
4737
4749
  path: "hair.sway",