@ikijs/editor 0.11.0 → 0.12.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 +4 -4
- package/dist/index.d.mts +4 -3
- package/dist/index.d.ts +4 -3
- package/dist/index.js +178 -16
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +178 -16
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -1569,7 +1569,7 @@ function createLayerSetMeasurer(canvas) {
|
|
|
1569
1569
|
if (bbox === null) return null;
|
|
1570
1570
|
const rowLeft = new Int32Array(canvasH).fill(canvasW);
|
|
1571
1571
|
const rowRight = new Int32Array(canvasH).fill(-1);
|
|
1572
|
-
const rowRuns = role === "hair_front" || role === "hair_back" ? [] : void 0;
|
|
1572
|
+
const rowRuns = role === "face" || role === "hair_front" || role === "hair_back" ? [] : void 0;
|
|
1573
1573
|
for (let y = 0; y < canvasH; y++) {
|
|
1574
1574
|
let runs;
|
|
1575
1575
|
if (rowRuns !== void 0 && y >= bbox.y && y < bbox.y + bbox.h) {
|
|
@@ -2737,12 +2737,14 @@ function buildHeadFrame(layers, opts) {
|
|
|
2737
2737
|
shellRight = axisX + headHalf;
|
|
2738
2738
|
}
|
|
2739
2739
|
}
|
|
2740
|
-
const
|
|
2740
|
+
const runsOf = (role, y) => {
|
|
2741
2741
|
const l = byRole.get(role);
|
|
2742
2742
|
if (l?.rowRuns === void 0) return void 0;
|
|
2743
2743
|
const runs = l.rowRuns[Math.floor(canvasH / 2 - y) - l.bbox.y];
|
|
2744
2744
|
return runs === void 0 ? [] : runs.map((c) => c - canvasW / 2);
|
|
2745
2745
|
};
|
|
2746
|
+
const hairRuns = (role, y) => runsOf(role, y);
|
|
2747
|
+
const faceRuns = (y) => runsOf("face", y);
|
|
2746
2748
|
return {
|
|
2747
2749
|
canvasW,
|
|
2748
2750
|
canvasH,
|
|
@@ -2765,7 +2767,8 @@ function buildHeadFrame(layers, opts) {
|
|
|
2765
2767
|
shellRight,
|
|
2766
2768
|
hairShell,
|
|
2767
2769
|
holdBase: headHalf ?? plateHalf,
|
|
2768
|
-
hairRuns
|
|
2770
|
+
hairRuns,
|
|
2771
|
+
faceRuns
|
|
2769
2772
|
};
|
|
2770
2773
|
}
|
|
2771
2774
|
|
|
@@ -2967,22 +2970,42 @@ function hairFrontField(m) {
|
|
|
2967
2970
|
return core + (m.hairOuter - core) * out;
|
|
2968
2971
|
};
|
|
2969
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));
|
|
2974
|
+
const drift = Math.abs(m.hairBack);
|
|
2975
|
+
const crown = crownRide(m, cap, (x, y, s) => followOf(x, y, s) + drift);
|
|
2970
2976
|
return (x, y, ax, ay) => {
|
|
2971
2977
|
const s = Math.sign(ax);
|
|
2972
2978
|
const a = Math.abs(ax) / 30;
|
|
2973
|
-
const
|
|
2974
|
-
const rx = s * a * follow + a * (m.shellScale - 1) * (x - f.axisX);
|
|
2979
|
+
const rx = s * a * followOf(x, y, s) + a * (m.shellScale - 1) * (x - f.axisX);
|
|
2975
2980
|
const ry = nodDy(NOD.hairFront, ay, f.hh);
|
|
2976
2981
|
const bx = back(x, y, ax, ay)[0];
|
|
2977
2982
|
const by = nodDy(cap, ay, f.hh);
|
|
2978
2983
|
const w = crownBlend(m, y);
|
|
2979
|
-
|
|
2984
|
+
const wx = crown === void 0 ? w : w + crown(y, s);
|
|
2985
|
+
return [bx + wx * (rx - bx), by + w * (ry - by)];
|
|
2980
2986
|
};
|
|
2981
2987
|
}
|
|
2982
2988
|
function crownBlend(m, y) {
|
|
2983
2989
|
const top = m.frame.face.y1;
|
|
2984
2990
|
return 1 - smoothstep(top, Math.max(top + 1, m.hairTop), y);
|
|
2985
2991
|
}
|
|
2992
|
+
function gridRow(ys, y, snap = false) {
|
|
2993
|
+
const n = ys.length;
|
|
2994
|
+
let v = clamp((ys[0] - y) / (ys[0] - ys[n - 1]) * (n - 1), 0, n - 1);
|
|
2995
|
+
if (snap && Math.abs(v - Math.round(v)) < 1e-3) v = Math.round(v);
|
|
2996
|
+
const r = Math.min(n - 2, Math.floor(v));
|
|
2997
|
+
return { r, t: v - r };
|
|
2998
|
+
}
|
|
2999
|
+
function renderedCrownBlend(m, ys) {
|
|
3000
|
+
const wr = ys.map((y) => crownBlend(m, y));
|
|
3001
|
+
return {
|
|
3002
|
+
wr,
|
|
3003
|
+
blendAt: (y) => {
|
|
3004
|
+
const { r, t } = gridRow(ys, y);
|
|
3005
|
+
return wr[r] + (wr[r + 1] - wr[r]) * t;
|
|
3006
|
+
}
|
|
3007
|
+
};
|
|
3008
|
+
}
|
|
2986
3009
|
var CAP_COVER_LOSS = 0.01;
|
|
2987
3010
|
function capSlide(m) {
|
|
2988
3011
|
const f = m.frame;
|
|
@@ -2995,12 +3018,7 @@ function capSlide(m) {
|
|
|
2995
3018
|
const top = ys[0];
|
|
2996
3019
|
const n = ys.length;
|
|
2997
3020
|
const bangs = NOD.hairFront.down;
|
|
2998
|
-
const wr
|
|
2999
|
-
const blendAt = (y) => {
|
|
3000
|
-
const v = clamp((top - y) / (top - ys[n - 1]) * (n - 1), 0, n - 1);
|
|
3001
|
-
const r = Math.min(n - 2, Math.floor(v));
|
|
3002
|
-
return wr[r] + (wr[r + 1] - wr[r]) * (v - r);
|
|
3003
|
-
};
|
|
3021
|
+
const { wr, blendAt } = renderedCrownBlend(m, ys);
|
|
3004
3022
|
const flat = ys[wr.findIndex((w) => w >= 1)] ?? ys[n - 1];
|
|
3005
3023
|
const x0 = xs[0];
|
|
3006
3024
|
const tops = new Float64Array(Math.round(xs[xs.length - 1] - x0)).fill(NaN);
|
|
@@ -3129,13 +3147,157 @@ function outerRide(m, inner, coreAt, followAt) {
|
|
|
3129
3147
|
if (s === 0 || d <= inner) return 0;
|
|
3130
3148
|
const k = x < f.axisX ? 0 : 1;
|
|
3131
3149
|
const j = s < 0 ? 0 : 1;
|
|
3132
|
-
|
|
3133
|
-
if (Math.abs(v - Math.round(v)) < 1e-3) v = Math.round(v);
|
|
3134
|
-
const r = Math.min(n - 2, Math.floor(v));
|
|
3135
|
-
const t = v - r;
|
|
3150
|
+
const { r, t } = gridRow(ys, y, true);
|
|
3136
3151
|
return rowRide(r, k, j, d) * (1 - t) + rowRide(r + 1, k, j, d) * t;
|
|
3137
3152
|
};
|
|
3138
3153
|
}
|
|
3154
|
+
function crownRide(m, cap, relAt) {
|
|
3155
|
+
const f = m.frame;
|
|
3156
|
+
const grid = m.hairFrontGrid;
|
|
3157
|
+
if (grid === void 0) return void 0;
|
|
3158
|
+
const { xs, ys } = grid;
|
|
3159
|
+
const top = ys[0];
|
|
3160
|
+
if (f.hairRuns("hair_front", top) === void 0 || f.hairRuns("hair_back", top) === void 0) {
|
|
3161
|
+
return void 0;
|
|
3162
|
+
}
|
|
3163
|
+
const n = ys.length;
|
|
3164
|
+
const count = Math.round(top - ys[n - 1]);
|
|
3165
|
+
const hh = f.hh;
|
|
3166
|
+
const face = faceField(m);
|
|
3167
|
+
const { wr, blendAt } = renderedCrownBlend(m, ys);
|
|
3168
|
+
const backRows = /* @__PURE__ */ new Map();
|
|
3169
|
+
const backAt = (j) => {
|
|
3170
|
+
let runs = backRows.get(j);
|
|
3171
|
+
if (runs === void 0) {
|
|
3172
|
+
runs = f.hairRuns("hair_back", top - j - 0.5);
|
|
3173
|
+
backRows.set(j, runs);
|
|
3174
|
+
}
|
|
3175
|
+
return runs;
|
|
3176
|
+
};
|
|
3177
|
+
const faceAt = (j) => {
|
|
3178
|
+
const y = top - j - 0.5;
|
|
3179
|
+
return f.faceRuns(y) ?? (y > f.face.y0 && y < f.face.y1 ? [f.face.x0, f.face.x1] : []);
|
|
3180
|
+
};
|
|
3181
|
+
const passed = (i, dys) => [
|
|
3182
|
+
Math.floor(i - Math.max(0, ...dys)),
|
|
3183
|
+
Math.ceil(i + 1 - Math.min(0, ...dys))
|
|
3184
|
+
];
|
|
3185
|
+
const rel = new Float64Array(n * xs.length * 2).fill(NaN);
|
|
3186
|
+
const relOf = (r, c, k) => {
|
|
3187
|
+
const at = (r * xs.length + c) * 2 + k;
|
|
3188
|
+
if (Number.isNaN(rel[at])) rel[at] = relAt(xs[c], ys[r], k === 0 ? -1 : 1);
|
|
3189
|
+
return rel[at];
|
|
3190
|
+
};
|
|
3191
|
+
const cellR = (x, y, k) => {
|
|
3192
|
+
let c = 0;
|
|
3193
|
+
while (c < xs.length - 2 && xs[c + 1] < x) c++;
|
|
3194
|
+
const { r } = gridRow(ys, y);
|
|
3195
|
+
return Math.max(
|
|
3196
|
+
relOf(r, c, k),
|
|
3197
|
+
relOf(r, c + 1, k),
|
|
3198
|
+
relOf(r + 1, c, k),
|
|
3199
|
+
relOf(r + 1, c + 1, k)
|
|
3200
|
+
);
|
|
3201
|
+
};
|
|
3202
|
+
const someAngle = (pairs) => {
|
|
3203
|
+
let lo = 0;
|
|
3204
|
+
let hi = 1;
|
|
3205
|
+
for (const [c0, c1] of pairs) {
|
|
3206
|
+
if (c1 === 0) {
|
|
3207
|
+
if (c0 <= 0) return false;
|
|
3208
|
+
} else if (c1 > 0) lo = Math.max(lo, -c0 / c1);
|
|
3209
|
+
else hi = Math.min(hi, -c0 / c1);
|
|
3210
|
+
}
|
|
3211
|
+
return lo < hi;
|
|
3212
|
+
};
|
|
3213
|
+
const slack = [-1, 1].map(() => new Float64Array(count).fill(NaN));
|
|
3214
|
+
const slackAt = (i, k) => {
|
|
3215
|
+
if (!Number.isNaN(slack[k][i])) return slack[k][i];
|
|
3216
|
+
const y = top - i - 0.5;
|
|
3217
|
+
const w = blendAt(y);
|
|
3218
|
+
const nod = {
|
|
3219
|
+
up: w * NOD.hairFront.up + (1 - w) * cap.up,
|
|
3220
|
+
down: w * NOD.hairFront.down + (1 - w) * cap.down
|
|
3221
|
+
};
|
|
3222
|
+
const ays = [-30, 30];
|
|
3223
|
+
const [b0, b1] = passed(
|
|
3224
|
+
i,
|
|
3225
|
+
ays.map((ay) => nodDy(nod, ay, hh) - nodDy(NOD.hairBack, ay, hh))
|
|
3226
|
+
);
|
|
3227
|
+
const band = [];
|
|
3228
|
+
for (let j = b0; j < b1; j++) band.push(backAt(j));
|
|
3229
|
+
const faces = [];
|
|
3230
|
+
const [f0, f1] = passed(
|
|
3231
|
+
i,
|
|
3232
|
+
ays.map((ay) => nodDy(nod, ay, hh) - face(f.axisX, y, 0, ay)[1])
|
|
3233
|
+
);
|
|
3234
|
+
for (let j = f0; j < f1; j++)
|
|
3235
|
+
faces.push({ y: top - j - 0.5, runs: faceAt(j) });
|
|
3236
|
+
const front = f.hairRuns("hair_front", y);
|
|
3237
|
+
[-1, 1].forEach((s, side) => {
|
|
3238
|
+
let least = Infinity;
|
|
3239
|
+
const far = outerEdge(front, f.axisX, s);
|
|
3240
|
+
if (far !== void 0) {
|
|
3241
|
+
const R = cellR(far, y, side);
|
|
3242
|
+
const c = Math.min(...band.map((b) => runReach(b, far, s).outward));
|
|
3243
|
+
if (R > 0) least = Math.min(least, m.shellScale * c / R);
|
|
3244
|
+
}
|
|
3245
|
+
for (let t = s > 0 ? 0 : 1; t < front.length; t += 2) {
|
|
3246
|
+
const x = front[t];
|
|
3247
|
+
const R = cellR(x, y, side);
|
|
3248
|
+
if (R <= 0) continue;
|
|
3249
|
+
let c = Math.min(
|
|
3250
|
+
...band.map((b) => runReach(b, x, s > 0 ? -1 : 1).inward)
|
|
3251
|
+
);
|
|
3252
|
+
const sh = (m.shellScale - 1) * (x - f.axisX);
|
|
3253
|
+
const d0 = sh + s * m.hairBack;
|
|
3254
|
+
const d1 = d0 + s * R;
|
|
3255
|
+
const lo = Math.min(d0, d1);
|
|
3256
|
+
const hi = Math.max(d0, d1);
|
|
3257
|
+
const bares = faces.some(({ y: fy, runs }) => {
|
|
3258
|
+
for (let r = 0; r < runs.length; r += 2) {
|
|
3259
|
+
const e0 = runs[r];
|
|
3260
|
+
const e1 = runs[r + 1];
|
|
3261
|
+
const de0 = face(e0, fy, 30 * s, 0)[0];
|
|
3262
|
+
const de1 = face(e1, fy, 30 * s, 0)[0];
|
|
3263
|
+
if (someAngle([
|
|
3264
|
+
[e1 - x, de1 - lo],
|
|
3265
|
+
[x - e0, hi - de0]
|
|
3266
|
+
])) {
|
|
3267
|
+
return true;
|
|
3268
|
+
}
|
|
3269
|
+
}
|
|
3270
|
+
return false;
|
|
3271
|
+
});
|
|
3272
|
+
if (bares) c = 0;
|
|
3273
|
+
least = Math.min(least, m.shellScale * c / R);
|
|
3274
|
+
}
|
|
3275
|
+
slack[side][i] = least - w;
|
|
3276
|
+
});
|
|
3277
|
+
return slack[k][i];
|
|
3278
|
+
};
|
|
3279
|
+
const extra = [0, 1].map((k) => {
|
|
3280
|
+
const out = wr.slice();
|
|
3281
|
+
for (let r = 0; r < n; r++) {
|
|
3282
|
+
if (wr[r] >= 1) continue;
|
|
3283
|
+
const i0 = Math.max(0, Math.floor(top - ys[Math.max(0, r - 1)]));
|
|
3284
|
+
const i1 = Math.min(count, Math.ceil(top - ys[Math.min(n - 1, r + 1)]));
|
|
3285
|
+
let least = Infinity;
|
|
3286
|
+
for (let i = i0; i < i1; i++) least = Math.min(least, slackAt(i, k));
|
|
3287
|
+
out[r] = wr[r] + clamp(least, 0, 1 - wr[r]);
|
|
3288
|
+
}
|
|
3289
|
+
for (let r = n - 2; r >= 0; r--) {
|
|
3290
|
+
if (wr[r] < 1) out[r] = Math.max(wr[r], Math.min(out[r], out[r + 1]));
|
|
3291
|
+
}
|
|
3292
|
+
return out.map((v, r) => v - wr[r]);
|
|
3293
|
+
});
|
|
3294
|
+
return (y, s) => {
|
|
3295
|
+
if (s === 0) return 0;
|
|
3296
|
+
const e = extra[s < 0 ? 0 : 1];
|
|
3297
|
+
const { r, t } = gridRow(ys, y, true);
|
|
3298
|
+
return e[r] * (1 - t) + e[r + 1] * t;
|
|
3299
|
+
};
|
|
3300
|
+
}
|
|
3139
3301
|
function familyField(m, family) {
|
|
3140
3302
|
switch (family) {
|
|
3141
3303
|
case "face":
|