@ikijs/editor 0.12.1 → 0.12.2
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 +9 -5
- package/dist/index.d.mts +4 -1
- package/dist/index.d.ts +4 -1
- package/dist/index.js +223 -29
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +223 -29
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -2388,15 +2388,22 @@ var TURN = {
|
|
|
2388
2388
|
* where no back hair paints behind that edge. Where back hair does, the
|
|
2389
2389
|
* edge rides further, as far as that back hair stays behind it through
|
|
2390
2390
|
* the turn and the nod, up to the face's follow there (`hairFollow`;
|
|
2391
|
-
* over the far eye's rows, as far as that eye's outer corner goes).
|
|
2391
|
+
* over the far eye's rows, as far as that eye's outer corner goes). On
|
|
2392
|
+
* the cap (the rows above the eyes) it follows less where the turn would
|
|
2393
|
+
* otherwise carry it further outside its back hair than the nod alone
|
|
2394
|
+
* does. */
|
|
2392
2395
|
outlineFollow: 0,
|
|
2393
2396
|
/** The back hair's slight counter-motion. */
|
|
2394
2397
|
hairBack: -0.027,
|
|
2395
|
-
/** The ears lag the plate: the far one moves this share of
|
|
2396
|
-
* near one this at its widest reach, its root riding the
|
|
2397
|
-
* samples' parallax ratios, 0.44 and 0.87). */
|
|
2398
|
+
/** The ears lag the plate: the far one's outer edge moves this share of
|
|
2399
|
+
* its slide, the near one this at its widest reach, its root riding the
|
|
2400
|
+
* head (the samples' parallax ratios, 0.44 and 0.87). */
|
|
2398
2401
|
earFar: 0.44,
|
|
2399
|
-
earNear: 0.87
|
|
2402
|
+
earNear: 0.87,
|
|
2403
|
+
/** The far ear's width at full turn, about its outer edge, where the head
|
|
2404
|
+
* leaves it room (the samples' median: Haru 0.81 / 0.91, Hiyori 0.90 /
|
|
2405
|
+
* 0.85, Mao 0.77 / 0.79; none fades); less where it does not. */
|
|
2406
|
+
earFarScale: 0.83
|
|
2400
2407
|
};
|
|
2401
2408
|
var NOD = {
|
|
2402
2409
|
faceTop: { up: -0.081, down: 0.178 },
|
|
@@ -2817,16 +2824,45 @@ function earField(m) {
|
|
|
2817
2824
|
const s = Math.sign(ax);
|
|
2818
2825
|
const a = Math.abs(ax) / 30;
|
|
2819
2826
|
const dy = face(x, y, 0, ay)[1];
|
|
2820
|
-
if (sideOf(m, x, s) === "far") return [s * a * TURN.earFar * m.face, dy];
|
|
2821
2827
|
const d = Math.abs(x - f.axisX);
|
|
2822
2828
|
const attach = ears.attachAt(y);
|
|
2829
|
+
const span = Math.max(1, ears.outer - attach);
|
|
2830
|
+
if (sideOf(m, x, s) === "far") {
|
|
2831
|
+
const rim2 = TURN.earFar * m.face;
|
|
2832
|
+
const root2 = earRoot(m, face, y, s);
|
|
2833
|
+
const lead = Math.max(
|
|
2834
|
+
0,
|
|
2835
|
+
Math.min((1 - m.earFarScale) * span, root2 - rim2)
|
|
2836
|
+
);
|
|
2837
|
+
let along = rim2 + lead * (ears.outer - d) / span;
|
|
2838
|
+
if (d < attach) along = Math.min(along, s * face(x, y, 30 * s, 0)[0]);
|
|
2839
|
+
return [s * a * along, dy];
|
|
2840
|
+
}
|
|
2823
2841
|
if (d <= attach) return [face(x, y, ax, 0)[0], dy];
|
|
2824
2842
|
const root = face(f.axisX + Math.sign(x - f.axisX) * attach, y, ax, 0)[0];
|
|
2825
2843
|
const rim = s * a * TURN.earNear * m.face;
|
|
2826
|
-
const t = Math.min(1, (d - attach) /
|
|
2844
|
+
const t = Math.min(1, (d - attach) / span);
|
|
2827
2845
|
return [root + (rim - root) * t, dy];
|
|
2828
2846
|
};
|
|
2829
2847
|
}
|
|
2848
|
+
function earRoot(m, face, y, s) {
|
|
2849
|
+
const f = m.frame;
|
|
2850
|
+
return s * face(f.axisX + s * f.ears.attachAt(y), y, 30 * s, 0)[0];
|
|
2851
|
+
}
|
|
2852
|
+
function farEarCovered(m) {
|
|
2853
|
+
const ears = m.frame.ears;
|
|
2854
|
+
if (ears === void 0) return true;
|
|
2855
|
+
const face = faceField(m);
|
|
2856
|
+
const rim = TURN.earFar * m.face;
|
|
2857
|
+
const y0 = ears.bottom - 2;
|
|
2858
|
+
const y1 = ears.top + 2;
|
|
2859
|
+
const n = Math.max(1, Math.ceil(y1 - y0));
|
|
2860
|
+
for (let i = 0; i <= n; i++) {
|
|
2861
|
+
const y = y0 + (y1 - y0) * i / n;
|
|
2862
|
+
for (const s of [-1, 1]) if (earRoot(m, face, y, s) < rim) return false;
|
|
2863
|
+
}
|
|
2864
|
+
return true;
|
|
2865
|
+
}
|
|
2830
2866
|
function chinSlide(m) {
|
|
2831
2867
|
const f = m.frame;
|
|
2832
2868
|
return Math.abs(faceField(m)(f.axisX, f.chinY, 30, 0)[0]);
|
|
@@ -2969,14 +3005,19 @@ function hairFrontField(m) {
|
|
|
2969
3005
|
);
|
|
2970
3006
|
return core + (m.hairOuter - core) * out;
|
|
2971
3007
|
};
|
|
2972
|
-
const ride = outerRide(m, inner, coreAt, followAt);
|
|
2973
|
-
const followOf = (x, y, s) => followAt(x, y, s) + (ride === void 0 ? 0 : ride(x, y, s));
|
|
3008
|
+
const ride = outerRide(m, inner, coreAt, followAt, eyeY + 1.5 * eyeH);
|
|
3009
|
+
const followOf = (x, y, s) => followAt(x, y, s) + (ride === void 0 ? 0 : ride.lift(x, y, s));
|
|
2974
3010
|
const drift = Math.abs(m.hairBack);
|
|
2975
3011
|
const crown = crownRide(m, cap, (x, y, s) => followOf(x, y, s) + drift);
|
|
3012
|
+
const boundOf = (x, y, s) => {
|
|
3013
|
+
if (ride === void 0) return followOf(x, y, s);
|
|
3014
|
+
const b = ride.blend(y, s);
|
|
3015
|
+
return (1 - b) * (followOf(x, y, s) + ride.cut(x, y, s)) - b * drift;
|
|
3016
|
+
};
|
|
2976
3017
|
return (x, y, ax, ay) => {
|
|
2977
3018
|
const s = Math.sign(ax);
|
|
2978
3019
|
const a = Math.abs(ax) / 30;
|
|
2979
|
-
const rx = s * a *
|
|
3020
|
+
const rx = s * a * boundOf(x, y, s) + a * (m.shellScale - 1) * (x - f.axisX);
|
|
2980
3021
|
const ry = nodDy(NOD.hairFront, ay, f.hh);
|
|
2981
3022
|
const bx = back(x, y, ax, ay)[0];
|
|
2982
3023
|
const by = nodDy(cap, ay, f.hh);
|
|
@@ -2996,6 +3037,18 @@ function gridRow(ys, y, snap = false) {
|
|
|
2996
3037
|
const r = Math.min(n - 2, Math.floor(v));
|
|
2997
3038
|
return { r, t: v - r };
|
|
2998
3039
|
}
|
|
3040
|
+
function gridCol(xs, x) {
|
|
3041
|
+
let c = 0;
|
|
3042
|
+
while (c < xs.length - 2 && xs[c + 1] < x) c++;
|
|
3043
|
+
return { c, t: clamp((x - xs[c]) / (xs[c + 1] - xs[c]), 0, 1) };
|
|
3044
|
+
}
|
|
3045
|
+
function rowWindow(ys, r, limit) {
|
|
3046
|
+
const n = ys.length;
|
|
3047
|
+
return [
|
|
3048
|
+
Math.max(0, Math.floor(ys[0] - ys[Math.max(0, r - 1)])),
|
|
3049
|
+
Math.min(limit, Math.ceil(ys[0] - ys[Math.min(n - 1, r + 1)]))
|
|
3050
|
+
];
|
|
3051
|
+
}
|
|
2999
3052
|
function renderedCrownBlend(m, ys) {
|
|
3000
3053
|
const wr = ys.map((y) => crownBlend(m, y));
|
|
3001
3054
|
return {
|
|
@@ -3071,10 +3124,11 @@ function paints(runs, px) {
|
|
|
3071
3124
|
}
|
|
3072
3125
|
return false;
|
|
3073
3126
|
}
|
|
3074
|
-
function outerRide(m, inner, coreAt, followAt) {
|
|
3127
|
+
function outerRide(m, inner, coreAt, followAt, capLimit) {
|
|
3075
3128
|
const f = m.frame;
|
|
3076
|
-
const
|
|
3077
|
-
if (
|
|
3129
|
+
const grid = m.hairFrontGrid;
|
|
3130
|
+
if (grid === void 0) return void 0;
|
|
3131
|
+
const { xs, ys } = grid;
|
|
3078
3132
|
const top = ys[0];
|
|
3079
3133
|
if (f.hairRuns("hair_front", top) === void 0 || f.hairRuns("hair_back", top) === void 0) {
|
|
3080
3134
|
return void 0;
|
|
@@ -3083,6 +3137,9 @@ function outerRide(m, inner, coreAt, followAt) {
|
|
|
3083
3137
|
const sides = [-1, 1];
|
|
3084
3138
|
const n = ys.length;
|
|
3085
3139
|
const count = Math.round(top - ys[n - 1]);
|
|
3140
|
+
let last = 0;
|
|
3141
|
+
while (last + 1 < n && ys[last + 1] >= capLimit) last++;
|
|
3142
|
+
const capRows = last === 0 ? 0 : Math.min(count, Math.floor(top - ys[last] - 0.5) + 1);
|
|
3086
3143
|
const below = (NOD.hairFront.down - NOD.hairBack.down) * f.hh;
|
|
3087
3144
|
const above = (NOD.hairBack.up - NOD.hairFront.up) * f.hh;
|
|
3088
3145
|
const j0 = Math.floor(-above);
|
|
@@ -3091,8 +3148,9 @@ function outerRide(m, inner, coreAt, followAt) {
|
|
|
3091
3148
|
(_, j) => f.hairRuns("hair_back", top - (j + j0) - 0.5)
|
|
3092
3149
|
);
|
|
3093
3150
|
const offset = sides.map(() => new Float64Array(count).fill(NaN));
|
|
3094
|
-
const
|
|
3095
|
-
const
|
|
3151
|
+
const reachOut = sides.map(() => new Float64Array(count));
|
|
3152
|
+
const reachIn = sides.map(() => new Float64Array(count));
|
|
3153
|
+
const bound = sides.map(() => new Uint8Array(count));
|
|
3096
3154
|
for (let i = 0; i < count; i++) {
|
|
3097
3155
|
const y = top - i - 0.5;
|
|
3098
3156
|
const front = f.hairRuns("hair_front", y);
|
|
@@ -3107,24 +3165,25 @@ function outerRide(m, inner, coreAt, followAt) {
|
|
|
3107
3165
|
inward = Math.min(inward, c.inward);
|
|
3108
3166
|
}
|
|
3109
3167
|
offset[k][i] = side * (edge - f.axisX);
|
|
3110
|
-
|
|
3111
|
-
|
|
3168
|
+
reachOut[k][i] = outward;
|
|
3169
|
+
reachIn[k][i] = inward;
|
|
3170
|
+
if (i < capRows && runReach(backRows[i - j0], edge, side).inward > 0)
|
|
3171
|
+
bound[k][i] = 1;
|
|
3112
3172
|
});
|
|
3113
3173
|
}
|
|
3114
3174
|
const end = new Float64Array(n * 2);
|
|
3115
3175
|
const lift = new Float64Array(n * 4);
|
|
3116
3176
|
for (let r = 0; r < n; r++) {
|
|
3117
|
-
const i0 =
|
|
3118
|
-
const i1 = Math.min(count, Math.ceil(top - ys[Math.min(n - 1, r + 1)]));
|
|
3177
|
+
const [i0, i1] = rowWindow(ys, r, count);
|
|
3119
3178
|
sides.forEach((side, k) => {
|
|
3120
3179
|
let d = Infinity;
|
|
3121
|
-
let
|
|
3122
|
-
let
|
|
3180
|
+
let outward = Infinity;
|
|
3181
|
+
let inward = Infinity;
|
|
3123
3182
|
for (let i = i0; i < i1; i++) {
|
|
3124
3183
|
if (Number.isNaN(offset[k][i])) continue;
|
|
3125
3184
|
d = Math.min(d, offset[k][i]);
|
|
3126
|
-
|
|
3127
|
-
|
|
3185
|
+
outward = Math.min(outward, reachOut[k][i]);
|
|
3186
|
+
inward = Math.min(inward, reachIn[k][i]);
|
|
3128
3187
|
}
|
|
3129
3188
|
end[r * 2 + k] = d;
|
|
3130
3189
|
if (d === Infinity || d <= inner) return;
|
|
@@ -3132,7 +3191,7 @@ function outerRide(m, inner, coreAt, followAt) {
|
|
|
3132
3191
|
sides.forEach((s, j) => {
|
|
3133
3192
|
const allow = Math.min(
|
|
3134
3193
|
coreAt(x, ys[r], s),
|
|
3135
|
-
s === side ?
|
|
3194
|
+
m.shellScale * (s === side ? outward : inward) - drift
|
|
3136
3195
|
);
|
|
3137
3196
|
lift[(r * 2 + k) * 2 + j] = Math.max(0, allow - followAt(x, ys[r], s));
|
|
3138
3197
|
});
|
|
@@ -3142,7 +3201,7 @@ function outerRide(m, inner, coreAt, followAt) {
|
|
|
3142
3201
|
const v = lift[(r * 2 + k) * 2 + j];
|
|
3143
3202
|
return v === 0 ? 0 : v * Math.min(1, (d - inner) / (end[r * 2 + k] - inner));
|
|
3144
3203
|
};
|
|
3145
|
-
|
|
3204
|
+
const liftAt = (x, y, s) => {
|
|
3146
3205
|
const d = Math.abs(x - f.axisX);
|
|
3147
3206
|
if (s === 0 || d <= inner) return 0;
|
|
3148
3207
|
const k = x < f.axisX ? 0 : 1;
|
|
@@ -3150,6 +3209,130 @@ function outerRide(m, inner, coreAt, followAt) {
|
|
|
3150
3209
|
const { r, t } = gridRow(ys, y, true);
|
|
3151
3210
|
return rowRide(r, k, j, d) * (1 - t) + rowRide(r + 1, k, j, d) * t;
|
|
3152
3211
|
};
|
|
3212
|
+
const nc = xs.length;
|
|
3213
|
+
const along = (v, x) => {
|
|
3214
|
+
const { c, t } = gridCol(xs, x);
|
|
3215
|
+
return v[c] + (v[c + 1] - v[c]) * t;
|
|
3216
|
+
};
|
|
3217
|
+
const readAt = (r, x, y) => {
|
|
3218
|
+
const { c, t: fx } = gridCol(xs, x);
|
|
3219
|
+
const dx = xs[c + 1] - xs[c];
|
|
3220
|
+
if (r + 1 < n && y <= ys[r] && y >= ys[r + 1]) {
|
|
3221
|
+
const fy = (ys[r] - y) / (ys[r] - ys[r + 1]);
|
|
3222
|
+
if (fy >= 1) return void 0;
|
|
3223
|
+
return fx <= fy ? xs[c] : xs[c] + (fx - fy) / (1 - fy) * dx;
|
|
3224
|
+
}
|
|
3225
|
+
if (r > 0 && y > ys[r] && y < ys[r - 1]) {
|
|
3226
|
+
const fy = (ys[r - 1] - y) / (ys[r - 1] - ys[r]);
|
|
3227
|
+
return fx <= fy ? xs[c] + fx / fy * dx : xs[c + 1];
|
|
3228
|
+
}
|
|
3229
|
+
return void 0;
|
|
3230
|
+
};
|
|
3231
|
+
const ahead = (e, s) => s === e.side ? e.outward : e.inward;
|
|
3232
|
+
const behind = (e, s) => s === e.side ? e.inward : e.outward;
|
|
3233
|
+
const capCut = (r, s, side, today, mine) => {
|
|
3234
|
+
const reach = Math.max(...mine.map((e) => side * (e.x - f.axisX)));
|
|
3235
|
+
const quarter = new Float64Array(nc);
|
|
3236
|
+
const cols = xs.map((_, c) => c).filter((c) => (xs[c] < f.axisX ? -1 : 1) === side);
|
|
3237
|
+
if (side < 0) cols.reverse();
|
|
3238
|
+
let total = 0;
|
|
3239
|
+
let x0 = f.axisX;
|
|
3240
|
+
let d0 = s * followAt(f.axisX, ys[r], s);
|
|
3241
|
+
let open = true;
|
|
3242
|
+
for (const c of cols) {
|
|
3243
|
+
if (open) {
|
|
3244
|
+
const d = (m.shellScale - 1) * (xs[c] - f.axisX) + s * today[c];
|
|
3245
|
+
const rest = side * (xs[c] - x0);
|
|
3246
|
+
const full = rest + side * (d - d0);
|
|
3247
|
+
total += Math.max(0, Math.min(full, rest)) / 4;
|
|
3248
|
+
x0 = xs[c];
|
|
3249
|
+
d0 = d;
|
|
3250
|
+
open = side * (xs[c] - f.axisX) < reach;
|
|
3251
|
+
}
|
|
3252
|
+
quarter[c] = total;
|
|
3253
|
+
}
|
|
3254
|
+
let scale = 0;
|
|
3255
|
+
for (const e of mine) {
|
|
3256
|
+
const need = along(today, e.at) - (m.shellScale * ahead(e, s) - drift);
|
|
3257
|
+
if (need <= 0) continue;
|
|
3258
|
+
const q = along(quarter, e.at);
|
|
3259
|
+
scale = Math.max(scale, q > 0 ? need / q : 1);
|
|
3260
|
+
}
|
|
3261
|
+
scale = Math.min(1, scale);
|
|
3262
|
+
return quarter.map((q) => scale * q);
|
|
3263
|
+
};
|
|
3264
|
+
const cuts = new Float64Array(n * 2 * nc);
|
|
3265
|
+
const blend = new Float64Array(n * 2);
|
|
3266
|
+
const wr = ys.map((y) => crownBlend(m, y));
|
|
3267
|
+
for (let r = 0; r <= last && capRows > 0; r++) {
|
|
3268
|
+
const w = wr[r];
|
|
3269
|
+
if (w <= 0) continue;
|
|
3270
|
+
const edges = [];
|
|
3271
|
+
const [i0, i1] = rowWindow(ys, r, capRows);
|
|
3272
|
+
for (let i = i0; i < i1; i++) {
|
|
3273
|
+
sides.forEach((side, k) => {
|
|
3274
|
+
if (bound[k][i] === 0) return;
|
|
3275
|
+
const x = f.axisX + side * offset[k][i];
|
|
3276
|
+
const at = readAt(r, x, top - i - 0.5);
|
|
3277
|
+
if (at === void 0) return;
|
|
3278
|
+
edges.push({
|
|
3279
|
+
side,
|
|
3280
|
+
x,
|
|
3281
|
+
at,
|
|
3282
|
+
outward: reachOut[k][i],
|
|
3283
|
+
inward: reachIn[k][i]
|
|
3284
|
+
});
|
|
3285
|
+
});
|
|
3286
|
+
}
|
|
3287
|
+
if (edges.length === 0) continue;
|
|
3288
|
+
sides.forEach((s, j) => {
|
|
3289
|
+
const today = xs.map((x) => followAt(x, ys[r], s) + liftAt(x, ys[r], s));
|
|
3290
|
+
const after = today.slice();
|
|
3291
|
+
if (w >= 1) {
|
|
3292
|
+
for (const side of sides) {
|
|
3293
|
+
const mine = edges.filter((e) => e.side === side);
|
|
3294
|
+
if (mine.length === 0) continue;
|
|
3295
|
+
const cut = capCut(r, s, side, today, mine);
|
|
3296
|
+
for (let c = 0; c < nc; c++) after[c] -= cut[c];
|
|
3297
|
+
}
|
|
3298
|
+
}
|
|
3299
|
+
let b = 0;
|
|
3300
|
+
for (const e of edges) {
|
|
3301
|
+
const rel = w * (along(after, e.at) + drift);
|
|
3302
|
+
const hi = m.shellScale * ahead(e, s);
|
|
3303
|
+
const lo = -m.shellScale * behind(e, s);
|
|
3304
|
+
if (rel > hi) b = Math.max(b, 1 - hi / rel);
|
|
3305
|
+
else if (rel < lo) b = Math.max(b, 1 - lo / rel);
|
|
3306
|
+
}
|
|
3307
|
+
blend[r * 2 + j] = b;
|
|
3308
|
+
for (let c = 0; c < nc; c++)
|
|
3309
|
+
cuts[(r * 2 + j) * nc + c] = after[c] - today[c];
|
|
3310
|
+
});
|
|
3311
|
+
}
|
|
3312
|
+
for (let j = 0; j < 2; j++) {
|
|
3313
|
+
for (let r = n - 2; r >= 0; r--) {
|
|
3314
|
+
if (wr[r] < 1)
|
|
3315
|
+
blend[r * 2 + j] = Math.max(blend[r * 2 + j], blend[(r + 1) * 2 + j]);
|
|
3316
|
+
}
|
|
3317
|
+
}
|
|
3318
|
+
const cutRow = (r, j, x) => r > last ? 0 : along(cuts.subarray((r * 2 + j) * nc, (r * 2 + j + 1) * nc), x);
|
|
3319
|
+
return {
|
|
3320
|
+
lift: liftAt,
|
|
3321
|
+
// Between vertex rows, as the render interpolates; a vertex reads its own
|
|
3322
|
+
// row's alone.
|
|
3323
|
+
cut: (x, y, s) => {
|
|
3324
|
+
if (s === 0) return 0;
|
|
3325
|
+
const j = s < 0 ? 0 : 1;
|
|
3326
|
+
const { r, t } = gridRow(ys, y, true);
|
|
3327
|
+
return cutRow(r, j, x) * (1 - t) + cutRow(r + 1, j, x) * t;
|
|
3328
|
+
},
|
|
3329
|
+
blend: (y, s) => {
|
|
3330
|
+
if (s === 0) return 0;
|
|
3331
|
+
const j = s < 0 ? 0 : 1;
|
|
3332
|
+
const { r, t } = gridRow(ys, y, true);
|
|
3333
|
+
return blend[r * 2 + j] * (1 - t) + blend[(r + 1) * 2 + j] * t;
|
|
3334
|
+
}
|
|
3335
|
+
};
|
|
3153
3336
|
}
|
|
3154
3337
|
function crownRide(m, cap, relAt) {
|
|
3155
3338
|
const f = m.frame;
|
|
@@ -3189,8 +3372,7 @@ function crownRide(m, cap, relAt) {
|
|
|
3189
3372
|
return rel[at];
|
|
3190
3373
|
};
|
|
3191
3374
|
const cellR = (x, y, k) => {
|
|
3192
|
-
|
|
3193
|
-
while (c < xs.length - 2 && xs[c + 1] < x) c++;
|
|
3375
|
+
const { c } = gridCol(xs, x);
|
|
3194
3376
|
const { r } = gridRow(ys, y);
|
|
3195
3377
|
return Math.max(
|
|
3196
3378
|
relOf(r, c, k),
|
|
@@ -3280,8 +3462,7 @@ function crownRide(m, cap, relAt) {
|
|
|
3280
3462
|
const out = wr.slice();
|
|
3281
3463
|
for (let r = 0; r < n; r++) {
|
|
3282
3464
|
if (wr[r] >= 1) continue;
|
|
3283
|
-
const i0 =
|
|
3284
|
-
const i1 = Math.min(count, Math.ceil(top - ys[Math.min(n - 1, r + 1)]));
|
|
3465
|
+
const [i0, i1] = rowWindow(ys, r, count);
|
|
3285
3466
|
let least = Infinity;
|
|
3286
3467
|
for (let i = i0; i < i1; i++) least = Math.min(least, slackAt(i, k));
|
|
3287
3468
|
out[r] = wr[r] + clamp(least, 0, 1 - wr[r]);
|
|
@@ -3950,6 +4131,18 @@ function fitTurn(ctx, targets, style, build) {
|
|
|
3950
4131
|
}
|
|
3951
4132
|
p.shell = bisect((k) => at(k) - s, SHELL_RANGE[0], SHELL_RANGE[1]);
|
|
3952
4133
|
}
|
|
4134
|
+
const covered = (shell) => farEarCovered(build({ ...p, shell }));
|
|
4135
|
+
if (!covered(p.shell) && covered(Math.max(1, p.shell))) {
|
|
4136
|
+
let lo = p.shell;
|
|
4137
|
+
let hi = Math.max(1, p.shell);
|
|
4138
|
+
for (let i = 0; i < 50; i++) {
|
|
4139
|
+
const mid = (lo + hi) / 2;
|
|
4140
|
+
if (covered(mid)) hi = mid;
|
|
4141
|
+
else lo = mid;
|
|
4142
|
+
}
|
|
4143
|
+
p.shell = hi;
|
|
4144
|
+
clamped.push("silhouetteRatio");
|
|
4145
|
+
}
|
|
3953
4146
|
const hb = ctx.restHalf;
|
|
3954
4147
|
const feature = (key, family, at, box) => {
|
|
3955
4148
|
const base = build(p);
|
|
@@ -4108,6 +4301,7 @@ function buildTurn(frame, landmarks, q) {
|
|
|
4108
4301
|
hairOuter: q.outline * face,
|
|
4109
4302
|
hairBack: A * hh * TURN.hairBack,
|
|
4110
4303
|
shellScale: q.shell,
|
|
4304
|
+
earFarScale: 1 + A * (TURN.earFarScale - 1),
|
|
4111
4305
|
...landmarks
|
|
4112
4306
|
};
|
|
4113
4307
|
}
|