@ikijs/editor 0.8.1 → 0.10.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 +80 -67
- package/dist/index.d.mts +403 -339
- package/dist/index.d.ts +403 -339
- package/dist/index.js +2508 -2548
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2508 -2554
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -656,6 +656,408 @@ interface AlphaBbox {
|
|
|
656
656
|
*/
|
|
657
657
|
declare function detectAlphaBbox(rgba: ArrayLike<number>, width: number, height: number): AlphaBbox | null;
|
|
658
658
|
|
|
659
|
+
/**
|
|
660
|
+
* Per-character tuning, starting from the profile: only the quantities the
|
|
661
|
+
* Live2D samples themselves disagree on by more than their own median
|
|
662
|
+
* (style, not construction). `turn`, `featureLead` and `sway` multiply the
|
|
663
|
+
* profile (1 = as measured); `hairFollow`, `outlineFollow` and `blink`
|
|
664
|
+
* replace its value.
|
|
665
|
+
*/
|
|
666
|
+
interface RigStyle {
|
|
667
|
+
/** The whole turn — plate, features and hair (the samples' face slides
|
|
668
|
+
* 0.05–0.25 hh). Ignored when a `turnTargets.eyeShift` is given, which
|
|
669
|
+
* fits it instead. */
|
|
670
|
+
turn?: number;
|
|
671
|
+
/** How far the features lead the plate (the nose leads it 2.2–4.4×). */
|
|
672
|
+
featureLead?: number;
|
|
673
|
+
/** The front hair's share of the face's turn (the samples' 1.0–1.37;
|
|
674
|
+
* profile 1.1). */
|
|
675
|
+
hairFollow?: number;
|
|
676
|
+
/** Its outer edge's share, where the front hair draws the head's outline
|
|
677
|
+
* (profile 0: the outline holds; `hairFollow` rides it whole). */
|
|
678
|
+
outlineFollow?: number;
|
|
679
|
+
/** How far the upper lid comes down, over the eye's height (0.30–0.67). */
|
|
680
|
+
blink?: number;
|
|
681
|
+
/** Hair sway amplitude (0.02–0.36 hh at the extremes). */
|
|
682
|
+
sway?: number;
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
/**
|
|
686
|
+
* The public shapes of the auto-rig: what a host hands `generateIkiFromLayerSet`
|
|
687
|
+
* and what the turn solve reports back. Pixel measurement lives in
|
|
688
|
+
* `layer-measure.ts`; everything here is already-decoded geometry.
|
|
689
|
+
*/
|
|
690
|
+
|
|
691
|
+
/** One role layer, as a host measured it off its decoded pixels. */
|
|
692
|
+
interface LayerInput {
|
|
693
|
+
/** Canonical role (see `parseLayerRoles`); becomes the part id. */
|
|
694
|
+
role: string;
|
|
695
|
+
fileName: string;
|
|
696
|
+
canvasW: number;
|
|
697
|
+
canvasH: number;
|
|
698
|
+
/** Alpha bounding box in canvas px (y down) — the crop the atlas holds. */
|
|
699
|
+
bbox: {
|
|
700
|
+
x: number;
|
|
701
|
+
y: number;
|
|
702
|
+
w: number;
|
|
703
|
+
h: number;
|
|
704
|
+
};
|
|
705
|
+
cropW: number;
|
|
706
|
+
cropH: number;
|
|
707
|
+
/** Face only: half the opaque span of each crop row, canvas px (0 = none). */
|
|
708
|
+
rowHalfWidths?: number[];
|
|
709
|
+
/** Nose only: the tight box of its alpha ≥ 128 pixels, canvas px. */
|
|
710
|
+
denseCore?: {
|
|
711
|
+
x: number;
|
|
712
|
+
y: number;
|
|
713
|
+
w: number;
|
|
714
|
+
h: number;
|
|
715
|
+
};
|
|
716
|
+
/** Face only: per crop column, the canvas row of the last pixel of the
|
|
717
|
+
* first dark stroke met going down from the plate's widest row (the jaw's
|
|
718
|
+
* outline, across a neck), or −1 where the column leaves the paint first.
|
|
719
|
+
* Where the head ends and the neck the body keeps begins. */
|
|
720
|
+
jawRows?: number[];
|
|
721
|
+
}
|
|
722
|
+
/**
|
|
723
|
+
* One side's iris against the `hair_front` run it would slide under on the
|
|
724
|
+
* turn, on the row holding the iris centre. Model coordinates: x is canvas x
|
|
725
|
+
* minus half the canvas width, y is half the canvas height minus canvas y.
|
|
726
|
+
*/
|
|
727
|
+
interface IrisStrand {
|
|
728
|
+
y: number;
|
|
729
|
+
irisOuter: number;
|
|
730
|
+
irisInner: number;
|
|
731
|
+
runOuter: number;
|
|
732
|
+
/** The run's face-side end; `null` for a fringe spanning the face. */
|
|
733
|
+
runFace: number | null;
|
|
734
|
+
}
|
|
735
|
+
/**
|
|
736
|
+
* Head-turn cues at ±30°, as `measure_turn_reference` reads them off a front
|
|
737
|
+
* and a turned image. Shifts are fractions of the head half-width, taken as
|
|
738
|
+
* magnitudes: that tool signs them by the reference's turn direction.
|
|
739
|
+
*/
|
|
740
|
+
interface TurnTargets {
|
|
741
|
+
eyeShift?: number;
|
|
742
|
+
farEyeRatio?: number;
|
|
743
|
+
silhouetteRatio?: number;
|
|
744
|
+
noseShift?: number;
|
|
745
|
+
mouthShift?: number;
|
|
746
|
+
/** The head half-width the shifts are fractions of, canvas px. */
|
|
747
|
+
headHalfWidth?: number;
|
|
748
|
+
}
|
|
749
|
+
/** A turn target this layer set cannot reach, or turn input that is malformed. */
|
|
750
|
+
declare class TurnTargetError extends Error {
|
|
751
|
+
constructor(message: string);
|
|
752
|
+
}
|
|
753
|
+
/**
|
|
754
|
+
* The Live2D profile's own turn (`profile.ts`), in the cues' units — what a
|
|
755
|
+
* rig with no `turnTargets` renders on a head whose silhouette at the eye row
|
|
756
|
+
* is back hair about 1 hh (eye row → chin) wide. eyeShift: the eye pair moves
|
|
757
|
+
* 0.211 hh at ±30 and that silhouette's centre −0.009 hh, over a 1 hh head
|
|
758
|
+
* half-width, 0.22. farEyeRatio: the far eye narrows to 0.85 and the near one
|
|
759
|
+
* widens to 1.085, and the cue is their ratio, 0.78. silhouetteRatio: the
|
|
760
|
+
* silhouette holds its width.
|
|
761
|
+
*
|
|
762
|
+
* With no target given the rig renders the profile itself, whatever the cues
|
|
763
|
+
* then read on its art (a head whose side locks draw its silhouette, and ride
|
|
764
|
+
* the face, reads a smaller eyeShift); a given target is fitted.
|
|
765
|
+
*/
|
|
766
|
+
declare const DEFAULT_TURN_TARGETS: Readonly<Required<Pick<TurnTargets, "eyeShift" | "farEyeRatio" | "silhouetteRatio">>>;
|
|
767
|
+
/** How far each feature family stands in front of the plate, as the depth a
|
|
768
|
+
* rotation would need to give its shift at ±30 (shift / sin 30°), in head
|
|
769
|
+
* half-widths — the nose leads the mouth and the mouth the eyes. */
|
|
770
|
+
interface TurnDepths {
|
|
771
|
+
eye: number;
|
|
772
|
+
nose: number;
|
|
773
|
+
mouth: number;
|
|
774
|
+
}
|
|
775
|
+
/** How much of a far iris's painted row the bangs' run covers. */
|
|
776
|
+
interface StrandOverlap {
|
|
777
|
+
/** The turn stop (AngleX, degrees) where the coverage is largest. */
|
|
778
|
+
deg: number;
|
|
779
|
+
/** Covered width there, canvas px. */
|
|
780
|
+
px: number;
|
|
781
|
+
/** The same in head half-widths. */
|
|
782
|
+
hh: number;
|
|
783
|
+
/** Covered width at rest. */
|
|
784
|
+
restPx: number;
|
|
785
|
+
/** Whether the iris went no deeper under the run than it is painted.
|
|
786
|
+
* Always `false` for a fringe spanning the face (`runFace: null`), which
|
|
787
|
+
* has no side to keep the iris clear of. */
|
|
788
|
+
held: boolean;
|
|
789
|
+
}
|
|
790
|
+
interface TurnSolveReport {
|
|
791
|
+
/** The curvature the plate would need to put the eyes as far in front of
|
|
792
|
+
* its edge as their lead over it does, as a radius, canvas px. */
|
|
793
|
+
radius: number;
|
|
794
|
+
/** The measured head half-width, canvas px, which the shift cues are
|
|
795
|
+
* fractions of — on a set with none measured (hairless) the face crop's
|
|
796
|
+
* half-width, while the cues are then fractions of the plate's painted
|
|
797
|
+
* half-width at the eye row. */
|
|
798
|
+
holdBase: number;
|
|
799
|
+
depths: TurnDepths;
|
|
800
|
+
/** What the rig renders at ±30° (mean of both directions). */
|
|
801
|
+
achieved: {
|
|
802
|
+
eyeShift: number;
|
|
803
|
+
farEyeRatio: number;
|
|
804
|
+
silhouetteRatio: number;
|
|
805
|
+
};
|
|
806
|
+
/** Targets cut down to what this layer set can do. */
|
|
807
|
+
clamped: (keyof TurnTargets)[];
|
|
808
|
+
strandOverlap?: {
|
|
809
|
+
left?: StrandOverlap;
|
|
810
|
+
right?: StrandOverlap;
|
|
811
|
+
};
|
|
812
|
+
}
|
|
813
|
+
interface HeadEdges {
|
|
814
|
+
left: {
|
|
815
|
+
role: string;
|
|
816
|
+
x: number;
|
|
817
|
+
}[];
|
|
818
|
+
right: {
|
|
819
|
+
role: string;
|
|
820
|
+
x: number;
|
|
821
|
+
}[];
|
|
822
|
+
}
|
|
823
|
+
interface GenerateOptions {
|
|
824
|
+
turnTargets?: TurnTargets;
|
|
825
|
+
/** Per-character tuning from the profile's defaults (`profile.ts`). */
|
|
826
|
+
style?: RigStyle;
|
|
827
|
+
onTurnSolved?: (report: TurnSolveReport) => void;
|
|
828
|
+
headEdges?: HeadEdges;
|
|
829
|
+
strandEdges?: {
|
|
830
|
+
left?: IrisStrand;
|
|
831
|
+
right?: IrisStrand;
|
|
832
|
+
};
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
/**
|
|
836
|
+
* Map file names to roles. Throws on an unknown role, a role named twice, or
|
|
837
|
+
* a required role (`face`, `eye_L`, `eye_R`, `mouth`) missing.
|
|
838
|
+
*/
|
|
839
|
+
declare function parseLayerRoles(fileNames: string[]): {
|
|
840
|
+
role: string;
|
|
841
|
+
fileName: string;
|
|
842
|
+
}[];
|
|
843
|
+
|
|
844
|
+
/**
|
|
845
|
+
* `generateIkiFromLayerSet`: role layers in, a rigged `.iki` out.
|
|
846
|
+
*
|
|
847
|
+
* The rig's shape follows a Live2D default rig (`profile.ts`): a torso that
|
|
848
|
+
* only breathes; a head deformer that rolls about the chin and breathes; one
|
|
849
|
+
* small warp grid per feature (each eye, each brow, the nose, the mouth),
|
|
850
|
+
* translating and foreshortening it by its own lead over the plate; and the
|
|
851
|
+
* plate, the hair and the blush moved by per-vertex keyforms of their own —
|
|
852
|
+
* the plate as up to four islands of one drawing: a head that slides, a neck
|
|
853
|
+
* that stays and two ears that lag. Blink, gaze, brows, mouth, breath and
|
|
854
|
+
* hair sway are part warps and bindings under those.
|
|
855
|
+
*/
|
|
856
|
+
|
|
857
|
+
declare function generateIkiFromLayerSet(layers: LayerInput[], canvas: {
|
|
858
|
+
width: number;
|
|
859
|
+
height: number;
|
|
860
|
+
}, options?: GenerateOptions): IkiModel;
|
|
861
|
+
|
|
862
|
+
/**
|
|
863
|
+
* The alpha rules a layer set is measured by, and the pre-rig measurement of
|
|
864
|
+
* that set, in one DOM-free copy — the way `detectAlphaBbox` is one.
|
|
865
|
+
*
|
|
866
|
+
* Both auto-rig hosts — the editor app (canvas `getImageData`) and
|
|
867
|
+
* `@ikijs/mcp`'s `auto_rig_from_layers` (a `sharp` raw buffer) — hand
|
|
868
|
+
* `createLayerSetMeasurer` the RGBA they decoded and rig what it measures, and
|
|
869
|
+
* `@ikijs/mcp`'s other tools (`measure_turn_reference`, `measure_layers`, the
|
|
870
|
+
* composer) read the same rules. Two copies would have to be kept identical by
|
|
871
|
+
* hand, or the head a render measures back, or the nose core the composer
|
|
872
|
+
* placed, would part from the one the rig was fitted to. Only decoding differs
|
|
873
|
+
* between the hosts.
|
|
874
|
+
*/
|
|
875
|
+
|
|
876
|
+
/** Half-height of the row band the head span is taken over, in px. Fixed at
|
|
877
|
+
* ±10 rows rather than scaled to the image, and that is the convention: any
|
|
878
|
+
* other measurement of head width meant to be compared with the one
|
|
879
|
+
* `@ikijs/mcp`'s `measure_turn_reference` takes off a render — one taken off
|
|
880
|
+
* layer alpha, a hand script — has to span the same band. Shared by that tool
|
|
881
|
+
* and `createLayerSetMeasurer`, so the head it spans off the layers' alpha
|
|
882
|
+
* union spans this band too. */
|
|
883
|
+
declare const HEAD_BAND = 10;
|
|
884
|
+
/** Alpha at or above this is opaque. Shared by `@ikijs/mcp`'s
|
|
885
|
+
* `measure_turn_reference`, whose "alpha" foreground rule reads an engine
|
|
886
|
+
* render's silhouette by it, and by the layer-alpha silhouette
|
|
887
|
+
* `createLayerSetMeasurer` measures the head on, which has to follow that
|
|
888
|
+
* rule to be comparable with a render. */
|
|
889
|
+
declare const ALPHA_OPAQUE = 128;
|
|
890
|
+
/**
|
|
891
|
+
* A layer's dense core: the tight box of its pixels at alpha ≥ ALPHA_OPAQUE,
|
|
892
|
+
* not grown. A soft-alpha part (a nose drawn as a shaded bump) is mostly
|
|
893
|
+
* feather, and the core is the drawing a viewer reads inside it — `@ikijs/mcp`'s
|
|
894
|
+
* composer sizes and places the nose by it, and `createLayerSetMeasurer` reads
|
|
895
|
+
* the same core as the nose's `LayerInput.denseCore`, its turn landmark. It is
|
|
896
|
+
* not the crop: that stays the alpha ≥ 8 box grown by 1 px (`detectAlphaBbox`).
|
|
897
|
+
* `null` when no pixel reaches the threshold, a part painted wholly
|
|
898
|
+
* translucent. A core that is a speck of its part (`isSpeckCore`) is not the
|
|
899
|
+
* drawing either, and both readers fall back to the whole part, as they do for
|
|
900
|
+
* `null`.
|
|
901
|
+
*/
|
|
902
|
+
declare function denseCoreOf(rgba: ArrayLike<number>, width: number, height: number): {
|
|
903
|
+
x: number;
|
|
904
|
+
y: number;
|
|
905
|
+
w: number;
|
|
906
|
+
h: number;
|
|
907
|
+
} | null;
|
|
908
|
+
/**
|
|
909
|
+
* The fraction of its part's width, or of its height, under which a dense core
|
|
910
|
+
* is a speck (`isSpeckCore`). Measured core/part ratios, width / height: the
|
|
911
|
+
* hero's composed nose 0.72 / 0.76 (core 39×63 in a 54×83 crop), three
|
|
912
|
+
* generated shaded-bump noses (the plugin's 0.11 nose prompt) 0.75–0.85 /
|
|
913
|
+
* 0.79–0.91, `@ikijs/mcp`'s `writeSoftNose` test fixture 0.56 / 0.63 and its
|
|
914
|
+
* `writeSoftNoseLayers` 0.40 / 0.40, a nostril mark ≤ 0.1. So the line sits at
|
|
915
|
+
* least 1.6× under every real nose and fixture, and 2.5× over a speck. At the
|
|
916
|
+
* line, sizing by the core would scale the whole part to 4× its layout width,
|
|
917
|
+
* the largest blow-up `@ikijs/mcp`'s composer still accepts.
|
|
918
|
+
*/
|
|
919
|
+
declare const SPECK_CORE_FRACTION = 0.25;
|
|
920
|
+
/**
|
|
921
|
+
* Whether a dense core is a speck of its part: narrower than
|
|
922
|
+
* SPECK_CORE_FRACTION of the part's width, or shorter than that fraction of
|
|
923
|
+
* its height (strictly `<`). Either dimension counts, because the core does
|
|
924
|
+
* more than size a nose: its bottom row is the tip the composer places, and
|
|
925
|
+
* its top-centre the rig's tilt pivot, so a sliver misplaces the nose as
|
|
926
|
+
* surely as a speck mis-sizes it. A speck is a lone nostril mark or highlight,
|
|
927
|
+
* not the drawing, and every reader falls back to the whole part: `@ikijs/mcp`'s
|
|
928
|
+
* compose sizes and places its trimmed source part whole, and
|
|
929
|
+
* `createLayerSetMeasurer` hands the rig no core, so the crop stands in.
|
|
930
|
+
*
|
|
931
|
+
* The two apply it to different pixels — compose to the source part before
|
|
932
|
+
* resampling, the rig and `measure_layers` to the composed layer against its
|
|
933
|
+
* crop — so their verdicts can part, but only for a core within about a
|
|
934
|
+
* resampled pixel of the line, or one resampling pushes across ALPHA_OPAQUE.
|
|
935
|
+
* Compose's report carries its source verdict when that was a speck, and
|
|
936
|
+
* otherwise what the composed layer shows, so it warns of a parting either
|
|
937
|
+
* way; `measure_layers` reports the layer alone.
|
|
938
|
+
*/
|
|
939
|
+
declare function isSpeckCore(core: {
|
|
940
|
+
w: number;
|
|
941
|
+
h: number;
|
|
942
|
+
}, part: {
|
|
943
|
+
w: number;
|
|
944
|
+
h: number;
|
|
945
|
+
}): boolean;
|
|
946
|
+
/**
|
|
947
|
+
* Outermost set pixels of `mask` across the row band `rowLo..rowHi` (clamped
|
|
948
|
+
* to the image) — the silhouette span at that height. An empty band returns
|
|
949
|
+
* `left > right`, which every caller has to check for.
|
|
950
|
+
*
|
|
951
|
+
* Shared by `@ikijs/mcp`'s `measure_turn_reference`, which spans a render's
|
|
952
|
+
* head with it, and `createLayerSetMeasurer`, which spans the layers' alpha
|
|
953
|
+
* union: the two heads are compared, so they have to be spanned the same way.
|
|
954
|
+
*/
|
|
955
|
+
declare function foregroundSpan(mask: Uint8Array, width: number, height: number, rowLo: number, rowHi: number): {
|
|
956
|
+
left: number;
|
|
957
|
+
right: number;
|
|
958
|
+
};
|
|
959
|
+
/**
|
|
960
|
+
* Half of a silhouette span, in px. The span is INCLUSIVE — `left` and `right`
|
|
961
|
+
* are both foreground columns — so the head is `right - left + 1` px wide.
|
|
962
|
+
* Shared by `@ikijs/mcp`'s `measure_turn_reference` and
|
|
963
|
+
* `createLayerSetMeasurer`: the two head half-widths are compared against each
|
|
964
|
+
* other, so they cannot be halved differently.
|
|
965
|
+
*/
|
|
966
|
+
declare function headHalfOf(span: {
|
|
967
|
+
left: number;
|
|
968
|
+
right: number;
|
|
969
|
+
}): number;
|
|
970
|
+
/** One decoded layer, as a host hands it to `LayerSetMeasurer.add`. */
|
|
971
|
+
interface RgbaLayer {
|
|
972
|
+
/** Canonical role, e.g. "eye_L" — the host has run `parseLayerRoles` on
|
|
973
|
+
* the set already. */
|
|
974
|
+
role: string;
|
|
975
|
+
/** Original file name: the `LayerInput`'s own, and named in errors. */
|
|
976
|
+
fileName: string;
|
|
977
|
+
/** Straight-alpha RGBA, stride 4, `canvas.width × canvas.height` pixels —
|
|
978
|
+
* canvas `ImageData.data` or a `sharp` raw buffer alike. */
|
|
979
|
+
rgba: ArrayLike<number>;
|
|
980
|
+
}
|
|
981
|
+
/**
|
|
982
|
+
* Measures a layer set one decoded layer at a time, so a host holds one
|
|
983
|
+
* full-canvas RGBA buffer at once, however many layers the set has. Call
|
|
984
|
+
* `finish` once, after the last `add`: it hands back its own `layers` array,
|
|
985
|
+
* which a later `add` would change.
|
|
986
|
+
*/
|
|
987
|
+
interface LayerSetMeasurer {
|
|
988
|
+
/**
|
|
989
|
+
* Measure one layer while its `rgba` is in memory, keeping no reference to
|
|
990
|
+
* it: only the set's opaque union, this layer's per-row opaque extent and,
|
|
991
|
+
* for `hair_front`, its opaque mask outlive the call, so the host may drop
|
|
992
|
+
* `rgba` as soon as it returns. Returns the layer's `LayerInput` — its crop
|
|
993
|
+
* box, the face's `rowHalfWidths`, the nose's `denseCore` unless that is a
|
|
994
|
+
* speck of its crop (`isSpeckCore`) — or `null` for an empty layer, one with
|
|
995
|
+
* no pixel at or above `ALPHA_BBOX_THRESHOLD`, which it records nothing for:
|
|
996
|
+
* the host reports that with its own error. Throws, naming the file, when
|
|
997
|
+
* `rgba` is not `width × height × 4` long; checking the decoded dimensions
|
|
998
|
+
* is the host's job.
|
|
999
|
+
*/
|
|
1000
|
+
add(layer: RgbaLayer): LayerInput | null;
|
|
1001
|
+
/** The set's measurement, over every layer `add` recorded. The set must be
|
|
1002
|
+
* one `parseLayerRoles` accepted; otherwise this may throw, or measure a
|
|
1003
|
+
* partial set the generator then refuses. */
|
|
1004
|
+
finish(): LayerSetMeasurement;
|
|
1005
|
+
}
|
|
1006
|
+
interface LayerSetMeasurement {
|
|
1007
|
+
/** Every recorded layer's `LayerInput`, in `add` order. */
|
|
1008
|
+
layers: LayerInput[];
|
|
1009
|
+
/** The head's half-width at the eye row, canvas px: half the opaque
|
|
1010
|
+
* (alpha ≥ ALPHA_OPAQUE) union's span over ±HEAD_BAND rows about the row
|
|
1011
|
+
* of the iris centres, or of the eye centres without irises. Absent when
|
|
1012
|
+
* the union has no span there — a layer set painted translucent below that
|
|
1013
|
+
* threshold. */
|
|
1014
|
+
headHalfWidth?: number;
|
|
1015
|
+
/** Whether `headHalfWidth` is wider than the face plate, and so applied;
|
|
1016
|
+
* the face plate stands in for it otherwise, as the generator's own
|
|
1017
|
+
* fallback. */
|
|
1018
|
+
headHalfWidthApplied: boolean;
|
|
1019
|
+
/**
|
|
1020
|
+
* Ready to go into `generateIkiFromLayerSet`'s options, each key present
|
|
1021
|
+
* only when measured: `turnTargets.headHalfWidth` when it is applied (merge
|
|
1022
|
+
* the caller's own targets under it), `headEdges` — every layer's own
|
|
1023
|
+
* opaque extent in the same eye-row band, per side, in model x — with it,
|
|
1024
|
+
* and `strandEdges` whenever the set has `hair_front`, `iris_L` and `iris_R`
|
|
1025
|
+
* and a side measures one (`IrisStrand`).
|
|
1026
|
+
*/
|
|
1027
|
+
turnOptions: {
|
|
1028
|
+
turnTargets?: {
|
|
1029
|
+
headHalfWidth: number;
|
|
1030
|
+
};
|
|
1031
|
+
headEdges?: {
|
|
1032
|
+
left: {
|
|
1033
|
+
role: string;
|
|
1034
|
+
x: number;
|
|
1035
|
+
}[];
|
|
1036
|
+
right: {
|
|
1037
|
+
role: string;
|
|
1038
|
+
x: number;
|
|
1039
|
+
}[];
|
|
1040
|
+
};
|
|
1041
|
+
strandEdges?: {
|
|
1042
|
+
left?: IrisStrand;
|
|
1043
|
+
right?: IrisStrand;
|
|
1044
|
+
};
|
|
1045
|
+
};
|
|
1046
|
+
}
|
|
1047
|
+
/**
|
|
1048
|
+
* Start measuring a layer set on a `canvas`-sized canvas: the pass a host runs
|
|
1049
|
+
* between decoding its layers and `generateIkiFromLayerSet`. `add` each
|
|
1050
|
+
* decoded layer, then `finish`.
|
|
1051
|
+
*
|
|
1052
|
+
* Throws before allocating anything when `canvas.width` or `canvas.height` is
|
|
1053
|
+
* not a positive safe integer, or `width × height × 4` is not a safe integer.
|
|
1054
|
+
* How large a canvas to accept at all is each host's own limit.
|
|
1055
|
+
*/
|
|
1056
|
+
declare function createLayerSetMeasurer(canvas: {
|
|
1057
|
+
width: number;
|
|
1058
|
+
height: number;
|
|
1059
|
+
}): LayerSetMeasurer;
|
|
1060
|
+
|
|
659
1061
|
/**
|
|
660
1062
|
* Pure, binding-value logic for computing an endpoint (rest-to-posed delta or
|
|
661
1063
|
* ratio) when capturing a transform channel binding. No DOM, no @ikijs/engine —
|
|
@@ -842,342 +1244,4 @@ declare function createGridMesh(cols: number, rows: number): IkiMesh;
|
|
|
842
1244
|
*/
|
|
843
1245
|
declare function createDefaultWarpDeformer(model: IkiModel): IkiWarpDeformer;
|
|
844
1246
|
|
|
845
|
-
|
|
846
|
-
* Role table, role parsing, bbox→transform math, and model assembly for the
|
|
847
|
-
* AI auto-rig generator. All pure functions — no DOM, no canvas, no
|
|
848
|
-
* crypto.randomUUID.
|
|
849
|
-
*
|
|
850
|
-
* L/R = CHARACTER frame: *_L is the character's left = screen right.
|
|
851
|
-
*/
|
|
852
|
-
|
|
853
|
-
/**
|
|
854
|
-
* Input contract from the host app to this package's auto-rig functions.
|
|
855
|
-
* Passed in after the host has decoded PNGs, computed alpha bboxes, and
|
|
856
|
-
* mapped filenames to canonical roles.
|
|
857
|
-
*/
|
|
858
|
-
interface LayerInput {
|
|
859
|
-
/** Canonical role, e.g. "eye_L". */
|
|
860
|
-
role: string;
|
|
861
|
-
/** Original file name — used in error messages and as a stable id. */
|
|
862
|
-
fileName: string;
|
|
863
|
-
/** Shared canvas width (all layers have the same canvas size). */
|
|
864
|
-
canvasW: number;
|
|
865
|
-
/** Shared canvas height. */
|
|
866
|
-
canvasH: number;
|
|
867
|
-
/** Alpha-tight bounding box, top-left origin, +y down (image coords). */
|
|
868
|
-
bbox: {
|
|
869
|
-
x: number;
|
|
870
|
-
y: number;
|
|
871
|
-
w: number;
|
|
872
|
-
h: number;
|
|
873
|
-
};
|
|
874
|
-
/** Cropped image width = bbox.w. */
|
|
875
|
-
cropW: number;
|
|
876
|
-
/** Cropped image height = bbox.h. */
|
|
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[];
|
|
891
|
-
/**
|
|
892
|
-
* The tight box of the layer's alpha ≥ 128 pixels, in image coords like
|
|
893
|
-
* `bbox` and NOT grown: the drawing a viewer reads inside a soft feather,
|
|
894
|
-
* which the crop's alpha ≥ 8 rule takes in whole. Optional, and only the
|
|
895
|
-
* NOSE's is read: its centre and width are the nose's turn landmark
|
|
896
|
-
* (`turnLandmarks`), so a shaded nose is fitted by the drawing rather than
|
|
897
|
-
* by its feather, and its top edge at its centre x is the bridge top the
|
|
898
|
-
* nose tilts about on the turn (`NOSE_TURN_TILT_DEG`). Absent — as the
|
|
899
|
-
* editor's own import leaves it — the crop stands in for both. Validated
|
|
900
|
-
* before anything reads it: a plain object whose x, y, w and h are finite,
|
|
901
|
-
* w and h positive, and the box inside `bbox`.
|
|
902
|
-
*/
|
|
903
|
-
denseCore?: {
|
|
904
|
-
x: number;
|
|
905
|
-
y: number;
|
|
906
|
-
w: number;
|
|
907
|
-
h: number;
|
|
908
|
-
};
|
|
909
|
-
}
|
|
910
|
-
/**
|
|
911
|
-
* Map an array of raw filenames to canonical `{ role, fileName }` pairs.
|
|
912
|
-
*
|
|
913
|
-
* Steps:
|
|
914
|
-
* 1. Normalize each filename → role (normalizeRole).
|
|
915
|
-
* 2. Eagerly check each role against ROLE_TABLE — unknown roles throw early
|
|
916
|
-
* with the offending fileName included in the message.
|
|
917
|
-
* 3. Call assertRoleSet to check duplicates + required roles.
|
|
918
|
-
*
|
|
919
|
-
* Throws a path-qualified Error on any contract violation.
|
|
920
|
-
*/
|
|
921
|
-
declare function parseLayerRoles(fileNames: string[]): {
|
|
922
|
-
role: string;
|
|
923
|
-
fileName: string;
|
|
924
|
-
}[];
|
|
925
|
-
/**
|
|
926
|
-
* One side's iris and the bangs' run it would slide under on the turn, as
|
|
927
|
-
* real pixel boundaries on ONE image row — the row holding that iris's centre
|
|
928
|
-
* — in model coordinates: x is canvas x minus half the canvas width, like
|
|
929
|
-
* `bboxToTransform`'s crop edges, and `y` is that row's centre. Every x is the
|
|
930
|
-
* painted edge facing its clear neighbour, read under one opacity rule, so the
|
|
931
|
-
* gap between an iris edge and a run edge is a real distance with no margin to
|
|
932
|
-
* add. "Outer" is toward the head's edge on that side. The run is the one a
|
|
933
|
-
* producer judges a strand: it may skip runs it judges hair detail
|
|
934
|
-
* (`@ikijs/mcp` treats those narrower than half the iris's painted width on
|
|
935
|
-
* the row as clear), so an iris under only such a wisp counts as clear.
|
|
936
|
-
*
|
|
937
|
-
* One shape covers three cases:
|
|
938
|
-
* - an ordinary side strand outward of a clear iris (`runFace` outward of
|
|
939
|
-
* `irisOuter`);
|
|
940
|
-
* - a run over the iris centre that clears on the face side (`runFace`
|
|
941
|
-
* inward of that centre);
|
|
942
|
-
* - a fringe spanning the face: the run still covers the OTHER iris's
|
|
943
|
-
* centre, so it has no face-side end on this side and `runFace` is
|
|
944
|
-
* `null` — an absent edge, not an invented one.
|
|
945
|
-
*/
|
|
946
|
-
interface IrisStrand {
|
|
947
|
-
/** The iris row's centre, model y. */
|
|
948
|
-
y: number;
|
|
949
|
-
/** The outer end of that iris layer's opaque span on the row. */
|
|
950
|
-
irisOuter: number;
|
|
951
|
-
/** Its inner end, toward the face. */
|
|
952
|
-
irisInner: number;
|
|
953
|
-
/** The outer end of the `hair_front` run the iris would slide under. */
|
|
954
|
-
runOuter: number;
|
|
955
|
-
/** The run's face-side end, or `null` when it reaches the other iris's
|
|
956
|
-
* centre. */
|
|
957
|
-
runFace: number | null;
|
|
958
|
-
}
|
|
959
|
-
/**
|
|
960
|
-
* What a head does at full turn, as the cues `measure_turn_reference` reads off
|
|
961
|
-
* a front/turned reference pair. The turn is fitted to these — the cylinder's
|
|
962
|
-
* radius and the features' depths are solved from them, not tuned.
|
|
963
|
-
*
|
|
964
|
-
* Every field is optional: `DEFAULT_TURN_TARGETS` supplies the three a
|
|
965
|
-
* reference measures, and the nose and the mouth derive from the eyes when they
|
|
966
|
-
* are not given.
|
|
967
|
-
*
|
|
968
|
-
* What happens when the layer set cannot reach a target depends on where the
|
|
969
|
-
* target came from. A number the CALLER passed is a measurement, so an
|
|
970
|
-
* unreachable one throws, naming the field and what was on offer — except a
|
|
971
|
-
* `silhouetteRatio` when no ratio in its own [0.5, 1.5] range renders on this
|
|
972
|
-
* layer set: that throw names the field and the whole range instead of an
|
|
973
|
-
* offer, since there is none narrower. Leaving the field out still rigs,
|
|
974
|
-
* because a defaulted one clamps.
|
|
975
|
-
*
|
|
976
|
-
* A number this module filled in — a default, or a nose/mouth derived from the
|
|
977
|
-
* eyes — is a style prior, not a promise about this character: it is CLAMPED
|
|
978
|
-
* to what the layer set can do and the rig is built. The alternative is a
|
|
979
|
-
* generator that refuses its own defaults, which would leave a perfectly good
|
|
980
|
-
* layer set with no model at all. `solveTurnModel` reports which fields it
|
|
981
|
-
* clamped.
|
|
982
|
-
*
|
|
983
|
-
* One measurement is clamped too: an `eyeShift` past the room the art leaves
|
|
984
|
-
* the far eye — the face plate's edge, and the bangs' side strand when
|
|
985
|
-
* `strandEdges` is given — whoever passed it. That room is a fact about this
|
|
986
|
-
* layer set, not about the reference the shift was measured on, so the rig
|
|
987
|
-
* takes as much of the shift as the art allows and keeps fitting the other
|
|
988
|
-
* cues. An `eyeShift` below the slide the face's own turn already gives the
|
|
989
|
-
* eyes is still refused: no depth reaches it.
|
|
990
|
-
*/
|
|
991
|
-
interface TurnTargets {
|
|
992
|
-
/** How far the eye pair's centre slides toward the far side at full turn, as
|
|
993
|
-
* a fraction of the head's half-width. The sign is ignored: which way is
|
|
994
|
-
* the turn's own business. */
|
|
995
|
-
eyeShift?: number;
|
|
996
|
-
/** The far eye's width over the near eye's at full turn, divided by the same
|
|
997
|
-
* ratio at rest. 1 is no foreshortening; 0 is an edge-on far eye. */
|
|
998
|
-
farEyeRatio?: number;
|
|
999
|
-
/** The head's half-width at full turn over its half-width at rest, as a
|
|
1000
|
-
* RENDER measures it — the outermost opaque edge on each side, whichever
|
|
1001
|
-
* part owns it. 1 holds the silhouette; below 1 narrows it. The hold that
|
|
1002
|
-
* produces it is fitted to it, so a measured one is met or refused, never
|
|
1003
|
-
* quietly rendered as something else. */
|
|
1004
|
-
silhouetteRatio?: number;
|
|
1005
|
-
/** `eyeShift` for the nose. Derived from `eyeShift` when absent. */
|
|
1006
|
-
noseShift?: number;
|
|
1007
|
-
/** `eyeShift` for the mouth. Derived from `eyeShift` when absent. */
|
|
1008
|
-
mouthShift?: number;
|
|
1009
|
-
/** The head's own half-width at the eye row, in canvas px: what the shift
|
|
1010
|
-
* fractions are fractions OF, and the rest distance the silhouette hold
|
|
1011
|
-
* pivots on. Without it both fall back to the face plate's half-width, which
|
|
1012
|
-
* is narrower than the head the hair draws, so the shifts land short — an
|
|
1013
|
-
* approximation for callers that have no pixels, not a default to prefer. */
|
|
1014
|
-
headHalfWidth?: number;
|
|
1015
|
-
}
|
|
1016
|
-
/**
|
|
1017
|
-
* A `turnTargets` field this generator will not build a rig from: not a number,
|
|
1018
|
-
* outside its range, unreachable on this layer set, or a measured head narrower
|
|
1019
|
-
* than the face plate it is drawn around. Every one of them carries a number
|
|
1020
|
-
* the CALLER passed, so a host can tell them apart from the invariant breaks the
|
|
1021
|
-
* rest of the generator throws and report them as bad input instead of failing.
|
|
1022
|
-
*/
|
|
1023
|
-
declare class TurnTargetError extends Error {
|
|
1024
|
-
constructor(message: string);
|
|
1025
|
-
}
|
|
1026
|
-
/**
|
|
1027
|
-
* The reference the turn is fitted to when the caller measured none of its own:
|
|
1028
|
-
* a 3/4 portrait, which `measure_turn_reference` reads as 0.671 / −0.224 /
|
|
1029
|
-
* 1.012 (its raw turned far/near iris ratio is 0.71 — 0.67 is that ratio
|
|
1030
|
-
* normalised by the same ratio at rest, which is what the field means).
|
|
1031
|
-
* The silhouette is held exactly: a 1 % change is inside that tool's own noise.
|
|
1032
|
-
*/
|
|
1033
|
-
declare const DEFAULT_TURN_TARGETS: Readonly<Required<Pick<TurnTargets, "eyeShift" | "farEyeRatio" | "silhouetteRatio">>>;
|
|
1034
|
-
/** The turn depth of each feature family: the share of the pinned bulk slide
|
|
1035
|
-
* (`headTurnParallaxUnit`) it gets back at full turn. Solved, never tuned. */
|
|
1036
|
-
interface TurnDepths {
|
|
1037
|
-
eye: number;
|
|
1038
|
-
nose: number;
|
|
1039
|
-
mouth: number;
|
|
1040
|
-
}
|
|
1041
|
-
/** What the turn solve settled on, for a caller that wants to see it — the
|
|
1042
|
-
* cues it reached and the targets it had to cut down to reach them. */
|
|
1043
|
-
interface TurnSolveReport {
|
|
1044
|
-
/** Cylinder radius the face-warp bake and the parallax unit were built on. */
|
|
1045
|
-
radius: number;
|
|
1046
|
-
/** Rest distance from the face centre the silhouette hold pivots on. */
|
|
1047
|
-
holdBase: number;
|
|
1048
|
-
depths: TurnDepths;
|
|
1049
|
-
/** What the rig actually reaches, in the targets' own units. */
|
|
1050
|
-
achieved: {
|
|
1051
|
-
eyeShift: number;
|
|
1052
|
-
farEyeRatio: number;
|
|
1053
|
-
silhouetteRatio: number;
|
|
1054
|
-
};
|
|
1055
|
-
/** The targets the rig fell short of, cut down to what it can reach: every
|
|
1056
|
-
* defaulted field it could not reach, and an `eyeShift` — the caller's own
|
|
1057
|
-
* too — past the room the art leaves the far eye (the face plate's edge,
|
|
1058
|
-
* and the bangs' strand when `strandEdges` was given). Empty when every
|
|
1059
|
-
* target was met. */
|
|
1060
|
-
clamped: (keyof TurnTargets)[];
|
|
1061
|
-
/** Per side whose far iris the bangs' run covers at some far stop at the
|
|
1062
|
-
* fitted radius and depth (`options.strandEdges`): how much of it. Absent
|
|
1063
|
-
* when no side has an entry. Never a clamp: `clamped` still names an
|
|
1064
|
-
* `eyeShift` the strand bound cut short. */
|
|
1065
|
-
strandOverlap?: {
|
|
1066
|
-
left?: StrandOverlap;
|
|
1067
|
-
right?: StrandOverlap;
|
|
1068
|
-
};
|
|
1069
|
-
}
|
|
1070
|
-
/**
|
|
1071
|
-
* How much of one side's far iris the bangs' run covers: the length of the
|
|
1072
|
-
* landed iris span `[irisOuter, irisInner]` on its row that lies inside the
|
|
1073
|
-
* landed run `[runOuter, runFace]` — face-ward without end for a run with no
|
|
1074
|
-
* face-side edge (`runFace: null`) — at the far stop where that is largest.
|
|
1075
|
-
* Always read off real landed boundaries (`IrisStrand`).
|
|
1076
|
-
*/
|
|
1077
|
-
interface StrandOverlap {
|
|
1078
|
-
/** The far stop the covered width is largest at, degrees. */
|
|
1079
|
-
deg: number;
|
|
1080
|
-
/** That covered width, px. */
|
|
1081
|
-
px: number;
|
|
1082
|
-
/** `px` over the head half-width the shift cues are fractions of. */
|
|
1083
|
-
hh: number;
|
|
1084
|
-
/** The covered width as painted, at rest, px. */
|
|
1085
|
-
restPx: number;
|
|
1086
|
-
/** Whether this side's strand bound held at the fitted depth — the iris
|
|
1087
|
-
* kept no deeper under the run than it is painted, at every far stop. When
|
|
1088
|
-
* false the fitted radius could not hold it (always so for `runFace:
|
|
1089
|
-
* null`), the eyes' turn depth is 0, and `px` is the coverage that is
|
|
1090
|
-
* left. */
|
|
1091
|
-
held: boolean;
|
|
1092
|
-
}
|
|
1093
|
-
/**
|
|
1094
|
-
* Auto-rig: given decoded layer inputs and the shared canvas size, produce a
|
|
1095
|
-
* valid IkiModel ready for parseIkiModel.
|
|
1096
|
-
*
|
|
1097
|
-
* - Validate all inputs before deriving anything.
|
|
1098
|
-
* - Place parts at source-derived positions (bboxToTransform, unshifted).
|
|
1099
|
-
* - Emit the standard parameters (STANDARD_PARAMETERS), plus a conditional
|
|
1100
|
-
* HairSwayX/Z pair + hair-sway physics rigs when a hair_front layer is
|
|
1101
|
-
* present.
|
|
1102
|
-
* - Size every turn group's grid to its own members and the virtual lattice
|
|
1103
|
-
* to those grids, then solve the turn on them (turnSetup, solveTurnModel):
|
|
1104
|
-
* one surface — radius, travel, the head-level nod radius — that every
|
|
1105
|
-
* bake below samples.
|
|
1106
|
-
* - Build headDeformer (matrix, neck pivot, nod/tilt/breath bindings — the
|
|
1107
|
-
* turn translates nothing rigidly; its travel is in the group grids), one
|
|
1108
|
-
* warp deformer per turn group present — `faceWarp` for the plate,
|
|
1109
|
-
* `eyeWarp_L/R`, `mouthWarp`, `noseWarp`, `browWarp_L/R`, `blushWarp_L/R`
|
|
1110
|
-
* — each a warp2d over its own grid baked from that surface with the
|
|
1111
|
-
* family's solved depth as keyform geometry (bakeTurnGroupWarp2D), and
|
|
1112
|
-
* bodyDeformer when a body layer is present (matrix, torso-base pivot,
|
|
1113
|
-
* AngleX follow + Breath follow).
|
|
1114
|
-
* - Mesh parts (spec.mesh===true) → width:1, height:1, pixel grid mesh sized by
|
|
1115
|
-
* meshCellsFor + role bindings, each face-family part on its group's warp.
|
|
1116
|
-
* - Static parts (spec.mesh===false) → width:cropW, height:cropH, no mesh.
|
|
1117
|
-
* - Part ids equal the role string (deterministic, no crypto.randomUUID).
|
|
1118
|
-
* - Return parseIkiModel(structuredClone(model)) — every caller gets a
|
|
1119
|
-
* validated model; bad assembly fails loudly.
|
|
1120
|
-
*
|
|
1121
|
-
* `options.turnTargets` is what the head turn is fitted to: the cues a 30°
|
|
1122
|
-
* reference measures, defaulting to DEFAULT_TURN_TARGETS. A target the CALLER
|
|
1123
|
-
* passed that this layer set cannot reach throws; one that came from the
|
|
1124
|
-
* defaults is clamped to what it can do, and so is a caller's `eyeShift` past
|
|
1125
|
-
* the room the art leaves the far eye — see TurnTargets for why, and pass
|
|
1126
|
-
* `options.onTurnSolved` to see which ones were clamped and what the turn
|
|
1127
|
-
* ended up reaching.
|
|
1128
|
-
*
|
|
1129
|
-
* `options.strandEdges` gives each side's iris and the bangs' run it would
|
|
1130
|
-
* slide under as measured pixel edges (`IrisStrand`), and with it the turn
|
|
1131
|
-
* keeps each far iris from sliding under that run any further than it is
|
|
1132
|
-
* painted, best effort: where no radius the fit can use holds that, the rig
|
|
1133
|
-
* is still built, with the eyes' depth at 0, and the report's
|
|
1134
|
-
* `strandOverlap` says how much of the iris the bangs cover.
|
|
1135
|
-
*/
|
|
1136
|
-
declare function generateIkiFromLayerSet(layers: LayerInput[], canvas: {
|
|
1137
|
-
width: number;
|
|
1138
|
-
height: number;
|
|
1139
|
-
}, options?: {
|
|
1140
|
-
turnTargets?: TurnTargets;
|
|
1141
|
-
/** Called once, after a turn is solved, with what it settled on. Not called
|
|
1142
|
-
* for a layer set without a nose, which solves no turn at all. */
|
|
1143
|
-
onTurnSolved?: (report: TurnSolveReport) => void;
|
|
1144
|
-
/** Every role with an opaque pixel in the eye-row band, per side, when
|
|
1145
|
-
* `turnTargets.headHalfWidth` was measured off that same union — a
|
|
1146
|
-
* companion to it, not a turn target itself (the caller-facing contract
|
|
1147
|
-
* stays the three cues). See `TurnSolveContext.headEdges`. */
|
|
1148
|
-
headEdges?: {
|
|
1149
|
-
left: {
|
|
1150
|
-
role: string;
|
|
1151
|
-
x: number;
|
|
1152
|
-
}[];
|
|
1153
|
-
right: {
|
|
1154
|
-
role: string;
|
|
1155
|
-
x: number;
|
|
1156
|
-
}[];
|
|
1157
|
-
};
|
|
1158
|
-
/** Per side (`left` the −x side, `right` the +x side, like `headEdges`),
|
|
1159
|
-
* that side's iris and the bangs' run it would slide under on the turn,
|
|
1160
|
-
* as real pixel edges on the row holding the iris's centre — see
|
|
1161
|
-
* `IrisStrand` for the contract and its three cases (a strand outward of
|
|
1162
|
-
* a clear iris, a run over the iris centre that clears on the face side,
|
|
1163
|
-
* a fringe spanning the face). A side's iris is whichever of `iris_L` /
|
|
1164
|
-
* `iris_R` sits on that side; the sides are independent, so either may
|
|
1165
|
-
* be absent. With it the turn keeps each far iris from sliding under its
|
|
1166
|
-
* run any further than it is painted, best effort: never a refusal. The
|
|
1167
|
-
* report's `strandOverlap` gives, per side whose far iris the run still
|
|
1168
|
-
* covers at some far stop, the covered width at the stop where it is
|
|
1169
|
-
* largest, next to the painted one, and whether the bound held — `true`
|
|
1170
|
-
* for an iris painted under its run that the turn took no deeper,
|
|
1171
|
-
* `false` where the fitted radius could not hold it and the eyes' turn
|
|
1172
|
-
* depth is 0. It is always
|
|
1173
|
-
* validated against the layers first (`validateStrandEdges`), but only
|
|
1174
|
-
* the turn solve reads it, so like `headEdges` it has no effect on the
|
|
1175
|
-
* rig without a nose; absent or `{}`, the rig is the one built without
|
|
1176
|
-
* it. */
|
|
1177
|
-
strandEdges?: {
|
|
1178
|
-
left?: IrisStrand;
|
|
1179
|
-
right?: IrisStrand;
|
|
1180
|
-
};
|
|
1181
|
-
}): IkiModel;
|
|
1182
|
-
|
|
1183
|
-
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 IrisStrand, type LayerInput, 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, captureBindingEndpoint, computeGridOffsets, createDefaultMatrixDeformer, createDefaultPart, createDefaultWarpDeformer, createGridMesh, detectAlphaBbox, generateIkiFromLayerSet, interpolateGridOffsets, packAtlas, parseLayerRoles, upsertGridKeyform, uvRectFor, validateDeformerDelete, validateDeformerReparent, validatePartAttach };
|
|
1247
|
+
export { ALPHA_BBOX_THRESHOLD, ALPHA_OPAQUE, 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, HEAD_BAND, type IrisStrand, 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, captureBindingEndpoint, computeGridOffsets, createDefaultMatrixDeformer, createDefaultPart, createDefaultWarpDeformer, createGridMesh, createLayerSetMeasurer, denseCoreOf, detectAlphaBbox, foregroundSpan, generateIkiFromLayerSet, headHalfOf, interpolateGridOffsets, isSpeckCore, packAtlas, parseLayerRoles, upsertGridKeyform, uvRectFor, validateDeformerDelete, validateDeformerReparent, validatePartAttach };
|