@ikijs/editor 0.8.0 → 0.8.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/dist/index.d.mts +12 -6
- package/dist/index.d.ts +12 -6
- package/dist/index.js +73 -39
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +73 -39
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -967,12 +967,18 @@ interface IrisStrand {
|
|
|
967
967
|
*
|
|
968
968
|
* What happens when the layer set cannot reach a target depends on where the
|
|
969
969
|
* target came from. A number the CALLER passed is a measurement, so an
|
|
970
|
-
* unreachable one throws, naming the field and what was on offer
|
|
971
|
-
*
|
|
972
|
-
*
|
|
973
|
-
*
|
|
974
|
-
*
|
|
975
|
-
*
|
|
970
|
+
* unreachable one throws, naming the field and what was on offer — except a
|
|
971
|
+
* `silhouetteRatio` when no ratio in its own [0.5, 1.5] range renders on this
|
|
972
|
+
* layer set: that throw names the field and the whole range instead of an
|
|
973
|
+
* offer, since there is none narrower. Leaving the field out still rigs,
|
|
974
|
+
* because a defaulted one clamps.
|
|
975
|
+
*
|
|
976
|
+
* A number this module filled in — a default, or a nose/mouth derived from the
|
|
977
|
+
* eyes — is a style prior, not a promise about this character: it is CLAMPED
|
|
978
|
+
* to what the layer set can do and the rig is built. The alternative is a
|
|
979
|
+
* generator that refuses its own defaults, which would leave a perfectly good
|
|
980
|
+
* layer set with no model at all. `solveTurnModel` reports which fields it
|
|
981
|
+
* clamped.
|
|
976
982
|
*
|
|
977
983
|
* One measurement is clamped too: an `eyeShift` past the room the art leaves
|
|
978
984
|
* the far eye — the face plate's edge, and the bangs' side strand when
|
package/dist/index.d.ts
CHANGED
|
@@ -967,12 +967,18 @@ interface IrisStrand {
|
|
|
967
967
|
*
|
|
968
968
|
* What happens when the layer set cannot reach a target depends on where the
|
|
969
969
|
* target came from. A number the CALLER passed is a measurement, so an
|
|
970
|
-
* unreachable one throws, naming the field and what was on offer
|
|
971
|
-
*
|
|
972
|
-
*
|
|
973
|
-
*
|
|
974
|
-
*
|
|
975
|
-
*
|
|
970
|
+
* unreachable one throws, naming the field and what was on offer — except a
|
|
971
|
+
* `silhouetteRatio` when no ratio in its own [0.5, 1.5] range renders on this
|
|
972
|
+
* layer set: that throw names the field and the whole range instead of an
|
|
973
|
+
* offer, since there is none narrower. Leaving the field out still rigs,
|
|
974
|
+
* because a defaulted one clamps.
|
|
975
|
+
*
|
|
976
|
+
* A number this module filled in — a default, or a nose/mouth derived from the
|
|
977
|
+
* eyes — is a style prior, not a promise about this character: it is CLAMPED
|
|
978
|
+
* to what the layer set can do and the rig is built. The alternative is a
|
|
979
|
+
* generator that refuses its own defaults, which would leave a perfectly good
|
|
980
|
+
* layer set with no model at all. `solveTurnModel` reports which fields it
|
|
981
|
+
* clamped.
|
|
976
982
|
*
|
|
977
983
|
* One measurement is clamped too: an `eyeShift` past the room the art leaves
|
|
978
984
|
* the far eye — the face plate's edge, and the bangs' side strand when
|
package/dist/index.js
CHANGED
|
@@ -2052,6 +2052,7 @@ function meshCellsFor(w, h) {
|
|
|
2052
2052
|
}
|
|
2053
2053
|
var FACE_PLATE_CELLS = 10;
|
|
2054
2054
|
var FEATURE_GRID_CELLS = 4;
|
|
2055
|
+
var MOUTH_GRID_CELL_PX = 32;
|
|
2055
2056
|
var TURN_LATTICE_CELL_PX = 16;
|
|
2056
2057
|
var GRID_MARGIN = 0.12;
|
|
2057
2058
|
var HEAD_CYLINDER_RADIUS_FACTOR = 0.6 / 0.5;
|
|
@@ -2202,31 +2203,33 @@ function turnColumnMap(grid, faceCenterX, radiusX, angleX, travel) {
|
|
|
2202
2203
|
x + boundedCylinderBend(x - faceCenterX, radiusX, theta) + slide
|
|
2203
2204
|
);
|
|
2204
2205
|
}
|
|
2205
|
-
const cellFor = (
|
|
2206
|
+
const cellFor = (x) => {
|
|
2206
2207
|
let lo = 0;
|
|
2207
2208
|
let hi = grid.cols - 1;
|
|
2208
2209
|
while (lo < hi) {
|
|
2209
2210
|
const mid = lo + hi >> 1;
|
|
2210
|
-
if (
|
|
2211
|
+
if (x < restX[mid + 1]) hi = mid;
|
|
2211
2212
|
else lo = mid + 1;
|
|
2212
2213
|
}
|
|
2213
2214
|
return lo;
|
|
2214
2215
|
};
|
|
2215
|
-
const sample = (edges, values, v) => {
|
|
2216
|
-
const c = cellFor(v, edges);
|
|
2217
|
-
const s = Math.max(
|
|
2218
|
-
0,
|
|
2219
|
-
Math.min(1, (v - edges[c]) / (edges[c + 1] - edges[c]))
|
|
2220
|
-
);
|
|
2221
|
-
if (s === 0) return values[c];
|
|
2222
|
-
if (s === 1) return values[c + 1];
|
|
2223
|
-
return values[c] + (values[c + 1] - values[c]) * s;
|
|
2224
|
-
};
|
|
2225
2216
|
return {
|
|
2226
2217
|
restX,
|
|
2227
2218
|
warpedX,
|
|
2228
|
-
|
|
2229
|
-
|
|
2219
|
+
// Piecewise-linear read of `warpedX` over `restX`, the within-cell
|
|
2220
|
+
// fraction clamped to [0,1] as sampleWarpGrid clamps (s, t). A node reads
|
|
2221
|
+
// its own warped x: `warpedX[c] + Δ·1` is `warpedX[c + 1]` only to an
|
|
2222
|
+
// ulp, and the columns a bake reads are nodes.
|
|
2223
|
+
mapX: (x) => {
|
|
2224
|
+
const c = cellFor(x);
|
|
2225
|
+
const s = Math.max(
|
|
2226
|
+
0,
|
|
2227
|
+
Math.min(1, (x - restX[c]) / (restX[c + 1] - restX[c]))
|
|
2228
|
+
);
|
|
2229
|
+
if (s === 0) return warpedX[c];
|
|
2230
|
+
if (s === 1) return warpedX[c + 1];
|
|
2231
|
+
return warpedX[c] + (warpedX[c + 1] - warpedX[c]) * s;
|
|
2232
|
+
}
|
|
2230
2233
|
};
|
|
2231
2234
|
}
|
|
2232
2235
|
function renderedLandingX(part, vertexLanding, px, py) {
|
|
@@ -2332,7 +2335,7 @@ function plateLandingOn(plate, mapAt) {
|
|
|
2332
2335
|
y
|
|
2333
2336
|
);
|
|
2334
2337
|
}
|
|
2335
|
-
function plateGuardRowsFor(face, hairFront) {
|
|
2338
|
+
function plateGuardRowsFor(face, hairFront, widestY = -Infinity) {
|
|
2336
2339
|
const rowsOf = (part, y) => {
|
|
2337
2340
|
const { rows } = meshCellsFor(part.cropW, part.cropH);
|
|
2338
2341
|
return Array.from(
|
|
@@ -2347,7 +2350,7 @@ function plateGuardRowsFor(face, hairFront) {
|
|
|
2347
2350
|
return [
|
|
2348
2351
|
...faceRows,
|
|
2349
2352
|
...rowsOf(hairFront, hairFront.centerY).map(
|
|
2350
|
-
(y) => Math.max(bottom, Math.min(top, y))
|
|
2353
|
+
(y) => Math.max(bottom, Math.min(top, Math.max(y, widestY)))
|
|
2351
2354
|
)
|
|
2352
2355
|
];
|
|
2353
2356
|
}
|
|
@@ -2359,7 +2362,7 @@ function plateGuardsOf(carriers, faceHalfWidth, profile, hairFront) {
|
|
|
2359
2362
|
return {
|
|
2360
2363
|
plate,
|
|
2361
2364
|
edgeAt: profile === void 0 ? () => faceHalfWidth : profile.at,
|
|
2362
|
-
plateGuardRows: plateGuardRowsFor(plate.part, hairFront)
|
|
2365
|
+
plateGuardRows: plateGuardRowsFor(plate.part, hairFront, profile?.widestY)
|
|
2363
2366
|
};
|
|
2364
2367
|
}
|
|
2365
2368
|
var TurnTargetError = class extends Error {
|
|
@@ -2537,14 +2540,15 @@ var TURN_SWEEP_SAMPLES = 32;
|
|
|
2537
2540
|
var HOLD_CLEARANCE = 1;
|
|
2538
2541
|
var TURN_HOLD_SAMPLES = 16;
|
|
2539
2542
|
var TURN_SETTLE_PASSES = 24;
|
|
2540
|
-
function hairFrontHoldTarget(x, y, deg, faceCenterX, plateLandingAt, edgeAt, holdBase, holdEdge) {
|
|
2543
|
+
function hairFrontHoldTarget(x, y, deg, faceCenterX, plateLandingAt, edgeAt, holdBase, holdEdge, widestY = -Infinity) {
|
|
2541
2544
|
const local = x - faceCenterX;
|
|
2542
2545
|
const dist = Math.abs(local);
|
|
2543
2546
|
const side = Math.sign(local);
|
|
2544
|
-
const
|
|
2545
|
-
|
|
2547
|
+
const row = Math.max(y, widestY);
|
|
2548
|
+
const painted = edgeAt(row);
|
|
2549
|
+
if (dist <= painted) return plateLandingAt(deg, x, row);
|
|
2546
2550
|
if (dist <= holdBase) {
|
|
2547
|
-
const inner = plateLandingAt(deg, faceCenterX + side * painted,
|
|
2551
|
+
const inner = plateLandingAt(deg, faceCenterX + side * painted, row);
|
|
2548
2552
|
const u = (dist - painted) / (holdBase - painted);
|
|
2549
2553
|
return inner + (faceCenterX + side * holdEdge - inner) * u;
|
|
2550
2554
|
}
|
|
@@ -2649,7 +2653,8 @@ function evaluateTurnCandidate(ctx, radius) {
|
|
|
2649
2653
|
plateLandingAt,
|
|
2650
2654
|
ctx.edgeAt,
|
|
2651
2655
|
hold.holdBase,
|
|
2652
|
-
hold.holdEdgeAt(deg)
|
|
2656
|
+
hold.holdEdgeAt(deg),
|
|
2657
|
+
ctx.profile?.widestY
|
|
2653
2658
|
),
|
|
2654
2659
|
leadShift
|
|
2655
2660
|
);
|
|
@@ -3624,7 +3629,7 @@ function plateReachAt(plateLandingAt, faceCenterX, edgeAt, plateGuardRows, deg)
|
|
|
3624
3629
|
)
|
|
3625
3630
|
);
|
|
3626
3631
|
}
|
|
3627
|
-
function bakeHairFrontSilhouetteWarp(mesh, partX, partY, faceCenterX, plateLandingAt, edgeAt, holdBase, holdEdgeAt, plateGuardRows) {
|
|
3632
|
+
function bakeHairFrontSilhouetteWarp(mesh, partX, partY, faceCenterX, plateLandingAt, edgeAt, holdBase, holdEdgeAt, plateGuardRows, widestY = -Infinity) {
|
|
3628
3633
|
if (holdEdgeAt(0) !== holdBase) {
|
|
3629
3634
|
throw new Error(
|
|
3630
3635
|
`auto-rig: bakeHairFrontSilhouetteWarp: holdEdgeAt(0) is ${holdEdgeAt(0)}, not the hold edge's own rest distance ${holdBase}, so the bangs would move in the rest pose`
|
|
@@ -3662,7 +3667,8 @@ function bakeHairFrontSilhouetteWarp(mesh, partX, partY, faceCenterX, plateLandi
|
|
|
3662
3667
|
plateLandingAt,
|
|
3663
3668
|
edgeAt,
|
|
3664
3669
|
holdBase,
|
|
3665
|
-
holdEdge
|
|
3670
|
+
holdEdge,
|
|
3671
|
+
widestY
|
|
3666
3672
|
) - x,
|
|
3667
3673
|
0
|
|
3668
3674
|
);
|
|
@@ -3841,7 +3847,7 @@ function defaultPoseOf(bindings) {
|
|
|
3841
3847
|
return binding.from + (binding.to - binding.from) * t;
|
|
3842
3848
|
});
|
|
3843
3849
|
}
|
|
3844
|
-
function groupGridFor(members, cells, turnShiftPx, nodShiftPx) {
|
|
3850
|
+
function groupGridFor(members, cells, turnShiftPx, nodShiftPx, cellPx) {
|
|
3845
3851
|
let minX = Infinity;
|
|
3846
3852
|
let maxX = -Infinity;
|
|
3847
3853
|
let minY = Infinity;
|
|
@@ -3876,24 +3882,50 @@ function groupGridFor(members, cells, turnShiftPx, nodShiftPx) {
|
|
|
3876
3882
|
}
|
|
3877
3883
|
}
|
|
3878
3884
|
}
|
|
3885
|
+
const extentMinX = minX;
|
|
3886
|
+
const extentMaxX = maxX;
|
|
3879
3887
|
minX -= turnShiftPx;
|
|
3880
3888
|
maxX += turnShiftPx;
|
|
3881
3889
|
minY -= nodShiftPx;
|
|
3882
3890
|
maxY += nodShiftPx;
|
|
3883
3891
|
const marginX = (maxX - minX) * GRID_MARGIN + 1;
|
|
3884
3892
|
const marginY = (maxY - minY) * GRID_MARGIN + 1;
|
|
3885
|
-
|
|
3886
|
-
|
|
3887
|
-
|
|
3888
|
-
|
|
3889
|
-
|
|
3890
|
-
|
|
3891
|
-
|
|
3892
|
-
|
|
3893
|
-
|
|
3894
|
-
|
|
3895
|
-
|
|
3896
|
-
|
|
3893
|
+
const points = generateGridPoints(
|
|
3894
|
+
cells,
|
|
3895
|
+
cells,
|
|
3896
|
+
minX - marginX,
|
|
3897
|
+
maxX + marginX,
|
|
3898
|
+
minY - marginY,
|
|
3899
|
+
maxY + marginY
|
|
3900
|
+
);
|
|
3901
|
+
if (cellPx === void 0) return { cols: cells, rows: cells, points };
|
|
3902
|
+
const stride = cells + 1;
|
|
3903
|
+
const uniformXs = Array.from({ length: stride }, (_, c) => points[c * 2]);
|
|
3904
|
+
const ys = Array.from(
|
|
3905
|
+
{ length: stride },
|
|
3906
|
+
(_, r) => points[r * stride * 2 + 1]
|
|
3907
|
+
);
|
|
3908
|
+
const xs = [uniformXs[0]];
|
|
3909
|
+
for (let c = 0; c < cells; c++) {
|
|
3910
|
+
const left = uniformXs[c];
|
|
3911
|
+
const right = uniformXs[c + 1];
|
|
3912
|
+
const from = Math.max(left, extentMinX);
|
|
3913
|
+
const to = Math.min(right, extentMaxX);
|
|
3914
|
+
if (right - left > cellPx && from < to) {
|
|
3915
|
+
const steps = Math.ceil((to - from) / cellPx);
|
|
3916
|
+
if (from > left) xs.push(from);
|
|
3917
|
+
for (let k = 1; k < steps; k++) {
|
|
3918
|
+
xs.push(from + k * (to - from) / steps);
|
|
3919
|
+
}
|
|
3920
|
+
if (to < right) xs.push(to);
|
|
3921
|
+
}
|
|
3922
|
+
xs.push(right);
|
|
3923
|
+
}
|
|
3924
|
+
const merged = [];
|
|
3925
|
+
for (const y of ys) {
|
|
3926
|
+
for (const x of xs) merged.push(x, y);
|
|
3927
|
+
}
|
|
3928
|
+
return { cols: xs.length - 1, rows: cells, points: merged };
|
|
3897
3929
|
}
|
|
3898
3930
|
function eyelidFoldFor(layers, layer, mesh, t) {
|
|
3899
3931
|
const side = ROLE_TABLE[layer.role].eyeSide;
|
|
@@ -3994,7 +4026,8 @@ function turnSetup(layers) {
|
|
|
3994
4026
|
own,
|
|
3995
4027
|
isPlate ? FACE_PLATE_CELLS : FEATURE_GRID_CELLS,
|
|
3996
4028
|
isPlate ? 0 : turnShift[turnFamily(role)],
|
|
3997
|
-
nodShiftPx
|
|
4029
|
+
nodShiftPx,
|
|
4030
|
+
group === "mouthWarp" ? MOUTH_GRID_CELL_PX : void 0
|
|
3998
4031
|
)
|
|
3999
4032
|
);
|
|
4000
4033
|
}
|
|
@@ -4334,7 +4367,8 @@ function generateIkiFromLayerSet(layers, canvas, options = {}) {
|
|
|
4334
4367
|
edgeAt,
|
|
4335
4368
|
holdBase,
|
|
4336
4369
|
turn?.holdEdgeAt ?? (() => holdBase),
|
|
4337
|
-
plateGuardRows
|
|
4370
|
+
plateGuardRows,
|
|
4371
|
+
profile?.widestY
|
|
4338
4372
|
)
|
|
4339
4373
|
);
|
|
4340
4374
|
ownWarps.push(
|