@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 CHANGED
@@ -38,71 +38,75 @@ leaves the document untouched.
38
38
 
39
39
  ## API
40
40
 
41
- | Area | Exports |
42
- | ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
43
- | Document | `EditorDocument`, `EditCommand` |
44
- | Part edits | `AddPart`, `DeletePart`, `SetPartColor`, `SetPartWidth`, `SetPartHeight`, `SetPartOrder`, `SetPartTransform`, `SetPartBindings`, `SetPartMesh`, `SetPartDeformer` |
45
- | Deformer edits | `AddDeformer`, `DeleteDeformer`, `SetDeformerParent`, `SetDeformerTransform`, `SetDeformerBindings`, `SetDeformerPivot` (+ `X`/`Y`), `CaptureGridKeyform` |
46
- | Physics edits | `AddPhysicsRig`, `SetPhysicsRig`, `DeletePhysicsRig` |
47
- | Referential guards | `validateDeformerReparent`, `validateDeformerDelete`, `validatePartAttach` |
48
- | Atlas | `packAtlas`, `uvRectFor`, `ATLAS_PADDING`, `UV_INSET_PX` |
49
- | Grid keyforms | `computeGridOffsets`, `interpolateGridOffsets`, `upsertGridKeyform` |
50
- | Factories | `createDefaultPart`, `createDefaultMatrixDeformer`, `createDefaultWarpDeformer`, `createGridMesh` |
51
- | Pixels | `detectAlphaBbox`, `ALPHA_BBOX_THRESHOLD`, `AlphaBbox` |
52
- | Auto-rig | `generateIkiFromLayerSet`, `parseLayerRoles`, `TurnTargets`, `DEFAULT_TURN_TARGETS`, `TurnSolveReport` |
53
- | Bindings | `captureBindingEndpoint` |
41
+ | Area | Exports |
42
+ | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
43
+ | Document | `EditorDocument`, `EditCommand` |
44
+ | Part edits | `AddPart`, `DeletePart`, `SetPartColor`, `SetPartWidth`, `SetPartHeight`, `SetPartOrder`, `SetPartTransform`, `SetPartBindings`, `SetPartMesh`, `SetPartDeformer` |
45
+ | Deformer edits | `AddDeformer`, `DeleteDeformer`, `SetDeformerParent`, `SetDeformerTransform`, `SetDeformerBindings`, `SetDeformerPivot` (+ `X`/`Y`), `CaptureGridKeyform` |
46
+ | Physics edits | `AddPhysicsRig`, `SetPhysicsRig`, `DeletePhysicsRig` |
47
+ | Referential guards | `validateDeformerReparent`, `validateDeformerDelete`, `validatePartAttach` |
48
+ | Atlas | `packAtlas`, `uvRectFor`, `ATLAS_PADDING`, `UV_INSET_PX` |
49
+ | Grid keyforms | `computeGridOffsets`, `interpolateGridOffsets`, `upsertGridKeyform` |
50
+ | Factories | `createDefaultPart`, `createDefaultMatrixDeformer`, `createDefaultWarpDeformer`, `createGridMesh` |
51
+ | Pixels | `detectAlphaBbox`, `ALPHA_BBOX_THRESHOLD`, `AlphaBbox`, `ALPHA_OPAQUE`, `HEAD_BAND`, `SPECK_CORE_FRACTION`, `denseCoreOf`, `isSpeckCore`, `foregroundSpan`, `headHalfOf` |
52
+ | Auto-rig | `generateIkiFromLayerSet`, `parseLayerRoles`, `createLayerSetMeasurer`, `TurnTargets`, `DEFAULT_TURN_TARGETS`, `TurnSolveReport`, `RigStyle` |
53
+ | Bindings | `captureBindingEndpoint` |
54
54
 
55
55
  ## Auto-rig
56
56
 
