rig-play 1.1.0 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +34 -11
- package/dist/CanvasRenderer.d.ts +16 -1
- package/dist/CanvasRenderer.d.ts.map +1 -1
- package/dist/DomRenderer.d.ts.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +168 -38
- package/dist/index.js.map +1 -1
- package/dist/pixelRatio.d.ts +22 -0
- package/dist/pixelRatio.d.ts.map +1 -0
- package/dist/player.d.ts +56 -0
- package/dist/player.d.ts.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -263,6 +263,35 @@ box, an inverse clip removes nothing, and bounding boxes, paths and points do
|
|
|
263
263
|
not draw and do not count. Both cost nothing until called and read the same
|
|
264
264
|
numbers in `dom` and `canvas` mode.
|
|
265
265
|
|
|
266
|
+
### Adjusting bones
|
|
267
|
+
|
|
268
|
+
A per-pose write to bone locals — a breath's scale, a look's rotation, a mouth
|
|
269
|
+
driven while the body holds still:
|
|
270
|
+
|
|
271
|
+
```ts
|
|
272
|
+
const player = new RigPlayer(root, assets, {
|
|
273
|
+
adjust: (bones) => { bones.bone('mouth').scaleY = 1 + openness; },
|
|
274
|
+
});
|
|
275
|
+
player.adjust = null; // or set it later; setting it re-poses the held pose at once
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
The function runs once per pose, after the animation's timelines and before the
|
|
279
|
+
world transforms, constraints and physics — `rig-c/core`'s `openTrack({ adjust })`,
|
|
280
|
+
which rig-c holds to the Spine runtime at tolerance 0. **A write lasts one
|
|
281
|
+
pose.** The core poses every step from the setup pose, so a value you want held
|
|
282
|
+
is written again each pose and nothing compounds; a 0.8 caller that multiplied a
|
|
283
|
+
channel on a skeleton it kept between frames writes the value it wants, every
|
|
284
|
+
pose. It runs on every pose the animation is applied to, the first one after
|
|
285
|
+
`play` included, and `seek` and a `mode` change keep it.
|
|
286
|
+
|
|
287
|
+
A paused player and a zero step re-pose nothing, so a change the function would
|
|
288
|
+
make is not seen until the next step — unless you ask: setting `player.adjust`
|
|
289
|
+
re-poses the held pose at the same track time, and `player.repose()` does the
|
|
290
|
+
same for a function whose own state changed (the mouth above), without stepping,
|
|
291
|
+
firing events or drawing. A non-finite value, a bone the document does not have,
|
|
292
|
+
or an error the function throws refuses that step in the core's words, and the
|
|
293
|
+
track is reopened by the next `play` or `seek`.
|
|
294
|
+
|
|
266
295
|
### One load, several players
|
|
267
296
|
|
|
268
297
|
One `loadModel` backs any number of players: the regions are cut once, and
|
|
@@ -406,7 +435,7 @@ onto shared pages instead. Both play here, unchanged:
|
|
|
406
435
|
GPU pages handed back) and creates the other over the same root and assets;
|
|
407
436
|
`pixelRatio` and `clipping` carry over and the held pose is redrawn at once.
|
|
408
437
|
`player.renderer` is then a `CanvasRenderer`, whose knobs are `pixelRatio`,
|
|
409
|
-
`clipping`, `viewport` and `backend`. What each mode costs, as counters:
|
|
438
|
+
`syncPixelRatio()`, `clipping`, `viewport` and `backend`. What each mode costs, as counters:
|
|
410
439
|
- **dom** — one element per drawn slot, one style write per changed slot,
|
|
411
440
|
and the mesh tier's rasters (the knobs below).
|
|
412
441
|
- **canvas** — one canvas element per player and, per frame that changed,
|
|
@@ -559,7 +588,7 @@ name, and unloading is still elements first, bitmaps second.
|
|
|
559
588
|
| `new SpineHtmlRenderer(root, regionImages)` | `new DomRenderer(root, regionImages, pages?)` | Only for drawing your own frames; `RigPlayer` constructs its renderer. `pages` is the page-image map the mesh tier samples (`ModelAssets.pageImages`). A hand-built `regionImages` map is still accepted. |
|
|
560
589
|
| `renderer.render(skeleton)` | `renderer.render(frame)` | Takes a `PosedFrame`, not a skeleton. `player.posedFrame()` returns the one the player would draw. |
|
|
561
590
|
| `renderer.pixelRatio` | `player.renderer.pixelRatio` | On both renderers. Carried over when `player.mode` changes. |
|
|
562
|
-
| `renderer.syncPixelRatio()` | `
|
|
591
|
+
| `renderer.syncPixelRatio()` | `player.renderer.syncPixelRatio()` | On both renderers, the same meaning and the same deadband (since 1.3.0; before it, DOM mode only). |
|
|
563
592
|
| `renderer.meshBackingPixels` | `DomRenderer.meshBackingPixels` | DOM mode only. |
|
|
564
593
|
| `renderer.meshBackend`, `meshBackendActive`, type `MeshBackend` | `DomRenderer.meshBackend`, `meshBackendActive`, type `MeshBackend` | Same values (`'canvas2d'` default, `'webgl'`). Also settable up front as `RigPlayerOptions.meshBackend`. |
|
|
565
594
|
| `renderer.triangleExpand` | `DomRenderer.triangleExpand` | Same default (0.5). |
|
|
@@ -569,6 +598,9 @@ name, and unloading is still elements first, bitmaps second.
|
|
|
569
598
|
| tint: skeleton × slot × attachment colour | `player.color` × slot × attachment | `player.color` is `[r, g, b, a]` and takes the place of the skeleton's colour. The product arrives as `tint` on each `PosedSlot`. |
|
|
570
599
|
| dark (two-colour) tint: unsupported | `DomRenderer`: unsupported. `CanvasRenderer`: drawn | Carried as `dark` on a `PosedSlot`. The canvas mode's canvas2d fallback draws alpha only. |
|
|
571
600
|
| `renderer.dispose()` | `player.dispose()` | Then `assets.dispose()`, as before. `DomRenderer.dispose()` and `CanvasRenderer.dispose()` exist for a renderer you constructed yourself. |
|
|
601
|
+
| `skeleton.findBone('head').worldX`, `.worldY`, `.a` … `.d` | `player.bone('head')` | `{ x, y, a, b, c, d, rotationX, rotationY, scaleX, scaleY, active }`, the core's world transform for the pose the player holds (the setup pose before `play`). An unknown name is refused by name. Since 1.1.0. |
|
|
602
|
+
| `bone.scaleY = …` on a `Skeleton` between `state.apply` and `updateWorldTransform` | `RigPlayerOptions.adjust`, `player.adjust`, `player.repose()` | A function called once per pose with the pose's bone locals (`bones.bone('mouth').scaleY = v`). A write lasts one pose: the core poses every step from the setup pose, so write the value you want, every pose — nothing compounds. See *Adjusting bones*. Since 1.2.0. |
|
|
603
|
+
| `skeleton.getBounds(offset, size)` | `player.bounds()` | `{ x, y, width, height }` or null, world units, Y up: the box of every drawn region's corners and mesh's vertices, a slot under a clip counted inside the clip's own box. `ModelAssets.stage` remains the declared framing, not this box. Since 1.1.0. |
|
|
572
604
|
|
|
573
605
|
### No counterpart in 1.0.0
|
|
574
606
|
|
|
@@ -586,13 +618,6 @@ export, for which 1.0.0 declares nothing:
|
|
|
586
618
|
load, several players.
|
|
587
619
|
- **More than one track, mixing between animations, or queueing one after another.**
|
|
588
620
|
`RigPlayer` declares one track; `play` reopens it at the animation's first pose.
|
|
589
|
-
- **Reading or writing a bone.** No bone is exposed: nothing sets a bone's local values
|
|
590
|
-
between the step and the draw, and nothing returns a bone's world transform. What is
|
|
591
|
-
readable is the posed slots: `player.posedFrame()` carries each drawn slot's world
|
|
592
|
-
`corners` or `vertices`.
|
|
593
|
-
- **A bounds query for the current pose.** None is declared. `ModelAssets.stage` is the
|
|
594
|
-
stage the document states (or null), which is the rig's declared framing and not the
|
|
595
|
-
box of the pose being drawn.
|
|
596
621
|
- **Changing the skin after construction.** `skin` is a `RigPlayerOptions` field and
|
|
597
622
|
`RigPlayer` declares no way to change it later; a second player over the same assets
|
|
598
623
|
is the declared route.
|
|
@@ -608,8 +633,6 @@ export, for which 1.0.0 declares nothing:
|
|
|
608
633
|
|
|
609
634
|
Knobs on `DomRenderer` that `CanvasRenderer` does not declare:
|
|
610
635
|
|
|
611
|
-
- `syncPixelRatio()`. `CanvasRenderer.pixelRatio` exists and is set by hand
|
|
612
|
-
(`devicePixelRatio * rootScale`).
|
|
613
636
|
- `meshBackend` and `meshBackendActive`. The canvas mode has its own pair, `backend` and
|
|
614
637
|
`backendActive` (type `CanvasBackend`, `'webgl'` by default).
|
|
615
638
|
- `triangleExpand`, `meshBackingPixels`, `clipWriteCount`, `meshCount`, `meshReuseCount`,
|
package/dist/CanvasRenderer.d.ts
CHANGED
|
@@ -52,7 +52,12 @@ export interface CanvasViewport {
|
|
|
52
52
|
export declare class CanvasRenderer {
|
|
53
53
|
private readonly root;
|
|
54
54
|
private readonly pages;
|
|
55
|
-
/**
|
|
55
|
+
/**
|
|
56
|
+
* Backing-store pixels per root CSS pixel; defaults to the device pixel
|
|
57
|
+
* ratio. A root under a CSS-scaled ancestor rasters at the wrong resolution
|
|
58
|
+
* until the scale is folded in — `syncPixelRatio()` measures it, as for the
|
|
59
|
+
* DOM tier. The next `render` sizes the backing store to the new ratio.
|
|
60
|
+
*/
|
|
56
61
|
pixelRatio: number;
|
|
57
62
|
/** Apply the clips the frame carries (default). False draws every slot unclipped and counts the clips in `clipSkipCount`. */
|
|
58
63
|
clipping: boolean;
|
|
@@ -113,6 +118,16 @@ export declare class CanvasRenderer {
|
|
|
113
118
|
*/
|
|
114
119
|
constructor(root: HTMLElement, pages: ReadonlyMap<string, HTMLImageElement>);
|
|
115
120
|
/** Grows the automatic viewport to cover `box` (world units, Y up) ahead of the frames reaching it. */
|
|
121
|
+
/**
|
|
122
|
+
* Measures the root's effective on-screen scale and sets `pixelRatio` to
|
|
123
|
+
* `devicePixelRatio × scale`, returning the ratio now in effect — the same
|
|
124
|
+
* call, the same meaning and the same deadband as `DomRenderer`'s, so code
|
|
125
|
+
* written against `player.renderer` survives a `mode` change. One forced
|
|
126
|
+
* layout per call (a hidden box appended to the root, read, removed), which
|
|
127
|
+
* is why the renderer never calls it for you. The backing store follows on
|
|
128
|
+
* the next `render`; a root that is not laid out leaves the ratio alone.
|
|
129
|
+
*/
|
|
130
|
+
syncPixelRatio(): number;
|
|
116
131
|
reserve(box: CanvasViewport): void;
|
|
117
132
|
/** Draws one frame. */
|
|
118
133
|
render(frame: PosedFrame): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CanvasRenderer.d.ts","sourceRoot":"","sources":["../src/CanvasRenderer.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"CanvasRenderer.d.ts","sourceRoot":"","sources":["../src/CanvasRenderer.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAwB,UAAU,EAAa,MAAM,iBAAiB,CAAC;AAEnF,+DAA+D;AAC/D,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG,UAAU,CAAC;AAEjD,4EAA4E;AAC5E,MAAM,WAAW,cAAc;IAC7B,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAYD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AACH,qBAAa,cAAc;IAqEvB,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,KAAK;IArExB;;;;;OAKG;IACH,UAAU,SAA+D;IACzE,6HAA6H;IAC7H,QAAQ,UAAQ;IAChB;;;;OAIG;IACH,QAAQ,EAAE,cAAc,GAAG,IAAI,CAAQ;IACvC,mHAAmH;IACnH,OAAO,EAAE,aAAa,CAAW;IACjC,wEAAwE;IACxE,aAAa,EAAE,aAAa,CAAW;IACvC,mEAAmE;IACnE,QAAQ,CAAC,MAAM,EAAE,iBAAiB,CAAC;IAEnC,8EAA8E;IAC9E,SAAS,SAAK;IACd,oEAAoE;IACpE,YAAY,SAAK;IACjB,iEAAiE;IACjE,cAAc,SAAK;IACnB,oFAAoF;IACpF,YAAY,SAAK;IACjB,yDAAyD;IACzD,KAAK,SAAK;IACV,wEAAwE;IACxE,kBAAkB,SAAK;IACvB,yCAAyC;IACzC,SAAS,SAAK;IACd,gIAAgI;IAChI,aAAa,SAAK;IAElB,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAkC;IACtD,gHAAgH;IAChH,OAAO,CAAC,IAAI,CAAK;IACjB,OAAO,CAAC,GAAG,CAAK;IAChB,OAAO,CAAC,KAAK,CAAK;IAClB,OAAO,CAAC,MAAM,CAAK;IACnB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,QAAQ,CAAK;IACrB,OAAO,CAAC,QAAQ,CAAK;IACrB,OAAO,CAAC,YAAY,CAAM;IAC1B,OAAO,CAAC,IAAI,CAA0B;IACtC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAkB;IACvC,sFAAsF;IACtF,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA+B;IACvD,yGAAyG;IACzG,OAAO,CAAC,GAAG,CAAyB;IACpC,OAAO,CAAC,SAAS,CAAM;IACvB,OAAO,CAAC,OAAO,CAAiB;IAChC,OAAO,CAAC,OAAO,CAAyB;IACxC,OAAO,CAAC,QAAQ,CAAiB;IAEjC;;;;;;OAMG;gBAEgB,IAAI,EAAE,WAAW,EACjB,KAAK,EAAE,WAAW,CAAC,MAAM,EAAE,gBAAgB,CAAC;IAgB/D,uGAAuG;IACvG;;;;;;;;OAQG;IACH,cAAc,IAAI,MAAM;IAKxB,OAAO,CAAC,GAAG,EAAE,cAAc,GAAG,IAAI;IAIlC,uBAAuB;IACvB,MAAM,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI;IAuD/B,+GAA+G;IAC/G,OAAO,IAAI,IAAI;IAYf,OAAO,CAAC,MAAM;IAkBd,OAAO,CAAC,OAAO;IAUf,oFAAoF;IACpF,OAAO,CAAC,WAAW;IAkBnB,uGAAuG;IACvG,OAAO,CAAC,gBAAgB;IAoExB,iFAAiF;IACjF,OAAO,CAAC,UAAU;IAalB,OAAO,CAAC,MAAM,CAAa;IAE3B;;;OAGG;IACH,OAAO,CAAC,QAAQ;IA4BhB,+CAA+C;IAC/C,OAAO,CAAC,aAAa;IAmErB,OAAO,CAAC,MAAM;CAmEf"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DomRenderer.d.ts","sourceRoot":"","sources":["../src/DomRenderer.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"DomRenderer.d.ts","sourceRoot":"","sources":["../src/DomRenderer.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -2,6 +2,6 @@ export { type CanvasBackend, CanvasRenderer, type CanvasViewport } from './Canva
|
|
|
2
2
|
export { DomRenderer, type MeshBackend } from './DomRenderer.js';
|
|
3
3
|
export { type PageLayout, type PageRegion, type RegionImage, revokeRegions, unpackRegions } from './DomTexture.js';
|
|
4
4
|
export { type LoadModelOptions, loadModel, type ModelAssets, type ModelDocument } from './loadModel.js';
|
|
5
|
-
export { type BoneWorld, type PoseBounds, type RenderMode, type RigEvent, RigPlayer, type RigPlayerOptions } from './player.js';
|
|
5
|
+
export { type AdjustableBones, type BoneAdjust, type BoneLocals, type BoneWorld, type PoseBounds, type RenderMode, type RigEvent, RigPlayer, type RigPlayerOptions } from './player.js';
|
|
6
6
|
export type { BlendMode, PosedClip, PosedFrame, PosedMesh, PosedRegion, PosedSlot, } from './posedFrame.js';
|
|
7
7
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,aAAa,EAAE,cAAc,EAAE,KAAK,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAC9F,OAAO,EAAE,WAAW,EAAE,KAAK,WAAW,EAAE,MAAM,kBAAkB,CAAC;AACjE,OAAO,EAAE,KAAK,UAAU,EAAE,KAAK,UAAU,EAAE,KAAK,WAAW,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AACnH,OAAO,EAAE,KAAK,gBAAgB,EAAE,SAAS,EAAE,KAAK,WAAW,EAAE,KAAK,aAAa,EAAE,MAAM,gBAAgB,CAAC;AACxG,OAAO,EAAE,KAAK,SAAS,EAAE,KAAK,UAAU,EAAE,KAAK,UAAU,EAAE,KAAK,QAAQ,EAAE,SAAS,EAAE,KAAK,gBAAgB,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,aAAa,EAAE,cAAc,EAAE,KAAK,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAC9F,OAAO,EAAE,WAAW,EAAE,KAAK,WAAW,EAAE,MAAM,kBAAkB,CAAC;AACjE,OAAO,EAAE,KAAK,UAAU,EAAE,KAAK,UAAU,EAAE,KAAK,WAAW,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AACnH,OAAO,EAAE,KAAK,gBAAgB,EAAE,SAAS,EAAE,KAAK,WAAW,EAAE,KAAK,aAAa,EAAE,MAAM,gBAAgB,CAAC;AACxG,OAAO,EAAE,KAAK,eAAe,EAAE,KAAK,UAAU,EAAE,KAAK,UAAU,EAAE,KAAK,SAAS,EAAE,KAAK,UAAU,EAAE,KAAK,UAAU,EAAE,KAAK,QAAQ,EAAE,SAAS,EAAE,KAAK,gBAAgB,EAAE,MAAM,aAAa,CAAC;AACxL,YAAY,EACV,SAAS,EACT,SAAS,EACT,UAAU,EACV,SAAS,EACT,WAAW,EACX,SAAS,GACV,MAAM,iBAAiB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,31 @@
|
|
|
1
|
+
const SCALE_PROBE_PX = 100;
|
|
2
|
+
const PIXEL_RATIO_DEADBAND = 1e-3;
|
|
3
|
+
function syncedPixelRatio(root, current) {
|
|
4
|
+
const probe = document.createElement("div");
|
|
5
|
+
probe.style.position = "absolute";
|
|
6
|
+
probe.style.left = "0";
|
|
7
|
+
probe.style.top = "0";
|
|
8
|
+
probe.style.width = `${SCALE_PROBE_PX}px`;
|
|
9
|
+
probe.style.height = `${SCALE_PROBE_PX}px`;
|
|
10
|
+
probe.style.visibility = "hidden";
|
|
11
|
+
probe.style.pointerEvents = "none";
|
|
12
|
+
root.appendChild(probe);
|
|
13
|
+
let width = 0;
|
|
14
|
+
let height = 0;
|
|
15
|
+
try {
|
|
16
|
+
const rect = probe.getBoundingClientRect();
|
|
17
|
+
width = rect.width;
|
|
18
|
+
height = rect.height;
|
|
19
|
+
} finally {
|
|
20
|
+
probe.remove();
|
|
21
|
+
}
|
|
22
|
+
const scale = Math.max(width, height) / SCALE_PROBE_PX;
|
|
23
|
+
if (!(scale > 0)) return current;
|
|
24
|
+
const dpr = typeof devicePixelRatio === "number" ? devicePixelRatio : 1;
|
|
25
|
+
const next = dpr * scale;
|
|
26
|
+
if (current > 0 && Math.abs(next - current) < current * PIXEL_RATIO_DEADBAND) return current;
|
|
27
|
+
return next;
|
|
28
|
+
}
|
|
1
29
|
const RUNTIME_PI = 3.1415927;
|
|
2
30
|
const RAD$3 = RUNTIME_PI / 180;
|
|
3
31
|
const RUNTIME_DEG = 180 / RUNTIME_PI;
|
|
@@ -3819,6 +3847,81 @@ function posedBones(doc, timelines, t, plant = {}) {
|
|
|
3819
3847
|
return bone2;
|
|
3820
3848
|
});
|
|
3821
3849
|
}
|
|
3850
|
+
const LOCAL_CHANNELS = { rotation: 0, x: 0, y: 0, scaleX: 1, scaleY: 1, shearX: 0, shearY: 0 };
|
|
3851
|
+
class StepLocals {
|
|
3852
|
+
constructor(bones, index, setup) {
|
|
3853
|
+
this.bones = bones;
|
|
3854
|
+
this.index = index;
|
|
3855
|
+
this.setup = setup;
|
|
3856
|
+
}
|
|
3857
|
+
read(channel2) {
|
|
3858
|
+
return this.bones[this.index][channel2] ?? LOCAL_CHANNELS[channel2];
|
|
3859
|
+
}
|
|
3860
|
+
write(channel2, value) {
|
|
3861
|
+
if (typeof value !== "number" || !Number.isFinite(value)) throw new CoreInputError(`the adjustment wrote bone "${this.setup.name}" ${channel2} = ${String(value)}, not a finite number`);
|
|
3862
|
+
if (this.bones[this.index] === this.setup) this.bones[this.index] = { ...this.setup };
|
|
3863
|
+
this.bones[this.index][channel2] = value;
|
|
3864
|
+
}
|
|
3865
|
+
get rotation() {
|
|
3866
|
+
return this.read("rotation");
|
|
3867
|
+
}
|
|
3868
|
+
set rotation(v) {
|
|
3869
|
+
this.write("rotation", v);
|
|
3870
|
+
}
|
|
3871
|
+
get x() {
|
|
3872
|
+
return this.read("x");
|
|
3873
|
+
}
|
|
3874
|
+
set x(v) {
|
|
3875
|
+
this.write("x", v);
|
|
3876
|
+
}
|
|
3877
|
+
get y() {
|
|
3878
|
+
return this.read("y");
|
|
3879
|
+
}
|
|
3880
|
+
set y(v) {
|
|
3881
|
+
this.write("y", v);
|
|
3882
|
+
}
|
|
3883
|
+
get scaleX() {
|
|
3884
|
+
return this.read("scaleX");
|
|
3885
|
+
}
|
|
3886
|
+
set scaleX(v) {
|
|
3887
|
+
this.write("scaleX", v);
|
|
3888
|
+
}
|
|
3889
|
+
get scaleY() {
|
|
3890
|
+
return this.read("scaleY");
|
|
3891
|
+
}
|
|
3892
|
+
set scaleY(v) {
|
|
3893
|
+
this.write("scaleY", v);
|
|
3894
|
+
}
|
|
3895
|
+
get shearX() {
|
|
3896
|
+
return this.read("shearX");
|
|
3897
|
+
}
|
|
3898
|
+
set shearX(v) {
|
|
3899
|
+
this.write("shearX", v);
|
|
3900
|
+
}
|
|
3901
|
+
get shearY() {
|
|
3902
|
+
return this.read("shearY");
|
|
3903
|
+
}
|
|
3904
|
+
set shearY(v) {
|
|
3905
|
+
this.write("shearY", v);
|
|
3906
|
+
}
|
|
3907
|
+
}
|
|
3908
|
+
function adjustLocals(doc, bones, adjust) {
|
|
3909
|
+
const views = /* @__PURE__ */ new Map();
|
|
3910
|
+
const names = doc.bones.map((b) => b.name);
|
|
3911
|
+
adjust({
|
|
3912
|
+
names,
|
|
3913
|
+
bone(name) {
|
|
3914
|
+
let view = views.get(name);
|
|
3915
|
+
if (view === void 0) {
|
|
3916
|
+
const index = names.indexOf(name);
|
|
3917
|
+
if (index < 0) throw new CoreInputError(`the adjustment asked for bone ${JSON.stringify(name)}, which is not one of this document's bones`);
|
|
3918
|
+
view = new StepLocals(bones, index, doc.bones[index]);
|
|
3919
|
+
views.set(name, view);
|
|
3920
|
+
}
|
|
3921
|
+
return view;
|
|
3922
|
+
}
|
|
3923
|
+
});
|
|
3924
|
+
}
|
|
3822
3925
|
const clamp01 = (v) => v < 0 ? 0 : v > 1 ? 1 : v;
|
|
3823
3926
|
function posedSlots(doc, timelines, t, plant = {}, sliders = [], placeholders) {
|
|
3824
3927
|
const resolve = plant.shown ?? shownAttachment;
|
|
@@ -3895,6 +3998,7 @@ function stepRows(posed) {
|
|
|
3895
3998
|
function posedBoneStep(doc, timelines, t, plant, constraints, sliders, step) {
|
|
3896
3999
|
const active = activeBones(doc);
|
|
3897
4000
|
const bones = posedBones(doc, timelines, t, plant);
|
|
4001
|
+
if (step?.adjust !== void 0) adjustLocals(doc, bones, step.adjust);
|
|
3898
4002
|
let world = (plant.evaluate ?? worldTransforms)(bones, active);
|
|
3899
4003
|
if (constraints !== void 0 && step !== void 0) {
|
|
3900
4004
|
const records = stepPhysicsRecords(pathDeformed(doc, posedRecords(constraintRecords(doc), constraints, t), timelines, t, plant), constraints.physicsKeyed ?? [], t, active, step.ctx, step.before);
|
|
@@ -4819,16 +4923,16 @@ function animationOf(doc, animation) {
|
|
|
4819
4923
|
if (anim === void 0) throw new CoreInputError(`animation "${animation}" is not one of this document's [${doc.animations.map((a) => a.name).join(", ")}]`);
|
|
4820
4924
|
return anim;
|
|
4821
4925
|
}
|
|
4822
|
-
function openWalk(doc, anim, plant, reset, mode, shape) {
|
|
4926
|
+
function openWalk(doc, anim, plant, reset, mode, shape, adjust) {
|
|
4823
4927
|
const why = constraintsAbsentWhy(doc) ?? pathAnimationsWhy(doc) ?? steppedPreviousPassWhy(doc);
|
|
4824
4928
|
if (why !== null) throw new CoreInputError(`the raw walk leaves the bones out: ${why}`);
|
|
4825
4929
|
const ctx = freshStepContext(plant.physicsStep);
|
|
4826
4930
|
if (mode.loop && mode.wrapResets !== false) ctx.loop = true;
|
|
4827
|
-
const track = { doc, anim, duration: anim.timelines.duration, mode, plant, raw: { ...plant, ...roundPlant(mode) }, resolve: plant.shown ?? shownAttachment, shape, ctx, trackTime: 0, last: -1 };
|
|
4931
|
+
const track = { doc, anim, duration: anim.timelines.duration, mode, plant, raw: { ...plant, ...roundPlant(mode) }, resolve: plant.shown ?? shownAttachment, shape, ctx, adjust, trackTime: 0, last: -1 };
|
|
4828
4932
|
return { track, first: stepWalk(track, 0, true) };
|
|
4829
4933
|
}
|
|
4830
4934
|
function stepWalk(track, dt, first = false) {
|
|
4831
|
-
const { doc, anim, duration, mode, plant, raw, resolve, shape, ctx } = track;
|
|
4935
|
+
const { doc, anim, duration, mode, plant, raw, resolve, shape, ctx, adjust } = track;
|
|
4832
4936
|
track.trackTime += dt;
|
|
4833
4937
|
const trackTime = track.trackTime;
|
|
4834
4938
|
const last = track.last;
|
|
@@ -4836,7 +4940,7 @@ function stepWalk(track, dt, first = false) {
|
|
|
4836
4940
|
const before = ctx.time;
|
|
4837
4941
|
ctx.time += mode.clock === void 0 ? dt : mode.clock(dt, t - (first ? 0 : Math.max(last, 0)));
|
|
4838
4942
|
const sliders = [];
|
|
4839
|
-
const world = shape.rows ? posedBoneWorld(doc, anim.timelines, t, raw, anim.constraints, sliders, { ctx, before }).world : posedBoneWorldAlone(doc, anim.timelines, t, raw, anim.constraints, sliders, { ctx, before });
|
|
4943
|
+
const world = shape.rows ? posedBoneWorld(doc, anim.timelines, t, raw, anim.constraints, sliders, { ctx, before, adjust }).world : posedBoneWorldAlone(doc, anim.timelines, t, raw, anim.constraints, sliders, { ctx, before, adjust });
|
|
4840
4944
|
if (first) ctx.phase = "update";
|
|
4841
4945
|
const placeholders = /* @__PURE__ */ new Map();
|
|
4842
4946
|
const slots = posedSlots(doc, anim.timelines, t, raw, sliders, placeholders);
|
|
@@ -4856,8 +4960,8 @@ function stepWalk(track, dt, first = false) {
|
|
|
4856
4960
|
track.last = t;
|
|
4857
4961
|
return pose;
|
|
4858
4962
|
}
|
|
4859
|
-
function openRawTrack(doc, animation, plant, reset, mode) {
|
|
4860
|
-
return openWalk(doc, animationOf(doc, animation), plant, reset, mode, RAW_SHAPE);
|
|
4963
|
+
function openRawTrack(doc, animation, plant, reset, mode, adjust) {
|
|
4964
|
+
return openWalk(doc, animationOf(doc, animation), plant, reset, mode, RAW_SHAPE, adjust);
|
|
4861
4965
|
}
|
|
4862
4966
|
function stepRawTrack(track, dt) {
|
|
4863
4967
|
return stepWalk(track, dt);
|
|
@@ -4866,7 +4970,8 @@ const HELD = { loop: false, keep: false };
|
|
|
4866
4970
|
const LOOPED = { loop: true, keep: false, live: true };
|
|
4867
4971
|
function openTrack(doc, animation, options, plant = {}) {
|
|
4868
4972
|
if (typeof options !== "object" || options === null || typeof options.loop !== "boolean") throw new CoreInputError(`the live track's options.loop is ${JSON.stringify(options?.loop)}, not true or false`);
|
|
4869
|
-
|
|
4973
|
+
if (options.adjust !== void 0 && typeof options.adjust !== "function") throw new CoreInputError(`the live track's options.adjust is ${typeof options.adjust}, not a function`);
|
|
4974
|
+
const { track, first } = openRawTrack(doc, animation, plant, options.reset, options.loop ? LOOPED : HELD, options.adjust);
|
|
4870
4975
|
return new Track(track, first);
|
|
4871
4976
|
}
|
|
4872
4977
|
class Track {
|
|
@@ -4896,7 +5001,7 @@ class Track {
|
|
|
4896
5001
|
this.latest = pose.animationTime;
|
|
4897
5002
|
return pose;
|
|
4898
5003
|
} catch (err) {
|
|
4899
|
-
|
|
5004
|
+
this.refused = `${n} (${err instanceof Error ? err.message : String(err)})`;
|
|
4900
5005
|
throw err;
|
|
4901
5006
|
}
|
|
4902
5007
|
}
|
|
@@ -5661,8 +5766,6 @@ function getMeshGlBlitter() {
|
|
|
5661
5766
|
const regionVertices = new Float32Array(8);
|
|
5662
5767
|
const SVG_NS = "http://www.w3.org/2000/svg";
|
|
5663
5768
|
let tintFilterSeq = 0;
|
|
5664
|
-
const SCALE_PROBE_PX = 100;
|
|
5665
|
-
const PIXEL_RATIO_DEADBAND = 1e-3;
|
|
5666
5769
|
function expandPoint(x, y, cx, cy, amount) {
|
|
5667
5770
|
const dx = x - cx;
|
|
5668
5771
|
const dy = y - cy;
|
|
@@ -5873,32 +5976,8 @@ class DomRenderer {
|
|
|
5873
5976
|
* @returns the pixel ratio now in effect (the current one if nothing moved).
|
|
5874
5977
|
*/
|
|
5875
5978
|
syncPixelRatio() {
|
|
5876
|
-
|
|
5877
|
-
|
|
5878
|
-
probe.style.left = "0";
|
|
5879
|
-
probe.style.top = "0";
|
|
5880
|
-
probe.style.width = `${SCALE_PROBE_PX}px`;
|
|
5881
|
-
probe.style.height = `${SCALE_PROBE_PX}px`;
|
|
5882
|
-
probe.style.visibility = "hidden";
|
|
5883
|
-
probe.style.pointerEvents = "none";
|
|
5884
|
-
this.root.appendChild(probe);
|
|
5885
|
-
let width = 0;
|
|
5886
|
-
let height = 0;
|
|
5887
|
-
try {
|
|
5888
|
-
const rect = probe.getBoundingClientRect();
|
|
5889
|
-
width = rect.width;
|
|
5890
|
-
height = rect.height;
|
|
5891
|
-
} finally {
|
|
5892
|
-
probe.remove();
|
|
5893
|
-
}
|
|
5894
|
-
const scale = Math.max(width, height) / SCALE_PROBE_PX;
|
|
5895
|
-
if (!(scale > 0)) return this.pixelRatio;
|
|
5896
|
-
const dpr = typeof devicePixelRatio === "number" ? devicePixelRatio : 1;
|
|
5897
|
-
const next = dpr * scale;
|
|
5898
|
-
const current = this.pixelRatio;
|
|
5899
|
-
if (current > 0 && Math.abs(next - current) < current * PIXEL_RATIO_DEADBAND) return current;
|
|
5900
|
-
this.pixelRatio = next;
|
|
5901
|
-
return next;
|
|
5979
|
+
this.pixelRatio = syncedPixelRatio(this.root, this.pixelRatio);
|
|
5980
|
+
return this.pixelRatio;
|
|
5902
5981
|
}
|
|
5903
5982
|
/**
|
|
5904
5983
|
* Draws one frame. Slots the frame does not carry — no attachment, a
|
|
@@ -6563,7 +6642,12 @@ class CanvasRenderer {
|
|
|
6563
6642
|
this.ctx = canvas.getContext("2d");
|
|
6564
6643
|
root.appendChild(canvas);
|
|
6565
6644
|
}
|
|
6566
|
-
/**
|
|
6645
|
+
/**
|
|
6646
|
+
* Backing-store pixels per root CSS pixel; defaults to the device pixel
|
|
6647
|
+
* ratio. A root under a CSS-scaled ancestor rasters at the wrong resolution
|
|
6648
|
+
* until the scale is folded in — `syncPixelRatio()` measures it, as for the
|
|
6649
|
+
* DOM tier. The next `render` sizes the backing store to the new ratio.
|
|
6650
|
+
*/
|
|
6567
6651
|
pixelRatio = typeof devicePixelRatio === "number" ? devicePixelRatio : 1;
|
|
6568
6652
|
/** Apply the clips the frame carries (default). False draws every slot unclipped and counts the clips in `clipSkipCount`. */
|
|
6569
6653
|
clipping = true;
|
|
@@ -6616,6 +6700,19 @@ class CanvasRenderer {
|
|
|
6616
6700
|
nextSig = new Float64Array(256);
|
|
6617
6701
|
nextRefs = [];
|
|
6618
6702
|
/** Grows the automatic viewport to cover `box` (world units, Y up) ahead of the frames reaching it. */
|
|
6703
|
+
/**
|
|
6704
|
+
* Measures the root's effective on-screen scale and sets `pixelRatio` to
|
|
6705
|
+
* `devicePixelRatio × scale`, returning the ratio now in effect — the same
|
|
6706
|
+
* call, the same meaning and the same deadband as `DomRenderer`'s, so code
|
|
6707
|
+
* written against `player.renderer` survives a `mode` change. One forced
|
|
6708
|
+
* layout per call (a hidden box appended to the root, read, removed), which
|
|
6709
|
+
* is why the renderer never calls it for you. The backing store follows on
|
|
6710
|
+
* the next `render`; a root that is not laid out leaves the ratio alone.
|
|
6711
|
+
*/
|
|
6712
|
+
syncPixelRatio() {
|
|
6713
|
+
this.pixelRatio = syncedPixelRatio(this.root, this.pixelRatio);
|
|
6714
|
+
return this.pixelRatio;
|
|
6715
|
+
}
|
|
6619
6716
|
reserve(box) {
|
|
6620
6717
|
this.growTo(box.x, -(box.y + box.height), box.x + box.width, -box.y);
|
|
6621
6718
|
}
|
|
@@ -7221,6 +7318,11 @@ class RigPlayer {
|
|
|
7221
7318
|
paused = false;
|
|
7222
7319
|
pose = null;
|
|
7223
7320
|
setup = null;
|
|
7321
|
+
adjustFn = null;
|
|
7322
|
+
/** Handed to every track the player opens; reads the current `adjust` so setting it later needs no reopen. */
|
|
7323
|
+
adjustHook = (locals) => {
|
|
7324
|
+
if (this.adjustFn !== null) this.adjustFn(locals);
|
|
7325
|
+
};
|
|
7224
7326
|
listeners = {
|
|
7225
7327
|
event: /* @__PURE__ */ new Set(),
|
|
7226
7328
|
complete: /* @__PURE__ */ new Set()
|
|
@@ -7234,6 +7336,7 @@ class RigPlayer {
|
|
|
7234
7336
|
this.root = root;
|
|
7235
7337
|
this.assets = assets;
|
|
7236
7338
|
this.meshBackend = options.meshBackend;
|
|
7339
|
+
this.adjustFn = options.adjust ?? null;
|
|
7237
7340
|
this.active = this.create(options.mode ?? "dom");
|
|
7238
7341
|
}
|
|
7239
7342
|
/**
|
|
@@ -7342,6 +7445,33 @@ class RigPlayer {
|
|
|
7342
7445
|
if (this.pose === null) return;
|
|
7343
7446
|
this.renderer.render(this.posedFrame());
|
|
7344
7447
|
}
|
|
7448
|
+
/**
|
|
7449
|
+
* The per-pose adjustment of bone locals (`BoneAdjust`), or null for none.
|
|
7450
|
+
* It is applied by every track the player opens — `play`, the reopen inside
|
|
7451
|
+
* `seek` — and by the pose drawn again after `mode` changes, on every pose
|
|
7452
|
+
* the animation is applied to, the first one included. Setting it re-poses
|
|
7453
|
+
* the pose the player holds at once (`repose`), so a held or paused player
|
|
7454
|
+
* shows the change on its next `draw` without a step.
|
|
7455
|
+
*/
|
|
7456
|
+
get adjust() {
|
|
7457
|
+
return this.adjustFn;
|
|
7458
|
+
}
|
|
7459
|
+
set adjust(fn) {
|
|
7460
|
+
if (fn !== null && typeof fn !== "function") throw new Error(`adjust must be a function or null, got ${typeof fn}`);
|
|
7461
|
+
this.adjustFn = fn;
|
|
7462
|
+
this.repose();
|
|
7463
|
+
}
|
|
7464
|
+
/**
|
|
7465
|
+
* Poses the held pose again at the same track time, through the current
|
|
7466
|
+
* `adjust`, without stepping, firing or drawing. A paused player and a zero
|
|
7467
|
+
* step re-pose nothing, so a caller whose adjustment reads its own state
|
|
7468
|
+
* (a mouth driven while the body is held) calls this when that state
|
|
7469
|
+
* changed, then `draw`. Nothing before `play`.
|
|
7470
|
+
*/
|
|
7471
|
+
repose() {
|
|
7472
|
+
if (this.track === null || this.pose === null) return;
|
|
7473
|
+
this.pose = this.track.step(0);
|
|
7474
|
+
}
|
|
7345
7475
|
/**
|
|
7346
7476
|
* Where `name` is in the pose the player holds — the pose of the last
|
|
7347
7477
|
* `advance`/`frame`/`seek`, or the setup pose before `play`. Read off the
|
|
@@ -7427,7 +7557,7 @@ class RigPlayer {
|
|
|
7427
7557
|
this.pose = null;
|
|
7428
7558
|
}
|
|
7429
7559
|
open() {
|
|
7430
|
-
this.track = openTrack(this.view, this.animation, { loop: this.loop, reset: "animation" });
|
|
7560
|
+
this.track = openTrack(this.view, this.animation, { loop: this.loop, reset: "animation", adjust: this.adjustHook });
|
|
7431
7561
|
this.pose = this.track.first;
|
|
7432
7562
|
}
|
|
7433
7563
|
/**
|