@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/README.md CHANGED
@@ -72,10 +72,10 @@ The rig turns the head by parallax, not a reshaped face, with values of our
72
72
  own picked by eye (`auto-rig/profile.ts`, `AUTO-RIG.md`). The face plate
73
73
  translates, the chin leading it; the features lead the plate by their depth
74
74
  (the nose most, then the eyes, brows and mouth), the eyes foreshortening mildly
75
- about their own centres; the front hair rides the face where it lies over it and holds the head's
76
- outline where it draws it — though on its cap the turn never carries its outer
75
+ about their own centres; the front hair rides the face where it lies over it and turns the head's
76
+ outline, where it draws it, only as the back hair turns — though on its cap the turn never carries its outer
77
77
  edge further outside the back hair painted behind it than the nod alone does —
78
- and the back hair stays behind; the neck and the
78
+ and the back hair, the head's shell, follows the head by 0.3 of the face's turn and nod; the neck and the
79
79
  ears the face layer paints are cut off into islands of the plate's own mesh,
80
80
  drawn behind the head — the neck stays (only the chin's shade slides across
81
81
  it), the ears lag the face, the far one held while the head slides partly over it (about half of an ear of the default size). AngleZ rolls the head 14° about the chin.
@@ -176,7 +176,7 @@ row's alpha ≥ 128 runs as a flat list of canvas columns `[start, end, …]`, e
176
176
  end exclusive), measured for `face`, `hair_front`, `hair_back`, `hair_side_L`
177
177
  and `hair_side_R` so the turn and the nod can tell where back hair is painted
178
178
  behind the front hair and the rig can tell where a side lock first shows under
179
- the bangs; absent, the front hair keeps its outline hold and its crown's ease
179
+ the bangs; absent, the front hair's outline turns only as the back hair does and it keeps its crown's ease
180
180
  to the back hair on the turn, and that ease looking down on the nod, while a
181
181
  side lock's root band ends a quarter of the way down its box.
182
182
 
package/dist/index.d.mts CHANGED
@@ -717,7 +717,8 @@ interface RigPlan {
717
717
  /** The front hair's share of the face's turn. */
718
718
  follow?: number;
719
719
  /** Its outer edge's share, where the front hair draws the head's outline
720
- * and no back hair paints behind that edge (0: the outline holds;
720
+ * and no back hair paints behind that edge (never less than the back
721
+ * hair's own turn: at 0 the outline turns only as the back hair does;
721
722
  * `follow` rides it whole). Where back hair does, the edge rides
722
723
  * further, as far as that back hair stays behind it through the turn and
723
724
  * the nod, up to the front hair's follow there (over the far eye's rows,
@@ -883,7 +884,7 @@ declare class LayerGeometryError extends Error {
883
884
  * The profile's own turn (`profile.ts`), in the cues' units — what a rig with
884
885
  * no `turnTargets` renders on a head whose silhouette at the eye row is back
885
886
  * hair about 1 hh (eye row → chin) wide. eyeShift: the eye pair's mean shift
886
- * at ±30 less the back hair's drift, over a 1 hh head half-width.
887
+ * at ±30 less the back hair's own turn, over a 1 hh head half-width.
887
888
  * farEyeRatio: the far eye's width over the near one's. silhouetteRatio: the
888
889
  * silhouette holds its width.
889
890
  *
package/dist/index.d.ts CHANGED
@@ -717,7 +717,8 @@ interface RigPlan {
717
717
  /** The front hair's share of the face's turn. */
718
718
  follow?: number;
719
719
  /** Its outer edge's share, where the front hair draws the head's outline
720
- * and no back hair paints behind that edge (0: the outline holds;
720
+ * and no back hair paints behind that edge (never less than the back
721
+ * hair's own turn: at 0 the outline turns only as the back hair does;
721
722
  * `follow` rides it whole). Where back hair does, the edge rides
722
723
  * further, as far as that back hair stays behind it through the turn and
723
724
  * the nod, up to the front hair's follow there (over the far eye's rows,
@@ -883,7 +884,7 @@ declare class LayerGeometryError extends Error {
883
884
  * The profile's own turn (`profile.ts`), in the cues' units — what a rig with
884
885
  * no `turnTargets` renders on a head whose silhouette at the eye row is back
885
886
  * hair about 1 hh (eye row → chin) wide. eyeShift: the eye pair's mean shift
886
- * at ±30 less the back hair's drift, over a 1 hh head half-width.
887
+ * at ±30 less the back hair's own turn, over a 1 hh head half-width.
887
888
  * farEyeRatio: the far eye's width over the near one's. silhouetteRatio: the
888
889
  * silhouette holds its width.
889
890
  *
package/dist/index.js CHANGED
@@ -2063,7 +2063,7 @@ var PROFILE_VALUES = {
2063
2063
  noseDepth: 2,
2064
2064
  hairFollow: 1,
2065
2065
  fore: 0.2,
2066
- backShare: 0.1,
2066
+ backShare: 0.3,
2067
2067
  earFar: 0,
2068
2068
  earFore: 0.1,
2069
2069
  nodDown: 0.18,
@@ -2115,7 +2115,8 @@ function deriveProfile(v) {
2115
2115
  * takes it only down to its root, `hairSideField`) rides the face at this
2116
2116
  * share of its translation. */
2117
2117
  hairFollow: v.hairFollow,
2118
- /** The head's outline at the eye row holds: where the front hair draws
2118
+ /** The head's outline at the eye row turns only as the back hair does
2119
+ * (`hairBack`, which this share never undercuts): where the front hair draws
2119
2120
  * it, its outer edge follows the face this much on a row where no back
2120
2121
  * hair paints behind that edge. Where back hair does, the edge rides
2121
2122
  * further, as far as that back hair stays behind it through the turn and
@@ -2124,8 +2125,8 @@ function deriveProfile(v) {
2124
2125
  * rows above the eyes) it follows less where the turn would otherwise
2125
2126
  * carry it further outside its back hair than the nod alone does. */
2126
2127
  outlineFollow: 0,
2127
- /** The back hair's slight counter-motion. */
2128
- hairBack: -v.backShare * v.slide,
2128
+ /** The back hair turns with the head, a share of the plate's slide. */
2129
+ hairBack: v.backShare * v.slide,
2129
2130
  /** The ears lag the plate: the far one's outer edge moves this share of
2130
2131
  * its slide; the near one rides the head. */
2131
2132
  earFar: v.earFar,
@@ -3768,7 +3769,7 @@ function hairFrontField(m) {
3768
3769
  )
3769
3770
  );
