@ikijs/editor 0.33.0 → 0.34.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 +3 -2
- package/dist/index.d.ts +3 -2
- package/dist/index.js +121 -25
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +121 -25
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -1971,7 +1971,7 @@ var PROFILE_VALUES = {
|
|
|
1971
1971
|
noseDepth: 2,
|
|
1972
1972
|
hairFollow: 1,
|
|
1973
1973
|
fore: 0.2,
|
|
1974
|
-
backShare: 0.
|
|
1974
|
+
backShare: 0.3,
|
|
1975
1975
|
earFar: 0,
|
|
1976
1976
|
earFore: 0.1,
|
|
1977
1977
|
nodDown: 0.18,
|
|
@@ -2023,7 +2023,8 @@ function deriveProfile(v) {
|
|
|
2023
2023
|
* takes it only down to its root, `hairSideField`) rides the face at this
|
|
2024
2024
|
* share of its translation. */
|
|
2025
2025
|
hairFollow: v.hairFollow,
|
|
2026
|
-
/** The head's outline at the eye row
|
|
2026
|
+
/** The head's outline at the eye row turns only as the back hair does
|
|
2027
|
+
* (`hairBack`, which this share never undercuts): where the front hair draws
|
|
2027
2028
|
* it, its outer edge follows the face this much on a row where no back
|
|
2028
2029
|
* hair paints behind that edge. Where back hair does, the edge rides
|
|
2029
2030
|
* further, as far as that back hair stays behind it through the turn and
|
|
@@ -2032,8 +2033,8 @@ function deriveProfile(v) {
|
|
|
2032
2033
|
* rows above the eyes) it follows less where the turn would otherwise
|
|
2033
2034
|
* carry it further outside its back hair than the nod alone does. */
|
|
2034
2035
|
outlineFollow: 0,
|
|
2035
|
-
/** The back hair's
|
|
2036
|
-
hairBack:
|
|
2036
|
+
/** The back hair turns with the head, a share of the plate's slide. */
|
|
2037
|
+
hairBack: v.backShare * v.slide,
|
|
2037
2038
|
/** The ears lag the plate: the far one's outer edge moves this share of
|
|
2038
2039
|
* its slide; the near one rides the head. */
|
|
2039
2040
|
earFar: v.earFar,
|
|
@@ -3375,6 +3376,19 @@ function spin(x, y, ox, oy, a) {
|
|
|
3375
3376
|
];
|
|
3376
3377
|
}
|
|
3377
3378
|
function faceField(m) {
|
|
3379
|
+
const plate = plateField(m);
|
|
3380
|
+
const floor = foreheadFloor(m);
|
|
3381
|
+
let hold;
|
|
3382
|
+
return (x, y, ax, ay) => {
|
|
3383
|
+
const d = plate(x, y, ax, ay);
|
|
3384
|
+
if (ax === 0 || y <= floor) return d;
|
|
3385
|
+
if (hold === void 0) hold = foreheadHold(m, floor) ?? null;
|
|
3386
|
+
if (hold === null) return d;
|
|
3387
|
+
const s = Math.sign(ax);
|
|
3388
|
+
return [d[0] - s * Math.abs(ax) * hold(x, y, s) / 30, d[1]];
|
|
3389
|
+
};
|
|
3390
|
+
}
|
|
3391
|
+
function plateField(m) {
|
|
3378
3392
|
const f = m.frame;
|
|
3379
3393
|
return (x, y, ax, ay) => {
|
|
3380
3394
|
const s = Math.sign(ax);
|
|
@@ -3394,6 +3408,77 @@ function faceField(m) {
|
|
|
3394
3408
|
return [dx, nodDy(v, ay, f.hh)];
|
|
3395
3409
|
};
|
|
3396
3410
|
}
|
|
3411
|
+
function faceCellPx(frame) {
|
|
3412
|
+
return Math.min(FACE_CELL_PX, 2 * frame.wMax / 14);
|
|
3413
|
+
}
|
|
3414
|
+
var FACE_CELL_PX = 28;
|
|
3415
|
+
var FOREHEAD_FADE = 0.04;
|
|
3416
|
+
function foreheadFloor(m) {
|
|
3417
|
+
const f = m.frame;
|
|
3418
|
+
const eyes = [m.boxes.eye_L, m.boxes.eye_R].filter(
|
|
3419
|
+
(e) => e !== void 0
|
|
3420
|
+
);
|
|
3421
|
+
const eyeTop = eyes.length > 0 ? Math.max(...eyes.map((e) => e.y1)) : f.eyeY;
|
|
3422
|
+
return Math.max(eyeTop, f.ears === void 0 ? -Infinity : f.ears.top + 2);
|
|
3423
|
+
}
|
|
3424
|
+
function foreheadHold(m, floor) {
|
|
3425
|
+
const f = m.frame;
|
|
3426
|
+
const top = f.face.y1;
|
|
3427
|
+
const count = Math.floor(top - floor);
|
|
3428
|
+
if (count <= 0 || f.faceRuns(top - 0.5) === void 0 || f.hairRuns("hair_front", top - 0.5) === void 0) {
|
|
3429
|
+
return void 0;
|
|
3430
|
+
}
|
|
3431
|
+
const plate = plateField(m);
|
|
3432
|
+
const front = hairFrontField(m);
|
|
3433
|
+
const { inner } = hairFrontBends(f, m.boxes);
|
|
3434
|
+
const cell = faceCellPx(f);
|
|
3435
|
+
const fade = FOREHEAD_FADE * f.hh;
|
|
3436
|
+
const sides = [-1, 1];
|
|
3437
|
+
const hold = sides.map(() => new Float64Array(count));
|
|
3438
|
+
const whole = sides.map(() => new Float64Array(count));
|
|
3439
|
+
const span3 = sides.map(() => new Float64Array(count));
|
|
3440
|
+
let any = false;
|
|
3441
|
+
for (let i = 0; i < count; i++) {
|
|
3442
|
+
const y = top - i - 0.5;
|
|
3443
|
+
const faceRuns = f.faceRuns(y);
|
|
3444
|
+
const hairRuns = f.hairRuns("hair_front", y);
|
|
3445
|
+
sides.forEach((side, k) => {
|
|
3446
|
+
const edge = outerEdge(faceRuns, f.axisX, side);
|
|
3447
|
+
const hair = outerEdge(hairRuns, f.axisX, side);
|
|
3448
|
+
if (edge === void 0 || hair === void 0) return;
|
|
3449
|
+
const end = side * (edge - f.axisX) - cell;
|
|
3450
|
+
if (end <= 0) return;
|
|
3451
|
+
const cover = side * (hair - edge);
|
|
3452
|
+
const room = Math.max(0, cover);
|
|
3453
|
+
const goes = side * plate(edge - side * 0.5, y, 30 * side, 0)[0];
|
|
3454
|
+
const held = side * front(hair - side * 0.5, y, 30 * side, 0)[0];
|
|
3455
|
+
const need = clamp(1 + cover / fade, 0, 1) * Math.max(0, goes - held - room + Math.min(1, room));
|
|
3456
|
+
if (need <= 0) return;
|
|
3457
|
+
const over = Math.min(end, Math.max(2 * cell, end - inner, 2 * need));
|
|
3458
|
+
hold[k][i] = Math.min(need, over / 2);
|
|
3459
|
+
whole[k][i] = end;
|
|
3460
|
+
span3[k][i] = over;
|
|
3461
|
+
any = true;
|
|
3462
|
+
});
|
|
3463
|
+
}
|
|
3464
|
+
if (!any) return void 0;
|
|
3465
|
+
const reach = Math.ceil(cell);
|
|
3466
|
+
return (x, y, s) => {
|
|
3467
|
+
const u = s * (x - f.axisX);
|
|
3468
|
+
if (s === 0 || u <= 0 || y <= floor) return 0;
|
|
3469
|
+
const k = s < 0 ? 0 : 1;
|
|
3470
|
+
const i = clamp(Math.floor(top - y), 0, count - 1);
|
|
3471
|
+
let h = 0;
|
|
3472
|
+
const j1 = Math.min(count - 1, i + 3 * reach);
|
|
3473
|
+
for (let j = Math.max(0, i - 3 * reach); j <= j1; j++) {
|
|
3474
|
+
if (hold[k][j] === 0) continue;
|
|
3475
|
+
const near = clamp((3 * reach - Math.abs(i - j)) / (2 * reach), 0, 1);
|
|
3476
|
+
const across = clamp((u - (whole[k][j] - span3[k][j])) / span3[k][j], 0, 1);
|
|
3477
|
+
h = Math.max(h, hold[k][j] * near * across);
|
|
3478
|
+
}
|
|
3479
|
+
return h * clamp((y - floor) / (2 * cell), 0, 1);
|
|
3480
|
+
};
|
|
3481
|
+
}
|
|
3397
3482
|
function earField(m) {
|
|
3398
3483
|
const f = m.frame;
|
|
3399
3484
|
const ears = f.ears;
|
|
@@ -3596,7 +3681,7 @@ function hairFrontField(m) {
|
|
|
3596
3681
|
)
|
|
3597
3682
|
);
|
|
3598
3683
|
const followOf = (x, y, s) => followAt(x, y, s) + (ride === void 0 ? 0 : ride.lift(x, y, s));
|
|
3599
|
-
const drift =
|
|
3684
|
+
const drift = -m.hairBack;
|
|
3600
3685
|
const boundOf = (x, y, s) => {
|
|
3601
3686
|
if (ride === void 0) return followOf(x, y, s);
|
|
3602
3687
|
const b = ride.blend(x, y, s);
|
|
@@ -3763,7 +3848,7 @@ function outerRide(m, inner, coreAt, followAt, capLimit, eyeTop, irisTop) {
|
|
|
3763
3848
|
if (f.hairRuns("hair_front", top) === void 0 || f.hairRuns("hair_back", top) === void 0) {
|
|
3764
3849
|
return void 0;
|
|
3765
3850
|
}
|
|
3766
|
-
const drift =
|
|
3851
|
+
const drift = -m.hairBack;
|
|
3767
3852
|
const sides = [-1, 1];
|
|
3768
3853
|
const n = ys.length;
|
|
3769
3854
|
const count = Math.round(top - ys[n - 1]);
|
|
@@ -3894,6 +3979,7 @@ function outerRide(m, inner, coreAt, followAt, capLimit, eyeTop, irisTop) {
|
|
|
3894
3979
|
const cuts = new Float64Array(n * 2 * nc);
|
|
3895
3980
|
const blend = new Float64Array(n * 2 * nc);
|
|
3896
3981
|
const motion = new Array(n * 2);
|
|
3982
|
+
const held = new Array(n * 2);
|
|
3897
3983
|
const outward = sides.map((side) => {
|
|
3898
3984
|
const cols = xs.map((_, c) => c).filter((c) => side * (xs[c] - f.axisX) >= 0);
|
|
3899
3985
|
return side < 0 ? cols.reverse() : cols;
|
|
@@ -3950,22 +4036,24 @@ function outerRide(m, inner, coreAt, followAt, capLimit, eyeTop, irisTop) {
|
|
|
3950
4036
|
}
|
|
3951
4037
|
}
|
|
3952
4038
|
motion[r * 2 + j] = after.map((v) => v + drift);
|
|
4039
|
+
held[r * 2 + j] = today.map((v) => v + drift);
|
|
3953
4040
|
for (let c = 0; c < nc; c++)
|
|
3954
4041
|
cuts[(r * 2 + j) * nc + c] = after[c] - today[c];
|
|
3955
4042
|
});
|
|
3956
4043
|
}
|
|
4044
|
+
const at = (r, j, c) => (r * 2 + j) * nc + c;
|
|
4045
|
+
let zero = last + 1;
|
|
4046
|
+
while (zero < n && ys[zero] > eyeTop) zero++;
|
|
4047
|
+
while (zero > last + 1 && zero < n && ys[zero] < irisTop - 0.5) zero--;
|
|
4048
|
+
const endY = zero < n ? ys[zero] : Math.min(Math.max(eyeTop, irisTop - 0.5), ys[n - 1] - 1);
|
|
3957
4049
|
for (let r = 0; r < n; r++) {
|
|
3958
4050
|
sides.forEach((s, j) => {
|
|
3959
4051
|
motion[r * 2 + j] ??= xs.map(
|
|
3960
4052
|
(x) => followAt(x, ys[r], s) + liftAt(x, ys[r], s) + drift
|
|
3961
4053
|
);
|
|
4054
|
+
held[r * 2 + j] ??= motion[r * 2 + j];
|
|
3962
4055
|
});
|
|
3963
4056
|
}
|
|
3964
|
-
const at = (r, j, c) => (r * 2 + j) * nc + c;
|
|
3965
|
-
let zero = last + 1;
|
|
3966
|
-
while (zero < n && ys[zero] > eyeTop) zero++;
|
|
3967
|
-
while (zero > last + 1 && zero < n && ys[zero] < irisTop - 0.5) zero--;
|
|
3968
|
-
const endY = zero < n ? ys[zero] : Math.min(Math.max(eyeTop, irisTop - 0.5), ys[n - 1] - 1);
|
|
3969
4057
|
for (let pass = 0; pass < 8; pass++) {
|
|
3970
4058
|
const was = blend.slice();
|
|
3971
4059
|
for (let j = 0; j < 2; j++) {
|
|
@@ -3983,11 +4071,15 @@ function outerRide(m, inner, coreAt, followAt, capLimit, eyeTop, irisTop) {
|
|
|
3983
4071
|
for (let q = 0; q < r && q <= last; q++) {
|
|
3984
4072
|
if (wr[q] < 1) continue;
|
|
3985
4073
|
const k = (ys[r] - endY) / (ys[q] - endY);
|
|
3986
|
-
|
|
3987
|
-
|
|
3988
|
-
|
|
3989
|
-
|
|
3990
|
-
);
|
|
4074
|
+
const [mq, hq] = [motion[q * 2 + j], held[q * 2 + j]];
|
|
4075
|
+
const [mr, hr] = [motion[r * 2 + j], held[r * 2 + j]];
|
|
4076
|
+
for (let c = 0; c < nc; c++) {
|
|
4077
|
+
const bq = blend[at(q, j, c)];
|
|
4078
|
+
const ease = hq[c] > 0 ? clamp(1 - (1 - bq) * mq[c] / hq[c], 0, 1) : bq;
|
|
4079
|
+
const want = ease * k;
|
|
4080
|
+
const br = mr[c] > 0 && hr[c] > 0 ? 1 - (1 - want) * hr[c] / mr[c] : want;
|
|
4081
|
+
blend[at(r, j, c)] = Math.max(blend[at(r, j, c)], br);
|
|
4082
|
+
}
|
|
3991
4083
|
}
|
|
3992
4084
|
}
|
|
3993
4085
|
}
|
|
@@ -4004,7 +4096,7 @@ function outerRide(m, inner, coreAt, followAt, capLimit, eyeTop, irisTop) {
|
|
|
4004
4096
|
}
|
|
4005
4097
|
if (blend.every((v, i) => v === was[i])) break;
|
|
4006
4098
|
}
|
|
4007
|
-
const cutRow = (r, j, x) => r > last ? 0 : along(cuts.subarray((r
|
|
4099
|
+
const cutRow = (r, j, x) => r > last ? 0 : along(cuts.subarray(at(r, j, 0), at(r, j, nc)), x);
|
|
4008
4100
|
return {
|
|
4009
4101
|
lift: liftAt,
|
|
4010
4102
|
// Between vertex rows, as the render interpolates; a vertex reads its own
|
|
@@ -4488,6 +4580,7 @@ function buildTurn(frame, landmarks, q, turn) {
|
|
|
4488
4580
|
const eyeHalf = eyes.reduce((sum, e) => sum + (e.x1 - e.x0) / 2, 0) / Math.max(1, eyes.length);
|
|
4489
4581
|
const farScale = 1 + q.fore * A * (turn.eyeFarScale - 1);
|
|
4490
4582
|
const farCorner = lead(turn.eyeFar) - (1 - farScale) * eyeHalf;
|
|
4583
|
+
const shell = A * hh * turn.hairBack;
|
|
4491
4584
|
return {
|
|
4492
4585
|
frame,
|
|
4493
4586
|
face,
|
|
@@ -4505,10 +4598,13 @@ function buildTurn(frame, landmarks, q, turn) {
|
|
|
4505
4598
|
mouth: q.mouth ?? lead(turn.mouth),
|
|
4506
4599
|
mouthWidth: 1 + A * (turn.mouthWidth - 1),
|
|
4507
4600
|
tilt: A,
|
|
4508
|
-
|
|
4509
|
-
|
|
4510
|
-
|
|
4511
|
-
|
|
4601
|
+
// The front hair lies on the head in front of its shell: neither over
|
|
4602
|
+
// the face nor at the outline it draws does it turn less than the back
|
|
4603
|
+
// hair behind it.
|
|
4604
|
+
hairFront: Math.max(q.follow * face, shell),
|
|
4605
|
+
hairFar: Math.max(q.follow * face, shell, farCorner),
|
|
4606
|
+
hairOuter: Math.max(q.outline * face, shell),
|
|
4607
|
+
hairBack: shell,
|
|
4512
4608
|
shellScale: q.shell,
|
|
4513
4609
|
earFar: q.earFar,
|
|
4514
4610
|
earFarScale: 1 + A * (turn.earFarScale - 1),
|
|
@@ -4630,7 +4726,7 @@ var RIG_PLAN_FIELDS = [
|
|
|
4630
4726
|
fitBy: null,
|
|
4631
4727
|
provisional: false,
|
|
4632
4728
|
migratedFrom: "hairFollow",
|
|
4633
|
-
what: "the front hair's share of the face's turn"
|
|
4729
|
+
what: "the front hair's share of the face's turn (never less than the back hair's own turn, 0.3 of the face's: a share under that has no effect)"
|
|
4634
4730
|
},
|
|
4635
4731
|
{
|
|
4636
4732
|
path: "hair.outlineFollow",
|
|
@@ -4647,7 +4743,7 @@ var RIG_PLAN_FIELDS = [
|
|
|
4647
4743
|
fitBy: null,
|
|
4648
4744
|
provisional: false,
|
|
4649
4745
|
migratedFrom: "outlineFollow",
|
|
4650
|
-
what: "the front hair's outer edge's share where it draws the head's outline (
|
|
4746
|
+
what: "the front hair's outer edge's share where it draws the head's outline (never less than the back hair's own turn: at 0 the outline turns only as the back hair does, though where back hair paints behind it, it may ride further, as far as that back hair reaches; at hair.follow the front hair's side strands ride whole)"
|
|
4651
4747
|
},
|
|
4652
4748
|
{
|
|
4653
4749
|
path: "hair.sway",
|
|
@@ -7338,7 +7434,7 @@ function writeOcclusion(customParts, built, parts) {
|
|
|
7338
7434
|
|
|
7339
7435
|
// src/auto-rig/generate.ts
|
|
7340
7436
|
var MESH_CELLS = {
|
|
7341
|
-
face: { px:
|
|
7437
|
+
face: { px: FACE_CELL_PX, min: 4, max: 24 },
|
|
7342
7438
|
// The front hair eases from riding the face to holding the outline.
|
|
7343
7439
|
hair_front: { px: 32, min: 3, max: 24 },
|
|
7344
7440
|
// A side lock's rows come from `sideLockGrid`: even ones, plus the root
|
|
@@ -7646,7 +7742,7 @@ function buildPart(spec, b, order, frame, byRole, plan, frontGrid, lips) {
|
|
|
7646
7742
|
let region;
|
|
7647
7743
|
let headStart = 0;
|
|
7648
7744
|
if (spec.role === "face") {
|
|
7649
|
-
const px =
|
|
7745
|
+
const px = faceCellPx(frame);
|
|
7650
7746
|
const fm = buildFaceMesh(frame, b, { ...MESH_CELLS.face, px }, NECK_CELLS);
|
|
7651
7747
|
mesh = fm.mesh;
|
|
7652
7748
|
region = fm.region;
|