@glissade/scene 0.25.0 → 0.26.0-pre.1
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/dist/describe.js +25 -1
- package/dist/diagnostics.js +4 -2
- package/dist/echo.js +78 -0
- package/dist/examples.js +55 -2
- package/dist/identity.js +2 -1
- package/dist/index.d.ts +23 -1
- package/dist/index.js +2 -1
- package/dist/motion.d.ts +55 -1
- package/dist/motion.js +2 -2
- package/dist/nodes.d.ts +11 -1
- package/dist/{motionPath.js → orient.js} +108 -2
- package/dist/scene.js +2 -1
- package/package.json +2 -2
package/dist/describe.js
CHANGED
|
@@ -22,7 +22,7 @@ import { easings, listValueTypes } from "@glissade/core";
|
|
|
22
22
|
* never pulled onto the base embed path — a scene that never calls `describe()`
|
|
23
23
|
* pays zero bytes for it.
|
|
24
24
|
*/
|
|
25
|
-
const RAW_VERSION = "0.
|
|
25
|
+
const RAW_VERSION = "0.26.0-pre.1";
|
|
26
26
|
const PACKAGE_VERSION = RAW_VERSION.includes("GLISSADE_".concat("VERSION")) ? "0.0.0-dev" : RAW_VERSION;
|
|
27
27
|
/** Arity of a value type's numeric repr: vec2/vec2-arc → 2, number → 1; others (color/paint/path/string/boolean) carry no scalar arity. */
|
|
28
28
|
function arityOf(type) {
|
|
@@ -327,6 +327,24 @@ const HELPERS = [
|
|
|
327
327
|
import: "@glissade/scene/motion",
|
|
328
328
|
usage: "followPath(target: Node, path: Node, opts?: { id?, orient?: boolean }): FollowPath — drive '<id>/progress' with a track"
|
|
329
329
|
},
|
|
330
|
+
{
|
|
331
|
+
name: "orientToPath",
|
|
332
|
+
summary: "The rotation-only sibling of followPath: owns a target's rotation, banking it to the path tangent at progress, while POSITION is left to whatever drives it (keyframes, layout, a sibling followPath). Pure, tree-shakeable.",
|
|
333
|
+
import: "@glissade/scene/motion",
|
|
334
|
+
usage: "orientToPath(target: Node, path: PathValue | Path, opts?: { id?, progress?, offset?: number }): OrientToPath — drive '<id>/progress' with a track"
|
|
335
|
+
},
|
|
336
|
+
{
|
|
337
|
+
name: "lookAt",
|
|
338
|
+
summary: "A driver node that owns a target's rotation, aiming its local +x axis at another node's world origin — a turret tracking a mover, an arrow pointing at a label. Re-derives from both positions each frame; no stored state.",
|
|
339
|
+
import: "@glissade/scene/motion",
|
|
340
|
+
usage: "lookAt(target: Node, at: Node, opts?: { id?, offset?: number }): LookAt"
|
|
341
|
+
},
|
|
342
|
+
{
|
|
343
|
+
name: "echo",
|
|
344
|
+
summary: "Motion trails / onion-skin: wrap a child so it renders at K past playhead offsets (t − i·spacing), each trailing copy fading by decay. A pure multi-time re-eval (the playhead is re-addressed per copy and restored), byte-stable in the golden corpus. Add the returned Echo to the scene.",
|
|
345
|
+
import: "@glissade/scene",
|
|
346
|
+
usage: "echo(child: Node, opts?: { id?, count?: number, spacing?: number, decay?: number }): Echo"
|
|
347
|
+
},
|
|
330
348
|
{
|
|
331
349
|
name: "clip",
|
|
332
350
|
summary: "A reusable, target-agnostic motion captured once as a relative-time key schedule, then applied to a node at a wall-clock start time. Build-time sugar: clip.apply() compiles to ordinary Track[].",
|
|
@@ -339,6 +357,12 @@ const HELPERS = [
|
|
|
339
357
|
import: "@glissade/core/clips",
|
|
340
358
|
usage: "clipList(clip: Clip, targets: string[], startT: number, opts?: { stagger?: number }): { tracks }"
|
|
341
359
|
},
|
|
360
|
+
{
|
|
361
|
+
name: "retime",
|
|
362
|
+
summary: "Retime a set of tracks by remapping their key TIMES — speed (slow-mo/fast), shift (delay/advance), reverse, or pingpong — as a pure build-time transform to ordinary retimed Track[]. Reverse/pingpong time-mirror each ease exactly (built-ins + cubicBezier); springs/holds fail loud.",
|
|
363
|
+
import: "@glissade/core",
|
|
364
|
+
usage: "retime(tracks: Track[], { speed?, shift?, reverse?, pingpong? }): Track[]"
|
|
365
|
+
},
|
|
342
366
|
{
|
|
343
367
|
name: "renderToDataURL",
|
|
344
368
|
summary: "Capture a single frame as a PNG/WebP data URL — evaluate → render → data-URL, the no-build screenshot DX helper. Browser-only.",
|
package/dist/diagnostics.js
CHANGED
|
@@ -153,12 +153,14 @@ function auditCacheCold(createScene, doc, t) {
|
|
|
153
153
|
const ctxA = {
|
|
154
154
|
time: t,
|
|
155
155
|
frame,
|
|
156
|
-
measurer: warm.textMeasurer
|
|
156
|
+
measurer: warm.textMeasurer,
|
|
157
|
+
playhead: warm.playhead
|
|
157
158
|
};
|
|
158
159
|
const ctxB = {
|
|
159
160
|
time: t,
|
|
160
161
|
frame,
|
|
161
|
-
measurer: cold.textMeasurer
|
|
162
|
+
measurer: cold.textMeasurer,
|
|
163
|
+
playhead: cold.playhead
|
|
162
164
|
};
|
|
163
165
|
let groupFallback;
|
|
164
166
|
for (const [id, nodeA] of warm.nodes) {
|
package/dist/echo.js
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { r as Group } from "./nodes.js";
|
|
2
|
+
import { batch } from "@glissade/core";
|
|
3
|
+
//#region src/echo.ts
|
|
4
|
+
/**
|
|
5
|
+
* Echo (0.26): motion trails / onion-skin. A wrapper group that renders its
|
|
6
|
+
* children K times — at the playhead and at K−1 earlier offsets (t − i·spacing)
|
|
7
|
+
* — each trailing copy fading by `decay`. The leading copy is the live frame;
|
|
8
|
+
* the ghosts are the subtree "as it was" a few slices ago.
|
|
9
|
+
*
|
|
10
|
+
* It is the pure render form of "re-evaluate at t + k·spacing": within one
|
|
11
|
+
* frame it re-addresses the SCENE PLAYHEAD to each offset time, emits the
|
|
12
|
+
* children (whose bound signals re-derive at that time — tracks, followPath,
|
|
13
|
+
* computeds all follow), then RESTORES the playhead before the walk continues.
|
|
14
|
+
* The whole dance is wrapped in `batch()` so the playhead's subscribers (a
|
|
15
|
+
* player repaint) coalesce to a single, idempotent notification at the restored
|
|
16
|
+
* time — never a mid-emit reentrancy storm. Headless (goldens/export) the
|
|
17
|
+
* playhead has no subscribers, so it is a plain pure multi-sample: evaluate()
|
|
18
|
+
* stays a pure function of time and the DisplayList is byte-stable.
|
|
19
|
+
*
|
|
20
|
+
* Determinism note: the offsets are pure functions of the current playhead, so
|
|
21
|
+
* a cold re-eval reproduces byte-identically (the cache-cold audit passes).
|
|
22
|
+
* Ghost times before the first key clamp to the initial pose (track semantics).
|
|
23
|
+
*/
|
|
24
|
+
var Echo = class extends Group {
|
|
25
|
+
get describeType() {
|
|
26
|
+
return "Echo";
|
|
27
|
+
}
|
|
28
|
+
count;
|
|
29
|
+
spacing;
|
|
30
|
+
decay;
|
|
31
|
+
constructor(props = {}) {
|
|
32
|
+
super(props);
|
|
33
|
+
this.count = Math.max(1, Math.floor(props.count ?? 5));
|
|
34
|
+
this.spacing = props.spacing ?? .08;
|
|
35
|
+
this.decay = props.decay ?? .6;
|
|
36
|
+
}
|
|
37
|
+
draw(out, ctx) {
|
|
38
|
+
const playhead = ctx.playhead;
|
|
39
|
+
if (this.count <= 1 || this.spacing === 0 || playhead === void 0) {
|
|
40
|
+
super.draw(out, ctx);
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
const now = ctx.time;
|
|
44
|
+
batch(() => {
|
|
45
|
+
try {
|
|
46
|
+
for (let i = this.count - 1; i >= 0; i--) {
|
|
47
|
+
const alpha = i === 0 ? 1 : this.decay ** i;
|
|
48
|
+
if (alpha <= 0) continue;
|
|
49
|
+
const offsetT = now - i * this.spacing;
|
|
50
|
+
playhead.forceSet(offsetT);
|
|
51
|
+
out.push({
|
|
52
|
+
op: "pushGroup",
|
|
53
|
+
opacity: alpha,
|
|
54
|
+
blend: "source-over",
|
|
55
|
+
filters: []
|
|
56
|
+
});
|
|
57
|
+
super.draw(out, {
|
|
58
|
+
...ctx,
|
|
59
|
+
time: offsetT
|
|
60
|
+
});
|
|
61
|
+
out.push({ op: "popGroup" });
|
|
62
|
+
}
|
|
63
|
+
} finally {
|
|
64
|
+
playhead.forceSet(now);
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
/** `children: [echo(mover, { count: 6, spacing: 0.05 })]` — mover leaves a fading trail.
|
|
70
|
+
* A convenience wrapper: pass the trailing content as children of the returned Echo. */
|
|
71
|
+
function echo(child, props = {}) {
|
|
72
|
+
return new Echo({
|
|
73
|
+
...props,
|
|
74
|
+
children: [child]
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
//#endregion
|
|
78
|
+
export { echo as n, Echo as t };
|
package/dist/examples.js
CHANGED
|
@@ -4,9 +4,10 @@ import { a as evaluate, i as createScene } from "./scene.js";
|
|
|
4
4
|
import { Grid } from "./grid.js";
|
|
5
5
|
import { Stack } from "./layoutCtors.js";
|
|
6
6
|
import { splitText } from "./type.js";
|
|
7
|
-
import { r as motionPath } from "./
|
|
7
|
+
import { i as orientToPath, r as lookAt, s as motionPath } from "./orient.js";
|
|
8
|
+
import { n as echo } from "./echo.js";
|
|
8
9
|
import { pathFromSvg } from "./path.js";
|
|
9
|
-
import { key, timeline, track } from "@glissade/core";
|
|
10
|
+
import { key, retime, timeline, track } from "@glissade/core";
|
|
10
11
|
//#region src/examples.ts
|
|
11
12
|
/**
|
|
12
13
|
* `@glissade/scene/examples` — the runnable example corpus (§0.24 onboarding,
|
|
@@ -204,6 +205,58 @@ const EXAMPLES = [
|
|
|
204
205
|
key: "motionPath",
|
|
205
206
|
code: "import { motionPath } from '@glissade/scene/motion';\nimport { pathFromSvg } from '@glissade/scene/path';\nconst mp = motionPath(pathFromSvg('M0 0 C50 0 50 100 100 100'));\nconst pointHalfway = mp.atProgress(0.5); // { x, y }",
|
|
206
207
|
run: () => void motionPath(pathFromSvg("M0 0 C50 0 50 100 100 100")).atProgress(.5)
|
|
208
|
+
},
|
|
209
|
+
{
|
|
210
|
+
key: "orientToPath",
|
|
211
|
+
code: "import { Rect } from '@glissade/scene';\nimport { orientToPath } from '@glissade/scene/motion';\nimport { pathFromSvg } from '@glissade/scene/path';\n// rotation-only sibling of followPath: banks the target to the path tangent while its\n// POSITION comes from elsewhere. Drive '<id>/progress' with a track; `offset` if it rests facing up.\nconst sprite = new Rect({ id: 'sprite', width: 12, height: 12 });\norientToPath(sprite, pathFromSvg('M0 0 L100 0 L100 100'), { id: 'bank', progress: 0.5 });",
|
|
212
|
+
run: () => {
|
|
213
|
+
orientToPath(new Rect({
|
|
214
|
+
id: "sprite",
|
|
215
|
+
width: 12,
|
|
216
|
+
height: 12
|
|
217
|
+
}), pathFromSvg("M0 0 L100 0 L100 100"), {
|
|
218
|
+
id: "bank",
|
|
219
|
+
progress: .5
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
},
|
|
223
|
+
{
|
|
224
|
+
key: "lookAt",
|
|
225
|
+
code: "import { Rect, Circle } from '@glissade/scene';\nimport { lookAt } from '@glissade/scene/motion';\n// aim the target's local +x axis at another node's world origin (a turret tracking a mover)\nconst turret = new Rect({ id: 'turret', width: 12, height: 12, position: [0, 0] });\nconst mover = new Circle({ id: 'mover', radius: 6, position: [40, 20] });\nlookAt(turret, mover);",
|
|
226
|
+
run: () => {
|
|
227
|
+
lookAt(new Rect({
|
|
228
|
+
id: "turret",
|
|
229
|
+
width: 12,
|
|
230
|
+
height: 12,
|
|
231
|
+
position: [0, 0]
|
|
232
|
+
}), new Circle({
|
|
233
|
+
id: "mover",
|
|
234
|
+
radius: 6,
|
|
235
|
+
position: [40, 20]
|
|
236
|
+
}));
|
|
237
|
+
}
|
|
238
|
+
},
|
|
239
|
+
{
|
|
240
|
+
key: "retime",
|
|
241
|
+
code: "import { retime, track, key } from '@glissade/core';\n// pure key-time transform → ordinary retimed tracks (speed / shift / reverse / pingpong)\nconst move = [track('box/position.x', 'number', [key(0, 0), key(1, 100, 'easeInCubic')])];\nconst slow = retime(move, { speed: 0.5 }); // half speed\nconst back = retime(move, { reverse: true }); // play it backward",
|
|
242
|
+
run: () => {
|
|
243
|
+
const move = [track("box/position.x", "number", [key(0, 0), key(1, 100, "easeInCubic")])];
|
|
244
|
+
retime(move, { speed: .5 });
|
|
245
|
+
retime(move, { reverse: true });
|
|
246
|
+
}
|
|
247
|
+
},
|
|
248
|
+
{
|
|
249
|
+
key: "echo",
|
|
250
|
+
code: "import { Circle, echo } from '@glissade/scene';\n// motion trail / onion-skin: renders the child at K past playhead offsets, each fading by `decay`.\n// Add the returned Echo to the scene; drive the child however you like (its ghosts re-derive at each offset).\nconst dot = new Circle({ id: 'dot', radius: 8, fill: '#39e0ff' });\nconst trail = echo(dot, { count: 6, spacing: 0.05, decay: 0.7 });",
|
|
251
|
+
run: () => void echo(new Circle({
|
|
252
|
+
id: "dot",
|
|
253
|
+
radius: 8,
|
|
254
|
+
fill: "#39e0ff"
|
|
255
|
+
}), {
|
|
256
|
+
count: 6,
|
|
257
|
+
spacing: .05,
|
|
258
|
+
decay: .7
|
|
259
|
+
})
|
|
207
260
|
}
|
|
208
261
|
];
|
|
209
262
|
/**
|
package/dist/identity.js
CHANGED
|
@@ -52,7 +52,8 @@ function emitWithIds(scene, doc, t) {
|
|
|
52
52
|
const ctx = {
|
|
53
53
|
time: t,
|
|
54
54
|
frame: fps !== void 0 ? Math.round(t * fps) : -1,
|
|
55
|
-
measurer: scene.textMeasurer
|
|
55
|
+
measurer: scene.textMeasurer,
|
|
56
|
+
playhead: scene.playhead
|
|
56
57
|
};
|
|
57
58
|
return evaluateAt(scene.playhead, t, () => {
|
|
58
59
|
const builder = instrument(createDisplayListBuilder(scene.size));
|
package/dist/index.d.ts
CHANGED
|
@@ -423,6 +423,28 @@ declare class ShaderEffect extends Group {
|
|
|
423
423
|
protected groupShader(): ShaderRef;
|
|
424
424
|
}
|
|
425
425
|
//#endregion
|
|
426
|
+
//#region src/echo.d.ts
|
|
427
|
+
interface EchoProps extends NodeProps {
|
|
428
|
+
children?: Node[];
|
|
429
|
+
/** total copies including the live one (≥ 1); default 5. */
|
|
430
|
+
count?: number;
|
|
431
|
+
/** seconds between successive copies (the trail's time spread); default 0.08. */
|
|
432
|
+
spacing?: number;
|
|
433
|
+
/** opacity multiplier per trailing step — copy i has opacity decay^i (0..1); default 0.6. */
|
|
434
|
+
decay?: number;
|
|
435
|
+
}
|
|
436
|
+
declare class Echo extends Group {
|
|
437
|
+
get describeType(): string;
|
|
438
|
+
readonly count: number;
|
|
439
|
+
readonly spacing: number;
|
|
440
|
+
readonly decay: number;
|
|
441
|
+
constructor(props?: EchoProps);
|
|
442
|
+
protected draw(out: DisplayListBuilder, ctx: EvalContext): void;
|
|
443
|
+
}
|
|
444
|
+
/** `children: [echo(mover, { count: 6, spacing: 0.05 })]` — mover leaves a fading trail.
|
|
445
|
+
* A convenience wrapper: pass the trailing content as children of the returned Echo. */
|
|
446
|
+
declare function echo(child: Node, props?: Omit<EchoProps, 'children'>): Echo;
|
|
447
|
+
//#endregion
|
|
426
448
|
//#region src/raster2d.d.ts
|
|
427
449
|
/** A backend gradient handle (DOM CanvasGradient and @napi-rs CanvasGradient both satisfy it). */
|
|
428
450
|
interface CanvasGradientLike {
|
|
@@ -619,4 +641,4 @@ declare function meshRasterSize(bw: number, bh: number): {
|
|
|
619
641
|
h: number;
|
|
620
642
|
};
|
|
621
643
|
//#endregion
|
|
622
|
-
export { ALL_FILTER_KINDS, type AnchorSpec, type BackendCaps, type BindablePropTarget, type BlendMode, type CanvasLike, Circle, ColdAssetError, type Ctx2DLike, Custom, DeterminismViolationError, type DisplayList, type DisplayListBuilder, type DrawCommand, type DrawOnEachOptions, type DrawOnOptions, DuplicateNodeIdError, type EachBox, type EachContext, type EachDistribute, EachError, type EachLayout, type EachMotion, type EachOpts, type EachResult, type EditMark, type EvalContext, type FilterKind, type FilterSpec, FilterValidationError, type FontByteLoader, type FontSpec, type GraphemeBox, Group, type GuardMode, type HachureSpec, Highlight, type HighlightProps, type HitArea, IDENTITY, ImageNode as Image, ImageNode, type ImageDataLike, type ImageHandle, type ImageProps, type LayoutBox, type LayoutChildSpec, type LayoutContainerSpec, type LayoutEngine, LayoutEngineMissingError, type LineBox, MEASURE_QUANTUM_PX, MESH_DOWNSCALE, MESH_SHEPARD_POWER, MESH_SIGMA, type Mat2x3, type MeshInterpolation, type MeshPaint, type MeshPoint, NODE_TAXONOMY, Node, NodeConstructionError, type NodeProps, type NodeTypeName, type Paint, Path, type PathLike, type PathProps, type PathSeg, type Place, type Polyline, type PropInit, Raster2D, type Raster2DHost, Rect, type Rect$1 as RectShape, type RenderBackend, ReservedNodeIdError, type ResolvedSketch, type Resource, type ResourceId, type RevealMark, type Scene, type SceneInit, type SceneModule, type ShaderCaps, ShaderEffect, type ShaderEffectProps, type ShaderRef, type ShapeProps, type SketchStyle, SketchValidationError, type StepMark, type StrokeStyle, Text, TextCursor, type TextCursorProps, type TextMeasurer, type TextMetricsLite, type TextProps, type TypeEdit, type TypewriterResult, type ValidateSceneFontsOptions, Video, type VideoFrameSource, type VideoProps, type WordBox, type WrappedTextMetrics, __resetEstimateWarnings, applyToPoint, arcLength, assertFiniteFontSize, bindScene, breakLines, coercePathData, collapseReplacer, collectLocalizedTextUsages, collectTextUsages, createDisplayListBuilder, createScene, drawOn, drawOnEach, each, estimatingMeasurer, evaluate, filtersToCanvasFilter, flatten, fontString, fromTRS, getLayoutEngine, glow, hachureLines, highlight, invert, isEstimatingMeasurer, matEquals, measureWrappedText, meshRasterSize, multiply, pathFromSegs, quantize, rasterizeMesh, requireLayoutEngine, resolveAnchor, resolveSketch, revealSchedule, roughen, roundedRectSegs, segmentGraphemes, segmentWords, setDefaultMeasurer, setLayoutEngine, sketchStrokes, textCursor, typewriter, validateFilters, validateHachure, validateSceneFonts, validateSketch, withDeterminismGuards };
|
|
644
|
+
export { ALL_FILTER_KINDS, type AnchorSpec, type BackendCaps, type BindablePropTarget, type BlendMode, type CanvasLike, Circle, ColdAssetError, type Ctx2DLike, Custom, DeterminismViolationError, type DisplayList, type DisplayListBuilder, type DrawCommand, type DrawOnEachOptions, type DrawOnOptions, DuplicateNodeIdError, type EachBox, type EachContext, type EachDistribute, EachError, type EachLayout, type EachMotion, type EachOpts, type EachResult, Echo, type EchoProps, type EditMark, type EvalContext, type FilterKind, type FilterSpec, FilterValidationError, type FontByteLoader, type FontSpec, type GraphemeBox, Group, type GuardMode, type HachureSpec, Highlight, type HighlightProps, type HitArea, IDENTITY, ImageNode as Image, ImageNode, type ImageDataLike, type ImageHandle, type ImageProps, type LayoutBox, type LayoutChildSpec, type LayoutContainerSpec, type LayoutEngine, LayoutEngineMissingError, type LineBox, MEASURE_QUANTUM_PX, MESH_DOWNSCALE, MESH_SHEPARD_POWER, MESH_SIGMA, type Mat2x3, type MeshInterpolation, type MeshPaint, type MeshPoint, NODE_TAXONOMY, Node, NodeConstructionError, type NodeProps, type NodeTypeName, type Paint, Path, type PathLike, type PathProps, type PathSeg, type Place, type Polyline, type PropInit, Raster2D, type Raster2DHost, Rect, type Rect$1 as RectShape, type RenderBackend, ReservedNodeIdError, type ResolvedSketch, type Resource, type ResourceId, type RevealMark, type Scene, type SceneInit, type SceneModule, type ShaderCaps, ShaderEffect, type ShaderEffectProps, type ShaderRef, type ShapeProps, type SketchStyle, SketchValidationError, type StepMark, type StrokeStyle, Text, TextCursor, type TextCursorProps, type TextMeasurer, type TextMetricsLite, type TextProps, type TypeEdit, type TypewriterResult, type ValidateSceneFontsOptions, Video, type VideoFrameSource, type VideoProps, type WordBox, type WrappedTextMetrics, __resetEstimateWarnings, applyToPoint, arcLength, assertFiniteFontSize, bindScene, breakLines, coercePathData, collapseReplacer, collectLocalizedTextUsages, collectTextUsages, createDisplayListBuilder, createScene, drawOn, drawOnEach, each, echo, estimatingMeasurer, evaluate, filtersToCanvasFilter, flatten, fontString, fromTRS, getLayoutEngine, glow, hachureLines, highlight, invert, isEstimatingMeasurer, matEquals, measureWrappedText, meshRasterSize, multiply, pathFromSegs, quantize, rasterizeMesh, requireLayoutEngine, resolveAnchor, resolveSketch, revealSchedule, roughen, roundedRectSegs, segmentGraphemes, segmentWords, setDefaultMeasurer, setLayoutEngine, sketchStrokes, textCursor, typewriter, validateFilters, validateHachure, validateSceneFonts, validateSketch, withDeterminismGuards };
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { $ as multiply, A as setDefaultMeasurer, B as validateHachure, C as estimatingMeasurer, D as quantize, E as measureWrappedText, F as hachureLines, G as glow, H as FilterValidationError, I as hashStr, J as IDENTITY, K as validateFilters, L as resolveSketch, M as SketchValidationError, N as arcLength, O as segmentGraphemes, P as flatten, Q as matEquals, R as roughen, S as breakLines, T as isEstimatingMeasurer, U as createDisplayListBuilder, V as validateSketch, W as filtersToCanvasFilter, X as fromTRS, Y as applyToPoint, Z as invert, a as Path, b as __resetEstimateWarnings, c as Video, d as revealSchedule, f as roundedRectSegs, h as resolveAnchor, i as ImageNode, k as segmentWords, l as coercePathData, m as NodeConstructionError, n as Custom, o as Rect, p as Node, q as collapseReplacer, r as Group, s as Text, t as Circle, u as pathFromSegs, x as assertFiniteFontSize, y as MEASURE_QUANTUM_PX, z as sketchStrokes } from "./nodes.js";
|
|
2
2
|
import { a as evaluate, i as createScene, n as ReservedNodeIdError, r as bindScene, t as DuplicateNodeIdError } from "./scene.js";
|
|
3
3
|
import { i as setLayoutEngine, n as getLayoutEngine, r as requireLayoutEngine, t as LayoutEngineMissingError } from "./layoutEngine.js";
|
|
4
|
+
import { n as echo, t as Echo } from "./echo.js";
|
|
4
5
|
import { buildFontRegistry, emitDevWarning, key, lerpColor, oklabToRgba, parseCmap, parseColor, random, rgbaToOklab, signal, stagger, track, validateFonts } from "@glissade/core";
|
|
5
6
|
//#region src/taxonomy.ts
|
|
6
7
|
/**
|
|
@@ -1356,4 +1357,4 @@ var Raster2D = class {
|
|
|
1356
1357
|
}
|
|
1357
1358
|
};
|
|
1358
1359
|
//#endregion
|
|
1359
|
-
export { ALL_FILTER_KINDS, Circle, ColdAssetError, Custom, DeterminismViolationError, DuplicateNodeIdError, EachError, FilterValidationError, Group, Highlight, IDENTITY, ImageNode as Image, ImageNode, LayoutEngineMissingError, MEASURE_QUANTUM_PX, MESH_DOWNSCALE, MESH_SHEPARD_POWER, MESH_SIGMA, NODE_TAXONOMY, Node, NodeConstructionError, Path, Raster2D, Rect, ReservedNodeIdError, ShaderEffect, SketchValidationError, Text, TextCursor, Video, __resetEstimateWarnings, applyToPoint, arcLength, assertFiniteFontSize, bindScene, breakLines, coercePathData, collapseReplacer, collectLocalizedTextUsages, collectTextUsages, createDisplayListBuilder, createScene, drawOn, drawOnEach, each, estimatingMeasurer, evaluate, filtersToCanvasFilter, flatten, fontString, fromTRS, getLayoutEngine, glow, hachureLines, highlight, invert, isEstimatingMeasurer, matEquals, measureWrappedText, meshRasterSize, multiply, pathFromSegs, quantize, rasterizeMesh, requireLayoutEngine, resolveAnchor, resolveSketch, revealSchedule, roughen, roundedRectSegs, segmentGraphemes, segmentWords, setDefaultMeasurer, setLayoutEngine, sketchStrokes, textCursor, typewriter, validateFilters, validateHachure, validateSceneFonts, validateSketch, withDeterminismGuards };
|
|
1360
|
+
export { ALL_FILTER_KINDS, Circle, ColdAssetError, Custom, DeterminismViolationError, DuplicateNodeIdError, EachError, Echo, FilterValidationError, Group, Highlight, IDENTITY, ImageNode as Image, ImageNode, LayoutEngineMissingError, MEASURE_QUANTUM_PX, MESH_DOWNSCALE, MESH_SHEPARD_POWER, MESH_SIGMA, NODE_TAXONOMY, Node, NodeConstructionError, Path, Raster2D, Rect, ReservedNodeIdError, ShaderEffect, SketchValidationError, Text, TextCursor, Video, __resetEstimateWarnings, applyToPoint, arcLength, assertFiniteFontSize, bindScene, breakLines, coercePathData, collapseReplacer, collectLocalizedTextUsages, collectTextUsages, createDisplayListBuilder, createScene, drawOn, drawOnEach, each, echo, estimatingMeasurer, evaluate, filtersToCanvasFilter, flatten, fontString, fromTRS, getLayoutEngine, glow, hachureLines, highlight, invert, isEstimatingMeasurer, matEquals, measureWrappedText, meshRasterSize, multiply, pathFromSegs, quantize, rasterizeMesh, requireLayoutEngine, resolveAnchor, resolveSketch, revealSchedule, roughen, roundedRectSegs, segmentGraphemes, segmentWords, setDefaultMeasurer, setLayoutEngine, sketchStrokes, textCursor, typewriter, validateFilters, validateHachure, validateSceneFonts, validateSketch, withDeterminismGuards };
|
package/dist/motion.d.ts
CHANGED
|
@@ -57,4 +57,58 @@ declare class FollowPath extends Node {
|
|
|
57
57
|
* Pass the Path *node* to follow it as it morphs; pass a PathValue for a fixed route. */
|
|
58
58
|
declare function followPath(target: Node, path: PathValue | Path, props?: Omit<FollowPathProps, 'target' | 'path'>): FollowPath;
|
|
59
59
|
//#endregion
|
|
60
|
-
|
|
60
|
+
//#region src/orient.d.ts
|
|
61
|
+
interface OrientToPathProps extends NodeProps {
|
|
62
|
+
/** the node whose `rotation` this owns (position is left to whatever drives it) */
|
|
63
|
+
target: Node;
|
|
64
|
+
/** a static PathValue, or a Path node followed LIVE (re-sampled as its `data` morphs) */
|
|
65
|
+
path: PathValue | Path;
|
|
66
|
+
/** 0→1 arc-length position whose TANGENT sets the angle; default 1. Track `<id>/progress`. */
|
|
67
|
+
progress?: PropInit<number>;
|
|
68
|
+
/** degrees added to the tangent angle (e.g. if the sprite points up at rest) */
|
|
69
|
+
offset?: number;
|
|
70
|
+
samplesPerSegment?: number;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Owns `target.rotation`, binding it to the path tangent at `progress` — a node
|
|
74
|
+
* banks to face the direction of travel while its POSITION is driven elsewhere
|
|
75
|
+
* (keyframes, layout, or a sibling followPath sharing the same `progress`). The
|
|
76
|
+
* rotation-only half of followPath's `orient`. Add it to the scene; it draws
|
|
77
|
+
* nothing. Animate `<id>/progress`.
|
|
78
|
+
*/
|
|
79
|
+
declare class OrientToPath extends Node {
|
|
80
|
+
readonly target: Node;
|
|
81
|
+
readonly progress: BindableSignal<number>;
|
|
82
|
+
constructor(props: OrientToPathProps);
|
|
83
|
+
protected draw(): void;
|
|
84
|
+
}
|
|
85
|
+
/** `children: [node, orientToPath(node, route, { progress: 0.5 })]` — node banks
|
|
86
|
+
* to the route's direction at progress 0.5 while its position comes from elsewhere. */
|
|
87
|
+
declare function orientToPath(target: Node, path: PathValue | Path, props?: Omit<OrientToPathProps, 'target' | 'path'>): OrientToPath;
|
|
88
|
+
interface LookAtProps extends NodeProps {
|
|
89
|
+
/** the node whose `rotation` this owns */
|
|
90
|
+
target: Node;
|
|
91
|
+
/** the node to face — `target` rotates so its +x axis points at `at`'s origin */
|
|
92
|
+
at: Node;
|
|
93
|
+
/** degrees added to the facing angle (e.g. if the sprite points up at rest, pass -90) */
|
|
94
|
+
offset?: number;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Owns `target.rotation`, aiming target's local +x axis at `at`'s world origin —
|
|
98
|
+
* a turret tracking a mover, an arrow pointing at a label. The angle is computed
|
|
99
|
+
* in WORLD space and applied as `target`'s LOCAL rotation, which is exact when
|
|
100
|
+
* target's parent is unrotated (the common case); a rotated parent would need
|
|
101
|
+
* the parent's world rotation subtracted (v1 does not). Pure: rotation re-derives
|
|
102
|
+
* from both nodes' positions each read, no stored state. Add it to the scene; it
|
|
103
|
+
* draws nothing.
|
|
104
|
+
*/
|
|
105
|
+
declare class LookAt extends Node {
|
|
106
|
+
readonly target: Node;
|
|
107
|
+
readonly at: Node;
|
|
108
|
+
constructor(props: LookAtProps);
|
|
109
|
+
protected draw(): void;
|
|
110
|
+
}
|
|
111
|
+
/** `children: [turret, mover, lookAt(turret, mover)]` — turret always faces the mover. */
|
|
112
|
+
declare function lookAt(target: Node, at: Node, props?: Omit<LookAtProps, 'target' | 'at'>): LookAt;
|
|
113
|
+
//#endregion
|
|
114
|
+
export { FollowPath, type FollowPathProps, LookAt, type LookAtProps, OrientToPath, type OrientToPathProps, type PathSampler, followPath, lookAt, motionPath, orientToPath, pathLength, pointAtLength };
|
package/dist/motion.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as
|
|
2
|
-
export { FollowPath, followPath, motionPath, pathLength, pointAtLength };
|
|
1
|
+
import { a as FollowPath, c as pathLength, i as orientToPath, l as pointAtLength, n as OrientToPath, o as followPath, r as lookAt, s as motionPath, t as LookAt } from "./orient.js";
|
|
2
|
+
export { FollowPath, LookAt, OrientToPath, followPath, lookAt, motionPath, orientToPath, pathLength, pointAtLength };
|
package/dist/nodes.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { C as Mat2x3, a as FilterSpec, d as Paint, f as PathSeg, g as ShaderRef, r as DisplayListBuilder, s as FontSpec, t as BlendMode } from "./displayList.js";
|
|
2
|
-
import { BindableSignal, FontAxes, PathValue, ReadonlySignal, Rng, Track, ValueTypeId, Vec2, Vec2Signal } from "@glissade/core";
|
|
2
|
+
import { BindableSignal, FontAxes, PathValue, Playhead, ReadonlySignal, Rng, Track, ValueTypeId, Vec2, Vec2Signal } from "@glissade/core";
|
|
3
3
|
|
|
4
4
|
//#region src/text.d.ts
|
|
5
5
|
|
|
@@ -125,6 +125,16 @@ interface EvalContext {
|
|
|
125
125
|
readonly frame: number;
|
|
126
126
|
/** Injected by mount()/CLI/exporters (§3.2): the active backend's measurer. */
|
|
127
127
|
readonly measurer: TextMeasurer;
|
|
128
|
+
/**
|
|
129
|
+
* The scene playhead being driven this evaluate. The one channel a node may
|
|
130
|
+
* re-address WITHIN a frame to sample its subtree at an OFFSET time (Echo's
|
|
131
|
+
* trails / onion-skin) — always restored before the walk continues, so it is
|
|
132
|
+
* a pure, re-entrant read. Everything else reads it only through bound signals.
|
|
133
|
+
* Optional: the real evaluate()/emitWithIds()/cache-cold audit always supply
|
|
134
|
+
* it; a bare hand-built ctx (a unit test emitting one node) may omit it, and a
|
|
135
|
+
* playhead-dependent node degrades gracefully (Echo → a plain group).
|
|
136
|
+
*/
|
|
137
|
+
readonly playhead?: Playhead;
|
|
128
138
|
}
|
|
129
139
|
/** A property initializer: a value, or a computed source (§2.1). */
|
|
130
140
|
type PropInit<T> = T | (() => T);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as Path, p as Node } from "./nodes.js";
|
|
1
|
+
import { Y as applyToPoint, a as Path, p as Node } from "./nodes.js";
|
|
2
2
|
import { signal } from "@glissade/core";
|
|
3
3
|
//#region src/motionPath.ts
|
|
4
4
|
/**
|
|
@@ -168,4 +168,110 @@ function followPath(target, path, props = {}) {
|
|
|
168
168
|
});
|
|
169
169
|
}
|
|
170
170
|
//#endregion
|
|
171
|
-
|
|
171
|
+
//#region src/orient.ts
|
|
172
|
+
/**
|
|
173
|
+
* Orientation drivers: rotate a node to face where it's *heading* (a path
|
|
174
|
+
* tangent) or to *look at* another node. Both are the rotation-only siblings of
|
|
175
|
+
* `followPath` — companion driver nodes that own only the target's `rotation`
|
|
176
|
+
* via pull-based binding, so position stays whatever else drives it (keyframes,
|
|
177
|
+
* layout, a separate followPath). Nothing executes at play time beyond a pure
|
|
178
|
+
* read of signals already in the graph, so evaluate() stays a pure function of
|
|
179
|
+
* time and the goldens are byte-stable.
|
|
180
|
+
*
|
|
181
|
+
* These live on the tree-shakeable `@glissade/scene/motion` subpath (like
|
|
182
|
+
* followPath) — the base embed doesn't pay for them.
|
|
183
|
+
*/
|
|
184
|
+
const DEG = 180 / Math.PI;
|
|
185
|
+
/**
|
|
186
|
+
* A node's origin in WORLD space, computed WITHOUT reading its own
|
|
187
|
+
* `worldMatrix`/`localMatrix` signals. That matters for the node being oriented:
|
|
188
|
+
* its localMatrix depends on `rotation`, so a rotation source that read
|
|
189
|
+
* `this.worldMatrix()` would form a rotation → worldMatrix → rotation cycle.
|
|
190
|
+
* Projecting `position` through the PARENT's world transform breaks the cycle
|
|
191
|
+
* (a parent's world never depends on its child's rotation). This aims from the
|
|
192
|
+
* node's position origin; an explicit `anchor` offset is not applied (v1).
|
|
193
|
+
*/
|
|
194
|
+
function worldOrigin(n) {
|
|
195
|
+
return n.parent ? applyToPoint(n.parent.worldMatrix(), n.position()) : n.position();
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Owns `target.rotation`, binding it to the path tangent at `progress` — a node
|
|
199
|
+
* banks to face the direction of travel while its POSITION is driven elsewhere
|
|
200
|
+
* (keyframes, layout, or a sibling followPath sharing the same `progress`). The
|
|
201
|
+
* rotation-only half of followPath's `orient`. Add it to the scene; it draws
|
|
202
|
+
* nothing. Animate `<id>/progress`.
|
|
203
|
+
*/
|
|
204
|
+
var OrientToPath = class extends Node {
|
|
205
|
+
target;
|
|
206
|
+
progress;
|
|
207
|
+
constructor(props) {
|
|
208
|
+
super(props);
|
|
209
|
+
this.target = props.target;
|
|
210
|
+
this.progress = signal(1);
|
|
211
|
+
if (typeof props.progress === "function") this.progress.bindSource(props.progress);
|
|
212
|
+
else if (props.progress !== void 0) this.progress.set(props.progress);
|
|
213
|
+
this.registerTarget("progress", this.progress, "number");
|
|
214
|
+
const sOpts = props.samplesPerSegment !== void 0 ? { samplesPerSegment: props.samplesPerSegment } : {};
|
|
215
|
+
const getPath = props.path instanceof Path ? () => props.path.data() : () => props.path;
|
|
216
|
+
let cachedPath = getPath();
|
|
217
|
+
let cachedSampler = motionPath(cachedPath, sOpts);
|
|
218
|
+
const sampler = () => {
|
|
219
|
+
const pv = getPath();
|
|
220
|
+
if (pv !== cachedPath) {
|
|
221
|
+
cachedPath = pv;
|
|
222
|
+
cachedSampler = motionPath(pv, sOpts);
|
|
223
|
+
}
|
|
224
|
+
return cachedSampler;
|
|
225
|
+
};
|
|
226
|
+
const offset = props.offset ?? 0;
|
|
227
|
+
props.target.rotation.bindSource(() => {
|
|
228
|
+
const t = sampler().tangentAtProgress(this.progress());
|
|
229
|
+
return Math.atan2(t[1], t[0]) * DEG + offset;
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
draw() {}
|
|
233
|
+
};
|
|
234
|
+
/** `children: [node, orientToPath(node, route, { progress: 0.5 })]` — node banks
|
|
235
|
+
* to the route's direction at progress 0.5 while its position comes from elsewhere. */
|
|
236
|
+
function orientToPath(target, path, props = {}) {
|
|
237
|
+
return new OrientToPath({
|
|
238
|
+
...props,
|
|
239
|
+
target,
|
|
240
|
+
path
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Owns `target.rotation`, aiming target's local +x axis at `at`'s world origin —
|
|
245
|
+
* a turret tracking a mover, an arrow pointing at a label. The angle is computed
|
|
246
|
+
* in WORLD space and applied as `target`'s LOCAL rotation, which is exact when
|
|
247
|
+
* target's parent is unrotated (the common case); a rotated parent would need
|
|
248
|
+
* the parent's world rotation subtracted (v1 does not). Pure: rotation re-derives
|
|
249
|
+
* from both nodes' positions each read, no stored state. Add it to the scene; it
|
|
250
|
+
* draws nothing.
|
|
251
|
+
*/
|
|
252
|
+
var LookAt = class extends Node {
|
|
253
|
+
target;
|
|
254
|
+
at;
|
|
255
|
+
constructor(props) {
|
|
256
|
+
super(props);
|
|
257
|
+
this.target = props.target;
|
|
258
|
+
this.at = props.at;
|
|
259
|
+
const offset = props.offset ?? 0;
|
|
260
|
+
props.target.rotation.bindSource(() => {
|
|
261
|
+
const self = worldOrigin(props.target);
|
|
262
|
+
const to = worldOrigin(props.at);
|
|
263
|
+
return Math.atan2(to[1] - self[1], to[0] - self[0]) * DEG + offset;
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
draw() {}
|
|
267
|
+
};
|
|
268
|
+
/** `children: [turret, mover, lookAt(turret, mover)]` — turret always faces the mover. */
|
|
269
|
+
function lookAt(target, at, props = {}) {
|
|
270
|
+
return new LookAt({
|
|
271
|
+
...props,
|
|
272
|
+
target,
|
|
273
|
+
at
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
//#endregion
|
|
277
|
+
export { FollowPath as a, pathLength as c, orientToPath as i, pointAtLength as l, OrientToPath as n, followPath as o, lookAt as r, motionPath as s, LookAt as t };
|
package/dist/scene.js
CHANGED
|
@@ -109,7 +109,8 @@ function evaluate(scene, doc, t = 0) {
|
|
|
109
109
|
const ctx = {
|
|
110
110
|
time: t,
|
|
111
111
|
frame: fps !== void 0 ? Math.round(t * fps) : -1,
|
|
112
|
-
measurer: scene.textMeasurer
|
|
112
|
+
measurer: scene.textMeasurer,
|
|
113
|
+
playhead: scene.playhead
|
|
113
114
|
};
|
|
114
115
|
return evaluateAt(scene.playhead, t, () => {
|
|
115
116
|
const out = createDisplayListBuilder(scene.size);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@glissade/scene",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.26.0-pre.1",
|
|
4
4
|
"description": "glissade scene graph: nodes, transforms, DisplayList emission. Renderer-agnostic; zero DOM/Node dependencies.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"engines": {
|
|
@@ -65,7 +65,7 @@
|
|
|
65
65
|
],
|
|
66
66
|
"dependencies": {
|
|
67
67
|
"yoga-layout": "^3.2.1",
|
|
68
|
-
"@glissade/core": "0.
|
|
68
|
+
"@glissade/core": "0.26.0-pre.1"
|
|
69
69
|
},
|
|
70
70
|
"repository": {
|
|
71
71
|
"type": "git",
|