@ikijs/editor 0.26.0 → 0.28.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`, `mouthOpening`, `mouthFold`, `mouthRestShift`, `columnRuns`, `LIP_ROLES`, `LipRole`, `Opening`, `OpeningColumn`, `TurnTargets`, `DEFAULT_TURN_TARGETS`, `TurnSolveReport`, `RigStyle`, `RigPatch`, `PatchReport`, `PatchError`, `SHUT_BY`, `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`, `RigPlan`, `RigPlanError`, `RIG_PLAN_FIELDS`, `RigPlanReport`, `RigPatch`, `PatchReport`, `PatchError`, `SHUT_BY`, `LayerGeometryError` |
55
+ | Bindings | `captureBindingEndpoint` |
56
56
 
57
57
  ## Auto-rig
58
58
 
@@ -95,10 +95,10 @@ measures (how far the eye pair slides, how much the far eye foreshortens,
95
95
  whether the silhouette holds) — are **fitted** each by the one knob it reads:
96
96
  the turn's amount, the eyes' foreshortening, the hair's width, a feature's own
97
97
  shift. With none given the profile itself renders (`DEFAULT_TURN_TARGETS`
98
- expresses it in the cues' units). `options.style` tunes a character from the
99
- profile: the whole turn, the
98
+ expresses it in the cues' units). `options.rigPlan` tunes a character from the
99
+ profile in three groups (`turn`, `hair`, `eyes`: the whole turn, the
100
100
  features' lead, the front hair's follow and its outline's, the blink and the
101
- hair sway. `options.patches` adds displacements to a generated rig's keyform
101
+ hair sway), validated against `RIG_PLAN_FIELDS`. `options.patches` adds displacements to a generated rig's keyform
102
102
  stops (`RigPatch`), replayed on every rig and reported through
103
103
  `options.onPatchesApplied`; a refused op throws `PatchError`. The turn
104
104
  is bounded by the art: the far eye stays inside the plate's outline and, given
package/dist/index.d.mts CHANGED
@@ -658,34 +658,127 @@ interface AlphaBbox {
658
658
  */
659
659
  declare function detectAlphaBbox(rgba: ArrayLike<number>, width: number, height: number): AlphaBbox | null;
660
660
 
661
+ /** The lip set's roles, back to front (`mouth.ts`). */
662
+ declare const LIP_ROLES: readonly ["mouth_inner", "lip_lower", "lip_upper"];
663
+ type LipRole = (typeof LIP_ROLES)[number];
664
+ /** The lip set's optional inside parts, back to front: cut from the interior
665
+ * and clipped to `mouth_inner` (`mouth.ts`). */
666
+ declare const LIP_INSIDE_ROLES: readonly ["mouth_tongue", "mouth_teeth"];
667
+ type LipInsideRole = (typeof LIP_INSIDE_ROLES)[number];
668
+ declare const isLipInsideRole: (role: string) => role is LipInsideRole;
669
+ /**
670
+ * The parts the auto-rig flattens on purpose, each with the parameter that
671
+ * shuts it at 0: an eye's sclera and lower lid as ITS OWN lid closes, and the
672
+ * lip set with its tongue and teeth as the mouth shuts. A fold check exempts
673
+ * these closures; a patch may not land on them.
674
+ */
675
+ declare const SHUT_BY: ReadonlyMap<string, string>;
676
+ /** The part ids a `role`'s layer becomes, back to front: every one of them
677
+ * takes its texture from the layer's crop. Throws on an unknown role. */
678
+ declare function partIdsOfRole(role: string): readonly string[];
661
679
  /**
662
- * Per-character tuning, starting from the profile. `turn`, `featureLead` and
663
- * `sway` multiply the profile (1 = the profile); `hairFollow`,
664
- * `outlineFollow` and `blink` replace its value. `AUTO-RIG.md` lists the
665
- * ranges that read well.
680
+ * Map file names to roles. Throws on an unknown role, a role named twice, a
681
+ * required role (`face`, `eye_L`, `eye_R`) missing, no mouth (`mouth`, or the
682
+ * lip set) or a partial or mixed one, a lip set's inside part without the set,
683
+ * an arm without a body, or a pose forearm without its arm.
666
684
  */
667
- interface RigStyle {
668
- /** The whole turn — plate, features and hair. Ignored when a
669
- * `turnTargets.eyeShift` is given, which fits it instead. */
670
- turn?: number;
671
- /** How far the features lead the plate. */
672
- featureLead?: number;
673
- /** The front hair's share of the face's turn. */
674
- hairFollow?: number;
675
- /** Its outer edge's share, where the front hair draws the head's outline
676
- * and no back hair paints behind that edge (profile 0: the outline holds;
677
- * `hairFollow` rides it whole). Where back hair does, the edge rides
678
- * further, as far as that back hair stays behind it through the turn and
679
- * the nod, up to the front hair's follow there (`hairFollow`; over the
680
- * far eye's rows, as far as that eye's outer corner goes). On the cap
681
- * (the rows above the eyes) the edge follows less where the turn would
682
- * otherwise carry it further outside its back hair than the nod alone
683
- * does. */
684
- outlineFollow?: number;
685
- /** How far the upper lid comes down, over the eye's height. */
686
- blink?: number;
687
- /** Hair sway amplitude. */
688
- sway?: number;
685
+ declare function parseLayerRoles(fileNames: string[]): {
686
+ role: string;
687
+ fileName: string;
688
+ }[];
689
+
690
+ /**
691
+ * The rig plan: per-character tuning the auto-rig starts from the profile
692
+ * with, validated against one catalog. `RIG_PLAN_FIELDS` is the ONE source of
693
+ * the fields' defaults, ranges, recommended spans, steps and poses —
694
+ * `AUTO-RIG.md`, the MCP tool and the plugin point here.
695
+ */
696
+
697
+ /** Per-character tuning from the profile's defaults, validated against
698
+ * `RIG_PLAN_FIELDS`; a field not set is the profile's. */
699
+ interface RigPlan {
700
+ turn?: {
701
+ /** The whole turn — plate, features and hair. */
702
+ amount?: number;
703
+ /** How far the features lead the plate. */
704
+ featureLead?: number;
705
+ /** How far the eyes foreshorten as the head turns (turn only). */
706
+ eyeForeshorten?: number;
707
+ /** How far the nose leads the plate (turn only — the nod keeps the
708
+ * profile's nose depth). */
709
+ noseLead?: number;
710
+ };
711
+ hair?: {
712
+ /** The front hair's share of the face's turn. */
713
+ follow?: number;
714
+ /** Its outer edge's share, where the front hair draws the head's outline
715
+ * and no back hair paints behind that edge (0: the outline holds;
716
+ * `follow` rides it whole). Where back hair does, the edge rides
717
+ * further, as far as that back hair stays behind it through the turn and
718
+ * the nod, up to the front hair's follow there (over the far eye's rows,
719
+ * as far as that eye's outer corner goes). On the cap (the rows above
720
+ * the eyes) the edge follows less where the turn would otherwise carry
721
+ * it further outside its back hair than the nod alone does. */
722
+ outlineFollow?: number;
723
+ /** Hair sway amplitude. */
724
+ sway?: number;
725
+ };
726
+ eyes?: {
727
+ /** How far the upper lid comes down, over the eye's height. */
728
+ blink?: number;
729
+ };
730
+ }
731
+ /** A rig plan a caller passed that is malformed: a non-object, an unknown
732
+ * group or key, a non-number or a value out of its range. Its message
733
+ * starts with the field's path (`rigPlan.turn.amount …`). */
734
+ declare class RigPlanError extends Error {
735
+ constructor(message: string);
736
+ }
737
+ interface RigPlanField {
738
+ /** `group.key`, as a plan names it. */
739
+ path: string;
740
+ group: string;
741
+ key: string;
742
+ /** `multiplier`: a multiple of the profile's value (1 = the profile).
743
+ * `absolute`: replaces the profile's value. */
744
+ kind: "multiplier" | "absolute";
745
+ unit: string;
746
+ default: number;
747
+ /** What the rig accepts. */
748
+ range: [number, number];
749
+ /** Where a character's tuning stays: the span that still reads well. */
750
+ recommended: [number, number];
751
+ /** One step is a visible change on the turn renders. */
752
+ step: number;
753
+ /** Parameter settings where the field shows, as `render_poses` takes them. */
754
+ poses: Record<string, number>[];
755
+ /** The report reading that shows the field's effect, or null. */
756
+ cue: string | null;
757
+ /** The `turnTargets` cue that fits the field when given, or null. */
758
+ fitBy: string | null;
759
+ /** Whether the numbers are planner choices not yet picked by an A/B. */
760
+ provisional: boolean;
761
+ /** The removed `style` knob this field replaced, or null. */
762
+ migratedFrom: string | null;
763
+ what: string;
764
+ }
765
+ declare const RIG_PLAN_FIELDS: readonly RigPlanField[];
766
+ /** The message for a caller still passing the removed `style` input: the
767
+ * field map, built from the catalog. */
768
+ declare function styleRemovedMessage(where: string): string;
769
+ /** What the rig built with, next to what was asked. */
770
+ interface RigPlanReport {
771
+ /** The plan as passed (`{}` for none). */
772
+ requested: RigPlan;
773
+ /** Every catalog path → the value the rig built with. */
774
+ effective: Record<string, number>;
775
+ /** Plan-owned fields the art's room cut, with the value they stopped at. */
776
+ clamped: {
777
+ field: string;
778
+ bound: number;
779
+ }[];
780
+ /** Fields a given `turnTargets` cue fitted instead: path → cue. */
781
+ overriddenBy: Record<string, string>;
689
782
  }
690
783
 
691
784
  /**
@@ -913,8 +1006,14 @@ interface HeadEdges {
913
1006
  }
914
1007
  interface GenerateOptions {
915
1008
  turnTargets?: TurnTargets;
916
- /** Per-character tuning from the profile's defaults (`profile.ts`). */
917
- style?: RigStyle;
1009
+ /** Per-character tuning from the profile's defaults, validated against
1010
+ * `RIG_PLAN_FIELDS`; a bad field throws `RigPlanError`. */
1011
+ rigPlan?: RigPlan;
1012
+ /** Removed: pass `rigPlan`. A value here throws `RigPlanError`. */
1013
+ style?: never;
1014
+ /** What the rig built with, next to what `rigPlan` asked — called for every
1015
+ * model that validates, with or without a nose. */
1016
+ onRigPlanResolved?: (report: RigPlanReport) => void;
918
1017
  onTurnSolved?: (report: TurnSolveReport) => void;
919
1018
  /** Displacements added to the generated rig's stops (`RigPatch`), replayed
920
1019
  * on every rig; a refused op throws `PatchError` and rigs nothing. */
@@ -1064,35 +1163,6 @@ interface ForearmPoseGeometry {
1064
1163
  */
1065
1164
  declare function forearmPoseGeometry(layer: LayerInput): ForearmPoseGeometry;
1066
1165
 
1067
- /** The lip set's roles, back to front (`mouth.ts`). */
1068
- declare const LIP_ROLES: readonly ["mouth_inner", "lip_lower", "lip_upper"];
1069
- type LipRole = (typeof LIP_ROLES)[number];
1070
- /** The lip set's optional inside parts, back to front: cut from the interior
1071
- * and clipped to `mouth_inner` (`mouth.ts`). */
1072
- declare const LIP_INSIDE_ROLES: readonly ["mouth_tongue", "mouth_teeth"];
1073
- type LipInsideRole = (typeof LIP_INSIDE_ROLES)[number];
1074
- declare const isLipInsideRole: (role: string) => role is LipInsideRole;
1075
- /**
1076
- * The parts the auto-rig flattens on purpose, each with the parameter that
1077
- * shuts it at 0: an eye's sclera and lower lid as ITS OWN lid closes, and the
1078
- * lip set with its tongue and teeth as the mouth shuts. A fold check exempts
1079
- * these closures; a patch may not land on them.
1080
- */
1081
- declare const SHUT_BY: ReadonlyMap<string, string>;
1082
- /** The part ids a `role`'s layer becomes, back to front: every one of them
1083
- * takes its texture from the layer's crop. Throws on an unknown role. */
1084
- declare function partIdsOfRole(role: string): readonly string[];
1085
- /**
1086
- * Map file names to roles. Throws on an unknown role, a role named twice, a
1087
- * required role (`face`, `eye_L`, `eye_R`) missing, no mouth (`mouth`, or the
1088
- * lip set) or a partial or mixed one, a lip set's inside part without the set,
1089
- * an arm without a body, or a pose forearm without its arm.
1090
- */
1091
- declare function parseLayerRoles(fileNames: string[]): {
1092
- role: string;
1093
- fileName: string;
1094
- }[];
1095
-
1096
1166
  /**
1097
1167
  * The lip set: a mouth that folds open the way the eyelid folds shut. Three
1098
1168
  * parts, back to front: `mouth_inner` (the interior and the opening's lower
@@ -1692,4 +1762,4 @@ declare function createGridMesh(cols: number, rows: number): IkiMesh;
1692
1762
  */
1693
1763
  declare function createDefaultWarpDeformer(model: IkiModel): IkiWarpDeformer;
1694
1764
 
1695
- 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_INSIDE_ROLES, LIP_ROLES, LayerGeometryError, type LayerInput, type LayerSetMeasurement, type LayerSetMeasurer, type LipInsideRole, type LipRole, type Opening, type OpeningColumn, PatchError, type PatchReport, type RgbaLayer, type RigPatch, type RigStyle, SHUT_BY, 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, isLipInsideRole, isSpeckCore, mouthFold, mouthOpening, mouthRestShift, packAtlas, parseLayerRoles, partIdsOfRole, upsertGridKeyform, uvRectFor, validateDeformerDelete, validateDeformerReparent, validatePartAttach };
1765
+ 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_INSIDE_ROLES, LIP_ROLES, LayerGeometryError, type LayerInput, type LayerSetMeasurement, type LayerSetMeasurer, type LipInsideRole, type LipRole, type Opening, type OpeningColumn, PatchError, type PatchReport, RIG_PLAN_FIELDS, type RgbaLayer, type RigPatch, type RigPlan, RigPlanError, type RigPlanField, type RigPlanReport, SHUT_BY, 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, isLipInsideRole, isSpeckCore, mouthFold, mouthOpening, mouthRestShift, packAtlas, parseLayerRoles, partIdsOfRole, styleRemovedMessage, upsertGridKeyform, uvRectFor, validateDeformerDelete, validateDeformerReparent, validatePartAttach };
package/dist/index.d.ts CHANGED
@@ -658,34 +658,127 @@ interface AlphaBbox {
658
658
  */
659
659
  declare function detectAlphaBbox(rgba: ArrayLike<number>, width: number, height: number): AlphaBbox | null;
660
660
 
661
+ /** The lip set's roles, back to front (`mouth.ts`). */
662
+ declare const LIP_ROLES: readonly ["mouth_inner", "lip_lower", "lip_upper"];
663
+ type LipRole = (typeof LIP_ROLES)[number];
664
+ /** The lip set's optional inside parts, back to front: cut from the interior
665
+ * and clipped to `mouth_inner` (`mouth.ts`). */
666
+ declare const LIP_INSIDE_ROLES: readonly ["mouth_tongue", "mouth_teeth"];
667
+ type LipInsideRole = (typeof LIP_INSIDE_ROLES)[number];
668
+ declare const isLipInsideRole: (role: string) => role is LipInsideRole;
669
+ /**
670
+ * The parts the auto-rig flattens on purpose, each with the parameter that
671
+ * shuts it at 0: an eye's sclera and lower lid as ITS OWN lid closes, and the
672
+ * lip set with its tongue and teeth as the mouth shuts. A fold check exempts
673
+ * these closures; a patch may not land on them.
674
+ */
675
+ declare const SHUT_BY: ReadonlyMap<string, string>;
676
+ /** The part ids a `role`'s layer becomes, back to front: every one of them
677
+ * takes its texture from the layer's crop. Throws on an unknown role. */
678
+ declare function partIdsOfRole(role: string): readonly string[];
661
679
  /**
662
- * Per-character tuning, starting from the profile. `turn`, `featureLead` and
663
- * `sway` multiply the profile (1 = the profile); `hairFollow`,
664
- * `outlineFollow` and `blink` replace its value. `AUTO-RIG.md` lists the
665
- * ranges that read well.
680
+ * Map file names to roles. Throws on an unknown role, a role named twice, a
681
+ * required role (`face`, `eye_L`, `eye_R`) missing, no mouth (`mouth`, or the
682
+ * lip set) or a partial or mixed one, a lip set's inside part without the set,
683
+ * an arm without a body, or a pose forearm without its arm.
666
684
  */
667
- interface RigStyle {
668
- /** The whole turn — plate, features and hair. Ignored when a
669
- * `turnTargets.eyeShift` is given, which fits it instead. */
670
- turn?: number;
671
- /** How far the features lead the plate. */
672
- featureLead?: number;
673
- /** The front hair's share of the face's turn. */
674
- hairFollow?: number;
675
- /** Its outer edge's share, where the front hair draws the head's outline
676
- * and no back hair paints behind that edge (profile 0: the outline holds;
677
- * `hairFollow` rides it whole). Where back hair does, the edge rides
678
- * further, as far as that back hair stays behind it through the turn and
679
- * the nod, up to the front hair's follow there (`hairFollow`; over the
680
- * far eye's rows, as far as that eye's outer corner goes). On the cap
681
- * (the rows above the eyes) the edge follows less where the turn would
682
- * otherwise carry it further outside its back hair than the nod alone
683
- * does. */
684
- outlineFollow?: number;
685
- /** How far the upper lid comes down, over the eye's height. */
686
- blink?: number;
687
- /** Hair sway amplitude. */
688
- sway?: number;
685
+ declare function parseLayerRoles(fileNames: string[]): {
686
+ role: string;
687
+ fileName: string;
688
+ }[];
689
+
690
+ /**
691
+ * The rig plan: per-character tuning the auto-rig starts from the profile
692
+ * with, validated against one catalog. `RIG_PLAN_FIELDS` is the ONE source of
693
+ * the fields' defaults, ranges, recommended spans, steps and poses —
694
+ * `AUTO-RIG.md`, the MCP tool and the plugin point here.
695
+ */
696
+
697
+ /** Per-character tuning from the profile's defaults, validated against
698
+ * `RIG_PLAN_FIELDS`; a field not set is the profile's. */
699
+ interface RigPlan {
700
+ turn?: {
701
+ /** The whole turn — plate, features and hair. */
702
+ amount?: number;
703
+ /** How far the features lead the plate. */
704
+ featureLead?: number;
705
+ /** How far the eyes foreshorten as the head turns (turn only). */
706
+ eyeForeshorten?: number;
707
+ /** How far the nose leads the plate (turn only — the nod keeps the
708
+ * profile's nose depth). */
709
+ noseLead?: number;
710
+ };
711
+ hair?: {
712
+ /** The front hair's share of the face's turn. */
713
+ follow?: number;
714
+ /** Its outer edge's share, where the front hair draws the head's outline
715
+ * and no back hair paints behind that edge (0: the outline holds;
716
+ * `follow` rides it whole). Where back hair does, the edge rides
717
+ * further, as far as that back hair stays behind it through the turn and
718
+ * the nod, up to the front hair's follow there (over the far eye's rows,
719
+ * as far as that eye's outer corner goes). On the cap (the rows above
720
+ * the eyes) the edge follows less where the turn would otherwise carry
721
+ * it further outside its back hair than the nod alone does. */
722
+ outlineFollow?: number;
723
+ /** Hair sway amplitude. */
724
+ sway?: number;
725
+ };
726
+ eyes?: {
727
+ /** How far the upper lid comes down, over the eye's height. */
728
+ blink?: number;
729
+ };
730
+ }
731
+ /** A rig plan a caller passed that is malformed: a non-object, an unknown
732
+ * group or key, a non-number or a value out of its range. Its message
733
+ * starts with the field's path (`rigPlan.turn.amount …`). */
734
+ declare class RigPlanError extends Error {
735
+ constructor(message: string);
736
+ }
737
+ interface RigPlanField {
738
+ /** `group.key`, as a plan names it. */
739
+ path: string;
740
+ group: string;
741
+ key: string;
742
+ /** `multiplier`: a multiple of the profile's value (1 = the profile).
743
+ * `absolute`: replaces the profile's value. */
744
+ kind: "multiplier" | "absolute";
745
+ unit: string;
746
+ default: number;
747
+ /** What the rig accepts. */
748
+ range: [number, number];
749
+ /** Where a character's tuning stays: the span that still reads well. */
750
+ recommended: [number, number];
751
+ /** One step is a visible change on the turn renders. */
752
+ step: number;
753
+ /** Parameter settings where the field shows, as `render_poses` takes them. */
754
+ poses: Record<string, number>[];
755
+ /** The report reading that shows the field's effect, or null. */
756
+ cue: string | null;
757
+ /** The `turnTargets` cue that fits the field when given, or null. */
758
+ fitBy: string | null;
759
+ /** Whether the numbers are planner choices not yet picked by an A/B. */
760
+ provisional: boolean;
761
+ /** The removed `style` knob this field replaced, or null. */
762
+ migratedFrom: string | null;
763
+ what: string;
764
+ }
765
+ declare const RIG_PLAN_FIELDS: readonly RigPlanField[];
766
+ /** The message for a caller still passing the removed `style` input: the
767
+ * field map, built from the catalog. */
768
+ declare function styleRemovedMessage(where: string): string;
769
+ /** What the rig built with, next to what was asked. */
770
+ interface RigPlanReport {
771
+ /** The plan as passed (`{}` for none). */
772
+ requested: RigPlan;
773
+ /** Every catalog path → the value the rig built with. */
774
+ effective: Record<string, number>;
775
+ /** Plan-owned fields the art's room cut, with the value they stopped at. */
776
+ clamped: {
777
+ field: string;
778
+ bound: number;
779
+ }[];
780
+ /** Fields a given `turnTargets` cue fitted instead: path → cue. */
781
+ overriddenBy: Record<string, string>;
689
782
  }
690
783
 
691
784
  /**
@@ -913,8 +1006,14 @@ interface HeadEdges {
913
1006
  }
914
1007
  interface GenerateOptions {
915
1008
  turnTargets?: TurnTargets;
916
- /** Per-character tuning from the profile's defaults (`profile.ts`). */
917
- style?: RigStyle;
1009
+ /** Per-character tuning from the profile's defaults, validated against
1010
+ * `RIG_PLAN_FIELDS`; a bad field throws `RigPlanError`. */
1011
+ rigPlan?: RigPlan;
1012
+ /** Removed: pass `rigPlan`. A value here throws `RigPlanError`. */
1013
+ style?: never;
1014
+ /** What the rig built with, next to what `rigPlan` asked — called for every
1015
+ * model that validates, with or without a nose. */
1016
+ onRigPlanResolved?: (report: RigPlanReport) => void;
918
1017
  onTurnSolved?: (report: TurnSolveReport) => void;
919
1018
  /** Displacements added to the generated rig's stops (`RigPatch`), replayed
920
1019
  * on every rig; a refused op throws `PatchError` and rigs nothing. */
@@ -1064,35 +1163,6 @@ interface ForearmPoseGeometry {
1064
1163
  */
1065
1164
  declare function forearmPoseGeometry(layer: LayerInput): ForearmPoseGeometry;
1066
1165
 
1067
- /** The lip set's roles, back to front (`mouth.ts`). */
1068
- declare const LIP_ROLES: readonly ["mouth_inner", "lip_lower", "lip_upper"];
1069
- type LipRole = (typeof LIP_ROLES)[number];
1070
- /** The lip set's optional inside parts, back to front: cut from the interior
1071
- * and clipped to `mouth_inner` (`mouth.ts`). */
1072
- declare const LIP_INSIDE_ROLES: readonly ["mouth_tongue", "mouth_teeth"];
1073
- type LipInsideRole = (typeof LIP_INSIDE_ROLES)[number];
1074
- declare const isLipInsideRole: (role: string) => role is LipInsideRole;
1075
- /**
1076
- * The parts the auto-rig flattens on purpose, each with the parameter that
1077
- * shuts it at 0: an eye's sclera and lower lid as ITS OWN lid closes, and the
1078
- * lip set with its tongue and teeth as the mouth shuts. A fold check exempts
1079
- * these closures; a patch may not land on them.
1080
- */
1081
- declare const SHUT_BY: ReadonlyMap<string, string>;
1082
- /** The part ids a `role`'s layer becomes, back to front: every one of them
1083
- * takes its texture from the layer's crop. Throws on an unknown role. */
1084
- declare function partIdsOfRole(role: string): readonly string[];
1085
- /**
1086
- * Map file names to roles. Throws on an unknown role, a role named twice, a
1087
- * required role (`face`, `eye_L`, `eye_R`) missing, no mouth (`mouth`, or the
1088
- * lip set) or a partial or mixed one, a lip set's inside part without the set,
1089
- * an arm without a body, or a pose forearm without its arm.
1090
- */
1091
- declare function parseLayerRoles(fileNames: string[]): {
1092
- role: string;
1093
- fileName: string;
1094
- }[];
1095
-
1096
1166
  /**
1097
1167
  * The lip set: a mouth that folds open the way the eyelid folds shut. Three
1098
1168
  * parts, back to front: `mouth_inner` (the interior and the opening's lower
@@ -1692,4 +1762,4 @@ declare function createGridMesh(cols: number, rows: number): IkiMesh;
1692
1762
  */
1693
1763
  declare function createDefaultWarpDeformer(model: IkiModel): IkiWarpDeformer;
1694
1764
 
1695
- 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_INSIDE_ROLES, LIP_ROLES, LayerGeometryError, type LayerInput, type LayerSetMeasurement, type LayerSetMeasurer, type LipInsideRole, type LipRole, type Opening, type OpeningColumn, PatchError, type PatchReport, type RgbaLayer, type RigPatch, type RigStyle, SHUT_BY, 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, isLipInsideRole, isSpeckCore, mouthFold, mouthOpening, mouthRestShift, packAtlas, parseLayerRoles, partIdsOfRole, upsertGridKeyform, uvRectFor, validateDeformerDelete, validateDeformerReparent, validatePartAttach };
1765
+ 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_INSIDE_ROLES, LIP_ROLES, LayerGeometryError, type LayerInput, type LayerSetMeasurement, type LayerSetMeasurer, type LipInsideRole, type LipRole, type Opening, type OpeningColumn, PatchError, type PatchReport, RIG_PLAN_FIELDS, type RgbaLayer, type RigPatch, type RigPlan, RigPlanError, type RigPlanField, type RigPlanReport, SHUT_BY, 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, isLipInsideRole, isSpeckCore, mouthFold, mouthOpening, mouthRestShift, packAtlas, parseLayerRoles, partIdsOfRole, styleRemovedMessage, upsertGridKeyform, uvRectFor, validateDeformerDelete, validateDeformerReparent, validatePartAttach };