57
57
  `generateIkiFromLayerSet` turns role-named layers (`face`, `eye_L`, `eye_R`,
58
58
  `mouth`, plus optional `iris_*`, `brow_*`, `lash_*`, `hair_front`, `hair_back`,
59
- …) into a rigged model that blinks, gazes, opens its mouth, turns its head on a
60
- torso that follows and breathes, and emotes with its brows — including a
59
+ …) into a rigged model that blinks, gazes, opens its mouth, turns its head over
60
+ a torso that 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. Given `options.strandEdges`, the eyes are also
76
- kept from sliding the far iris under the bangs' side strand any further than it
77
- is painted — best effort, never a refusal: the fit prefers radii where some eye
78
- depth holds that, and where none does (a fringe spanning the face included) the
79
- rig is still built with the eyes' turn depth at 0. The report's `strandOverlap`
80
- gives, per side whose far iris the bangs' run still covers at some far stop,
81
- the covered width: how much of the iris's painted row the run covers at the
82
- stop where that is largest, in px and head half-widths, next to how much it
83
- covers at rest, and whether the bound held — `true` for an iris painted under
84
- its run that the turn took no deeper, `false` where the fitted radius could not
85
- hold it and the eyes' turn depth is 0.
86
-
87
- A target that came from the defaults is a style prior, not a measurement of
88
- this character, so it is **clamped** to what the layer set can do and the rig is
89
- built. So is an `eyeShift` you passed that runs past the room the art leaves the
90
- far eye — the face plate's edge, or the bangs' strand — since that room is a
91
- fact about these layers, not about the reference. Every other target you passed
92
- that this layer set cannot reach **throws**, naming the field and the range it
93
- could have had: a `farEyeRatio` or `silhouetteRatio` no radius renders, a
94
- `noseShift` / `mouthShift` outside what that feature can reach, or an
95
- `eyeShift` smaller than the slide the face's own turn already gives the eyes.
96
- Pass `options.onTurnSolved` to see what the turn settled on and which targets
97
- were clamped.
63
+ The rig reproduces a Live2D default rig, measured on the Cubism sample models
64
+ (the numbers are in `auto-rig/profile.ts`): the turn reads through parallax,
65
+ not a reshaped face. The face plate translates, changing its width by a few
66
+ percent, the chin leading it; the features lead the plate by their own amounts
67
+ (the nose most, then the brows, eyes and mouth), the eyes foreshortening mildly
68
+ about their own centres; the front hair rides the face where it lies over it and holds the head's
69
+ outline where it draws it, and the back hair stays behind; the neck and the
70
+ ears the face layer paints are cut off into islands of the plate's own mesh,
71
+ drawn behind the head — the neck stays (only the chin's shade slides across
72
+ it), the ears lag the face. AngleZ rolls the head 10° about the chin; the torso only
73
+ breathes.
74
+
75
+ On a layer set with a `nose`, `options.turnTargets` — the cues a 30° reference
76
+ measures (how far the eye pair slides, how much the far eye foreshortens,
77
+ whether the silhouette holds) — are **fitted** each by the one knob it reads:
78
+ the turn's amount, the eyes' foreshortening, the hair's width, a feature's own
79
+ shift. With none given the profile itself renders (`DEFAULT_TURN_TARGETS`
80
+ expresses it in the cues' units). `options.style` tunes a character from the
81
+ profile where the Live2D samples themselves disagree: the whole turn, the
82
+ features' lead, the front hair's follow and its outline's, the blink and the
83
+ hair sway. The turn
84
+ is bounded by the art: the far eye stays inside the plate's outline and, given
85
+ `options.strandEdges`, the far iris may not slide under the bangs' side strand
86
+ any further than it is painted — best effort, never a refusal. The report's
87
+ `strandOverlap` gives, per side whose far iris the bangs' run still covers at
88
+ some far stop, the covered width: how much of the iris's painted row the run
89
+ covers at the stop where that is largest, in px and head half-widths, next to
90
+ how much it covers at rest, and whether the bound held.
91
+ [`AUTO-RIG.md`](./AUTO-RIG.md) describes the model, the fit and the limits.
92
+
93
+ An `eyeShift` you passed that runs past the room the art leaves the far eye —
94
+ the face plate's edge, or the bangs' strand — is **clamped** and the rig is
95
+ built, since that room is a fact about these layers, not about the reference;
96
+ so is the profile's own turn where the art has less room than it needs. Every
97
+ other target you passed that this layer set cannot reach **throws**, naming the
98
+ field and the range it could have had: a `farEyeRatio` or `silhouetteRatio`
99
+ out of the rig's reach, or a `noseShift` / `mouthShift` outside what that
100
+ feature can reach. Pass `options.onTurnSolved` to see what the turn settled on
101
+ and which targets were clamped.
98
102
 
99
103
  `turnTargets.headHalfWidth` is what the shift targets are fractions of, when a
100
104
  caller has measured the actual head (the face plate stands in otherwise).
101
105
  Alongside it, `options.headEdges` names, per side, every layer with an opaque
102
106
  pixel at the eye row and its own rest x there — a companion to a measured
103
107
  `headHalfWidth`, absent otherwise — so the solver can land each candidate
104
- through its OWN part's deformation (the bangs move with their hold, the back
105
- hair holds still, a face-plate or body edge slide) rather than assume the head
108
+ through its OWN part's deformation (the bangs ride the face, the back hair
109
+ stays behind it, a face-plate edge slides) rather than assume the head
106
110
  is centred on the face plate or that whichever part drew furthest out at rest
