@ikijs/editor 0.20.0 → 0.22.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 +14 -14
- package/dist/index.d.mts +140 -11
- package/dist/index.d.ts +140 -11
- package/dist/index.js +415 -116
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +410 -114
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -40,24 +40,24 @@ leaves the document untouched.
|
|
|
40
40
|
|
|
41
41
|
## API
|
|
42
42
|
|
|
43
|
-
| Area | Exports
|
|
44
|
-
| ------------------ |
|
|
45
|
-
| Document | `EditorDocument`, `EditCommand`
|
|
46
|
-
| Part edits | `AddPart`, `DeletePart`, `SetPartColor`, `SetPartWidth`, `SetPartHeight`, `SetPartOrder`, `SetPartTransform`, `SetPartBindings`, `SetPartMesh`, `SetPartDeformer`
|
|
47
|
-
| Deformer edits | `AddDeformer`, `DeleteDeformer`, `SetDeformerParent`, `SetDeformerTransform`, `SetDeformerBindings`, `SetDeformerPivot` (+ `X`/`Y`), `CaptureGridKeyform`
|
|
48
|
-
| Physics edits | `AddPhysicsRig`, `SetPhysicsRig`, `DeletePhysicsRig`
|
|
49
|
-
| Referential guards | `validateDeformerReparent`, `validateDeformerDelete`, `validatePartAttach`
|
|
50
|
-
| Atlas | `packAtlas`, `uvRectFor`, `ATLAS_PADDING`, `UV_INSET_PX`
|
|
51
|
-
| Grid keyforms | `computeGridOffsets`, `interpolateGridOffsets`, `upsertGridKeyform`
|
|
52
|
-
| Factories | `createDefaultPart`, `createDefaultMatrixDeformer`, `createDefaultWarpDeformer`, `createGridMesh`
|
|
53
|
-
| Pixels | `detectAlphaBbox`, `ALPHA_BBOX_THRESHOLD`, `AlphaBbox`, `ALPHA_OPAQUE`, `HEAD_BAND`, `SPECK_CORE_FRACTION`, `denseCoreOf`, `isSpeckCore`, `foregroundSpan`, `headHalfOf`
|
|
54
|
-
| Auto-rig | `generateIkiFromLayerSet`, `parseLayerRoles`, `partIdsOfRole`, `createLayerSetMeasurer`, `armGeometry`, `ArmGeometry`, `forearmPoseGeometry`, `ForearmPoseGeometry`, `TurnTargets`, `DEFAULT_TURN_TARGETS`, `TurnSolveReport`, `RigStyle`, `LayerGeometryError` |
|
|
55
|
-
| Bindings | `captureBindingEndpoint`
|
|
43
|
+
| Area | Exports |
|
|
44
|
+
| ------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
45
|
+
| Document | `EditorDocument`, `EditCommand` |
|
|
46
|
+
| Part edits | `AddPart`, `DeletePart`, `SetPartColor`, `SetPartWidth`, `SetPartHeight`, `SetPartOrder`, `SetPartTransform`, `SetPartBindings`, `SetPartMesh`, `SetPartDeformer` |
|
|
47
|
+
| Deformer edits | `AddDeformer`, `DeleteDeformer`, `SetDeformerParent`, `SetDeformerTransform`, `SetDeformerBindings`, `SetDeformerPivot` (+ `X`/`Y`), `CaptureGridKeyform` |
|
|
48
|
+
| Physics edits | `AddPhysicsRig`, `SetPhysicsRig`, `DeletePhysicsRig` |
|
|
49
|
+
| Referential guards | `validateDeformerReparent`, `validateDeformerDelete`, `validatePartAttach` |
|
|
50
|
+
| Atlas | `packAtlas`, `uvRectFor`, `ATLAS_PADDING`, `UV_INSET_PX` |
|
|
51
|
+
| Grid keyforms | `computeGridOffsets`, `interpolateGridOffsets`, `upsertGridKeyform` |
|
|
52
|
+
| Factories | `createDefaultPart`, `createDefaultMatrixDeformer`, `createDefaultWarpDeformer`, `createGridMesh` |
|
|
53
|
+
| Pixels | `detectAlphaBbox`, `ALPHA_BBOX_THRESHOLD`, `AlphaBbox`, `ALPHA_OPAQUE`, `HEAD_BAND`, `SPECK_CORE_FRACTION`, `denseCoreOf`, `isSpeckCore`, `foregroundSpan`, `headHalfOf` |
|
|
54
|
+
| Auto-rig | `generateIkiFromLayerSet`, `parseLayerRoles`, `partIdsOfRole`, `createLayerSetMeasurer`, `armGeometry`, `ArmGeometry`, `forearmPoseGeometry`, `ForearmPoseGeometry`, `mouthOpening`, `mouthFold`, `mouthRestShift`, `columnRuns`, `LIP_ROLES`, `LipRole`, `Opening`, `OpeningColumn`, `TurnTargets`, `DEFAULT_TURN_TARGETS`, `TurnSolveReport`, `RigStyle`, `LayerGeometryError` |
|
|
55
|
+
| Bindings | `captureBindingEndpoint` |
|
|
56
56
|
|
|
57
57
|
## Auto-rig
|
|
58
58
|
|
|
59
59
|
`generateIkiFromLayerSet` turns role-named layers (`face`, `eye_L`, `eye_R`,
|
|
60
|
-
`mouth`, plus optional `iris_*`, `brow_*`, `lash_*`, `lash_lower_*`, `hair_front`, `hair_back`,
|
|
60
|
+
and `mouth` — or the lip set `mouth_inner`, `lip_lower`, `lip_upper`, which folds open instead of crossfading — plus optional `iris_*`, `brow_*`, `lash_*`, `lash_lower_*`, `hair_front`, `hair_back`,
|
|
61
61
|
`body`, `arm_*`, …) into a rigged model that blinks, gazes, opens its mouth, turns its head over
|
|
62
62
|
a body warp that breathes, follows the head a little and turns on
|
|
63
63
|
`ParamBodyAngleX/Y/Z`, and emotes with its brows — including a
|
package/dist/index.d.mts
CHANGED
|
@@ -714,14 +714,17 @@ interface LayerInput {
|
|
|
714
714
|
/** Face only: half the opaque span of each crop row, canvas px (0 = none). */
|
|
715
715
|
rowHalfWidths?: number[];
|
|
716
716
|
/** `face` / `hair_front` / `hair_back` / `body` / `arm_L` / `arm_R` /
|
|
717
|
-
* `forearm_pose_L` / `forearm_pose_R`
|
|
717
|
+
* `forearm_pose_L` / `forearm_pose_R` /
|
|
718
|
+
* `mouth_inner` / `lip_lower` / `lip_upper` only:
|
|
718
719
|
* per crop row, its opaque (alpha ≥ 128) runs as a flat list of canvas
|
|
719
720
|
* columns `[start0, end0, start1, end1, …]`, each end exclusive (`[]` for a
|
|
720
721
|
* row with none). The actual pixels, so a gap in the hair is a gap here:
|
|
721
722
|
* the turn and the nod read off it where back hair is painted behind the
|
|
722
723
|
* front hair, and the crown's turn reads the face's so it never bares skin.
|
|
723
724
|
* The body's place the hips (where it splits into legs), and an arm's place
|
|
724
|
-
* its shoulder and elbow pivots, a pose forearm's its elbow-end pivot
|
|
725
|
+
* its shoulder and elbow pivots, a pose forearm's its elbow-end pivot, and
|
|
726
|
+
* a lip set's place the mouth's opening: the upper line's bottom, the
|
|
727
|
+
* interior's span, the skin's top. */
|
|
725
728
|
rowRuns?: number[][];
|
|
726
729
|
/** Nose only: the tight box of its alpha ≥ 128 pixels, canvas px. */
|
|
727
730
|
denseCore?: {
|
|
@@ -768,9 +771,11 @@ declare class TurnTargetError extends Error {
|
|
|
768
771
|
constructor(message: string);
|
|
769
772
|
}
|
|
770
773
|
/**
|
|
771
|
-
* Art geometry the rig cannot build on —
|
|
772
|
-
*
|
|
773
|
-
*
|
|
774
|
+
* Art geometry the rig cannot build on — a body whose hips leave no row line
|
|
775
|
+
* under its pivots, or too little room for its motion (`body.ts`), an arm
|
|
776
|
+
* without room for its pivots, a lip set whose interior has no opaque pixel
|
|
777
|
+
* (`mouth.ts`). A fact about the layers, not a bug, so a host reports it as an
|
|
778
|
+
* input error.
|
|
774
779
|
*/
|
|
775
780
|
declare class LayerGeometryError extends Error {
|
|
776
781
|
constructor(message: string);
|
|
@@ -988,19 +993,141 @@ interface ForearmPoseGeometry {
|
|
|
988
993
|
*/
|
|
989
994
|
declare function forearmPoseGeometry(layer: LayerInput): ForearmPoseGeometry;
|
|
990
995
|
|
|
996
|
+
/** The lip set's roles, back to front (`mouth.ts`). */
|
|
997
|
+
declare const LIP_ROLES: readonly ["mouth_inner", "lip_lower", "lip_upper"];
|
|
998
|
+
type LipRole = (typeof LIP_ROLES)[number];
|
|
991
999
|
/** The part ids a `role`'s layer becomes, back to front: every one of them
|
|
992
1000
|
* takes its texture from the layer's crop. Throws on an unknown role. */
|
|
993
1001
|
declare function partIdsOfRole(role: string): readonly string[];
|
|
994
1002
|
/**
|
|
995
1003
|
* Map file names to roles. Throws on an unknown role, a role named twice, a
|
|
996
|
-
* required role (`face`, `eye_L`, `eye_R
|
|
997
|
-
* body, or a pose forearm
|
|
1004
|
+
* required role (`face`, `eye_L`, `eye_R`) missing, no mouth (`mouth`, or the
|
|
1005
|
+
* lip set) or a partial or mixed one, an arm without a body, or a pose forearm
|
|
1006
|
+
* without its arm.
|
|
998
1007
|
*/
|
|
999
1008
|
declare function parseLayerRoles(fileNames: string[]): {
|
|
1000
1009
|
role: string;
|
|
1001
1010
|
fileName: string;
|
|
1002
1011
|
}[];
|
|
1003
1012
|
|
|
1013
|
+
/**
|
|
1014
|
+
* The lip set: a mouth that folds open the way the eyelid folds shut. Three
|
|
1015
|
+
* parts, back to front: `mouth_inner` (the interior and the opening's lower
|
|
1016
|
+
* outline band), `lip_lower` (the lower lip's skin) and `lip_upper` (the upper
|
|
1017
|
+
* lip line and the corner hooks).
|
|
1018
|
+
*
|
|
1019
|
+
* What the rig reads is the layers' own alpha edges, as exclusive run
|
|
1020
|
+
* boundaries in model y (+y up; a run of crop rows `[a, b)` is the span
|
|
1021
|
+
* `[rowTop(b), rowTop(a)]`). Per canvas column inside the opening (where
|
|
1022
|
+
* `mouth_inner` has a run): the interior's top `T` and bottom `Bb`, the line
|
|
1023
|
+
* of `lip_upper` at the interior's top (`Tu` its bottom edge, `lineTop` its
|
|
1024
|
+
* top) and the skin's top `Bl`. Each part has one affine warp per column that
|
|
1025
|
+
* lands its OWN boundary on its target, so no relation between two parts'
|
|
1026
|
+
* boundaries enters the closure; there is no colour read. The art's contract:
|
|
1027
|
+
* the interior reaches the line (`T >= Tu`; lower would show the face between
|
|
1028
|
+
* them at rest) and the skin reaches the interior (`Bl >= Bb`; an overlap is
|
|
1029
|
+
* fine, a gap is not).
|
|
1030
|
+
*
|
|
1031
|
+
* MouthOpen 0 folds shut onto a seam, 1 is the art as drawn (the widening of
|
|
1032
|
+
* `mouthWiden` apart). The line comes down until its bottom edge is on the
|
|
1033
|
+
* seam, the skin until its top is one overlap `w'` up under the line (`w'` is
|
|
1034
|
+
* the stroke `w`, the line's height at the opening's centre, capped by the
|
|
1035
|
+
* line's own ink at that column so tucked skin never shows above it), and the
|
|
1036
|
+
* interior's bottom rides the skin's top, so the band is never covered. The
|
|
1037
|
+
* interior's top closes onto the same edge, `S + w'`: the whole column scales
|
|
1038
|
+
* by `v` about it, so shut its height is zero (a degenerate triangle draws
|
|
1039
|
+
* nothing, at every column and between knots; an inverted sliver would leak
|
|
1040
|
+
* through texture filtering where the line is thin), and its top sits
|
|
1041
|
+
* `w'(1 - v)` above the line's bottom edge, under the line's ink, so the slit
|
|
1042
|
+
* between the line and the skin is always backed. The slit opens once
|
|
1043
|
+
* `v * H > w'(1 - v)`; that dead zone is accepted: lip-sync noise near 0 does
|
|
1044
|
+
* not flicker through it.
|
|
1045
|
+
*
|
|
1046
|
+
* The seam is the line's drawn bottom edge `Tu` plus one smooth closing
|
|
1047
|
+
* travel `D`, a cubic in the column index pinned to zero at the opening's two
|
|
1048
|
+
* end columns, so the closed line's ends are the drawn line's own ends. It is
|
|
1049
|
+
* fitted by least squares to the raw per-column travel `(T - UPPER_SHARE * H)
|
|
1050
|
+
* - Tu`, how far the line's bottom is from the share's point in the
|
|
1051
|
+
* interior's span. The travel is fitted, not the seam, because `Tu` is an
|
|
1052
|
+
* integer-row read: a smooth seam minus that staircase is a sawtooth the mesh
|
|
1053
|
+
* would carry into the rendered line, while a smooth travel carries the
|
|
1054
|
+
* drawn stroke (whose antialiased edge is smooth) as one piece. The share sets
|
|
1055
|
+
* the travel's depth (the sag), the art's two arcs its shape; the cubic keeps
|
|
1056
|
+
* an asymmetric mouth's tilt and cannot wobble.
|
|
1057
|
+
*
|
|
1058
|
+
* A lip's columns outside the opening (the corner hooks) do not fold; only
|
|
1059
|
+
* the interior's edge columns read the nearest opening column.
|
|
1060
|
+
*
|
|
1061
|
+
* Knots: the three parts share column knots every `MOUTH_KNOT_PX` plus the
|
|
1062
|
+
* opening's boundaries `x0`, `x1` and `x1 + 1`, each an integer canvas
|
|
1063
|
+
* boundary. The fit's x is the column index, which is the mesh's boundary
|
|
1064
|
+
* position: a knot at boundary `b` samples column `b`, so the mesh renders the
|
|
1065
|
+
* fit exactly at its knots and the pinned ends render as pinned (`x1` and
|
|
1066
|
+
* `x1 + 1` both sample the last column; the second sits at its right edge).
|
|
1067
|
+
* The preview shifts each column by the field at the column's centre
|
|
1068
|
+
* (`mouthRestShift`). A part's mesh rounds its local x, so one knot
|
|
1069
|
+
* reconstructs to slightly different model x per part; the fold therefore
|
|
1070
|
+
* reads the knot itself, by vertex index, and maps every knot through one rule
|
|
1071
|
+
* (`columnOfKnot`).
|
|
1072
|
+
*
|
|
1073
|
+
* The anchor (`mouthAnchor`) is where the head's frame and the turn read the
|
|
1074
|
+
* mouth's resting place (the chin lead, the chin estimate, the solve). A
|
|
1075
|
+
* single closed mouth is its box centre (its box the union with `mouth_open`);
|
|
1076
|
+
* for a lip set it is the seam at the opening's centre column, where the lips
|
|
1077
|
+
* meet, with the interior's centre across and the union of the three as box.
|
|
1078
|
+
*/
|
|
1079
|
+
|
|
1080
|
+
/** Per canvas column, its opaque runs as `[top, bottom)` crop rows, top
|
|
1081
|
+
* first: the transpose of `rowRuns`. */
|
|
1082
|
+
declare function columnRuns(layer: LayerInput): Map<number, number[][]>;
|
|
1083
|
+
/** What the fold reads off one canvas column, all model y. */
|
|
1084
|
+
interface OpeningColumn {
|
|
1085
|
+
/** The interior's top and bottom edges. */
|
|
1086
|
+
T: number;
|
|
1087
|
+
Bb: number;
|
|
1088
|
+
/** The upper line's bottom and top edges, and its height. */
|
|
1089
|
+
Tu: number;
|
|
1090
|
+
lineTop: number;
|
|
1091
|
+
lineH: number;
|
|
1092
|
+
/** The skin's top edge. */
|
|
1093
|
+
Bl: number;
|
|
1094
|
+
/** The interior's span. */
|
|
1095
|
+
H: number;
|
|
1096
|
+
/** How far the skin tucks up under the line. */
|
|
1097
|
+
overlap: number;
|
|
1098
|
+
/** Where the lips meet when shut: the line's bottom edge after its smooth
|
|
1099
|
+
* closing travel. */
|
|
1100
|
+
seam: number;
|
|
1101
|
+
}
|
|
1102
|
+
interface Opening {
|
|
1103
|
+
/** The first and last canvas columns where `mouth_inner` has a run. */
|
|
1104
|
+
x0: number;
|
|
1105
|
+
x1: number;
|
|
1106
|
+
/** The opening's centre column: where the stroke and the anchor are read. */
|
|
1107
|
+
centre: number;
|
|
1108
|
+
canvasW: number;
|
|
1109
|
+
/** The stroke: the line's height at the opening's centre column. */
|
|
1110
|
+
w: number;
|
|
1111
|
+
/** A column's values; outside `x0..x1`, or in a gap, the nearest one's. */
|
|
1112
|
+
at(col: number): OpeningColumn;
|
|
1113
|
+
}
|
|
1114
|
+
declare function mouthOpening(byRole: Map<string, LayerInput>): Opening;
|
|
1115
|
+
/**
|
|
1116
|
+
* dy of a model-y rest position at pixel column `col` for MouthOpen `v`
|
|
1117
|
+
* (0 shut, 1 as drawn). A lip outside the opening moves 0; `mouth_inner`
|
|
1118
|
+
* scales its column by `v` about `S + w'`, which carries its top and bottom
|
|
1119
|
+
* edges along the line and the skin and needs no read of its own height.
|
|
1120
|
+
*/
|
|
1121
|
+
declare function mouthFold(role: LipRole, opening: Opening): (col: number, y: number, v: number) => number;
|
|
1122
|
+
/** A lip's translate at MouthOpen 0 per canvas column as the mesh renders it,
|
|
1123
|
+
* for the composer's closed preview: the linear interpolation at the column's
|
|
1124
|
+
* centre between the STORED offsets of the part's two bracketing knots (the
|
|
1125
|
+
* numbers the `.iki` carries, rounded as `localWarp` rounds them), 0 outside
|
|
1126
|
+
* the part's box. `byRole` must be the layers as the rig measures them (the
|
|
1127
|
+
* canvas-sized ones): a box measured on a smaller frame clamps differently,
|
|
1128
|
+
* and the knot grid starts at the union's edge. */
|
|
1129
|
+
declare function mouthRestShift(byRole: Map<string, LayerInput>): (role: "lip_upper" | "lip_lower", col: number) => number;
|
|
1130
|
+
|
|
1004
1131
|
/**
|
|
1005
1132
|
* `generateIkiFromLayerSet`: role layers in, a rigged `.iki` out.
|
|
1006
1133
|
*
|
|
@@ -1018,7 +1145,9 @@ declare function parseLayerRoles(fileNames: string[]): {
|
|
|
1018
1145
|
* and two ears that lag. Blink, gaze, brows, mouth, breath and
|
|
1019
1146
|
* hair sway are part warps and bindings under those. The model also declares
|
|
1020
1147
|
* the default expressions and the Nod, Shake and Tilt motions, and
|
|
1021
|
-
* with a pose forearm the Wave (`animations.ts`).
|
|
1148
|
+
* with a pose forearm the Wave (`animations.ts`). The mouth has two paths,
|
|
1149
|
+
* chosen by the layers: `mouth` alone stretches, with `mouth_open` it
|
|
1150
|
+
* crossfades; the lip set folds open like the eyelid (`mouth.ts`).
|
|
1022
1151
|
*/
|
|
1023
1152
|
|
|
1024
1153
|
declare function generateIkiFromLayerSet(layers: LayerInput[], canvas: {
|
|
@@ -1158,8 +1287,8 @@ interface LayerSetMeasurer {
|
|
|
1158
1287
|
* of it and of the per-role edges), this layer's per-row opaque extent and
|
|
1159
1288
|
* the `LayerInput` it returns outlive the call, so the host may drop `rgba`
|
|
1160
1289
|
* as soon as it returns. Returns the layer's `LayerInput` — its crop
|
|
1161
|
-
* box, the face's `rowHalfWidths`, the face's, hair layers', body's, arms'
|
|
1162
|
-
*
|
|
1290
|
+
* box, the face's `rowHalfWidths`, the face's, hair layers', body's, arms',
|
|
1291
|
+
* pose forearms' and lip set's `rowRuns`, the nose's
|
|
1163
1292
|
* `denseCore` unless that is a speck of its crop (`isSpeckCore`) — or
|
|
1164
1293
|
* `null` for an empty layer, one with no pixel at or above
|
|
1165
1294
|
* `ALPHA_BBOX_THRESHOLD`, which it records nothing for: the host reports
|
|
@@ -1416,4 +1545,4 @@ declare function createGridMesh(cols: number, rows: number): IkiMesh;
|
|
|
1416
1545
|
*/
|
|
1417
1546
|
declare function createDefaultWarpDeformer(model: IkiModel): IkiWarpDeformer;
|
|
1418
1547
|
|
|
1419
|
-
export { ALPHA_BBOX_THRESHOLD, ALPHA_OPAQUE, ATLAS_PADDING, AddDeformer, AddPart, AddPhysicsRig, type AlphaBbox, type ApplyAtlasInput, type ArmGeometry, type AtlasAssignment, type AtlasLayout, type AtlasPlacement, type AtlasSource, CaptureGridKeyform, DEFAULT_TURN_TARGETS, type DeformerTransformChannel, DeleteDeformer, DeletePart, DeletePhysicsRig, type EditCommand, type EditTransformChannel, EditorDocument, type ForearmPoseGeometry, HEAD_BAND, type IrisStrand, LayerGeometryError, type LayerInput, type LayerSetMeasurement, type LayerSetMeasurer, type RgbaLayer, type RigStyle, SPECK_CORE_FRACTION, SetDeformerBindings, SetDeformerParent, SetDeformerPivot, SetDeformerPivotX, SetDeformerPivotY, SetDeformerTransform, SetPartBindings, SetPartColor, SetPartDeformer, SetPartHeight, SetPartMesh, SetPartOrder, SetPartTransform, SetPartWidth, SetPhysicsRig, type StrandOverlap, type TurnDepths, type TurnSolveReport, TurnTargetError, type TurnTargets, UV_INSET_PX, armGeometry, captureBindingEndpoint, computeGridOffsets, createDefaultMatrixDeformer, createDefaultPart, createDefaultWarpDeformer, createGridMesh, createLayerSetMeasurer, denseCoreOf, detectAlphaBbox, forearmPoseGeometry, foregroundSpan, generateIkiFromLayerSet, headHalfOf, interpolateGridOffsets, isSpeckCore, packAtlas, parseLayerRoles, partIdsOfRole, upsertGridKeyform, uvRectFor, validateDeformerDelete, validateDeformerReparent, validatePartAttach };
|
|
1548
|
+
export { ALPHA_BBOX_THRESHOLD, ALPHA_OPAQUE, ATLAS_PADDING, AddDeformer, AddPart, AddPhysicsRig, type AlphaBbox, type ApplyAtlasInput, type ArmGeometry, type AtlasAssignment, type AtlasLayout, type AtlasPlacement, type AtlasSource, CaptureGridKeyform, DEFAULT_TURN_TARGETS, type DeformerTransformChannel, DeleteDeformer, DeletePart, DeletePhysicsRig, type EditCommand, type EditTransformChannel, EditorDocument, type ForearmPoseGeometry, HEAD_BAND, type IrisStrand, LIP_ROLES, LayerGeometryError, type LayerInput, type LayerSetMeasurement, type LayerSetMeasurer, type LipRole, type Opening, type OpeningColumn, type RgbaLayer, type RigStyle, SPECK_CORE_FRACTION, SetDeformerBindings, SetDeformerParent, SetDeformerPivot, SetDeformerPivotX, SetDeformerPivotY, SetDeformerTransform, SetPartBindings, SetPartColor, SetPartDeformer, SetPartHeight, SetPartMesh, SetPartOrder, SetPartTransform, SetPartWidth, SetPhysicsRig, type StrandOverlap, type TurnDepths, type TurnSolveReport, TurnTargetError, type TurnTargets, UV_INSET_PX, armGeometry, captureBindingEndpoint, columnRuns, computeGridOffsets, createDefaultMatrixDeformer, createDefaultPart, createDefaultWarpDeformer, createGridMesh, createLayerSetMeasurer, denseCoreOf, detectAlphaBbox, forearmPoseGeometry, foregroundSpan, generateIkiFromLayerSet, headHalfOf, interpolateGridOffsets, isSpeckCore, mouthFold, mouthOpening, mouthRestShift, packAtlas, parseLayerRoles, partIdsOfRole, upsertGridKeyform, uvRectFor, validateDeformerDelete, validateDeformerReparent, validatePartAttach };
|
package/dist/index.d.ts
CHANGED
|
@@ -714,14 +714,17 @@ interface LayerInput {
|
|
|
714
714
|
/** Face only: half the opaque span of each crop row, canvas px (0 = none). */
|
|
715
715
|
rowHalfWidths?: number[];
|
|
716
716
|
/** `face` / `hair_front` / `hair_back` / `body` / `arm_L` / `arm_R` /
|
|
717
|
-
* `forearm_pose_L` / `forearm_pose_R`
|
|
717
|
+
* `forearm_pose_L` / `forearm_pose_R` /
|
|
718
|
+
* `mouth_inner` / `lip_lower` / `lip_upper` only:
|
|
718
719
|
* per crop row, its opaque (alpha ≥ 128) runs as a flat list of canvas
|
|
719
720
|
* columns `[start0, end0, start1, end1, …]`, each end exclusive (`[]` for a
|
|
720
721
|
* row with none). The actual pixels, so a gap in the hair is a gap here:
|
|
721
722
|
* the turn and the nod read off it where back hair is painted behind the
|
|
722
723
|
* front hair, and the crown's turn reads the face's so it never bares skin.
|
|
723
724
|
* The body's place the hips (where it splits into legs), and an arm's place
|
|
724
|
-
* its shoulder and elbow pivots, a pose forearm's its elbow-end pivot
|
|
725
|
+
* its shoulder and elbow pivots, a pose forearm's its elbow-end pivot, and
|
|
726
|
+
* a lip set's place the mouth's opening: the upper line's bottom, the
|
|
727
|
+
* interior's span, the skin's top. */
|
|
725
728
|
rowRuns?: number[][];
|
|
726
729
|
/** Nose only: the tight box of its alpha ≥ 128 pixels, canvas px. */
|
|
727
730
|
denseCore?: {
|
|
@@ -768,9 +771,11 @@ declare class TurnTargetError extends Error {
|
|
|
768
771
|
constructor(message: string);
|
|
769
772
|
}
|
|
770
773
|
/**
|
|
771
|
-
* Art geometry the rig cannot build on —
|
|
772
|
-
*
|
|
773
|
-
*
|
|
774
|
+
* Art geometry the rig cannot build on — a body whose hips leave no row line
|
|
775
|
+
* under its pivots, or too little room for its motion (`body.ts`), an arm
|
|
776
|
+
* without room for its pivots, a lip set whose interior has no opaque pixel
|
|
777
|
+
* (`mouth.ts`). A fact about the layers, not a bug, so a host reports it as an
|
|
778
|
+
* input error.
|
|
774
779
|
*/
|
|
775
780
|
declare class LayerGeometryError extends Error {
|
|
776
781
|
constructor(message: string);
|
|
@@ -988,19 +993,141 @@ interface ForearmPoseGeometry {
|
|
|
988
993
|
*/
|
|
989
994
|
declare function forearmPoseGeometry(layer: LayerInput): ForearmPoseGeometry;
|
|
990
995
|
|
|
996
|
+
/** The lip set's roles, back to front (`mouth.ts`). */
|
|
997
|
+
declare const LIP_ROLES: readonly ["mouth_inner", "lip_lower", "lip_upper"];
|
|
998
|
+
type LipRole = (typeof LIP_ROLES)[number];
|
|
991
999
|
/** The part ids a `role`'s layer becomes, back to front: every one of them
|
|
992
1000
|
* takes its texture from the layer's crop. Throws on an unknown role. */
|
|
993
1001
|
declare function partIdsOfRole(role: string): readonly string[];
|
|
994
1002
|
/**
|
|
995
1003
|
* Map file names to roles. Throws on an unknown role, a role named twice, a
|
|
996
|
-
* required role (`face`, `eye_L`, `eye_R
|
|
997
|
-
* body, or a pose forearm
|
|
1004
|
+
* required role (`face`, `eye_L`, `eye_R`) missing, no mouth (`mouth`, or the
|
|
1005
|
+
* lip set) or a partial or mixed one, an arm without a body, or a pose forearm
|
|
1006
|
+
* without its arm.
|
|
998
1007
|
*/
|
|
999
1008
|
declare function parseLayerRoles(fileNames: string[]): {
|
|
1000
1009
|
role: string;
|
|
1001
1010
|
fileName: string;
|
|
1002
1011
|
}[];
|
|
1003
1012
|
|
|
1013
|
+
/**
|
|
1014
|
+
* The lip set: a mouth that folds open the way the eyelid folds shut. Three
|
|
1015
|
+
* parts, back to front: `mouth_inner` (the interior and the opening's lower
|
|
1016
|
+
* outline band), `lip_lower` (the lower lip's skin) and `lip_upper` (the upper
|
|
1017
|
+
* lip line and the corner hooks).
|
|
1018
|
+
*
|
|
1019
|
+
* What the rig reads is the layers' own alpha edges, as exclusive run
|
|
1020
|
+
* boundaries in model y (+y up; a run of crop rows `[a, b)` is the span
|
|
1021
|
+
* `[rowTop(b), rowTop(a)]`). Per canvas column inside the opening (where
|
|
1022
|
+
* `mouth_inner` has a run): the interior's top `T` and bottom `Bb`, the line
|
|
1023
|
+
* of `lip_upper` at the interior's top (`Tu` its bottom edge, `lineTop` its
|
|
1024
|
+
* top) and the skin's top `Bl`. Each part has one affine warp per column that
|
|
1025
|
+
* lands its OWN boundary on its target, so no relation between two parts'
|
|
1026
|
+
* boundaries enters the closure; there is no colour read. The art's contract:
|
|
1027
|
+
* the interior reaches the line (`T >= Tu`; lower would show the face between
|
|
1028
|
+
* them at rest) and the skin reaches the interior (`Bl >= Bb`; an overlap is
|
|
1029
|
+
* fine, a gap is not).
|
|
1030
|
+
*
|
|
1031
|
+
* MouthOpen 0 folds shut onto a seam, 1 is the art as drawn (the widening of
|
|
1032
|
+
* `mouthWiden` apart). The line comes down until its bottom edge is on the
|
|
1033
|
+
* seam, the skin until its top is one overlap `w'` up under the line (`w'` is
|
|
1034
|
+
* the stroke `w`, the line's height at the opening's centre, capped by the
|
|
1035
|
+
* line's own ink at that column so tucked skin never shows above it), and the
|
|
1036
|
+
* interior's bottom rides the skin's top, so the band is never covered. The
|
|
1037
|
+
* interior's top closes onto the same edge, `S + w'`: the whole column scales
|
|
1038
|
+
* by `v` about it, so shut its height is zero (a degenerate triangle draws
|
|
1039
|
+
* nothing, at every column and between knots; an inverted sliver would leak
|
|
1040
|
+
* through texture filtering where the line is thin), and its top sits
|
|
1041
|
+
* `w'(1 - v)` above the line's bottom edge, under the line's ink, so the slit
|
|
1042
|
+
* between the line and the skin is always backed. The slit opens once
|
|
1043
|
+
* `v * H > w'(1 - v)`; that dead zone is accepted: lip-sync noise near 0 does
|
|
1044
|
+
* not flicker through it.
|
|
1045
|
+
*
|
|
1046
|
+
* The seam is the line's drawn bottom edge `Tu` plus one smooth closing
|
|
1047
|
+
* travel `D`, a cubic in the column index pinned to zero at the opening's two
|
|
1048
|
+
* end columns, so the closed line's ends are the drawn line's own ends. It is
|
|
1049
|
+
* fitted by least squares to the raw per-column travel `(T - UPPER_SHARE * H)
|
|
1050
|
+
* - Tu`, how far the line's bottom is from the share's point in the
|
|
1051
|
+
* interior's span. The travel is fitted, not the seam, because `Tu` is an
|
|
1052
|
+
* integer-row read: a smooth seam minus that staircase is a sawtooth the mesh
|
|
1053
|
+
* would carry into the rendered line, while a smooth travel carries the
|
|
1054
|
+
* drawn stroke (whose antialiased edge is smooth) as one piece. The share sets
|
|
1055
|
+
* the travel's depth (the sag), the art's two arcs its shape; the cubic keeps
|
|
1056
|
+
* an asymmetric mouth's tilt and cannot wobble.
|
|
1057
|
+
*
|
|
1058
|
+
* A lip's columns outside the opening (the corner hooks) do not fold; only
|
|
1059
|
+
* the interior's edge columns read the nearest opening column.
|
|
1060
|
+
*
|
|
1061
|
+
* Knots: the three parts share column knots every `MOUTH_KNOT_PX` plus the
|
|
1062
|
+
* opening's boundaries `x0`, `x1` and `x1 + 1`, each an integer canvas
|
|
1063
|
+
* boundary. The fit's x is the column index, which is the mesh's boundary
|
|
1064
|
+
* position: a knot at boundary `b` samples column `b`, so the mesh renders the
|
|
1065
|
+
* fit exactly at its knots and the pinned ends render as pinned (`x1` and
|
|
1066
|
+
* `x1 + 1` both sample the last column; the second sits at its right edge).
|
|
1067
|
+
* The preview shifts each column by the field at the column's centre
|
|
1068
|
+
* (`mouthRestShift`). A part's mesh rounds its local x, so one knot
|
|
1069
|
+
* reconstructs to slightly different model x per part; the fold therefore
|
|
1070
|
+
* reads the knot itself, by vertex index, and maps every knot through one rule
|
|
1071
|
+
* (`columnOfKnot`).
|
|
1072
|
+
*
|
|
1073
|
+
* The anchor (`mouthAnchor`) is where the head's frame and the turn read the
|
|
1074
|
+
* mouth's resting place (the chin lead, the chin estimate, the solve). A
|
|
1075
|
+
* single closed mouth is its box centre (its box the union with `mouth_open`);
|
|
1076
|
+
* for a lip set it is the seam at the opening's centre column, where the lips
|
|
1077
|
+
* meet, with the interior's centre across and the union of the three as box.
|
|
1078
|
+
*/
|
|
1079
|
+
|
|
1080
|
+
/** Per canvas column, its opaque runs as `[top, bottom)` crop rows, top
|
|
1081
|
+
* first: the transpose of `rowRuns`. */
|
|
1082
|
+
declare function columnRuns(layer: LayerInput): Map<number, number[][]>;
|
|
1083
|
+
/** What the fold reads off one canvas column, all model y. */
|
|
1084
|
+
interface OpeningColumn {
|
|
1085
|
+
/** The interior's top and bottom edges. */
|
|
1086
|
+
T: number;
|
|
1087
|
+
Bb: number;
|
|
1088
|
+
/** The upper line's bottom and top edges, and its height. */
|
|
1089
|
+
Tu: number;
|
|
1090
|
+
lineTop: number;
|
|
1091
|
+
lineH: number;
|
|
1092
|
+
/** The skin's top edge. */
|
|
1093
|
+
Bl: number;
|
|
1094
|
+
/** The interior's span. */
|
|
1095
|
+
H: number;
|
|
1096
|
+
/** How far the skin tucks up under the line. */
|
|
1097
|
+
overlap: number;
|
|
1098
|
+
/** Where the lips meet when shut: the line's bottom edge after its smooth
|
|
1099
|
+
* closing travel. */
|
|
1100
|
+
seam: number;
|
|
1101
|
+
}
|
|
1102
|
+
interface Opening {
|
|
1103
|
+
/** The first and last canvas columns where `mouth_inner` has a run. */
|
|
1104
|
+
x0: number;
|
|
1105
|
+
x1: number;
|
|
1106
|
+
/** The opening's centre column: where the stroke and the anchor are read. */
|
|
1107
|
+
centre: number;
|
|
1108
|
+
canvasW: number;
|
|
1109
|
+
/** The stroke: the line's height at the opening's centre column. */
|
|
1110
|
+
w: number;
|
|
1111
|
+
/** A column's values; outside `x0..x1`, or in a gap, the nearest one's. */
|
|
1112
|
+
at(col: number): OpeningColumn;
|
|
1113
|
+
}
|
|
1114
|
+
declare function mouthOpening(byRole: Map<string, LayerInput>): Opening;
|
|
1115
|
+
/**
|
|
1116
|
+
* dy of a model-y rest position at pixel column `col` for MouthOpen `v`
|
|
1117
|
+
* (0 shut, 1 as drawn). A lip outside the opening moves 0; `mouth_inner`
|
|
1118
|
+
* scales its column by `v` about `S + w'`, which carries its top and bottom
|
|
1119
|
+
* edges along the line and the skin and needs no read of its own height.
|
|
1120
|
+
*/
|
|
1121
|
+
declare function mouthFold(role: LipRole, opening: Opening): (col: number, y: number, v: number) => number;
|
|
1122
|
+
/** A lip's translate at MouthOpen 0 per canvas column as the mesh renders it,
|
|
1123
|
+
* for the composer's closed preview: the linear interpolation at the column's
|
|
1124
|
+
* centre between the STORED offsets of the part's two bracketing knots (the
|
|
1125
|
+
* numbers the `.iki` carries, rounded as `localWarp` rounds them), 0 outside
|
|
1126
|
+
* the part's box. `byRole` must be the layers as the rig measures them (the
|
|
1127
|
+
* canvas-sized ones): a box measured on a smaller frame clamps differently,
|
|
1128
|
+
* and the knot grid starts at the union's edge. */
|
|
1129
|
+
declare function mouthRestShift(byRole: Map<string, LayerInput>): (role: "lip_upper" | "lip_lower", col: number) => number;
|
|
1130
|
+
|
|
1004
1131
|
/**
|
|
1005
1132
|
* `generateIkiFromLayerSet`: role layers in, a rigged `.iki` out.
|
|
1006
1133
|
*
|
|
@@ -1018,7 +1145,9 @@ declare function parseLayerRoles(fileNames: string[]): {
|
|
|
1018
1145
|
* and two ears that lag. Blink, gaze, brows, mouth, breath and
|
|
1019
1146
|
* hair sway are part warps and bindings under those. The model also declares
|
|
1020
1147
|
* the default expressions and the Nod, Shake and Tilt motions, and
|
|
1021
|
-
* with a pose forearm the Wave (`animations.ts`).
|
|
1148
|
+
* with a pose forearm the Wave (`animations.ts`). The mouth has two paths,
|
|
1149
|
+
* chosen by the layers: `mouth` alone stretches, with `mouth_open` it
|
|
1150
|
+
* crossfades; the lip set folds open like the eyelid (`mouth.ts`).
|
|
1022
1151
|
*/
|
|
1023
1152
|
|
|
1024
1153
|
declare function generateIkiFromLayerSet(layers: LayerInput[], canvas: {
|
|
@@ -1158,8 +1287,8 @@ interface LayerSetMeasurer {
|
|
|
1158
1287
|
* of it and of the per-role edges), this layer's per-row opaque extent and
|
|
1159
1288
|
* the `LayerInput` it returns outlive the call, so the host may drop `rgba`
|
|
1160
1289
|
* as soon as it returns. Returns the layer's `LayerInput` — its crop
|
|
1161
|
-
* box, the face's `rowHalfWidths`, the face's, hair layers', body's, arms'
|
|
1162
|
-
*
|
|
1290
|
+
* box, the face's `rowHalfWidths`, the face's, hair layers', body's, arms',
|
|
1291
|
+
* pose forearms' and lip set's `rowRuns`, the nose's
|
|
1163
1292
|
* `denseCore` unless that is a speck of its crop (`isSpeckCore`) — or
|
|
1164
1293
|
* `null` for an empty layer, one with no pixel at or above
|
|
1165
1294
|
* `ALPHA_BBOX_THRESHOLD`, which it records nothing for: the host reports
|
|
@@ -1416,4 +1545,4 @@ declare function createGridMesh(cols: number, rows: number): IkiMesh;
|
|
|
1416
1545
|
*/
|
|
1417
1546
|
declare function createDefaultWarpDeformer(model: IkiModel): IkiWarpDeformer;
|
|
1418
1547
|
|
|
1419
|
-
export { ALPHA_BBOX_THRESHOLD, ALPHA_OPAQUE, ATLAS_PADDING, AddDeformer, AddPart, AddPhysicsRig, type AlphaBbox, type ApplyAtlasInput, type ArmGeometry, type AtlasAssignment, type AtlasLayout, type AtlasPlacement, type AtlasSource, CaptureGridKeyform, DEFAULT_TURN_TARGETS, type DeformerTransformChannel, DeleteDeformer, DeletePart, DeletePhysicsRig, type EditCommand, type EditTransformChannel, EditorDocument, type ForearmPoseGeometry, HEAD_BAND, type IrisStrand, LayerGeometryError, type LayerInput, type LayerSetMeasurement, type LayerSetMeasurer, type RgbaLayer, type RigStyle, SPECK_CORE_FRACTION, SetDeformerBindings, SetDeformerParent, SetDeformerPivot, SetDeformerPivotX, SetDeformerPivotY, SetDeformerTransform, SetPartBindings, SetPartColor, SetPartDeformer, SetPartHeight, SetPartMesh, SetPartOrder, SetPartTransform, SetPartWidth, SetPhysicsRig, type StrandOverlap, type TurnDepths, type TurnSolveReport, TurnTargetError, type TurnTargets, UV_INSET_PX, armGeometry, captureBindingEndpoint, computeGridOffsets, createDefaultMatrixDeformer, createDefaultPart, createDefaultWarpDeformer, createGridMesh, createLayerSetMeasurer, denseCoreOf, detectAlphaBbox, forearmPoseGeometry, foregroundSpan, generateIkiFromLayerSet, headHalfOf, interpolateGridOffsets, isSpeckCore, packAtlas, parseLayerRoles, partIdsOfRole, upsertGridKeyform, uvRectFor, validateDeformerDelete, validateDeformerReparent, validatePartAttach };
|
|
1548
|
+
export { ALPHA_BBOX_THRESHOLD, ALPHA_OPAQUE, ATLAS_PADDING, AddDeformer, AddPart, AddPhysicsRig, type AlphaBbox, type ApplyAtlasInput, type ArmGeometry, type AtlasAssignment, type AtlasLayout, type AtlasPlacement, type AtlasSource, CaptureGridKeyform, DEFAULT_TURN_TARGETS, type DeformerTransformChannel, DeleteDeformer, DeletePart, DeletePhysicsRig, type EditCommand, type EditTransformChannel, EditorDocument, type ForearmPoseGeometry, HEAD_BAND, type IrisStrand, LIP_ROLES, LayerGeometryError, type LayerInput, type LayerSetMeasurement, type LayerSetMeasurer, type LipRole, type Opening, type OpeningColumn, type RgbaLayer, type RigStyle, SPECK_CORE_FRACTION, SetDeformerBindings, SetDeformerParent, SetDeformerPivot, SetDeformerPivotX, SetDeformerPivotY, SetDeformerTransform, SetPartBindings, SetPartColor, SetPartDeformer, SetPartHeight, SetPartMesh, SetPartOrder, SetPartTransform, SetPartWidth, SetPhysicsRig, type StrandOverlap, type TurnDepths, type TurnSolveReport, TurnTargetError, type TurnTargets, UV_INSET_PX, armGeometry, captureBindingEndpoint, columnRuns, computeGridOffsets, createDefaultMatrixDeformer, createDefaultPart, createDefaultWarpDeformer, createGridMesh, createLayerSetMeasurer, denseCoreOf, detectAlphaBbox, forearmPoseGeometry, foregroundSpan, generateIkiFromLayerSet, headHalfOf, interpolateGridOffsets, isSpeckCore, mouthFold, mouthOpening, mouthRestShift, packAtlas, parseLayerRoles, partIdsOfRole, upsertGridKeyform, uvRectFor, validateDeformerDelete, validateDeformerReparent, validatePartAttach };
|