@ikijs/editor 0.32.1 → 0.33.1
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 +1 -1
- package/dist/index.d.mts +3 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +128 -8
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +128 -8
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -2937,6 +2937,7 @@ function mouthAnchor(byRole) {
|
|
|
2937
2937
|
}
|
|
2938
2938
|
|
|
2939
2939
|
// src/auto-rig/head.ts
|
|
2940
|
+
var EAR_EDGE = 3;
|
|
2940
2941
|
function outerEdge(runs, axisX, side) {
|
|
2941
2942
|
if (runs.length === 0) return void 0;
|
|
2942
2943
|
const x = side > 0 ? runs[runs.length - 1] : runs[0];
|
|
@@ -2970,7 +2971,11 @@ function findEars(raw, wMax, firstRow, lastRow, minRows, maxRows) {
|
|
|
2970
2971
|
if (raw[t] - raw[t - EAR_ROWS] < step) continue;
|
|
2971
2972
|
for (let b = t + minRows; b <= Math.min(lastRow, t + maxRows); b++) {
|
|
2972
2973
|
if (raw[b - EAR_ROWS] - raw[b] >= step) {
|
|
2973
|
-
|
|
2974
|
+
let end = b;
|
|
2975
|
+
for (let k = b + 1; k <= Math.min(lastRow, b + minRows); k++) {
|
|
2976
|
+
if (raw[k - EAR_ROWS] - raw[k] >= step) end = k;
|
|
2977
|
+
}
|
|
2978
|
+
return { top: t - EAR_ROWS, bottom: end };
|
|
2974
2979
|
}
|
|
2975
2980
|
}
|
|
2976
2981
|
return void 0;
|
|
@@ -3048,6 +3053,7 @@ function buildHeadFrame(layers, opts) {
|
|
|
3048
3053
|
return rows[i] + (rows[j] - rows[i]) * clamp(r - i, 0, 1);
|
|
3049
3054
|
};
|
|
3050
3055
|
const rawRows = raw;
|
|
3056
|
+
let earRows;
|
|
3051
3057
|
const earless = (chin) => {
|
|
3052
3058
|
const rowOf = (y) => Math.round(canvasH / 2 - y - faceLayer.bbox.y - 0.5);
|
|
3053
3059
|
const span3 = Math.max(1, eyeY - chin);
|
|
@@ -3070,8 +3076,10 @@ function buildHeadFrame(layers, opts) {
|
|
|
3070
3076
|
top: rowY(top) + 0.5,
|
|
3071
3077
|
bottom: rowY(bottom) - 0.5,
|
|
3072
3078
|
attachAt: (y) => attach(clamp(canvasH / 2 - y - faceLayer.bbox.y - 0.5, top, bottom)),
|
|
3073
|
-
outer
|
|
3079
|
+
outer,
|
|
3080
|
+
edgeAt: (y) => attach(clamp(canvasH / 2 - y - faceLayer.bbox.y - 0.5, top, bottom)) + EAR_EDGE
|
|
3074
3081
|
};
|
|
3082
|
+
earRows = { top, bottom };
|
|
3075
3083
|
return smoothProfile(
|
|
3076
3084
|
rawRows.map((v, r) => r > top && r < bottom ? attach(r) : v)
|
|
3077
3085
|
);
|
|
@@ -3193,9 +3201,13 @@ function buildHeadFrame(layers, opts) {
|
|
|
3193
3201
|
headHalfAt = readRows(head);
|
|
3194
3202
|
headMax = Math.max(...head);
|
|
3195
3203
|
if (ears !== void 0) {
|
|
3204
|
+
const band = earRows;
|
|
3205
|
+
const edge = head.map(
|
|
3206
|
+
(w, r) => r >= band.top && r <= band.bottom ? w + EAR_EDGE : w
|
|
3207
|
+
);
|
|
3196
3208
|
const bottomOf = (d) => {
|
|
3197
|
-
for (let r =
|
|
3198
|
-
if (
|
|
3209
|
+
for (let r = edge.length - 1; r >= 0; r--) {
|
|
3210
|
+
if (edge[r] >= d) return rowY(r) - 0.5;
|
|
3199
3211
|
}
|
|
3200
3212
|
return face.y1;
|
|
3201
3213
|
};
|
|
@@ -3363,6 +3375,19 @@ function spin(x, y, ox, oy, a) {
|
|
|
3363
3375
|
];
|
|
3364
3376
|
}
|
|
3365
3377
|
function faceField(m) {
|
|
3378
|
+
const plate = plateField(m);
|
|
3379
|
+
const floor = foreheadFloor(m);
|
|
3380
|
+
let hold;
|
|
3381
|
+
return (x, y, ax, ay) => {
|
|
3382
|
+
const d = plate(x, y, ax, ay);
|
|
3383
|
+
if (ax === 0 || y <= floor) return d;
|
|
3384
|
+
if (hold === void 0) hold = foreheadHold(m, floor) ?? null;
|
|
3385
|
+
if (hold === null) return d;
|
|
3386
|
+
const s = Math.sign(ax);
|
|
3387
|
+
return [d[0] - s * Math.abs(ax) * hold(x, y, s) / 30, d[1]];
|
|
3388
|
+
};
|
|
3389
|
+
}
|
|
3390
|
+
function plateField(m) {
|
|
3366
3391
|
const f = m.frame;
|
|
3367
3392
|
return (x, y, ax, ay) => {
|
|
3368
3393
|
const s = Math.sign(ax);
|
|
@@ -3382,6 +3407,77 @@ function faceField(m) {
|
|
|
3382
3407
|
return [dx, nodDy(v, ay, f.hh)];
|
|
3383
3408
|
};
|
|
3384
3409
|
}
|
|
3410
|
+
function faceCellPx(frame) {
|
|
3411
|
+
return Math.min(FACE_CELL_PX, 2 * frame.wMax / 14);
|
|
3412
|
+
}
|
|
3413
|
+
var FACE_CELL_PX = 28;
|
|
3414
|
+
var FOREHEAD_FADE = 0.04;
|
|
3415
|
+
function foreheadFloor(m) {
|
|
3416
|
+
const f = m.frame;
|
|
3417
|
+
const eyes = [m.boxes.eye_L, m.boxes.eye_R].filter(
|
|
3418
|
+
(e) => e !== void 0
|
|
3419
|
+
);
|
|
3420
|
+
const eyeTop = eyes.length > 0 ? Math.max(...eyes.map((e) => e.y1)) : f.eyeY;
|
|
3421
|
+
return Math.max(eyeTop, f.ears === void 0 ? -Infinity : f.ears.top + 2);
|
|
3422
|
+
}
|
|
3423
|
+
function foreheadHold(m, floor) {
|
|
3424
|
+
const f = m.frame;
|
|
3425
|
+
const top = f.face.y1;
|
|
3426
|
+
const count = Math.floor(top - floor);
|
|
3427
|
+
if (count <= 0 || f.faceRuns(top - 0.5) === void 0 || f.hairRuns("hair_front", top - 0.5) === void 0) {
|
|
3428
|
+
return void 0;
|
|
3429
|
+
}
|
|
3430
|
+
const plate = plateField(m);
|
|
3431
|
+
const front = hairFrontField(m);
|
|
3432
|
+
const { inner } = hairFrontBends(f, m.boxes);
|
|
3433
|
+
const cell = faceCellPx(f);
|
|
3434
|
+
const fade = FOREHEAD_FADE * f.hh;
|
|
3435
|
+
const sides = [-1, 1];
|
|
3436
|
+
const hold = sides.map(() => new Float64Array(count));
|
|
3437
|
+
const whole = sides.map(() => new Float64Array(count));
|
|
3438
|
+
const span3 = sides.map(() => new Float64Array(count));
|
|
3439
|
+
let any = false;
|
|
3440
|
+
for (let i = 0; i < count; i++) {
|
|
3441
|
+
const y = top - i - 0.5;
|
|
3442
|
+
const faceRuns = f.faceRuns(y);
|
|
3443
|
+
const hairRuns = f.hairRuns("hair_front", y);
|
|
3444
|
+
sides.forEach((side, k) => {
|
|
3445
|
+
const edge = outerEdge(faceRuns, f.axisX, side);
|
|
3446
|
+
const hair = outerEdge(hairRuns, f.axisX, side);
|
|
3447
|
+
if (edge === void 0 || hair === void 0) return;
|
|
3448
|
+
const end = side * (edge - f.axisX) - cell;
|
|
3449
|
+
if (end <= 0) return;
|
|
3450
|
+
const cover = side * (hair - edge);
|
|
3451
|
+
const room = Math.max(0, cover);
|
|
3452
|
+
const goes = side * plate(edge - side * 0.5, y, 30 * side, 0)[0];
|
|
3453
|
+
const held = side * front(hair - side * 0.5, y, 30 * side, 0)[0];
|
|
3454
|
+
const need = clamp(1 + cover / fade, 0, 1) * Math.max(0, goes - held - room + Math.min(1, room));
|
|
3455
|
+
if (need <= 0) return;
|
|
3456
|
+
const over = Math.min(end, Math.max(2 * cell, end - inner, 2 * need));
|
|
3457
|
+
hold[k][i] = Math.min(need, over / 2);
|
|
3458
|
+
whole[k][i] = end;
|
|
3459
|
+
span3[k][i] = over;
|
|
3460
|
+
any = true;
|
|
3461
|
+
});
|
|
3462
|
+
}
|
|
3463
|
+
if (!any) return void 0;
|
|
3464
|
+
const reach = Math.ceil(cell);
|
|
3465
|
+
return (x, y, s) => {
|
|
3466
|
+
const u = s * (x - f.axisX);
|
|
3467
|
+
if (s === 0 || u <= 0 || y <= floor) return 0;
|
|
3468
|
+
const k = s < 0 ? 0 : 1;
|
|
3469
|
+
const i = clamp(Math.floor(top - y), 0, count - 1);
|
|
3470
|
+
let h = 0;
|
|
3471
|
+
const j1 = Math.min(count - 1, i + 3 * reach);
|
|
3472
|
+
for (let j = Math.max(0, i - 3 * reach); j <= j1; j++) {
|
|
3473
|
+
if (hold[k][j] === 0) continue;
|
|
3474
|
+
const near = clamp((3 * reach - Math.abs(i - j)) / (2 * reach), 0, 1);
|
|
3475
|
+
const across = clamp((u - (whole[k][j] - span3[k][j])) / span3[k][j], 0, 1);
|
|
3476
|
+
h = Math.max(h, hold[k][j] * near * across);
|
|
3477
|
+
}
|
|
3478
|
+
return h * clamp((y - floor) / (2 * cell), 0, 1);
|
|
3479
|
+
};
|
|
3480
|
+
}
|
|
3385
3481
|
function earField(m) {
|
|
3386
3482
|
const f = m.frame;
|
|
3387
3483
|
const ears = f.ears;
|
|
@@ -3395,7 +3491,7 @@ function earField(m) {
|
|
|
3395
3491
|
const attach = ears.attachAt(y);
|
|
3396
3492
|
const span3 = Math.max(1, ears.outer - attach);
|
|
3397
3493
|
if (sideOf(m, x, s) === "far") {
|
|
3398
|
-
const rim2 =
|
|
3494
|
+
const rim2 = m.earFar * m.face;
|
|
3399
3495
|
const root2 = earRoot(m, face, y, s);
|
|
3400
3496
|
const lead = Math.max(
|
|
3401
3497
|
0,
|
|
@@ -3420,7 +3516,7 @@ function farEarCovered(m) {
|
|
|
3420
3516
|
const ears = m.frame.ears;
|
|
3421
3517
|
if (ears === void 0) return true;
|
|
3422
3518
|
const face = faceField(m);
|
|
3423
|
-
const rim =
|
|
3519
|
+
const rim = m.earFar * m.face;
|
|
3424
3520
|
const y0 = ears.bottom - 2;
|
|
3425
3521
|
const y1 = ears.top + 2;
|
|
3426
3522
|
const n = Math.max(1, Math.ceil(y1 - y0));
|
|
@@ -4261,6 +4357,7 @@ function fitTurn(ctx, targets, plan, build) {
|
|
|
4261
4357
|
lead: plan.turn.featureLead,
|
|
4262
4358
|
follow: plan.hair.follow,
|
|
4263
4359
|
outline: plan.hair.outlineFollow,
|
|
4360
|
+
earFar: plan.turn.farEar,
|
|
4264
4361
|
fore: targets.farEyeRatio === void 0 ? plan.turn.eyeForeshorten : 1,
|
|
4265
4362
|
shell: 1,
|
|
4266
4363
|
noseLead: targets.noseShift !== void 0 || plan.turn.noseLead === 1 ? void 0 : plan.turn.noseLead
|
|
@@ -4497,6 +4594,7 @@ function buildTurn(frame, landmarks, q, turn) {
|
|
|
4497
4594
|
hairOuter: q.outline * face,
|
|
4498
4595
|
hairBack: A * hh * turn.hairBack,
|
|
4499
4596
|
shellScale: q.shell,
|
|
4597
|
+
earFar: q.earFar,
|
|
4500
4598
|
earFarScale: 1 + A * (turn.earFarScale - 1),
|
|
4501
4599
|
...landmarks
|
|
4502
4600
|
};
|
|
@@ -4584,6 +4682,23 @@ var RIG_PLAN_FIELDS = [
|
|
|
4584
4682
|
migratedFrom: null,
|
|
4585
4683
|
what: "how far the nose leads the face plate on the turn \u2014 turn only, the nod keeps the profile's nose depth"
|
|
4586
4684
|
},
|
|
4685
|
+
{
|
|
4686
|
+
path: "turn.farEar",
|
|
4687
|
+
group: "turn",
|
|
4688
|
+
key: "farEar",
|
|
4689
|
+
kind: "absolute",
|
|
4690
|
+
unit: "share of the face plate's slide",
|
|
4691
|
+
default: PROFILE.TURN.earFar,
|
|
4692
|
+
range: [0, 1],
|
|
4693
|
+
recommended: [0, 0.5],
|
|
4694
|
+
step: 0.1,
|
|
4695
|
+
poses: TURN_POSES,
|
|
4696
|
+
cue: null,
|
|
4697
|
+
fitBy: null,
|
|
4698
|
+
provisional: false,
|
|
4699
|
+
migratedFrom: null,
|
|
4700
|
+
what: "how far the far ear's outer edge follows the face on the turn (0: it holds and the head slides over it, about half of a default-sized ear going under the face; 1: it rides with the face and stays whole)"
|
|
4701
|
+
},
|
|
4587
4702
|
{
|
|
4588
4703
|
path: "hair.follow",
|
|
4589
4704
|
group: "hair",
|
|
@@ -6682,6 +6797,11 @@ function buildFaceMesh(frame, b, head, neckCells) {
|
|
|
6682
6797
|
frame.axisX - frame.headMax + 0.5,
|
|
6683
6798
|
frame.axisX + frame.headMax - 0.5
|
|
6684
6799
|
];
|
|
6800
|
+
const ears2 = frame.ears;
|
|
6801
|
+
if (ears2 !== void 0 && neck2 === void 0) {
|
|
6802
|
+
const d = ears2.edgeAt(ears2.bottom);
|
|
6803
|
+
at.push(frame.axisX - d, frame.axisX + d);
|
|
6804
|
+
}
|
|
6685
6805
|
if (neck2 !== void 0) {
|
|
6686
6806
|
at.push(
|
|
6687
6807
|
frame.axisX,
|
|
@@ -7302,7 +7422,7 @@ function writeOcclusion(customParts, built, parts) {
|
|
|
7302
7422
|
|
|
7303
7423
|
// src/auto-rig/generate.ts
|
|
7304
7424
|
var MESH_CELLS = {
|
|
7305
|
-
face: { px:
|
|
7425
|
+
face: { px: FACE_CELL_PX, min: 4, max: 24 },
|
|
7306
7426
|
// The front hair eases from riding the face to holding the outline.
|
|
7307
7427
|
hair_front: { px: 32, min: 3, max: 24 },
|
|
7308
7428
|
// A side lock's rows come from `sideLockGrid`: even ones, plus the root
|
|
@@ -7610,7 +7730,7 @@ function buildPart(spec, b, order, frame, byRole, plan, frontGrid, lips) {
|
|
|
7610
7730
|
let region;
|
|
7611
7731
|
let headStart = 0;
|
|
7612
7732
|
if (spec.role === "face") {
|
|
7613
|
-
const px =
|
|
7733
|
+
const px = faceCellPx(frame);
|
|
7614
7734
|
const fm = buildFaceMesh(frame, b, { ...MESH_CELLS.face, px }, NECK_CELLS);
|
|
7615
7735
|
mesh = fm.mesh;
|
|
7616
7736
|
region = fm.region;
|