@ikijs/editor 0.28.0 → 0.29.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.js CHANGED
@@ -3458,12 +3458,12 @@ function hairFrontBends(frame, boxes) {
3458
3458
  }
3459
3459
  function hairFrontField(m) {
3460
3460
  const f = m.frame;
3461
- const back = hairField(m, m.hairBack, NOD.hairBack);
3462
3461
  const cap = { up: NOD.hairFrontTop.up, down: capSlide(m) };
3463
3462
  const eyes = [m.boxes.eye_L, m.boxes.eye_R].filter(
3464
3463
  (e) => e !== void 0
3465
3464
  );
3466
3465
  const { inner, left, right } = hairFrontBends(f, m.boxes);
3466
+ const capTop = m.hairFrontGrid?.ys.find((y) => y <= f.face.y1) ?? f.face.y1;
3467
3467
  const eyeY = eyes.length > 0 ? eyes.reduce((t, e) => t + cy(e), 0) / eyes.length : f.eyeY;
3468
3468
  const eyeH = eyes.length > 0 ? eyes.reduce((t, e) => t + (e.y1 - e.y0), 0) / eyes.length : 1;
3469
3469
  const coreAt = (x, y, s) => {
@@ -3496,22 +3496,18 @@ function hairFrontField(m) {
3496
3496
  );
3497
3497
  const followOf = (x, y, s) => followAt(x, y, s) + (ride === void 0 ? 0 : ride.lift(x, y, s));
3498
3498
  const drift = Math.abs(m.hairBack);
3499
- const crown = crownRide(m, cap, (x, y, s) => followOf(x, y, s) + drift);
3500
3499
  const boundOf = (x, y, s) => {
3501
3500
  if (ride === void 0) return followOf(x, y, s);
3502
- const b = ride.blend(y, s);
3501
+ const b = ride.blend(x, y, s);
3503
3502
  return (1 - b) * (followOf(x, y, s) + ride.cut(x, y, s)) - b * drift;
3504
3503
  };
3505
3504
  return (x, y, ax, ay) => {
3506
3505
  const s = Math.sign(ax);
3507
3506
  const a = Math.abs(ax) / 30;
3508
- const rx = s * a * boundOf(x, y, s) + a * (m.shellScale - 1) * (x - f.axisX);
3507
+ const rx = s * a * boundOf(x, Math.min(y, capTop), s) + a * (m.shellScale - 1) * (x - f.axisX);
3509
3508
  const ry = nodDy(NOD.hairFront, ay, f.hh);
3510
- const bx = back(x, y, ax, ay)[0];
3511
3509
  const by = nodDy(cap, ay, f.hh);
3512
- const w = crownBlend(m, y);
3513
- const wx = crown === void 0 ? w : w + crown(y, s);
3514
- return [bx + wx * (rx - bx), by + w * (ry - by)];
3510
+ return [rx, by + crownBlend(m, y) * (ry - by)];
3515
3511
  };
3516
3512
  }
3517
3513
  function crownBlend(m, y) {
@@ -3648,17 +3644,17 @@ function outerRide(m, inner, coreAt, followAt, capLimit, eyeTop, irisTop) {
3648
3644
  sides.forEach((side, k) => {
3649
3645
  const edge = outerEdge(front, f.axisX, side);
3650
3646
  if (edge === void 0) return;
3651
- let outward = Infinity;
3647
+ let outward2 = Infinity;
3652
3648
  let inward = Infinity;
3653
3649
  for (let j = Math.floor(i - above); j < Math.ceil(i + 1 + below); j++) {
3654
3650
  const c = runReach(backRows[j - j0], edge, side);
3655
- outward = Math.min(outward, c.outward);
3651
+ outward2 = Math.min(outward2, c.outward);
3656
3652
  inward = Math.min(inward, c.inward);
3657
3653
  }
3658
3654
  offset[k][i] = side * (edge - f.axisX);
3659
- reachOut[k][i] = outward;
3655
+ reachOut[k][i] = outward2;
3660
3656
  reachIn[k][i] = inward;
3661
- if (i < capRows && runReach(backRows[i - j0], edge, side).inward > 0)
3657
+ if (i < capRows && y <= f.face.y1 && runReach(backRows[i - j0], edge, side).inward > 0)
3662
3658
  bound[k][i] = 1;
3663
3659
  });
3664
3660
  }
@@ -3668,12 +3664,12 @@ function outerRide(m, inner, coreAt, followAt, capLimit, eyeTop, irisTop) {
3668
3664
  const [i0, i1] = rowWindow(ys, r, count);
3669
3665
  sides.forEach((side, k) => {
3670
3666
  let d = Infinity;
3671
- let outward = Infinity;
3667
+ let outward2 = Infinity;
3672
3668
  let inward = Infinity;
3673
3669
  for (let i = i0; i < i1; i++) {
3674
3670
  if (Number.isNaN(offset[k][i])) continue;
3675
3671
  d = Math.min(d, offset[k][i]);
3676
- outward = Math.min(outward, reachOut[k][i]);
3672
+ outward2 = Math.min(outward2, reachOut[k][i]);
3677
3673
  inward = Math.min(inward, reachIn[k][i]);
3678
3674
  }
3679
3675
  end[r * 2 + k] = d;
@@ -3682,7 +3678,7 @@ function outerRide(m, inner, coreAt, followAt, capLimit, eyeTop, irisTop) {
3682
3678
  sides.forEach((s, j) => {
3683
3679
  const allow = Math.min(
3684
3680
  coreAt(x, ys[r], s),
3685
- m.shellScale * (s === side ? outward : inward) - drift
3681
+ m.shellScale * (s === side ? outward2 : inward) - drift
3686
3682
  );
3687
3683
  lift[(r * 2 + k) * 2 + j] = Math.max(0, allow - followAt(x, ys[r], s));
3688
3684
  });
@@ -3753,73 +3749,117 @@ function outerRide(m, inner, coreAt, followAt, capLimit, eyeTop, irisTop) {
3753
3749
  return quarter.map((q) => scale * q);
3754
3750
  };
3755
3751
  const cuts = new Float64Array(n * 2 * nc);
3756
- const blend = new Float64Array(n * 2);
3752
+ const blend = new Float64Array(n * 2 * nc);
3753
+ const motion = new Array(n * 2);
3754
+ const outward = sides.map((side) => {
3755
+ const cols = xs.map((_, c) => c).filter((c) => side * (xs[c] - f.axisX) >= 0);
3756
+ return side < 0 ? cols.reverse() : cols;
3757
+ });
3757
3758
  const wr = ys.map((y) => crownBlend(m, y));
3758
3759
  for (let r = 0; r <= last && capRows > 0; r++) {
3759
- const w = wr[r];
3760
- if (w <= 0) continue;
3760
+ if (wr[r] < 1) continue;
3761
3761
  const edges = [];
3762
+ const copied = r > 0 && wr[r - 1] < 1;
3762
3763
  const [i0, i1] = rowWindow(ys, r, capRows);
3763
3764
  for (let i = i0; i < i1; i++) {
3765
+ const y = top - i - 0.5;
3764
3766
  sides.forEach((side, k) => {
3765
3767
  if (bound[k][i] === 0) return;
3766
3768
  const x = f.axisX + side * offset[k][i];
3767
- const at = readAt(r, x, top - i - 0.5);
3768
- if (at === void 0) return;
3769
- edges.push({
3770
- side,
3771
- x,
3772
- at,
3773
- outward: reachOut[k][i],
3774
- inward: reachIn[k][i]
3775
- });
3769
+ for (const at2 of [
3770
+ readAt(r, x, y),
3771
+ copied && y > ys[r] ? readAt(r - 1, x, y) : void 0
3772
+ ]) {
3773
+ if (at2 === void 0) continue;
3774
+ edges.push({
3775
+ side,
3776
+ x,
3777
+ at: at2,
3778
+ outward: reachOut[k][i],
3779
+ inward: reachIn[k][i]
3780
+ });
3781
+ }
3776
3782
  });
3777
3783
  }
3778
3784
  if (edges.length === 0) continue;
3779
3785
  sides.forEach((s, j) => {
3780
3786
  const today = xs.map((x) => followAt(x, ys[r], s) + liftAt(x, ys[r], s));
3781
3787
  const after = today.slice();
3782
- if (w >= 1) {
3783
- for (const side of sides) {
3784
- const mine = edges.filter((e) => e.side === side);
3785
- if (mine.length === 0) continue;
3786
- const cut = capCut(r, s, side, today, mine);
3787
- for (let c = 0; c < nc; c++) after[c] -= cut[c];
3788
- }
3788
+ for (const side of sides) {
3789
+ const mine = edges.filter((e) => e.side === side);
3790
+ if (mine.length === 0) continue;
3791
+ const cut = capCut(r, s, side, today, mine);
3792
+ for (let c = 0; c < nc; c++) after[c] -= cut[c];
3789
3793
  }
3790
- let b = 0;
3794
+ const b = blend.subarray((r * 2 + j) * nc, (r * 2 + j + 1) * nc);
3791
3795
  for (const e of edges) {
3792
- const rel = w * (along(after, e.at) + drift);
3793
3796
  const hi = m.shellScale * ahead(e, s);
3794
3797
  const lo = -m.shellScale * behind(e, s);
3795
- if (rel > hi) b = Math.max(b, 1 - hi / rel);
3796
- else if (rel < lo) b = Math.max(b, 1 - lo / rel);
3798
+ const { c, t } = gridCol(xs, e.at);
3799
+ for (const [cc, weight] of [
3800
+ [c, 1 - t],
3801
+ [c + 1, t]
3802
+ ]) {
3803
+ if (weight <= 0) continue;
3804
+ const rel = after[cc] + drift;
3805
+ if (rel > hi) b[cc] = Math.max(b[cc], 1 - hi / rel);
3806
+ else if (rel < lo) b[cc] = Math.max(b[cc], 1 - lo / rel);
3807
+ }
3797
3808
  }
3798
- blend[r * 2 + j] = b;
3809
+ motion[r * 2 + j] = after.map((v) => v + drift);
3799
3810
  for (let c = 0; c < nc; c++)
3800
3811
  cuts[(r * 2 + j) * nc + c] = after[c] - today[c];
3801
3812
  });
3802
3813
  }
3803
- for (let j = 0; j < 2; j++) {
3804
- for (let r = n - 2; r >= 0; r--) {
3805
- if (r <= last || wr[r] < 1)
3806
- blend[r * 2 + j] = Math.max(blend[r * 2 + j], blend[(r + 1) * 2 + j]);
3807
- }
3814
+ for (let r = 0; r < n; r++) {
3815
+ sides.forEach((s, j) => {
3816
+ motion[r * 2 + j] ??= xs.map(
3817
+ (x) => followAt(x, ys[r], s) + liftAt(x, ys[r], s) + drift
3818
+ );
3819
+ });
3808
3820
  }
3821
+ const at = (r, j, c) => (r * 2 + j) * nc + c;
3809
3822
  let zero = last + 1;
3810
3823
  while (zero < n && ys[zero] > eyeTop) zero++;
3811
3824
  while (zero > last + 1 && zero < n && ys[zero] < irisTop - 0.5) zero--;
3812
3825
  const endY = zero < n ? ys[zero] : Math.min(Math.max(eyeTop, irisTop - 0.5), ys[n - 1] - 1);
3813
- for (let j = 0; j < 2 && last < n - 1; j++) {
3814
- for (let r = 1; r < zero; r++) {
3815
- for (let q = 0; q < r && q <= last; q++) {
3816
- if (wr[q] < 1) continue;
3817
- blend[r * 2 + j] = Math.max(
3818
- blend[r * 2 + j],
3819
- blend[q * 2 + j] * (ys[r] - endY) / (ys[q] - endY)
3820
- );
3826
+ for (let pass = 0; pass < 8; pass++) {
3827
+ const was = blend.slice();
3828
+ for (let j = 0; j < 2; j++) {
3829
+ for (let r = n - 2; r >= 0; r--) {
3830
+ if (r > last && wr[r] >= 1) continue;
3831
+ for (let c = 0; c < nc; c++)
3832
+ blend[at(r, j, c)] = Math.max(
3833
+ blend[at(r, j, c)],
3834
+ blend[at(r + 1, j, c)]
3835
+ );
3836
+ }
3837
+ }
3838
+ for (let j = 0; j < 2 && last < n - 1; j++) {
3839
+ for (let r = 1; r < zero; r++) {
3840
+ for (let q = 0; q < r && q <= last; q++) {
3841
+ if (wr[q] < 1) continue;
3842
+ const k = (ys[r] - endY) / (ys[q] - endY);
3843
+ for (let c = 0; c < nc; c++)
3844
+ blend[at(r, j, c)] = Math.max(
3845
+ blend[at(r, j, c)],
3846
+ blend[at(q, j, c)] * k
3847
+ );
3848
+ }
3821
3849
  }
3822
3850
  }
3851
+ for (let r = 0; r < n; r++) {
3852
+ sides.forEach(
3853
+ (s, j) => spreadBlend(
3854
+ blend.subarray(at(r, j, 0), at(r, j, nc)),
3855
+ motion[r * 2 + j],
3856
+ xs,
3857
+ outward,
3858
+ s
3859
+ )
3860
+ );
3861
+ }
3862
+ if (blend.every((v, i) => v === was[i])) break;
3823
3863
  }
3824
3864
  const cutRow = (r, j, x) => r > last ? 0 : along(cuts.subarray((r * 2 + j) * nc, (r * 2 + j + 1) * nc), x);
3825
3865
  return {
@@ -3832,158 +3872,47 @@ function outerRide(m, inner, coreAt, followAt, capLimit, eyeTop, irisTop) {
3832
3872
  const { r, t } = gridRow(ys, y, true);
3833
3873
  return cutRow(r, j, x) * (1 - t) + cutRow(r + 1, j, x) * t;
3834
3874
  },
3835
- blend: (y, s) => {
3875
+ blend: (x, y, s) => {
3836
3876
  if (s === 0) return 0;
3837
3877
  const j = s < 0 ? 0 : 1;
3838
3878
  const { r, t } = gridRow(ys, y, true);
3839
- return blend[r * 2 + j] * (1 - t) + blend[(r + 1) * 2 + j] * t;
3879
+ const row = (q) => along(blend.subarray(at(q, j, 0), at(q, j, nc)), x);
3880
+ return row(r) * (1 - t) + row(r + 1) * t;
3840
3881
  }
3841
3882
  };
3842
3883
  }
3843
- function crownRide(m, cap, relAt) {
3844
- const f = m.frame;
3845
- const grid = m.hairFrontGrid;
3846
- if (grid === void 0) return void 0;
3847
- const { xs, ys } = grid;
3848
- const top = ys[0];
3849
- if (f.hairRuns("hair_front", top) === void 0 || f.hairRuns("hair_back", top) === void 0) {
3850
- return void 0;
3851
- }
3852
- const n = ys.length;
3853
- const count = Math.round(top - ys[n - 1]);
3854
- const hh = f.hh;
3855
- const face = faceField(m);
3856
- const { wr, blendAt } = renderedCrownBlend(m, ys);
3857
- const backRows = /* @__PURE__ */ new Map();
3858
- const backAt = (j) => {
3859
- let runs = backRows.get(j);
3860
- if (runs === void 0) {
3861
- runs = f.hairRuns("hair_back", top - j - 0.5);
3862
- backRows.set(j, runs);
3863
- }
3864
- return runs;
3865
- };
3866
- const faceAt = (j) => {
3867
- const y = top - j - 0.5;
3868
- return f.faceRuns(y) ?? (y > f.face.y0 && y < f.face.y1 ? [f.face.x0, f.face.x1] : []);
3869
- };
3870
- const passed = (i, dys) => [
3871
- Math.floor(i - Math.max(0, ...dys)),
3872
- Math.ceil(i + 1 - Math.min(0, ...dys))
3873
- ];
3874
- const rel = new Float64Array(n * xs.length * 2).fill(NaN);
3875
- const relOf = (r, c, k) => {
3876
- const at = (r * xs.length + c) * 2 + k;
3877
- if (Number.isNaN(rel[at])) rel[at] = relAt(xs[c], ys[r], k === 0 ? -1 : 1);
3878
- return rel[at];
3879
- };
3880
- const cellR = (x, y, k) => {
3881
- const { c } = gridCol(xs, x);
3882
- const { r } = gridRow(ys, y);
3883
- return Math.max(
3884
- relOf(r, c, k),
3885
- relOf(r, c + 1, k),
3886
- relOf(r + 1, c, k),
3887
- relOf(r + 1, c + 1, k)
3888
- );
3889
- };
3890
- const someAngle = (pairs) => {
3891
- let lo = 0;
3892
- let hi = 1;
3893
- for (const [c0, c1] of pairs) {
3894
- if (c1 === 0) {
3895
- if (c0 <= 0) return false;
3896
- } else if (c1 > 0) lo = Math.max(lo, -c0 / c1);
3897
- else hi = Math.min(hi, -c0 / c1);
3898
- }
3899
- return lo < hi;
3884
+ function spreadBlend(b, a, xs, outward, s) {
3885
+ const fit = (ci, co, g) => {
3886
+ const quarter = Math.abs(xs[co] - xs[ci]) / 4;
3887
+ const today = g * (a[co] - a[ci]);
3888
+ const [lo, hi] = [
3889
+ Math.min(today, 0) - quarter,
3890
+ Math.max(today, 0) + quarter
3891
+ ];
3892
+ const ga = g * a[ci];
3893
+ if (ga === 0) return;
3894
+ const outer = g * (1 - b[co]) * a[co];
3895
+ const [b0, b1] = [1 - (outer - hi) / ga, 1 - (outer - lo) / ga];
3896
+ const least = Math.max(b[ci], Math.min(b0, b1));
3897
+ if (least <= Math.min(1, Math.max(b0, b1))) b[ci] = least;
3898
+ else if (Math.min(b0, b1) > 1) b[ci] = 1;
3900
3899
  };
3901
- const slack = [-1, 1].map(() => new Float64Array(count).fill(NaN));
3902
- const slackAt = (i, k) => {
3903
- if (!Number.isNaN(slack[k][i])) return slack[k][i];
3904
- const y = top - i - 0.5;
3905
- const w = blendAt(y);
3906
- const nod = {
3907
- up: w * NOD.hairFront.up + (1 - w) * cap.up,
3908
- down: w * NOD.hairFront.down + (1 - w) * cap.down
3909
- };
3910
- const ays = [-30, 30];
3911
- const [b0, b1] = passed(
3912
- i,
3913
- ays.map((ay) => nodDy(nod, ay, hh) - nodDy(NOD.hairBack, ay, hh))
3914
- );
3915
- const band = [];
3916
- for (let j = b0; j < b1; j++) band.push(backAt(j));
3917
- const faces = [];
3918
- const [f0, f1] = passed(
3919
- i,
3920
- ays.map((ay) => nodDy(nod, ay, hh) - face(f.axisX, y, 0, ay)[1])
3921
- );
3922
- for (let j = f0; j < f1; j++)
3923
- faces.push({ y: top - j - 0.5, runs: faceAt(j) });
3924
- const front = f.hairRuns("hair_front", y);
3925
- [-1, 1].forEach((s, side) => {
3926
- let least = Infinity;
3927
- const far = outerEdge(front, f.axisX, s);
3928
- if (far !== void 0) {
3929
- const R = cellR(far, y, side);
3930
- const c = Math.min(...band.map((b) => runReach(b, far, s).outward));
3931
- if (R > 0) least = Math.min(least, m.shellScale * c / R);
3932
- }
3933
- for (let t = s > 0 ? 0 : 1; t < front.length; t += 2) {
3934
- const x = front[t];
3935
- const R = cellR(x, y, side);
3936
- if (R <= 0) continue;
3937
- let c = Math.min(
3938
- ...band.map((b) => runReach(b, x, s > 0 ? -1 : 1).inward)
3939
- );
3940
- const sh = (m.shellScale - 1) * (x - f.axisX);
3941
- const d0 = sh + s * m.hairBack;
3942
- const d1 = d0 + s * R;
3943
- const lo = Math.min(d0, d1);
3944
- const hi = Math.max(d0, d1);
3945
- const bares = faces.some(({ y: fy, runs }) => {
3946
- for (let r = 0; r < runs.length; r += 2) {
3947
- const e0 = runs[r];
3948
- const e1 = runs[r + 1];
3949
- const de0 = face(e0, fy, 30 * s, 0)[0];
3950
- const de1 = face(e1, fy, 30 * s, 0)[0];
3951
- if (someAngle([
3952
- [e1 - x, de1 - lo],
3953
- [x - e0, hi - de0]
3954
- ])) {
3955
- return true;
3956
- }
3957
- }
3958
- return false;
3959
- });
3960
- if (bares) c = 0;
3961
- least = Math.min(least, m.shellScale * c / R);
3962
- }
3963
- slack[side][i] = least - w;
3900
+ for (const cols of outward) {
3901
+ for (let i = 1; i < cols.length; i++)
3902
+ b[cols[i]] = Math.max(b[cols[i]], b[cols[i - 1]]);
3903
+ }
3904
+ const [left, right] = outward.map((cols) => cols[0]);
3905
+ const across = left !== void 0 && right !== void 0 && left !== right;
3906
+ for (let pass = 0; pass < 2; pass++) {
3907
+ outward.forEach((cols, k) => {
3908
+ const g = (k === 0 ? -1 : 1) * s;
3909
+ for (let i = cols.length - 1; i > 0; i--) fit(cols[i - 1], cols[i], g);
3964
3910
  });
3965
- return slack[k][i];
3966
- };
3967
- const extra = [0, 1].map((k) => {
3968
- const out = wr.slice();
3969
- for (let r = 0; r < n; r++) {
3970
- if (wr[r] >= 1) continue;
3971
- const [i0, i1] = rowWindow(ys, r, count);
3972
- let least = Infinity;
3973
- for (let i = i0; i < i1; i++) least = Math.min(least, slackAt(i, k));
3974
- out[r] = wr[r] + clamp(least, 0, 1 - wr[r]);
3911
+ if (across) {
3912
+ if (b[left] <= b[right]) fit(left, right, s);
3913
+ else fit(right, left, -s);
3975
3914
  }
3976
- for (let r = n - 2; r >= 0; r--) {
3977
- if (wr[r] < 1) out[r] = Math.max(wr[r], Math.min(out[r], out[r + 1]));
3978
- }
3979
- return out.map((v, r) => v - wr[r]);
3980
- });
3981
- return (y, s) => {
3982
- if (s === 0) return 0;
3983
- const e = extra[s < 0 ? 0 : 1];
3984
- const { r, t } = gridRow(ys, y, true);
3985
- return e[r] * (1 - t) + e[r + 1] * t;
3986
- };
3915
+ }
3987
3916
  }
3988
3917
  function familyField(m, family) {
3989
3918
  switch (family) {