@ikijs/editor 0.21.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 CHANGED
@@ -40,19 +40,19 @@ 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
 
package/dist/index.d.mts CHANGED
@@ -993,6 +993,9 @@ interface ForearmPoseGeometry {
993
993
  */
994
994
  declare function forearmPoseGeometry(layer: LayerInput): ForearmPoseGeometry;
995
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];
996
999
  /** The part ids a `role`'s layer becomes, back to front: every one of them
997
1000
  * takes its texture from the layer's crop. Throws on an unknown role. */
998
1001
  declare function partIdsOfRole(role: string): readonly string[];
@@ -1007,6 +1010,124 @@ declare function parseLayerRoles(fileNames: string[]): {
1007
1010
  fileName: string;
1008
1011
  }[];
1009
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
+
1010
1131
  /**
1011
1132
  * `generateIkiFromLayerSet`: role layers in, a rigged `.iki` out.
1012
1133
  *
@@ -1424,4 +1545,4 @@ declare function createGridMesh(cols: number, rows: number): IkiMesh;
1424
1545
  */
1425
1546
  declare function createDefaultWarpDeformer(model: IkiModel): IkiWarpDeformer;
1426
1547
 
1427
- 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
@@ -993,6 +993,9 @@ interface ForearmPoseGeometry {
993
993
  */
994
994
  declare function forearmPoseGeometry(layer: LayerInput): ForearmPoseGeometry;
995
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];
996
999
  /** The part ids a `role`'s layer becomes, back to front: every one of them
997
1000
  * takes its texture from the layer's crop. Throws on an unknown role. */
998
1001
  declare function partIdsOfRole(role: string): readonly string[];
@@ -1007,6 +1010,124 @@ declare function parseLayerRoles(fileNames: string[]): {
1007
1010
  fileName: string;
1008
1011
  }[];
1009
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
+
1010
1131
  /**
1011
1132
  * `generateIkiFromLayerSet`: role layers in, a rigged `.iki` out.
1012
1133
  *
@@ -1424,4 +1545,4 @@ declare function createGridMesh(cols: number, rows: number): IkiMesh;
1424
1545
  */
1425
1546
  declare function createDefaultWarpDeformer(model: IkiModel): IkiWarpDeformer;
1426
1547
 
1427
- 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 };