@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.mjs CHANGED
@@ -3373,12 +3373,12 @@ function hairFrontBends(frame, boxes) {
3373
3373
  }
3374
3374
  function hairFrontField(m) {
3375
3375
  const f = m.frame;
3376
- const back = hairField(m, m.hairBack, NOD.hairBack);
3377
3376
  const cap = { up: NOD.hairFrontTop.up, down: capSlide(m) };
3378
3377
  const eyes = [m.boxes.eye_L, m.boxes.eye_R].filter(
3379
3378
  (e) => e !== void 0
3380
3379
  );
3381
3380
  const { inner, left, right } = hairFrontBends(f, m.boxes);
3381
+ const capTop = m.hairFrontGrid?.ys.find((y) => y <= f.face.y1) ?? f.face.y1;
3382
3382
  const eyeY = eyes.length > 0 ? eyes.reduce((t, e) => t + cy(e), 0) / eyes.length : f.eyeY;
3383
3383
  const eyeH = eyes.length > 0 ? eyes.reduce((t, e) => t + (e.y1 - e.y0), 0) / eyes.length : 1;
3384
3384
  const coreAt = (x, y, s) => {
@@ -3411,22 +3411,18 @@ function hairFrontField(m) {
3411
3411
  );
3412
3412
  const followOf = (x, y, s) => followAt(x, y, s) + (ride === void 0 ? 0 : ride.lift(x, y, s));
3413
3413
  const drift = Math.abs(m.hairBack);
3414
- const crown = crownRide(m, cap, (x, y, s) => followOf(x, y, s) + drift);
3415
3414
  const boundOf = (x, y, s) => {
3416
3415
  if (ride === void 0) return followOf(x, y, s);
3417
- const b = ride.blend(y, s);
3416
+ const b = ride.blend(x, y, s);
3418
3417
  return (1 - b) * (followOf(x, y, s) + ride.cut(x, y, s)) - b * drift;
3419
3418
  };
3420
3419
  return (x, y, ax, ay) => {
3421
3420
  const s = Math.sign(ax);
3422
3421
  const a = Math.abs(ax) / 30;
3423
- const rx = s * a * boundOf(x, y, s) + a * (m.shellScale - 1) * (x - f.axisX);
3422
+ const rx = s * a * boundOf(x, Math.min(y, capTop), s) + a * (m.shellScale - 1) * (x - f.axisX);
3424
3423
  const ry = nodDy(NOD.hairFront, ay, f.hh);
3425
- const bx = back(x, y, ax, ay)[0];
3426
3424
  const by = nodDy(cap, ay, f.hh);
3427
- const w = crownBlend(m, y);
3428
- const wx = crown === void 0 ? w : w + crown(y, s);
3429
- return [bx + wx * (rx - bx), by + w * (ry - by)];
3425
+ return [rx, by + crownBlend(m, y) * (ry - by)];
3430
3426
  };
3431
3427
  }
3432
3428
  function crownBlend(m, y) {
@@ -3563,17 +3559,17 @@ function outerRide(m, inner, coreAt, followAt, capLimit, eyeTop, irisTop) {
3563
3559
  sides.forEach((side, k) => {
3564
3560
  const edge = outerEdge(front, f.axisX, side);
3565
3561
  if (edge === void 0) return;
3566
- let outward = Infinity;
3562
+ let outward2 = Infinity;
3567
3563
  let inward = Infinity;
3568
3564
  for (let j = Math.floor(i - above); j < Math.ceil(i + 1 + below); j++) {
3569
3565
  const c = runReach(backRows[j - j0], edge, side);
3570
- outward = Math.min(outward, c.outward);
3566
+ outward2 = Math.min(outward2, c.outward);
3571
3567
  inward = Math.min(inward, c.inward);
3572
3568
  }
3573
3569
  offset[k][i] = side * (edge - f.axisX);
3574
- reachOut[k][i] = outward;
3570
+ reachOut[k][i] = outward2;
3575
3571
  reachIn[k][i] = inward;
3576
- if (i < capRows && runReach(backRows[i - j0], edge, side).inward > 0)
3572
+ if (i < capRows && y <= f.face.y1 && runReach(backRows[i - j0], edge, side).inward > 0)
3577
3573
  bound[k][i] = 1;
3578
3574
  });
3579
3575
  }
@@ -3583,12 +3579,12 @@ function outerRide(m, inner, coreAt, followAt, capLimit, eyeTop, irisTop) {
3583
3579
  const [i0, i1] = rowWindow(ys, r, count);
3584
3580
  sides.forEach((side, k) => {
3585
3581
  let d = Infinity;
3586
- let outward = Infinity;
3582
+ let outward2 = Infinity;
3587
3583
  let inward = Infinity;
3588
3584
  for (let i = i0; i < i1; i++) {
3589
3585
  if (Number.isNaN(offset[k][i])) continue;
3590
3586
  d = Math.min(d, offset[k][i]);
3591
- outward = Math.min(outward, reachOut[k][i]);
3587
+ outward2 = Math.min(outward2, reachOut[k][i]);
3592
3588
  inward = Math.min(inward, reachIn[k][i]);
3593
3589
  }
3594
3590
  end[r * 2 + k] = d;
@@ -3597,7 +3593,7 @@ function outerRide(m, inner, coreAt, followAt, capLimit, eyeTop, irisTop) {
3597
3593
  sides.forEach((s, j) => {
3598
3594
  const allow = Math.min(
3599
3595
  coreAt(x, ys[r], s),
3600
- m.shellScale * (s === side ? outward : inward) - drift
3596
+ m.shellScale * (s === side ? outward2 : inward) - drift
3601
3597
  );
3602
3598
  lift[(r * 2 + k) * 2 + j] = Math.max(0, allow - followAt(x, ys[r], s));
3603
3599
  });
@@ -3668,73 +3664,117 @@ function outerRide(m, inner, coreAt, followAt, capLimit, eyeTop, irisTop) {
3668
3664
  return quarter.map((q) => scale * q);
3669
3665
  };
3670
3666
  const cuts = new Float64Array(n * 2 * nc);
3671
- const blend = new Float64Array(n * 2);
3667
+ const blend = new Float64Array(n * 2 * nc);
3668
+ const motion = new Array(n * 2);
3669
+ const outward = sides.map((side) => {
3670
+ const cols = xs.map((_, c) => c).filter((c) => side * (xs[c] - f.axisX) >= 0);
3671
+ return side < 0 ? cols.reverse() : cols;
3672
+ });
3672
3673
  const wr = ys.map((y) => crownBlend(m, y));
3673
3674
  for (let r = 0; r <= last && capRows > 0; r++) {
3674
- const w = wr[r];
3675
- if (w <= 0) continue;
3675
+ if (wr[r] < 1) continue;
3676
3676
  const edges = [];
3677
+ const copied = r > 0 && wr[r - 1] < 1;
3677
3678
  const [i0, i1] = rowWindow(ys, r, capRows);
3678
3679
  for (let i = i0; i < i1; i++) {
3680
+ const y = top - i - 0.5;
3679
3681
  sides.forEach((side, k) => {
3680
3682
  if (bound[k][i] === 0) return;
3681
3683
  const x = f.axisX + side * offset[k][i];
3682
- const at = readAt(r, x, top - i - 0.5);
3683
- if (at === void 0) return;
3684
- edges.push({
3685
- side,
3686
- x,
3687
- at,
3688
- outward: reachOut[k][i],
3689
- inward: reachIn[k][i]
3690
- });
3684
+ for (const at2 of [
3685
+ readAt(r, x, y),
3686
+ copied && y > ys[r] ? readAt(r - 1, x, y) : void 0
3687
+ ]) {
3688
+ if (at2 === void 0) continue;
3689
+ edges.push({
3690
+ side,
3691
+ x,
3692
+ at: at2,
3693
+ outward: reachOut[k][i],
3694
+ inward: reachIn[k][i]
3695
+ });
3696
+ }
3691
3697
  });
3692
3698
  }
3693
3699
  if (edges.length === 0) continue;
3694
3700
  sides.forEach((s, j) => {
3695
3701
  const today = xs.map((x) => followAt(x, ys[r], s) + liftAt(x, ys[r], s));
3696
3702
  const after = today.slice();
3697
- if (w >= 1) {
3698
- for (const side of sides) {
3699
- const mine = edges.filter((e) => e.side === side);
3700
- if (mine.length === 0) continue;
3701
- const cut = capCut(r, s, side, today, mine);
3702
- for (let c = 0; c < nc; c++) after[c] -= cut[c];
3703
- }
3703
+ for (const side of sides) {
3704
+ const mine = edges.filter((e) => e.side === side);
3705
+ if (mine.length === 0) continue;
3706
+ const cut = capCut(r, s, side, today, mine);
3707
+ for (let c = 0; c < nc; c++) after[c] -= cut[c];
3704
3708
  }
3705
- let b = 0;
3709
+ const b = blend.subarray((r * 2 + j) * nc, (r * 2 + j + 1) * nc);
3706
3710
  for (const e of edges) {
3707
- const rel = w * (along(after, e.at) + drift);
3708
3711
  const hi = m.shellScale * ahead(e, s);
3709
3712
  const lo = -m.shellScale * behind(e, s);
3710
- if (rel > hi) b = Math.max(b, 1 - hi / rel);
3711
- else if (rel < lo) b = Math.max(b, 1 - lo / rel);
3713
+ const { c, t } = gridCol(xs, e.at);
3714
+ for (const [cc, weight] of [
3715
+ [c, 1 - t],
3716
+ [c + 1, t]
3717
+ ]) {
3718
+ if (weight <= 0) continue;
3719
+ const rel = after[cc] + drift;
3720
+ if (rel > hi) b[cc] = Math.max(b[cc], 1 - hi / rel);
3721
+ else if (rel < lo) b[cc] = Math.max(b[cc], 1 - lo / rel);
3722
+ }
3712
3723
  }
3713
- blend[r * 2 + j] = b;
3724
+ motion[r * 2 + j] = after.map((v) => v + drift);
3714
3725
  for (let c = 0; c < nc; c++)
3715
3726
  cuts[(r * 2 + j) * nc + c] = after[c] - today[c];
3716
3727
  });
3717
3728
  }
3718
- for (let j = 0; j < 2; j++) {
3719
- for (let r = n - 2; r >= 0; r--) {
3720
- if (r <= last || wr[r] < 1)
3721
- blend[r * 2 + j] = Math.max(blend[r * 2 + j], blend[(r + 1) * 2 + j]);
3722
- }
3729
+ for (let r = 0; r < n; r++) {
3730
+ sides.forEach((s, j) => {
3731
+ motion[r * 2 + j] ??= xs.map(
3732
+ (x) => followAt(x, ys[r], s) + liftAt(x, ys[r], s) + drift
3733
+ );
3734
+ });
3723
3735
  }
3736
+ const at = (r, j, c) => (r * 2 + j) * nc + c;
3724
3737
  let zero = last + 1;
3725
3738
  while (zero < n && ys[zero] > eyeTop) zero++;
3726
3739
  while (zero > last + 1 && zero < n && ys[zero] < irisTop - 0.5) zero--;
3727
3740
  const endY = zero < n ? ys[zero] : Math.min(Math.max(eyeTop, irisTop - 0.5), ys[n - 1] - 1);
3728
- for (let j = 0; j < 2 && last < n - 1; j++) {
3729
- for (let r = 1; r < zero; r++) {
3730
- for (let q = 0; q < r && q <= last; q++) {
3731
- if (wr[q] < 1) continue;
3732
- blend[r * 2 + j] = Math.max(
3733
- blend[r * 2 + j],
3734
- blend[q * 2 + j] * (ys[r] - endY) / (ys[q] - endY)
3735
- );
3741
+ for (let pass = 0; pass < 8; pass++) {
3742
+ const was = blend.slice();
3743
+ for (let j = 0; j < 2; j++) {
3744
+ for (let r = n - 2; r >= 0; r--) {
3745
+ if (r > last && wr[r] >= 1) continue;
3746
+ for (let c = 0; c < nc; c++)
3747
+ blend[at(r, j, c)] = Math.max(
3748
+ blend[at(r, j, c)],
3749
+ blend[at(r + 1, j, c)]
3750
+ );
3751
+ }
3752
+ }
3753
+ for (let j = 0; j < 2 && last < n - 1; j++) {
3754
+ for (let r = 1; r < zero; r++) {
3755
+ for (let q = 0; q < r && q <= last; q++) {
3756
+ if (wr[q] < 1) continue;
3757
+ const k = (ys[r] - endY) / (ys[q] - endY);
3758
+ for (let c = 0; c < nc; c++)
3759
+ blend[at(r, j, c)] = Math.max(
3760
+ blend[at(r, j, c)],
3761
+ blend[at(q, j, c)] * k
3762
+ );
3763
+ }
3736
3764
  }
3737
3765
  }
3766
+ for (let r = 0; r < n; r++) {
3767
+ sides.forEach(
3768
+ (s, j) => spreadBlend(
3769
+ blend.subarray(at(r, j, 0), at(r, j, nc)),
3770
+ motion[r * 2 + j],
3771
+ xs,
3772
+ outward,
3773
+ s
3774
+ )
3775
+ );
3776
+ }
3777
+ if (blend.every((v, i) => v === was[i])) break;
3738
3778
  }
3739
3779
  const cutRow = (r, j, x) => r > last ? 0 : along(cuts.subarray((r * 2 + j) * nc, (r * 2 + j + 1) * nc), x);
3740
3780
  return {
@@ -3747,158 +3787,47 @@ function outerRide(m, inner, coreAt, followAt, capLimit, eyeTop, irisTop) {
3747
3787
  const { r, t } = gridRow(ys, y, true);
3748
3788
  return cutRow(r, j, x) * (1 - t) + cutRow(r + 1, j, x) * t;
3749
3789
  },
3750
- blend: (y, s) => {
3790
+ blend: (x, y, s) => {
3751
3791
  if (s === 0) return 0;
3752
3792
  const j = s < 0 ? 0 : 1;
3753
3793
  const { r, t } = gridRow(ys, y, true);
3754
- return blend[r * 2 + j] * (1 - t) + blend[(r + 1) * 2 + j] * t;
3794
+ const row = (q) => along(blend.subarray(at(q, j, 0), at(q, j, nc)), x);
3795
+ return row(r) * (1 - t) + row(r + 1) * t;
3755
3796
  }
3756
3797
  };
3757
3798
  }
3758
- function crownRide(m, cap, relAt) {
3759
- const f = m.frame;
3760
- const grid = m.hairFrontGrid;
3761
- if (grid === void 0) return void 0;
3762
- const { xs, ys } = grid;
3763
- const top = ys[0];
3764
- if (f.hairRuns("hair_front", top) === void 0 || f.hairRuns("hair_back", top) === void 0) {
3765
- return void 0;
3766
- }
3767
- const n = ys.length;
3768
- const count = Math.round(top - ys[n - 1]);
3769
- const hh = f.hh;
3770
- const face = faceField(m);
3771
- const { wr, blendAt } = renderedCrownBlend(m, ys);
3772
- const backRows = /* @__PURE__ */ new Map();
3773
- const backAt = (j) => {
3774
- let runs = backRows.get(j);
3775
- if (runs === void 0) {
3776
- runs = f.hairRuns("hair_back", top - j - 0.5);
3777
- backRows.set(j, runs);
3778
- }
3779
- return runs;
3780
- };
3781
- const faceAt = (j) => {
3782
- const y = top - j - 0.5;
3783
- return f.faceRuns(y) ?? (y > f.face.y0 && y < f.face.y1 ? [f.face.x0, f.face.x1] : []);
3784
- };
3785
- const passed = (i, dys) => [
3786
- Math.floor(i - Math.max(0, ...dys)),
3787
- Math.ceil(i + 1 - Math.min(0, ...dys))
3788
- ];
3789
- const rel = new Float64Array(n * xs.length * 2).fill(NaN);
3790
- const relOf = (r, c, k) => {
3791
- const at = (r * xs.length + c) * 2 + k;
3792
- if (Number.isNaN(rel[at])) rel[at] = relAt(xs[c], ys[r], k === 0 ? -1 : 1);
3793
- return rel[at];
3794
- };
3795
- const cellR = (x, y, k) => {
3796
- const { c } = gridCol(xs, x);
3797
- const { r } = gridRow(ys, y);
3798
- return Math.max(
3799
- relOf(r, c, k),
3800
- relOf(r, c + 1, k),
3801
- relOf(r + 1, c, k),
3802
- relOf(r + 1, c + 1, k)
3803
- );
3804
- };
3805
- const someAngle = (pairs) => {
3806
- let lo = 0;
3807
- let hi = 1;
3808
- for (const [c0, c1] of pairs) {
3809
- if (c1 === 0) {
3810
- if (c0 <= 0) return false;
3811
- } else if (c1 > 0) lo = Math.max(lo, -c0 / c1);
3812
- else hi = Math.min(hi, -c0 / c1);
3813
- }
3814
- return lo < hi;
3799
+ function spreadBlend(b, a, xs, outward, s) {
3800
+ const fit = (ci, co, g) => {
3801
+ const quarter = Math.abs(xs[co] - xs[ci]) / 4;
3802
+ const today = g * (a[co] - a[ci]);
3803
+ const [lo, hi] = [
3804
+ Math.min(today, 0) - quarter,
3805
+ Math.max(today, 0) + quarter
3806
+ ];
3807
+ const ga = g * a[ci];
3808
+ if (ga === 0) return;
3809
+ const outer = g * (1 - b[co]) * a[co];
3810
+ const [b0, b1] = [1 - (outer - hi) / ga, 1 - (outer - lo) / ga];
3811
+ const least = Math.max(b[ci], Math.min(b0, b1));
3812
+ if (least <= Math.min(1, Math.max(b0, b1))) b[ci] = least;
3813
+ else if (Math.min(b0, b1) > 1) b[ci] = 1;
3815
3814
  };
3816
- const slack = [-1, 1].map(() => new Float64Array(count).fill(NaN));
3817
- const slackAt = (i, k) => {
3818
- if (!Number.isNaN(slack[k][i])) return slack[k][i];
3819
- const y = top - i - 0.5;
3820
- const w = blendAt(y);
3821
- const nod = {
3822
- up: w * NOD.hairFront.up + (1 - w) * cap.up,
3823
- down: w * NOD.hairFront.down + (1 - w) * cap.down
3824
- };
3825
- const ays = [-30, 30];
3826
- const [b0, b1] = passed(
3827
- i,
3828
- ays.map((ay) => nodDy(nod, ay, hh) - nodDy(NOD.hairBack, ay, hh))
3829
- );
3830
- const band = [];
3831
- for (let j = b0; j < b1; j++) band.push(backAt(j));
3832
- const faces = [];
3833
- const [f0, f1] = passed(
3834
- i,
3835
- ays.map((ay) => nodDy(nod, ay, hh) - face(f.axisX, y, 0, ay)[1])
3836
- );
3837
- for (let j = f0; j < f1; j++)
3838
- faces.push({ y: top - j - 0.5, runs: faceAt(j) });
3839
- const front = f.hairRuns("hair_front", y);
3840
- [-1, 1].forEach((s, side) => {
3841
- let least = Infinity;
3842
- const far = outerEdge(front, f.axisX, s);
3843
- if (far !== void 0) {
3844
- const R = cellR(far, y, side);
3845
- const c = Math.min(...band.map((b) => runReach(b, far, s).outward));
3846
- if (R > 0) least = Math.min(least, m.shellScale * c / R);
3847
- }
3848
- for (let t = s > 0 ? 0 : 1; t < front.length; t += 2) {
3849
- const x = front[t];
3850
- const R = cellR(x, y, side);
3851
- if (R <= 0) continue;
3852
- let c = Math.min(
3853
- ...band.map((b) => runReach(b, x, s > 0 ? -1 : 1).inward)
3854
- );
3855
- const sh = (m.shellScale - 1) * (x - f.axisX);
3856
- const d0 = sh + s * m.hairBack;
3857
- const d1 = d0 + s * R;
3858
- const lo = Math.min(d0, d1);
3859
- const hi = Math.max(d0, d1);
3860
- const bares = faces.some(({ y: fy, runs }) => {
3861
- for (let r = 0; r < runs.length; r += 2) {
3862
- const e0 = runs[r];
3863
- const e1 = runs[r + 1];
3864
- const de0 = face(e0, fy, 30 * s, 0)[0];
3865
- const de1 = face(e1, fy, 30 * s, 0)[0];
3866
- if (someAngle([
3867
- [e1 - x, de1 - lo],
3868
- [x - e0, hi - de0]
3869
- ])) {
3870
- return true;
3871
- }
3872
- }
3873
- return false;
3874
- });
3875
- if (bares) c = 0;
3876
- least = Math.min(least, m.shellScale * c / R);
3877
- }
3878
- slack[side][i] = least - w;
3815
+ for (const cols of outward) {
3816
+ for (let i = 1; i < cols.length; i++)
3817
+ b[cols[i]] = Math.max(b[cols[i]], b[cols[i - 1]]);
3818
+ }
3819
+ const [left, right] = outward.map((cols) => cols[0]);
3820
+ const across = left !== void 0 && right !== void 0 && left !== right;
3821
+ for (let pass = 0; pass < 2; pass++) {
3822
+ outward.forEach((cols, k) => {
3823
+ const g = (k === 0 ? -1 : 1) * s;
3824
+ for (let i = cols.length - 1; i > 0; i--) fit(cols[i - 1], cols[i], g);
3879
3825
  });
3880
- return slack[k][i];
3881
- };
3882
- const extra = [0, 1].map((k) => {
3883
- const out = wr.slice();
3884
- for (let r = 0; r < n; r++) {
3885
- if (wr[r] >= 1) continue;
3886
- const [i0, i1] = rowWindow(ys, r, count);
3887
- let least = Infinity;
3888
- for (let i = i0; i < i1; i++) least = Math.min(least, slackAt(i, k));
3889
- out[r] = wr[r] + clamp(least, 0, 1 - wr[r]);
3826
+ if (across) {
3827
+ if (b[left] <= b[right]) fit(left, right, s);
3828
+ else fit(right, left, -s);
3890
3829
  }
3891
- for (let r = n - 2; r >= 0; r--) {
3892
- if (wr[r] < 1) out[r] = Math.max(wr[r], Math.min(out[r], out[r + 1]));
3893
- }
3894
- return out.map((v, r) => v - wr[r]);
3895
- });
3896
- return (y, s) => {
3897
- if (s === 0) return 0;
3898
- const e = extra[s < 0 ? 0 : 1];
3899
- const { r, t } = gridRow(ys, y, true);
3900
- return e[r] * (1 - t) + e[r + 1] * t;
3901
- };
3830
+ }
3902
3831
  }
3903
3832
  function familyField(m, family) {
3904
3833
  switch (family) {