@ikijs/editor 0.4.0 → 0.6.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 +34 -2
- package/dist/index.d.mts +141 -8
- package/dist/index.d.ts +141 -8
- package/dist/index.js +1739 -358
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1737 -358
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -49,7 +49,7 @@ leaves the document untouched.
|
|
|
49
49
|
| Grid keyforms | `computeGridOffsets`, `interpolateGridOffsets`, `upsertGridKeyform` |
|
|
50
50
|
| Factories | `createDefaultPart`, `createDefaultMatrixDeformer`, `createDefaultWarpDeformer`, `createGridMesh` |
|
|
51
51
|
| Pixels | `detectAlphaBbox`, `ALPHA_BBOX_THRESHOLD`, `AlphaBbox` |
|
|
52
|
-
| Auto-rig | `generateIkiFromLayerSet`, `parseLayerRoles`
|
|
52
|
+
| Auto-rig | `generateIkiFromLayerSet`, `parseLayerRoles`, `TurnTargets`, `DEFAULT_TURN_TARGETS`, `TurnSolveReport` |
|
|
53
53
|
| Bindings | `captureBindingEndpoint` |
|
|
54
54
|
|
|
55
55
|
## Auto-rig
|
|
@@ -60,10 +60,42 @@ leaves the document untouched.
|
|
|
60
60
|
torso that follows and breathes, and emotes with its brows — including a
|
|
61
61
|
hair-sway physics rig when a `hair_front` layer is present.
|
|
62
62
|
|
|
63
|
+
On the turn the head's outline holds still — the bangs that draw it pin their
|
|
64
|
+
own silhouette — while everything inside it slides: the face plate, its
|
|
65
|
+
features, and the bangs' own inner strands. That slide is the plate's own ask, a
|
|
66
|
+
quarter of its half-width, capped to what the held outline has room for when
|
|
67
|
+
`turnTargets.headHalfWidth` was measured and uncut when it was not. That turn is
|
|
68
|
+
**fitted, not tuned**: on a layer set with a `nose`, the cylinder's radius and
|
|
69
|
+
each feature's own depth on top of that slide are solved from
|
|
70
|
+
`options.turnTargets` — the cues a 30° reference measures (how far the eye pair
|
|
71
|
+
slides, how much the far eye foreshortens, whether the silhouette holds),
|
|
72
|
+
defaulting to `DEFAULT_TURN_TARGETS`. The features' slide is bounded by the art:
|
|
73
|
+
every feature has to stay on the face plate, because past its contour the far
|
|
74
|
+
eye is drawn over the side hair, which still bends with the plate inside that
|
|
75
|
+
held outline and swallows it. A target you passed that this layer set cannot
|
|
76
|
+
reach **throws**, naming the field and the range it could have had; a target
|
|
77
|
+
that came from the defaults is a style prior, not a measurement of this
|
|
78
|
+
character, so it is **clamped** to what the layer set can do and the rig is
|
|
79
|
+
built. Pass `options.onTurnSolved` to see what the turn settled on and which
|
|
80
|
+
targets were clamped.
|
|
81
|
+
|
|
82
|
+
`turnTargets.headHalfWidth` is what the shift targets are fractions of, when a
|
|
83
|
+
caller has measured the actual head (the face plate stands in otherwise).
|
|
84
|
+
Alongside it, `options.headEdges` names, per side, every layer with an opaque
|
|
85
|
+
pixel at the eye row and its own rest x there — a companion to a measured
|
|
86
|
+
`headHalfWidth`, absent otherwise — so the solver can land each candidate
|
|
87
|
+
through its OWN part's deformation (the bangs move with their hold, the back
|
|
88
|
+
hair holds still, a face-plate or body edge slide) rather than assume the head
|
|
89
|
+
is centred on the face plate or that whichever part drew furthest out at rest
|
|
90
|
+
is still the furthest out after it turns.
|
|
91
|
+
|
|
63
92
|
It takes **already-decoded** layer geometry (`LayerInput`), never pixels, which
|
|
64
93
|
is what keeps this package free of any image dependency: the editor app
|
|
65
94
|
decodes with canvas, `@ikijs/mcp` decodes with `sharp`, and both feed the same
|
|
66
|
-
pure function
|
|
95
|
+
pure function — including the optional `rowHalfWidths` (one entry per crop row,
|
|
96
|
+
half that row's opaque span in canvas px), which `@ikijs/mcp` measures for the
|
|
97
|
+
face so the plate turns on a radius that varies by row; absent, it turns on one
|
|
98
|
+
constant radius.
|
|
67
99
|
|
|
68
100
|
`*_L` / `*_R` are the **character's** sides — `eye_L` is the character's left
|
|
69
101
|
eye, which appears on the viewer's right.
|
package/dist/index.d.mts
CHANGED
|
@@ -875,6 +875,19 @@ interface LayerInput {
|
|
|
875
875
|
cropW: number;
|
|
876
876
|
/** Cropped image height = bbox.h. */
|
|
877
877
|
cropH: number;
|
|
878
|
+
/**
|
|
879
|
+
* The layer's painted half-width on every CROP row, top → bottom, one entry
|
|
880
|
+
* per row (`cropH` of them): half the row's opaque span in canvas px under
|
|
881
|
+
* the same alpha ≥ 128 rule the head's `headEdges` are measured with
|
|
882
|
+
* (`@ikijs/mcp`'s `measure-turn`), 0 for a row with no opaque pixel.
|
|
883
|
+
* Optional, and only the FACE's is read: it gives the face plate a
|
|
884
|
+
* row-dependent turn radius and a chin swing (`faceRowProfile`,
|
|
885
|
+
* `turnSurface`); absent — as the editor's own import leaves it — the plate
|
|
886
|
+
* turns on one radius on every row. Validated before anything reads it: an
|
|
887
|
+
* array of length `cropH` whose every entry is a finite number in
|
|
888
|
+
* [0, cropW / 2], the most an inclusive span inside the crop can be.
|
|
889
|
+
*/
|
|
890
|
+
rowHalfWidths?: number[];
|
|
878
891
|
}
|
|
879
892
|
/**
|
|
880
893
|
* Map an array of raw filenames to canonical `{ role, fileName }` pairs.
|
|
@@ -891,29 +904,149 @@ declare function parseLayerRoles(fileNames: string[]): {
|
|
|
891
904
|
role: string;
|
|
892
905
|
fileName: string;
|
|
893
906
|
}[];
|
|
907
|
+
/**
|
|
908
|
+
* What a head does at full turn, as the cues `measure_turn_reference` reads off
|
|
909
|
+
* a front/turned reference pair. The turn is fitted to these — the cylinder's
|
|
910
|
+
* radius and the features' depths are solved from them, not tuned.
|
|
911
|
+
*
|
|
912
|
+
* Every field is optional: `DEFAULT_TURN_TARGETS` supplies the three a
|
|
913
|
+
* reference measures, and the nose and the mouth derive from the eyes when they
|
|
914
|
+
* are not given.
|
|
915
|
+
*
|
|
916
|
+
* What happens when the layer set cannot reach a target depends on where the
|
|
917
|
+
* target came from. A number the CALLER passed is a measurement, so an
|
|
918
|
+
* unreachable one throws, naming the field and what was on offer. A number this
|
|
919
|
+
* module filled in — a default, or a nose/mouth derived from the eyes — is a
|
|
920
|
+
* style prior, not a promise about this character: it is CLAMPED to what the
|
|
921
|
+
* layer set can do and the rig is built. The alternative is a generator that
|
|
922
|
+
* refuses its own defaults, which would leave a perfectly good layer set with
|
|
923
|
+
* no model at all. `solveTurnModel` reports which fields it clamped.
|
|
924
|
+
*/
|
|
925
|
+
interface TurnTargets {
|
|
926
|
+
/** How far the eye pair's centre slides toward the far side at full turn, as
|
|
927
|
+
* a fraction of the head's half-width. The sign is ignored: which way is
|
|
928
|
+
* the turn's own business. */
|
|
929
|
+
eyeShift?: number;
|
|
930
|
+
/** The far eye's width over the near eye's at full turn, divided by the same
|
|
931
|
+
* ratio at rest. 1 is no foreshortening; 0 is an edge-on far eye. */
|
|
932
|
+
farEyeRatio?: number;
|
|
933
|
+
/** The head's half-width at full turn over its half-width at rest, as a
|
|
934
|
+
* RENDER measures it — the outermost opaque edge on each side, whichever
|
|
935
|
+
* part owns it. 1 holds the silhouette; below 1 narrows it. The hold that
|
|
936
|
+
* produces it is fitted to it, so a measured one is met or refused, never
|
|
937
|
+
* quietly rendered as something else. */
|
|
938
|
+
silhouetteRatio?: number;
|
|
939
|
+
/** `eyeShift` for the nose. Derived from `eyeShift` when absent. */
|
|
940
|
+
noseShift?: number;
|
|
941
|
+
/** `eyeShift` for the mouth. Derived from `eyeShift` when absent. */
|
|
942
|
+
mouthShift?: number;
|
|
943
|
+
/** The head's own half-width at the eye row, in canvas px: what the shift
|
|
944
|
+
* fractions are fractions OF, and the rest distance the silhouette hold
|
|
945
|
+
* pivots on. Without it both fall back to the face plate's half-width, which
|
|
946
|
+
* is narrower than the head the hair draws, so the shifts land short — an
|
|
947
|
+
* approximation for callers that have no pixels, not a default to prefer. */
|
|
948
|
+
headHalfWidth?: number;
|
|
949
|
+
}
|
|
950
|
+
/**
|
|
951
|
+
* A `turnTargets` field this generator will not build a rig from: not a number,
|
|
952
|
+
* outside its range, unreachable on this layer set, or a measured head narrower
|
|
953
|
+
* than the face plate it is drawn around. Every one of them carries a number
|
|
954
|
+
* the CALLER passed, so a host can tell them apart from the invariant breaks the
|
|
955
|
+
* rest of the generator throws and report them as bad input instead of failing.
|
|
956
|
+
*/
|
|
957
|
+
declare class TurnTargetError extends Error {
|
|
958
|
+
constructor(message: string);
|
|
959
|
+
}
|
|
960
|
+
/**
|
|
961
|
+
* The reference the turn is fitted to when the caller measured none of its own:
|
|
962
|
+
* a 3/4 portrait, which `measure_turn_reference` reads as 0.671 / −0.224 /
|
|
963
|
+
* 1.012 (its raw turned far/near iris ratio is 0.71 — 0.67 is that ratio
|
|
964
|
+
* normalised by the same ratio at rest, which is what the field means).
|
|
965
|
+
* The silhouette is held exactly: a 1 % change is inside that tool's own noise.
|
|
966
|
+
*/
|
|
967
|
+
declare const DEFAULT_TURN_TARGETS: Readonly<Required<Pick<TurnTargets, "eyeShift" | "farEyeRatio" | "silhouetteRatio">>>;
|
|
968
|
+
/** The turn depth of each feature family: the share of the pinned bulk slide
|
|
969
|
+
* (`headTurnParallaxUnit`) it gets back at full turn. Solved, never tuned. */
|
|
970
|
+
interface TurnDepths {
|
|
971
|
+
eye: number;
|
|
972
|
+
nose: number;
|
|
973
|
+
mouth: number;
|
|
974
|
+
}
|
|
975
|
+
/** What the turn solve settled on, for a caller that wants to see it — the
|
|
976
|
+
* cues it reached and the targets it had to cut down to reach them. */
|
|
977
|
+
interface TurnSolveReport {
|
|
978
|
+
/** Cylinder radius the face-warp bake and the parallax unit were built on. */
|
|
979
|
+
radius: number;
|
|
980
|
+
/** Rest distance from the face centre the silhouette hold pivots on. */
|
|
981
|
+
holdBase: number;
|
|
982
|
+
depths: TurnDepths;
|
|
983
|
+
/** What the rig actually reaches, in the targets' own units. */
|
|
984
|
+
achieved: {
|
|
985
|
+
eyeShift: number;
|
|
986
|
+
farEyeRatio: number;
|
|
987
|
+
silhouetteRatio: number;
|
|
988
|
+
};
|
|
989
|
+
/** Defaulted fields the layer set could not reach, cut down to what it can.
|
|
990
|
+
* Empty when every target was met. */
|
|
991
|
+
clamped: (keyof TurnTargets)[];
|
|
992
|
+
}
|
|
894
993
|
/**
|
|
895
994
|
* Auto-rig: given decoded layer inputs and the shared canvas size, produce a
|
|
896
995
|
* valid IkiModel ready for parseIkiModel.
|
|
897
996
|
*
|
|
898
997
|
* - Validate all inputs before deriving anything.
|
|
899
998
|
* - Place parts at source-derived positions (bboxToTransform, unshifted).
|
|
900
|
-
* - Emit the standard parameters (
|
|
901
|
-
*
|
|
902
|
-
*
|
|
903
|
-
* -
|
|
999
|
+
* - Emit the standard parameters (STANDARD_PARAMETERS), plus a conditional
|
|
1000
|
+
* HairSwayX/Z pair + hair-sway physics rigs when a hair_front layer is
|
|
1001
|
+
* present.
|
|
1002
|
+
* - Size every turn group's grid to its own members and the virtual lattice
|
|
1003
|
+
* to those grids, then solve the turn on them (turnSetup, solveTurnModel):
|
|
1004
|
+
* one surface — radius, travel, the head-level nod radius — that every
|
|
1005
|
+
* bake below samples.
|
|
1006
|
+
* - Build headDeformer (matrix, neck pivot, nod/tilt/breath bindings — the
|
|
1007
|
+
* turn translates nothing rigidly; its travel is in the group grids), one
|
|
1008
|
+
* warp deformer per turn group present — `faceWarp` for the plate,
|
|
1009
|
+
* `eyeWarp_L/R`, `mouthWarp`, `noseWarp`, `browWarp_L/R`, `blushWarp_L/R`
|
|
1010
|
+
* — each a warp2d over its own grid baked from that surface with the
|
|
1011
|
+
* family's solved depth as keyform geometry (bakeTurnGroupWarp2D), and
|
|
904
1012
|
* bodyDeformer when a body layer is present (matrix, torso-base pivot,
|
|
905
|
-
* AngleX follow + Breath follow)
|
|
906
|
-
* baked cylinder warp center-relative on faceCenterX).
|
|
1013
|
+
* AngleX follow + Breath follow).
|
|
907
1014
|
* - Mesh parts (spec.mesh===true) → width:1, height:1, pixel grid mesh sized by
|
|
908
|
-
* meshCellsFor + role bindings.
|
|
1015
|
+
* meshCellsFor + role bindings, each face-family part on its group's warp.
|
|
909
1016
|
* - Static parts (spec.mesh===false) → width:cropW, height:cropH, no mesh.
|
|
910
1017
|
* - Part ids equal the role string (deterministic, no crypto.randomUUID).
|
|
911
1018
|
* - Return parseIkiModel(structuredClone(model)) — every caller gets a
|
|
912
1019
|
* validated model; bad assembly fails loudly.
|
|
1020
|
+
*
|
|
1021
|
+
* `options.turnTargets` is what the head turn is fitted to: the cues a 30°
|
|
1022
|
+
* reference measures, defaulting to DEFAULT_TURN_TARGETS. A target the CALLER
|
|
1023
|
+
* passed that this layer set cannot reach throws; one that came from the
|
|
1024
|
+
* defaults is clamped to what it can do — see TurnTargets for why the two
|
|
1025
|
+
* differ, and pass `options.onTurnSolved` to see which ones were clamped and
|
|
1026
|
+
* what the turn ended up reaching.
|
|
913
1027
|
*/
|
|
914
1028
|
declare function generateIkiFromLayerSet(layers: LayerInput[], canvas: {
|
|
915
1029
|
width: number;
|
|
916
1030
|
height: number;
|
|
1031
|
+
}, options?: {
|
|
1032
|
+
turnTargets?: TurnTargets;
|
|
1033
|
+
/** Called once, after a turn is solved, with what it settled on. Not called
|
|
1034
|
+
* for a layer set without a nose, which solves no turn at all. */
|
|
1035
|
+
onTurnSolved?: (report: TurnSolveReport) => void;
|
|
1036
|
+
/** Every role with an opaque pixel in the eye-row band, per side, when
|
|
1037
|
+
* `turnTargets.headHalfWidth` was measured off that same union — a
|
|
1038
|
+
* companion to it, not a turn target itself (the caller-facing contract
|
|
1039
|
+
* stays the three cues). See `TurnSolveContext.headEdges`. */
|
|
1040
|
+
headEdges?: {
|
|
1041
|
+
left: {
|
|
1042
|
+
role: string;
|
|
1043
|
+
x: number;
|
|
1044
|
+
}[];
|
|
1045
|
+
right: {
|
|
1046
|
+
role: string;
|
|
1047
|
+
x: number;
|
|
1048
|
+
}[];
|
|
1049
|
+
};
|
|
917
1050
|
}): IkiModel;
|
|
918
1051
|
|
|
919
|
-
export { ALPHA_BBOX_THRESHOLD, ATLAS_PADDING, AddDeformer, AddPart, AddPhysicsRig, type AlphaBbox, type ApplyAtlasInput, type AtlasAssignment, type AtlasLayout, type AtlasPlacement, type AtlasSource, CaptureGridKeyform, type DeformerTransformChannel, DeleteDeformer, DeletePart, DeletePhysicsRig, type EditCommand, type EditTransformChannel, EditorDocument, type LayerInput, SetDeformerBindings, SetDeformerParent, SetDeformerPivot, SetDeformerPivotX, SetDeformerPivotY, SetDeformerTransform, SetPartBindings, SetPartColor, SetPartDeformer, SetPartHeight, SetPartMesh, SetPartOrder, SetPartTransform, SetPartWidth, SetPhysicsRig, UV_INSET_PX, captureBindingEndpoint, computeGridOffsets, createDefaultMatrixDeformer, createDefaultPart, createDefaultWarpDeformer, createGridMesh, detectAlphaBbox, generateIkiFromLayerSet, interpolateGridOffsets, packAtlas, parseLayerRoles, upsertGridKeyform, uvRectFor, validateDeformerDelete, validateDeformerReparent, validatePartAttach };
|
|
1052
|
+
export { ALPHA_BBOX_THRESHOLD, ATLAS_PADDING, AddDeformer, AddPart, AddPhysicsRig, type AlphaBbox, type ApplyAtlasInput, type AtlasAssignment, type AtlasLayout, type AtlasPlacement, type AtlasSource, CaptureGridKeyform, DEFAULT_TURN_TARGETS, type DeformerTransformChannel, DeleteDeformer, DeletePart, DeletePhysicsRig, type EditCommand, type EditTransformChannel, EditorDocument, type LayerInput, SetDeformerBindings, SetDeformerParent, SetDeformerPivot, SetDeformerPivotX, SetDeformerPivotY, SetDeformerTransform, SetPartBindings, SetPartColor, SetPartDeformer, SetPartHeight, SetPartMesh, SetPartOrder, SetPartTransform, SetPartWidth, SetPhysicsRig, type TurnDepths, type TurnSolveReport, TurnTargetError, type TurnTargets, UV_INSET_PX, captureBindingEndpoint, computeGridOffsets, createDefaultMatrixDeformer, createDefaultPart, createDefaultWarpDeformer, createGridMesh, detectAlphaBbox, generateIkiFromLayerSet, interpolateGridOffsets, packAtlas, parseLayerRoles, upsertGridKeyform, uvRectFor, validateDeformerDelete, validateDeformerReparent, validatePartAttach };
|
package/dist/index.d.ts
CHANGED
|
@@ -875,6 +875,19 @@ interface LayerInput {
|
|
|
875
875
|
cropW: number;
|
|
876
876
|
/** Cropped image height = bbox.h. */
|
|
877
877
|
cropH: number;
|
|
878
|
+
/**
|
|
879
|
+
* The layer's painted half-width on every CROP row, top → bottom, one entry
|
|
880
|
+
* per row (`cropH` of them): half the row's opaque span in canvas px under
|
|
881
|
+
* the same alpha ≥ 128 rule the head's `headEdges` are measured with
|
|
882
|
+
* (`@ikijs/mcp`'s `measure-turn`), 0 for a row with no opaque pixel.
|
|
883
|
+
* Optional, and only the FACE's is read: it gives the face plate a
|
|
884
|
+
* row-dependent turn radius and a chin swing (`faceRowProfile`,
|
|
885
|
+
* `turnSurface`); absent — as the editor's own import leaves it — the plate
|
|
886
|
+
* turns on one radius on every row. Validated before anything reads it: an
|
|
887
|
+
* array of length `cropH` whose every entry is a finite number in
|
|
888
|
+
* [0, cropW / 2], the most an inclusive span inside the crop can be.
|
|
889
|
+
*/
|
|
890
|
+
rowHalfWidths?: number[];
|
|
878
891
|
}
|
|
879
892
|
/**
|
|
880
893
|
* Map an array of raw filenames to canonical `{ role, fileName }` pairs.
|
|
@@ -891,29 +904,149 @@ declare function parseLayerRoles(fileNames: string[]): {
|
|
|
891
904
|
role: string;
|
|
892
905
|
fileName: string;
|
|
893
906
|
}[];
|
|
907
|
+
/**
|
|
908
|
+
* What a head does at full turn, as the cues `measure_turn_reference` reads off
|
|
909
|
+
* a front/turned reference pair. The turn is fitted to these — the cylinder's
|
|
910
|
+
* radius and the features' depths are solved from them, not tuned.
|
|
911
|
+
*
|
|
912
|
+
* Every field is optional: `DEFAULT_TURN_TARGETS` supplies the three a
|
|
913
|
+
* reference measures, and the nose and the mouth derive from the eyes when they
|
|
914
|
+
* are not given.
|
|
915
|
+
*
|
|
916
|
+
* What happens when the layer set cannot reach a target depends on where the
|
|
917
|
+
* target came from. A number the CALLER passed is a measurement, so an
|
|
918
|
+
* unreachable one throws, naming the field and what was on offer. A number this
|
|
919
|
+
* module filled in — a default, or a nose/mouth derived from the eyes — is a
|
|
920
|
+
* style prior, not a promise about this character: it is CLAMPED to what the
|
|
921
|
+
* layer set can do and the rig is built. The alternative is a generator that
|
|
922
|
+
* refuses its own defaults, which would leave a perfectly good layer set with
|
|
923
|
+
* no model at all. `solveTurnModel` reports which fields it clamped.
|
|
924
|
+
*/
|
|
925
|
+
interface TurnTargets {
|
|
926
|
+
/** How far the eye pair's centre slides toward the far side at full turn, as
|
|
927
|
+
* a fraction of the head's half-width. The sign is ignored: which way is
|
|
928
|
+
* the turn's own business. */
|
|
929
|
+
eyeShift?: number;
|
|
930
|
+
/** The far eye's width over the near eye's at full turn, divided by the same
|
|
931
|
+
* ratio at rest. 1 is no foreshortening; 0 is an edge-on far eye. */
|
|
932
|
+
farEyeRatio?: number;
|
|
933
|
+
/** The head's half-width at full turn over its half-width at rest, as a
|
|
934
|
+
* RENDER measures it — the outermost opaque edge on each side, whichever
|
|
935
|
+
* part owns it. 1 holds the silhouette; below 1 narrows it. The hold that
|
|
936
|
+
* produces it is fitted to it, so a measured one is met or refused, never
|
|
937
|
+
* quietly rendered as something else. */
|
|
938
|
+
silhouetteRatio?: number;
|
|
939
|
+
/** `eyeShift` for the nose. Derived from `eyeShift` when absent. */
|
|
940
|
+
noseShift?: number;
|
|
941
|
+
/** `eyeShift` for the mouth. Derived from `eyeShift` when absent. */
|
|
942
|
+
mouthShift?: number;
|
|
943
|
+
/** The head's own half-width at the eye row, in canvas px: what the shift
|
|
944
|
+
* fractions are fractions OF, and the rest distance the silhouette hold
|
|
945
|
+
* pivots on. Without it both fall back to the face plate's half-width, which
|
|
946
|
+
* is narrower than the head the hair draws, so the shifts land short — an
|
|
947
|
+
* approximation for callers that have no pixels, not a default to prefer. */
|
|
948
|
+
headHalfWidth?: number;
|
|
949
|
+
}
|
|
950
|
+
/**
|
|
951
|
+
* A `turnTargets` field this generator will not build a rig from: not a number,
|
|
952
|
+
* outside its range, unreachable on this layer set, or a measured head narrower
|
|
953
|
+
* than the face plate it is drawn around. Every one of them carries a number
|
|
954
|
+
* the CALLER passed, so a host can tell them apart from the invariant breaks the
|
|
955
|
+
* rest of the generator throws and report them as bad input instead of failing.
|
|
956
|
+
*/
|
|
957
|
+
declare class TurnTargetError extends Error {
|
|
958
|
+
constructor(message: string);
|
|
959
|
+
}
|
|
960
|
+
/**
|
|
961
|
+
* The reference the turn is fitted to when the caller measured none of its own:
|
|
962
|
+
* a 3/4 portrait, which `measure_turn_reference` reads as 0.671 / −0.224 /
|
|
963
|
+
* 1.012 (its raw turned far/near iris ratio is 0.71 — 0.67 is that ratio
|
|
964
|
+
* normalised by the same ratio at rest, which is what the field means).
|
|
965
|
+
* The silhouette is held exactly: a 1 % change is inside that tool's own noise.
|
|
966
|
+
*/
|
|
967
|
+
declare const DEFAULT_TURN_TARGETS: Readonly<Required<Pick<TurnTargets, "eyeShift" | "farEyeRatio" | "silhouetteRatio">>>;
|
|
968
|
+
/** The turn depth of each feature family: the share of the pinned bulk slide
|
|
969
|
+
* (`headTurnParallaxUnit`) it gets back at full turn. Solved, never tuned. */
|
|
970
|
+
interface TurnDepths {
|
|
971
|
+
eye: number;
|
|
972
|
+
nose: number;
|
|
973
|
+
mouth: number;
|
|
974
|
+
}
|
|
975
|
+
/** What the turn solve settled on, for a caller that wants to see it — the
|
|
976
|
+
* cues it reached and the targets it had to cut down to reach them. */
|
|
977
|
+
interface TurnSolveReport {
|
|
978
|
+
/** Cylinder radius the face-warp bake and the parallax unit were built on. */
|
|
979
|
+
radius: number;
|
|
980
|
+
/** Rest distance from the face centre the silhouette hold pivots on. */
|
|
981
|
+
holdBase: number;
|
|
982
|
+
depths: TurnDepths;
|
|
983
|
+
/** What the rig actually reaches, in the targets' own units. */
|
|
984
|
+
achieved: {
|
|
985
|
+
eyeShift: number;
|
|
986
|
+
farEyeRatio: number;
|
|
987
|
+
silhouetteRatio: number;
|
|
988
|
+
};
|
|
989
|
+
/** Defaulted fields the layer set could not reach, cut down to what it can.
|
|
990
|
+
* Empty when every target was met. */
|
|
991
|
+
clamped: (keyof TurnTargets)[];
|
|
992
|
+
}
|
|
894
993
|
/**
|
|
895
994
|
* Auto-rig: given decoded layer inputs and the shared canvas size, produce a
|
|
896
995
|
* valid IkiModel ready for parseIkiModel.
|
|
897
996
|
*
|
|
898
997
|
* - Validate all inputs before deriving anything.
|
|
899
998
|
* - Place parts at source-derived positions (bboxToTransform, unshifted).
|
|
900
|
-
* - Emit the standard parameters (
|
|
901
|
-
*
|
|
902
|
-
*
|
|
903
|
-
* -
|
|
999
|
+
* - Emit the standard parameters (STANDARD_PARAMETERS), plus a conditional
|
|
1000
|
+
* HairSwayX/Z pair + hair-sway physics rigs when a hair_front layer is
|
|
1001
|
+
* present.
|
|
1002
|
+
* - Size every turn group's grid to its own members and the virtual lattice
|
|
1003
|
+
* to those grids, then solve the turn on them (turnSetup, solveTurnModel):
|
|
1004
|
+
* one surface — radius, travel, the head-level nod radius — that every
|
|
1005
|
+
* bake below samples.
|
|
1006
|
+
* - Build headDeformer (matrix, neck pivot, nod/tilt/breath bindings — the
|
|
1007
|
+
* turn translates nothing rigidly; its travel is in the group grids), one
|
|
1008
|
+
* warp deformer per turn group present — `faceWarp` for the plate,
|
|
1009
|
+
* `eyeWarp_L/R`, `mouthWarp`, `noseWarp`, `browWarp_L/R`, `blushWarp_L/R`
|
|
1010
|
+
* — each a warp2d over its own grid baked from that surface with the
|
|
1011
|
+
* family's solved depth as keyform geometry (bakeTurnGroupWarp2D), and
|
|
904
1012
|
* bodyDeformer when a body layer is present (matrix, torso-base pivot,
|
|
905
|
-
* AngleX follow + Breath follow)
|
|
906
|
-
* baked cylinder warp center-relative on faceCenterX).
|
|
1013
|
+
* AngleX follow + Breath follow).
|
|
907
1014
|
* - Mesh parts (spec.mesh===true) → width:1, height:1, pixel grid mesh sized by
|
|
908
|
-
* meshCellsFor + role bindings.
|
|
1015
|
+
* meshCellsFor + role bindings, each face-family part on its group's warp.
|
|
909
1016
|
* - Static parts (spec.mesh===false) → width:cropW, height:cropH, no mesh.
|
|
910
1017
|
* - Part ids equal the role string (deterministic, no crypto.randomUUID).
|
|
911
1018
|
* - Return parseIkiModel(structuredClone(model)) — every caller gets a
|
|
912
1019
|
* validated model; bad assembly fails loudly.
|
|
1020
|
+
*
|
|
1021
|
+
* `options.turnTargets` is what the head turn is fitted to: the cues a 30°
|
|
1022
|
+
* reference measures, defaulting to DEFAULT_TURN_TARGETS. A target the CALLER
|
|
1023
|
+
* passed that this layer set cannot reach throws; one that came from the
|
|
1024
|
+
* defaults is clamped to what it can do — see TurnTargets for why the two
|
|
1025
|
+
* differ, and pass `options.onTurnSolved` to see which ones were clamped and
|
|
1026
|
+
* what the turn ended up reaching.
|
|
913
1027
|
*/
|
|
914
1028
|
declare function generateIkiFromLayerSet(layers: LayerInput[], canvas: {
|
|
915
1029
|
width: number;
|
|
916
1030
|
height: number;
|
|
1031
|
+
}, options?: {
|
|
1032
|
+
turnTargets?: TurnTargets;
|
|
1033
|
+
/** Called once, after a turn is solved, with what it settled on. Not called
|
|
1034
|
+
* for a layer set without a nose, which solves no turn at all. */
|
|
1035
|
+
onTurnSolved?: (report: TurnSolveReport) => void;
|
|
1036
|
+
/** Every role with an opaque pixel in the eye-row band, per side, when
|
|
1037
|
+
* `turnTargets.headHalfWidth` was measured off that same union — a
|
|
1038
|
+
* companion to it, not a turn target itself (the caller-facing contract
|
|
1039
|
+
* stays the three cues). See `TurnSolveContext.headEdges`. */
|
|
1040
|
+
headEdges?: {
|
|
1041
|
+
left: {
|
|
1042
|
+
role: string;
|
|
1043
|
+
x: number;
|
|
1044
|
+
}[];
|
|
1045
|
+
right: {
|
|
1046
|
+
role: string;
|
|
1047
|
+
x: number;
|
|
1048
|
+
}[];
|
|
1049
|
+
};
|
|
917
1050
|
}): IkiModel;
|
|
918
1051
|
|
|
919
|
-
export { ALPHA_BBOX_THRESHOLD, ATLAS_PADDING, AddDeformer, AddPart, AddPhysicsRig, type AlphaBbox, type ApplyAtlasInput, type AtlasAssignment, type AtlasLayout, type AtlasPlacement, type AtlasSource, CaptureGridKeyform, type DeformerTransformChannel, DeleteDeformer, DeletePart, DeletePhysicsRig, type EditCommand, type EditTransformChannel, EditorDocument, type LayerInput, SetDeformerBindings, SetDeformerParent, SetDeformerPivot, SetDeformerPivotX, SetDeformerPivotY, SetDeformerTransform, SetPartBindings, SetPartColor, SetPartDeformer, SetPartHeight, SetPartMesh, SetPartOrder, SetPartTransform, SetPartWidth, SetPhysicsRig, UV_INSET_PX, captureBindingEndpoint, computeGridOffsets, createDefaultMatrixDeformer, createDefaultPart, createDefaultWarpDeformer, createGridMesh, detectAlphaBbox, generateIkiFromLayerSet, interpolateGridOffsets, packAtlas, parseLayerRoles, upsertGridKeyform, uvRectFor, validateDeformerDelete, validateDeformerReparent, validatePartAttach };
|
|
1052
|
+
export { ALPHA_BBOX_THRESHOLD, ATLAS_PADDING, AddDeformer, AddPart, AddPhysicsRig, type AlphaBbox, type ApplyAtlasInput, type AtlasAssignment, type AtlasLayout, type AtlasPlacement, type AtlasSource, CaptureGridKeyform, DEFAULT_TURN_TARGETS, type DeformerTransformChannel, DeleteDeformer, DeletePart, DeletePhysicsRig, type EditCommand, type EditTransformChannel, EditorDocument, type LayerInput, SetDeformerBindings, SetDeformerParent, SetDeformerPivot, SetDeformerPivotX, SetDeformerPivotY, SetDeformerTransform, SetPartBindings, SetPartColor, SetPartDeformer, SetPartHeight, SetPartMesh, SetPartOrder, SetPartTransform, SetPartWidth, SetPhysicsRig, type TurnDepths, type TurnSolveReport, TurnTargetError, type TurnTargets, UV_INSET_PX, captureBindingEndpoint, computeGridOffsets, createDefaultMatrixDeformer, createDefaultPart, createDefaultWarpDeformer, createGridMesh, detectAlphaBbox, generateIkiFromLayerSet, interpolateGridOffsets, packAtlas, parseLayerRoles, upsertGridKeyform, uvRectFor, validateDeformerDelete, validateDeformerReparent, validatePartAttach };
|