3770
3771
  const followOf = (x, y, s) => followAt(x, y, s) + (ride === void 0 ? 0 : ride.lift(x, y, s));
3771
- const drift = Math.abs(m.hairBack);
3772
+ const drift = -m.hairBack;
3772
3773
  const boundOf = (x, y, s) => {
3773
3774
  if (ride === void 0) return followOf(x, y, s);
3774
3775
  const b = ride.blend(x, y, s);
@@ -3935,7 +3936,7 @@ function outerRide(m, inner, coreAt, followAt, capLimit, eyeTop, irisTop) {
3935
3936
  if (f.hairRuns("hair_front", top) === void 0 || f.hairRuns("hair_back", top) === void 0) {
3936
3937
  return void 0;
3937
3938
  }
3938
- const drift = Math.abs(m.hairBack);
3939
+ const drift = -m.hairBack;
3939
3940
  const sides = [-1, 1];
3940
3941
  const n = ys.length;
3941
3942
  const count = Math.round(top - ys[n - 1]);
@@ -4066,6 +4067,7 @@ function outerRide(m, inner, coreAt, followAt, capLimit, eyeTop, irisTop) {
4066
4067
  const cuts = new Float64Array(n * 2 * nc);
4067
4068
  const blend = new Float64Array(n * 2 * nc);
4068
4069
  const motion = new Array(n * 2);
4070
+ const held = new Array(n * 2);
4069
4071
  const outward = sides.map((side) => {
4070
4072
  const cols = xs.map((_, c) => c).filter((c) => side * (xs[c] - f.axisX) >= 0);
4071
4073
  return side < 0 ? cols.reverse() : cols;
@@ -4122,22 +4124,24 @@ function outerRide(m, inner, coreAt, followAt, capLimit, eyeTop, irisTop) {
4122
4124
  }
4123
4125
  }
4124
4126
  motion[r * 2 + j] = after.map((v) => v + drift);
4127
+ held[r * 2 + j] = today.map((v) => v + drift);
4125
4128
  for (let c = 0; c < nc; c++)
4126
4129
  cuts[(r * 2 + j) * nc + c] = after[c] - today[c];
4127
4130
  });
4128
4131
  }
4132
+ const at = (r, j, c) => (r * 2 + j) * nc + c;
4133
+ let zero = last + 1;
4134
+ while (zero < n && ys[zero] > eyeTop) zero++;
4135
+ while (zero > last + 1 && zero < n && ys[zero] < irisTop - 0.5) zero--;
4136
+ const endY = zero < n ? ys[zero] : Math.min(Math.max(eyeTop, irisTop - 0.5), ys[n - 1] - 1);
4129
4137
  for (let r = 0; r < n; r++) {
4130
4138
  sides.forEach((s, j) => {
4131
4139
  motion[r * 2 + j] ??= xs.map(
4132
4140
  (x) => followAt(x, ys[r], s) + liftAt(x, ys[r], s) + drift
4133
4141
  );
4142
+ held[r * 2 + j] ??= motion[r * 2 + j];
4134
4143
  });
4135
4144
  }
4136
- const at = (r, j, c) => (r * 2 + j) * nc + c;
4137
- let zero = last + 1;
4138
- while (zero < n && ys[zero] > eyeTop) zero++;
4139
- while (zero > last + 1 && zero < n && ys[zero] < irisTop - 0.5) zero--;
4140
- const endY = zero < n ? ys[zero] : Math.min(Math.max(eyeTop, irisTop - 0.5), ys[n - 1] - 1);
4141
4145
  for (let pass = 0; pass < 8; pass++) {
4142
4146
  const was = blend.slice();
4143
4147
  for (let j = 0; j < 2; j++) {
@@ -4155,11 +4159,15 @@ function outerRide(m, inner, coreAt, followAt, capLimit, eyeTop, irisTop) {
4155
4159
  for (let q = 0; q < r && q <= last; q++) {
4156
4160
  if (wr[q] < 1) continue;
4157
4161
  const k = (ys[r] - endY) / (ys[q] - endY);
4158
- for (let c = 0; c < nc; c++)
4159
- blend[at(r, j, c)] = Math.max(
4160
- blend[at(r, j, c)],
4161
- blend[at(q, j, c)] * k
4162
- );
4162
+ const [mq, hq] = [motion[q * 2 + j], held[q * 2 + j]];
4163
+ const [mr, hr] = [motion[r * 2 + j], held[r * 2 + j]];
4164
+ for (let c = 0; c < nc; c++) {
4165
+ const bq = blend[at(q, j, c)];
4166
+ const ease = hq[c] > 0 ? clamp(1 - (1 - bq) * mq[c] / hq[c], 0, 1) : bq;
4167
+ const want = ease * k;
4168
+ const br = mr[c] > 0 && hr[c] > 0 ? 1 - (1 - want) * hr[c] / mr[c] : want;
4169
+ blend[at(r, j, c)] = Math.max(blend[at(r, j, c)], br);
4170
+ }
4163
4171
  }
4164
4172
  }
4165
4173
  }
@@ -4176,7 +4184,7 @@ function outerRide(m, inner, coreAt, followAt, capLimit, eyeTop, irisTop) {
4176
4184
  }
4177
4185
  if (blend.every((v, i) => v === was[i])) break;
4178
4186
  }
4179
- const cutRow = (r, j, x) => r > last ? 0 : along(cuts.subarray((r * 2 + j) * nc, (r * 2 + j + 1) * nc), x);
4187
+ const cutRow = (r, j, x) => r > last ? 0 : along(cuts.subarray(at(r, j, 0), at(r, j, nc)), x);
4180
4188
  return {
4181
4189
  lift: liftAt,
4182
4190
  // Between vertex rows, as the render interpolates; a vertex reads its own
@@ -4660,6 +4668,7 @@ function buildTurn(frame, landmarks, q, turn) {
4660
4668
  const eyeHalf = eyes.reduce((sum, e) => sum + (e.x1 - e.x0) / 2, 0) / Math.max(1, eyes.length);
4661
4669
  const farScale = 1 + q.fore * A * (turn.eyeFarScale - 1);
4662
4670
  const farCorner = lead(turn.eyeFar) - (1 - farScale) * eyeHalf;
4671
+ const shell = A * hh * turn.hairBack;
4663
4672
  return {
4664
4673
  frame,
4665
4674
  face,
@@ -4677,10 +4686,13 @@ function buildTurn(frame, landmarks, q, turn) {
4677
4686
  mouth: q.mouth ?? lead(turn.mouth),
4678
4687
  mouthWidth: 1 + A * (turn.mouthWidth - 1),
4679
4688
  tilt: A,
4680
- hairFront: q.follow * face,
4681
- hairFar: Math.max(q.follow * face, farCorner),
4682
- hairOuter: q.outline * face,
4683
- hairBack: A * hh * turn.hairBack,
4689
+ // The front hair lies on the head in front of its shell: neither over
4690
+ // the face nor at the outline it draws does it turn less than the back
4691
+ // hair behind it.
4692
+ hairFront: Math.max(q.follow * face, shell),
4693
+ hairFar: Math.max(q.follow * face, shell, farCorner),
4694
+ hairOuter: Math.max(q.outline * face, shell),
4695
+ hairBack: shell,
4684
4696
  shellScale: q.shell,
4685
4697
  earFar: q.earFar,
4686
4698
  earFarScale: 1 + A * (turn.earFarScale - 1),
@@ -4802,7 +4814,7 @@ var RIG_PLAN_FIELDS = [
4802
4814
  fitBy: null,
4803
4815
  provisional: false,
4804
4816
  migratedFrom: "hairFollow",
4805
- what: "the front hair's share of the face's turn"
4817
+ 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)"
4806
4818
  },
4807
4819
  {
4808
4820
  path: "hair.outlineFollow",
@@ -4819,7 +4831,7 @@ var RIG_PLAN_FIELDS = [
4819
4831
  fitBy: null,
4820
4832
  provisional: false,
4821
4833
  migratedFrom: "outlineFollow",
4822
- 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)"
4834
+ 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)"
4823
4835
  },
4824
4836
  {
4825
4837
  path: "hair.sway",