107
111
  is still the furthest out after it turns.
108
112
 
@@ -112,23 +116,32 @@ the iris's centre (`IrisStrand`: the iris's opaque span, and the run's outer
112
116
  and face-side ends). One shape covers a strand outward of a clear iris, a run
113
117
  over the iris centre that clears on the face side, and a fringe spanning the
114
118
  face, whose `runFace` is `null` because it has no face-side end on that side.
115
- `@ikijs/mcp` measures it off the pixels it decodes. It is always validated
116
- against the layers, but like `headEdges` it is only read by the turn solve, so
117
- it has no effect on the rig without a `nose`; absent or `{}`, the rig is exactly
118
- the one built without it.
119
+ `createLayerSetMeasurer` measures it off the RGBA a host decoded. It is always
120
+ validated against the layers, but like `headEdges` it is only read by the turn
121
+ solve, so it has no effect on the rig without a `nose`; absent or `{}`, the rig
122
+ is exactly the one built without it.
119
123
 
120
124
  It takes **already-decoded** layer geometry (`LayerInput`), never pixels, which
121
- is what keeps this package free of any image dependency: the editor app
122
- decodes with canvas, `@ikijs/mcp` decodes with `sharp`, and both feed the same
123
- pure function — including the optional `rowHalfWidths` (one entry per crop row,
124
- half that row's opaque span in canvas px), which `@ikijs/mcp` measures for the
125
- face so the plate turns on a radius that varies by row; absent, it turns on one
126
- constant radius. It also takes the optional `denseCore`, the tight box of a
127
- layer's alpha ≥ 128 pixels in image coords, not grown, which `@ikijs/mcp`
128
- measures for the nose: its centre and width are the nose's turn landmark, so a
129
- shaded nose is fitted by its drawing rather than its soft feather, and its top
130
- edge at its centre x is the bridge top the nose tilts 6° about on the turn, its
131
- tip toward the far side; absent, the crop stands in for both.
125
+ is what keeps this package free of any image dependency: the editor app decodes
126
+ with canvas, `@ikijs/mcp` decodes with `sharp`, and both hand the RGBA they
127
+ decoded to `createLayerSetMeasurer` (below) and feed what it measures to the
128
+ same pure function — including the optional `rowHalfWidths` (one entry per crop
129
+ row, half that row's opaque span in canvas px), measured for the face so the
130
+ plate turns on a radius that varies by row; absent, it turns on one constant
131
+ radius. It also takes the optional `denseCore`, the tight box of a layer's alpha
132
+ ≥ 128 pixels in image coords, not grown, measured for the nose: its centre and
133
+ width are the nose's turn landmark, so a shaded nose is fitted by its drawing
134
+ rather than its soft feather, and its top edge at its centre x is the bridge top
135
+ the nose tilts 6° about on the turn, its tip toward the far side; absent, the
136
+ crop stands in for both.
137
+
138
+ `createLayerSetMeasurer(canvas)` measures all of it — `rowHalfWidths`,
139
+ `denseCore`, `headHalfWidth`, `headEdges` and `strandEdges` — off the RGBA a
140
+ host decoded, one layer at a time: `add` each layer while its pixels are in
141
+ memory (it keeps no reference to them), then `finish` for the `LayerInput`s and
142
+ the `turnOptions` to pass the generator. A caller's own `turnTargets` go
143
+ underneath `turnOptions.turnTargets`: spread the caller's targets first, then
144
+ the measured ones.
132
145
 
133
146
  `*_L` / `*_R` are the **character's** sides — `eye_L` is the character's left
134
147
  eye, which appears on the viewer's right.