@glissade/scene 0.12.1 → 0.13.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/index.d.ts +133 -4
- package/dist/index.js +164 -6
- package/dist/layout.js +1 -1
- package/dist/layoutEngine.js +16 -3
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { $ as breakLines, A as Polyline, At as invert, B as validateHachure, C as Video, Ct as filtersToCanvasFilter, D as revealSchedule, Dt as Mat2x3, E as pathFromSegs, Et as IDENTITY, F as flatten, G as HitArea, H as AnchorSpec, I as hachureLines, J as PropInit, K as Node, L as resolveSketch, M as SketchStyle, Mt as multiply, N as SketchValidationError, O as roundedRectSegs, Ot as applyToPoint, P as arcLength, Q as TextMetricsLite, R as roughen, S as TextProps, St as createDisplayListBuilder, T as WordBox, Tt as validateFilters, U as BindablePropTarget, V as validateSketch, W as EvalContext, X as MEASURE_QUANTUM_PX, Y as resolveAnchor, Z as TextMeasurer, _ as PathProps, _t as Rect$1, a as LayoutEngineMissingError, at as BlendMode, b as ShapeProps, bt as ShaderRef, c as requireLayoutEngine, ct as DrawCommand, d as Custom, dt as FontSpec, et as estimatingMeasurer, f as Group, ft as MeshInterpolation, g as Path, gt as PathSeg, h as LineBox, ht as Paint, i as LayoutEngine, it as setDefaultMeasurer, j as ResolvedSketch, jt as matEquals, k as HachureSpec, kt as fromTRS, l as setLayoutEngine, lt as FilterSpec, m as ImageProps, mt as MeshPoint, n as LayoutChildSpec, nt as segmentGraphemes, ot as DisplayList, p as ImageNode, pt as MeshPaint, q as NodeProps, r as LayoutContainerSpec, rt as segmentWords, s as getLayoutEngine, st as DisplayListBuilder, t as LayoutBox, tt as quantize, u as Circle, ut as FilterValidationError, v as Rect, vt as Resource, w as VideoProps, wt as glow, x as Text, xt as StrokeStyle, y as RevealMark, yt as ResourceId, z as sketchStrokes } from "./layoutEngine.js";
|
|
2
|
-
import { BindableSignal, BoundTimeline, CompiledTimeline, CoverageReport, EaseSpec, FontMode, FontUsage, MeshPaint as MeshPaint$1, PathValue, Playhead, Timeline, Track, Vec2 } from "@glissade/core";
|
|
2
|
+
import { BindableSignal, BoundTimeline, CompiledTimeline, CoverageReport, EaseSpec, FontMode, FontUsage, MeshPaint as MeshPaint$1, PathValue, Playhead, Rng, Timeline, Track, Vec2 } from "@glissade/core";
|
|
3
|
+
import { ChannelOverride, Clip } from "@glissade/core/clips";
|
|
3
4
|
|
|
4
5
|
//#region src/taxonomy.d.ts
|
|
5
6
|
|
|
@@ -137,6 +138,125 @@ declare function typewriter(target: string, edits: readonly TypeEdit[], opts?: {
|
|
|
137
138
|
gap?: number;
|
|
138
139
|
}): TypewriterResult;
|
|
139
140
|
//#endregion
|
|
141
|
+
//#region src/each.d.ts
|
|
142
|
+
/** An aspect-fraction placement: [fx, fy], each conventionally in [0, 1]. */
|
|
143
|
+
type Place = readonly [number, number];
|
|
144
|
+
/**
|
|
145
|
+
* Built-in layouts (a discriminated union — NOT factory fns) plus the escape
|
|
146
|
+
* hatch `(i, n) => [fx, fy]`. Every built-in is PURE arithmetic in aspect
|
|
147
|
+
* fractions; mapping to px happens only when `box` is given (see `places`).
|
|
148
|
+
*/
|
|
149
|
+
type EachLayout = {
|
|
150
|
+
kind: 'row';
|
|
151
|
+
gap?: number;
|
|
152
|
+
align?: number;
|
|
153
|
+
} | {
|
|
154
|
+
kind: 'column';
|
|
155
|
+
gap?: number;
|
|
156
|
+
align?: number;
|
|
157
|
+
} | {
|
|
158
|
+
kind: 'grid';
|
|
159
|
+
cols: number;
|
|
160
|
+
rows?: number;
|
|
161
|
+
gapX?: number;
|
|
162
|
+
gapY?: number;
|
|
163
|
+
order?: 'row' | 'column';
|
|
164
|
+
} | {
|
|
165
|
+
kind: 'ring';
|
|
166
|
+
radius?: number;
|
|
167
|
+
center?: Place;
|
|
168
|
+
startAngle?: number;
|
|
169
|
+
sweep?: number;
|
|
170
|
+
} | ((i: number, n: number) => Place);
|
|
171
|
+
/** How a `stagger` delay distributes across the clones. */
|
|
172
|
+
type EachDistribute = 'delay' | 'from-center' | 'from-edges';
|
|
173
|
+
/** Per-index motion: a clip fanned across the clones with stagger + jitter. */
|
|
174
|
+
interface EachMotion {
|
|
175
|
+
/** The motion clip applied to every clone (TYPE: `Clip` from core/clips). */
|
|
176
|
+
clip: Clip;
|
|
177
|
+
/** Wall-clock start second of the first clone. Default 0. */
|
|
178
|
+
startSec?: number;
|
|
179
|
+
/** Per-index delay (seconds) or a function of the index. Default 0. */
|
|
180
|
+
stagger?: number | ((i: number) => number);
|
|
181
|
+
/**
|
|
182
|
+
* Shape a numeric `stagger` gap into a distribution. `from-center` ramps the
|
|
183
|
+
* delay outward from the middle clone, `from-edges` inward toward it; `delay`
|
|
184
|
+
* (the default) is the plain `i * gap` ramp. Ignored when `stagger` is a fn.
|
|
185
|
+
*/
|
|
186
|
+
distribute?: EachDistribute;
|
|
187
|
+
/** Per-index clip overrides, seeded — `(i, rng, n) => overrides`. */
|
|
188
|
+
jitter?: (i: number, rng: Rng, n: number) => Record<string, ChannelOverride>;
|
|
189
|
+
/** Clip speed (passed straight to `clip.apply`). */
|
|
190
|
+
speed?: number;
|
|
191
|
+
}
|
|
192
|
+
/** Pixel box for mapping aspect-fraction places to a concrete coordinate frame. */
|
|
193
|
+
interface EachBox {
|
|
194
|
+
w: number;
|
|
195
|
+
h: number;
|
|
196
|
+
/** Top-left of the box in scene coords; default [0, 0]. */
|
|
197
|
+
origin?: Place;
|
|
198
|
+
}
|
|
199
|
+
interface EachOpts {
|
|
200
|
+
/** Stable id prefix; clones are `${id}/${i}`, the wrapping group is `${id}`. */
|
|
201
|
+
id: string;
|
|
202
|
+
layout: EachLayout;
|
|
203
|
+
motion?: EachMotion;
|
|
204
|
+
/** Seed for per-clone RNG; defaults to a stable hash of `id`. */
|
|
205
|
+
seed?: number;
|
|
206
|
+
/** When given, `places` also carries the px-mapped points (see EachResult). */
|
|
207
|
+
box?: EachBox;
|
|
208
|
+
}
|
|
209
|
+
/** The per-clone authoring context handed to the factory. */
|
|
210
|
+
interface EachContext {
|
|
211
|
+
/** Clone index, 0..n-1. */
|
|
212
|
+
i: number;
|
|
213
|
+
/** Total clone count. */
|
|
214
|
+
n: number;
|
|
215
|
+
/** This clone's id (`${opts.id}/${i}`). */
|
|
216
|
+
id: string;
|
|
217
|
+
/** Aspect-fraction placement [fx, fy] — ALWAYS a fraction (px is separate). */
|
|
218
|
+
place: Place;
|
|
219
|
+
/** Seeded generator for this clone: `random(mix(seed, i))`. */
|
|
220
|
+
rng: Rng;
|
|
221
|
+
/** The resolved base seed (`opts.seed ?? hash(id)`). */
|
|
222
|
+
seed: number;
|
|
223
|
+
}
|
|
224
|
+
interface EachResult {
|
|
225
|
+
/** The wrapping group (`id: opts.id`) holding every generated child. */
|
|
226
|
+
node: Group;
|
|
227
|
+
/** The generated children, in index order. */
|
|
228
|
+
children: Node[];
|
|
229
|
+
/** The compiled motion tracks (empty when no `motion`). */
|
|
230
|
+
tracks: Track[];
|
|
231
|
+
/** Max child clip end (== startSec when no motion). */
|
|
232
|
+
end: number;
|
|
233
|
+
/**
|
|
234
|
+
* Per-clone placement. `frac` is the aspect-fraction [fx, fy] every layout
|
|
235
|
+
* produces; `px` is present only when `opts.box` was given (frac mapped into
|
|
236
|
+
* the box). Authoring a `box` once here is the single place fraction→px lives.
|
|
237
|
+
*/
|
|
238
|
+
places: {
|
|
239
|
+
frac: Place;
|
|
240
|
+
px?: Place;
|
|
241
|
+
}[];
|
|
242
|
+
}
|
|
243
|
+
declare class EachError extends Error {
|
|
244
|
+
constructor(message: string);
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* Generate `n` clones from `factory`, lay them out, and (optionally) stagger a
|
|
248
|
+
* motion clip across them.
|
|
249
|
+
*
|
|
250
|
+
* const grid = each(9, (i) => new Rect({ width: 40, height: 40, fill: '#9ef0c0' }), {
|
|
251
|
+
* id: 'card',
|
|
252
|
+
* layout: { kind: 'grid', cols: 3 },
|
|
253
|
+
* box: { w: 600, h: 360 },
|
|
254
|
+
* motion: { clip: popIn(), stagger: 0.08, distribute: 'from-center' },
|
|
255
|
+
* });
|
|
256
|
+
* // scene children: [grid.node]; timeline tracks: [...grid.tracks]
|
|
257
|
+
*/
|
|
258
|
+
declare function each(n: number, factory: (i: number, ctx: EachContext) => Node, opts: EachOpts): EachResult;
|
|
259
|
+
//#endregion
|
|
140
260
|
//#region src/drawOn.d.ts
|
|
141
261
|
interface DrawOnOptions {
|
|
142
262
|
/** when the stroke-on starts, seconds; default 0 */
|
|
@@ -192,12 +312,21 @@ declare function withDeterminismGuards<T>(mode: GuardMode, fn: () => T): T;
|
|
|
192
312
|
* - ArrayBuffer / typed-array views collapse to a `ab:<len>` / `view:<len>`
|
|
193
313
|
* length marker (opaque binary never belongs in a structural key).
|
|
194
314
|
* - Functions drop (JSON would already drop them; explicit for parity).
|
|
315
|
+
* - Non-finite numbers (`NaN`/`Infinity`/`-Infinity`) collapse to DISTINCT
|
|
316
|
+
* string sentinels. `JSON.stringify` natively serializes all three to the
|
|
317
|
+
* SAME token (`null`), which would collide the cacheKey of two DisplayLists
|
|
318
|
+
* that differ only in WHICH non-finite value reaches a draw field — a stale
|
|
319
|
+
* raster + an `auditCacheCold` false-OK. The distinct sentinels keep them
|
|
320
|
+
* apart. This does NOT touch FINITE numbers (the common path): only the
|
|
321
|
+
* three non-finite inputs change, so the §3.5 cacheKey bytes for every real
|
|
322
|
+
* (finite) list are byte-identical — pinned by the regression guard.
|
|
195
323
|
*
|
|
196
324
|
* NOTE: `-0` is intentionally NOT normalized here. The matrix layer
|
|
197
325
|
* (`matrix.ts`) already normalizes `-0 → 0` at the source, and adding a `-0`
|
|
198
326
|
* pass to THIS replacer would change the cacheKey bytes for any list that ever
|
|
199
|
-
* carried a raw `-0` — silently invalidating the cache cluster-wide.
|
|
200
|
-
*
|
|
327
|
+
* carried a raw `-0` — silently invalidating the cache cluster-wide. (`-0` is
|
|
328
|
+
* finite, so the non-finite branch never touches it.) Byte preservation wins;
|
|
329
|
+
* the regression guard pins the exact key.
|
|
201
330
|
*/
|
|
202
331
|
declare function collapseReplacer(_key: string, value: unknown): unknown;
|
|
203
332
|
/**
|
|
@@ -747,4 +876,4 @@ declare function meshRasterSize(bw: number, bh: number): {
|
|
|
747
876
|
h: number;
|
|
748
877
|
};
|
|
749
878
|
//#endregion
|
|
750
|
-
export { ALL_FILTER_KINDS, type AnchorSpec, type BackendCaps, type BindablePropTarget, type BlendMode, type CacheColdResult, type CanvasLike, Circle, ColdAssetError, type CommandDelta, type Ctx2DLike, Custom, DL_SNAPSHOT_VERSION, DeterminismViolationError, type DisplayDiff, type DisplayList, type DisplayListBuilder, type DlSnapshot, DlSnapshotError, type DrawCommand, type DrawOnEachOptions, type DrawOnOptions, DuplicateNodeIdError, type EditMark, type EvalContext, type FieldChange, type FilterKind, type FilterSpec, FilterValidationError, FollowPath, type FollowPathProps, type FontByteLoader, type FontSpec, 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, type NodeProps, type NodeTypeName, type Paint, Path, type PathLike, type PathProps, type PathSampler, type PathSeg, 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, TokenHighlight, type TokenHighlightProps, TokenMatchError, type TokenRange, type TypeEdit, type TypewriterResult, type ValidateSceneFontsOptions, Video, type VideoFrameSource, type VideoProps, type WordBox, applyToPoint, arcLength, auditCacheCold, bindScene, breakLines, collapseReplacer, collectTextUsages, createDisplayListBuilder, createScene, diffDisplayLists, drawOn, drawOnEach, estimatingMeasurer, evaluate, filtersToCanvasFilter, flatten, followPath, fontString, formatDisplayDiff, fromTRS, getLayoutEngine, glow, hachureLines, highlight, invert, matEquals, matchTokenRun, meshRasterSize, motionPath, multiply, parseDisplaySnapshot, pathFromSegs, pathLength, pointAtLength, quantize, rasterizeMesh, requireLayoutEngine, resolveAnchor, resolveSketch, revealSchedule, roughen, roundedRectSegs, segmentGraphemes, segmentWords, serializeDisplayList, setDefaultMeasurer, setLayoutEngine, sketchStrokes, textCursor, tokenHighlight, typewriter, validateFilters, validateHachure, validateSceneFonts, validateSketch, withDeterminismGuards };
|
|
879
|
+
export { ALL_FILTER_KINDS, type AnchorSpec, type BackendCaps, type BindablePropTarget, type BlendMode, type CacheColdResult, type CanvasLike, Circle, ColdAssetError, type CommandDelta, type Ctx2DLike, Custom, DL_SNAPSHOT_VERSION, DeterminismViolationError, type DisplayDiff, type DisplayList, type DisplayListBuilder, type DlSnapshot, DlSnapshotError, 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 FieldChange, type FilterKind, type FilterSpec, FilterValidationError, FollowPath, type FollowPathProps, type FontByteLoader, type FontSpec, 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, type NodeProps, type NodeTypeName, type Paint, Path, type PathLike, type PathProps, type PathSampler, 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, TokenHighlight, type TokenHighlightProps, TokenMatchError, type TokenRange, type TypeEdit, type TypewriterResult, type ValidateSceneFontsOptions, Video, type VideoFrameSource, type VideoProps, type WordBox, applyToPoint, arcLength, auditCacheCold, bindScene, breakLines, collapseReplacer, collectTextUsages, createDisplayListBuilder, createScene, diffDisplayLists, drawOn, drawOnEach, each, estimatingMeasurer, evaluate, filtersToCanvasFilter, flatten, followPath, fontString, formatDisplayDiff, fromTRS, getLayoutEngine, glow, hachureLines, highlight, invert, matEquals, matchTokenRun, meshRasterSize, motionPath, multiply, parseDisplaySnapshot, pathFromSegs, pathLength, pointAtLength, quantize, rasterizeMesh, requireLayoutEngine, resolveAnchor, resolveSketch, revealSchedule, roughen, roundedRectSegs, segmentGraphemes, segmentWords, serializeDisplayList, setDefaultMeasurer, setLayoutEngine, sketchStrokes, textCursor, tokenHighlight, typewriter, validateFilters, validateHachure, validateSceneFonts, validateSketch, withDeterminismGuards };
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { A as
|
|
2
|
-
import { bindTimeline, buildFontRegistry, compileTimeline, createPlayhead, emitDevWarning, evaluateAt, key, lerpColor, oklabToRgba, parseCmap, parseColor, rgbaToOklab, signal, stagger, track, validateFonts, vec2Signal } from "@glissade/core";
|
|
1
|
+
import { $ as multiply, A as estimatingMeasurer, B as validateFilters, C as sketchStrokes, D as resolveAnchor, E as Node, F as setDefaultMeasurer, G as formatDisplayDiff, H as DlSnapshotError, I as FilterValidationError, J as IDENTITY, K as parseDisplaySnapshot, L as createDisplayListBuilder, M as quantize, N as segmentGraphemes, O as MEASURE_QUANTUM_PX, P as segmentWords, Q as matEquals, R as filtersToCanvasFilter, S as roughen, T as validateSketch, U as collapseReplacer, V as DL_SNAPSHOT_VERSION, W as diffDisplayLists, X as fromTRS, Y as applyToPoint, Z as invert, _ as arcLength, a as Circle, b as hashStr, c as ImageNode, d as Text, f as Video, g as SketchValidationError, h as roundedRectSegs, i as setLayoutEngine, j as fallbackMeasurer, k as breakLines, l as Path, m as revealSchedule, n as getLayoutEngine, o as Custom, p as pathFromSegs, q as serializeDisplayList, r as requireLayoutEngine, s as Group, t as LayoutEngineMissingError, u as Rect, v as flatten, w as validateHachure, x as resolveSketch, y as hachureLines, z as glow } from "./layoutEngine.js";
|
|
2
|
+
import { bindTimeline, buildFontRegistry, compileTimeline, createPlayhead, emitDevWarning, evaluateAt, key, lerpColor, oklabToRgba, parseCmap, parseColor, random, rgbaToOklab, signal, stagger, track, validateFonts, vec2Signal } from "@glissade/core";
|
|
3
3
|
//#region src/taxonomy.ts
|
|
4
4
|
/**
|
|
5
5
|
* The CLOSED node taxonomy (DESIGN.md §3.1): exactly nine built-in node TYPES.
|
|
@@ -257,6 +257,163 @@ function typewriter(target, edits, opts = {}) {
|
|
|
257
257
|
};
|
|
258
258
|
}
|
|
259
259
|
//#endregion
|
|
260
|
+
//#region src/each.ts
|
|
261
|
+
/**
|
|
262
|
+
* `each()` — deterministic parametric instancing (0.13 clip-tier sugar). Pure
|
|
263
|
+
* BUILD-TIME fan-out: it generates N scene nodes from a factory, lays them out
|
|
264
|
+
* in aspect-fraction space, and (optionally) staggers a motion `clip` across
|
|
265
|
+
* them — compiling to ordinary keyed `Track[]` plus a `Group` of children with
|
|
266
|
+
* stable `${id}/${i}` ids. Nothing executes at play time; the emitted tracks are
|
|
267
|
+
* byte-indistinguishable from hand-authored ones, so goldens hold by
|
|
268
|
+
* construction and every `--workers` export shard reconstructs the same id set.
|
|
269
|
+
*
|
|
270
|
+
* The clip runtime is imported TYPE-ONLY (the `Clip` instance the author passes
|
|
271
|
+
* carries its own `apply`), so `each` adds no clip bytes to the embed: the
|
|
272
|
+
* `@glissade/core/clips` runtime lands in the consumer's bundle, never scene's.
|
|
273
|
+
*/
|
|
274
|
+
var EachError = class extends Error {
|
|
275
|
+
constructor(message) {
|
|
276
|
+
super(message);
|
|
277
|
+
this.name = "EachError";
|
|
278
|
+
}
|
|
279
|
+
};
|
|
280
|
+
/**
|
|
281
|
+
* Fold a base seed and an index into a fresh per-clone seed. splitmix-style
|
|
282
|
+
* avalanche so adjacent indices decorrelate (a bare `seed + i` would hand
|
|
283
|
+
* near-identical streams to neighbours).
|
|
284
|
+
*/
|
|
285
|
+
function mix(seed, i) {
|
|
286
|
+
let h = (seed ^ Math.imul(i + 1, 2654435769)) >>> 0;
|
|
287
|
+
h = Math.imul(h ^ h >>> 16, 569420461);
|
|
288
|
+
h = Math.imul(h ^ h >>> 15, 1935289751);
|
|
289
|
+
return (h ^ h >>> 15) >>> 0;
|
|
290
|
+
}
|
|
291
|
+
/** Resolve a built-in layout (or call the escape-hatch fn) to a fraction. */
|
|
292
|
+
function placeAt(layout, i, n) {
|
|
293
|
+
if (typeof layout === "function") return layout(i, n);
|
|
294
|
+
switch (layout.kind) {
|
|
295
|
+
case "row": {
|
|
296
|
+
const gap = layout.gap ?? (n > 1 ? 1 / (n - 1) : 0);
|
|
297
|
+
const align = layout.align ?? .5;
|
|
298
|
+
const x0 = .5 - gap * (n - 1) / 2;
|
|
299
|
+
return [n === 1 ? .5 : x0 + gap * i, align];
|
|
300
|
+
}
|
|
301
|
+
case "column": {
|
|
302
|
+
const gap = layout.gap ?? (n > 1 ? 1 / (n - 1) : 0);
|
|
303
|
+
const align = layout.align ?? .5;
|
|
304
|
+
const y0 = .5 - gap * (n - 1) / 2;
|
|
305
|
+
return [align, n === 1 ? .5 : y0 + gap * i];
|
|
306
|
+
}
|
|
307
|
+
case "grid": {
|
|
308
|
+
const cols = layout.cols;
|
|
309
|
+
if (!(cols >= 1)) throw new EachError(`grid layout needs cols >= 1 (got ${cols})`);
|
|
310
|
+
const rows = layout.rows ?? Math.ceil(n / cols);
|
|
311
|
+
const order = layout.order ?? "row";
|
|
312
|
+
const col = order === "row" ? i % cols : Math.floor(i / rows);
|
|
313
|
+
const row = order === "row" ? Math.floor(i / cols) : i % rows;
|
|
314
|
+
const gapX = layout.gapX ?? (cols > 1 ? 1 / (cols - 1) : 0);
|
|
315
|
+
const gapY = layout.gapY ?? (rows > 1 ? 1 / (rows - 1) : 0);
|
|
316
|
+
const spanX = gapX * (cols - 1);
|
|
317
|
+
const spanY = gapY * (rows - 1);
|
|
318
|
+
return [cols === 1 ? .5 : .5 - spanX / 2 + gapX * col, rows === 1 ? .5 : .5 - spanY / 2 + gapY * row];
|
|
319
|
+
}
|
|
320
|
+
case "ring": {
|
|
321
|
+
const radius = layout.radius ?? .5;
|
|
322
|
+
const [cx, cy] = layout.center ?? [.5, .5];
|
|
323
|
+
const theta = (layout.startAngle ?? -Math.PI / 2) + (layout.sweep ?? Math.PI * 2) * (n === 0 ? 0 : i / n);
|
|
324
|
+
return [cx + radius * Math.cos(theta), cy + radius * Math.sin(theta)];
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
/** Compile a `distribute` mode + numeric gap into a stagger delay fn. */
|
|
329
|
+
function distributeFn(distribute, gap, n) {
|
|
330
|
+
const mid = (n - 1) / 2;
|
|
331
|
+
switch (distribute) {
|
|
332
|
+
case "from-center": return (i) => Math.abs(i - mid) * gap;
|
|
333
|
+
case "from-edges": return (i) => (mid - Math.abs(i - mid)) * gap;
|
|
334
|
+
case "delay": return (i) => i * gap;
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
/** Resolve the motion's per-index delay into a plain `(i) => seconds` fn. */
|
|
338
|
+
function staggerFn(motion, n) {
|
|
339
|
+
const s = motion.stagger ?? 0;
|
|
340
|
+
if (typeof s === "function") return s;
|
|
341
|
+
return distributeFn(motion.distribute ?? "delay", s, n);
|
|
342
|
+
}
|
|
343
|
+
/**
|
|
344
|
+
* Generate `n` clones from `factory`, lay them out, and (optionally) stagger a
|
|
345
|
+
* motion clip across them.
|
|
346
|
+
*
|
|
347
|
+
* const grid = each(9, (i) => new Rect({ width: 40, height: 40, fill: '#9ef0c0' }), {
|
|
348
|
+
* id: 'card',
|
|
349
|
+
* layout: { kind: 'grid', cols: 3 },
|
|
350
|
+
* box: { w: 600, h: 360 },
|
|
351
|
+
* motion: { clip: popIn(), stagger: 0.08, distribute: 'from-center' },
|
|
352
|
+
* });
|
|
353
|
+
* // scene children: [grid.node]; timeline tracks: [...grid.tracks]
|
|
354
|
+
*/
|
|
355
|
+
function each(n, factory, opts) {
|
|
356
|
+
if (!Number.isInteger(n) || n < 0) throw new EachError(`each() count must be a non-negative integer (got ${n})`);
|
|
357
|
+
const baseSeed = (opts.seed ?? hashStr(opts.id)) >>> 0;
|
|
358
|
+
const box = opts.box;
|
|
359
|
+
const [ox, oy] = box?.origin ?? [0, 0];
|
|
360
|
+
const children = [];
|
|
361
|
+
const places = [];
|
|
362
|
+
const seen = /* @__PURE__ */ new Set();
|
|
363
|
+
for (let i = 0; i < n; i++) {
|
|
364
|
+
const id = `${opts.id}/${i}`;
|
|
365
|
+
const frac = placeAt(opts.layout, i, n);
|
|
366
|
+
const ctx = {
|
|
367
|
+
i,
|
|
368
|
+
n,
|
|
369
|
+
id,
|
|
370
|
+
place: frac,
|
|
371
|
+
rng: random(mix(baseSeed, i)),
|
|
372
|
+
seed: baseSeed
|
|
373
|
+
};
|
|
374
|
+
const child = factory(i, ctx);
|
|
375
|
+
if (!(child instanceof Node)) throw new EachError(`each() factory must return a Node for index ${i} (got ${typeof child})`);
|
|
376
|
+
if (seen.has(child)) throw new EachError(`each() factory returned the same Node instance for index ${i} — the factory must construct a new node per index (it is called once per clone)`);
|
|
377
|
+
seen.add(child);
|
|
378
|
+
if (child.id === void 0) child.id = id;
|
|
379
|
+
else if (child.id !== id) throw new EachError(`each() factory set id '${child.id}' on index ${i}, but each owns the id namespace — leave it unset so it becomes '${id}'`);
|
|
380
|
+
children.push(child);
|
|
381
|
+
places.push(box ? {
|
|
382
|
+
frac,
|
|
383
|
+
px: [ox + frac[0] * box.w, oy + frac[1] * box.h]
|
|
384
|
+
} : { frac });
|
|
385
|
+
}
|
|
386
|
+
const node = new Group({
|
|
387
|
+
id: opts.id,
|
|
388
|
+
children
|
|
389
|
+
});
|
|
390
|
+
const tracks = [];
|
|
391
|
+
let end = opts.motion?.startSec ?? 0;
|
|
392
|
+
if (opts.motion) {
|
|
393
|
+
const m = opts.motion;
|
|
394
|
+
const start = m.startSec ?? 0;
|
|
395
|
+
const at = staggerFn(m, n);
|
|
396
|
+
for (let i = 0; i < n; i++) {
|
|
397
|
+
const rngI = random(mix(baseSeed, i));
|
|
398
|
+
const overrides = m.jitter?.(i, rngI, n);
|
|
399
|
+
const applyOpts = {
|
|
400
|
+
...overrides !== void 0 ? { overrides } : {},
|
|
401
|
+
...m.speed !== void 0 ? { speed: m.speed } : {}
|
|
402
|
+
};
|
|
403
|
+
const r = m.clip.apply(`${opts.id}/${i}`, start + at(i), applyOpts);
|
|
404
|
+
tracks.push(...r.tracks);
|
|
405
|
+
if (r.end > end) end = r.end;
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
return {
|
|
409
|
+
node,
|
|
410
|
+
children,
|
|
411
|
+
tracks,
|
|
412
|
+
end,
|
|
413
|
+
places
|
|
414
|
+
};
|
|
415
|
+
}
|
|
416
|
+
//#endregion
|
|
260
417
|
//#region src/drawOn.ts
|
|
261
418
|
/**
|
|
262
419
|
* Whiteboard kit: one-call "draw this shape on" tracks. A stroked or sketched
|
|
@@ -404,9 +561,10 @@ function createScene(init) {
|
|
|
404
561
|
size: init.size,
|
|
405
562
|
playhead,
|
|
406
563
|
resolveTarget: (target) => {
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
564
|
+
for (let slash = target.lastIndexOf("/"); slash > 0; slash = target.lastIndexOf("/", slash - 1)) {
|
|
565
|
+
const node = nodes.get(target.slice(0, slash));
|
|
566
|
+
if (node) return node.resolveTarget(target.slice(slash + 1));
|
|
567
|
+
}
|
|
410
568
|
},
|
|
411
569
|
setTextMeasurer: (m) => {
|
|
412
570
|
measurer.set(m);
|
|
@@ -1655,4 +1813,4 @@ var Raster2D = class {
|
|
|
1655
1813
|
}
|
|
1656
1814
|
};
|
|
1657
1815
|
//#endregion
|
|
1658
|
-
export { ALL_FILTER_KINDS, Circle, ColdAssetError, Custom, DL_SNAPSHOT_VERSION, DeterminismViolationError, DlSnapshotError, DuplicateNodeIdError, FilterValidationError, FollowPath, Group, Highlight, IDENTITY, ImageNode as Image, ImageNode, LayoutEngineMissingError, MEASURE_QUANTUM_PX, MESH_DOWNSCALE, MESH_SHEPARD_POWER, MESH_SIGMA, NODE_TAXONOMY, Node, Path, Raster2D, Rect, ReservedNodeIdError, ShaderEffect, SketchValidationError, Text, TextCursor, TokenHighlight, TokenMatchError, Video, applyToPoint, arcLength, auditCacheCold, bindScene, breakLines, collapseReplacer, collectTextUsages, createDisplayListBuilder, createScene, diffDisplayLists, drawOn, drawOnEach, estimatingMeasurer, evaluate, filtersToCanvasFilter, flatten, followPath, fontString, formatDisplayDiff, fromTRS, getLayoutEngine, glow, hachureLines, highlight, invert, matEquals, matchTokenRun, meshRasterSize, motionPath, multiply, parseDisplaySnapshot, pathFromSegs, pathLength, pointAtLength, quantize, rasterizeMesh, requireLayoutEngine, resolveAnchor, resolveSketch, revealSchedule, roughen, roundedRectSegs, segmentGraphemes, segmentWords, serializeDisplayList, setDefaultMeasurer, setLayoutEngine, sketchStrokes, textCursor, tokenHighlight, typewriter, validateFilters, validateHachure, validateSceneFonts, validateSketch, withDeterminismGuards };
|
|
1816
|
+
export { ALL_FILTER_KINDS, Circle, ColdAssetError, Custom, DL_SNAPSHOT_VERSION, DeterminismViolationError, DlSnapshotError, DuplicateNodeIdError, EachError, FilterValidationError, FollowPath, Group, Highlight, IDENTITY, ImageNode as Image, ImageNode, LayoutEngineMissingError, MEASURE_QUANTUM_PX, MESH_DOWNSCALE, MESH_SHEPARD_POWER, MESH_SIGMA, NODE_TAXONOMY, Node, Path, Raster2D, Rect, ReservedNodeIdError, ShaderEffect, SketchValidationError, Text, TextCursor, TokenHighlight, TokenMatchError, Video, applyToPoint, arcLength, auditCacheCold, bindScene, breakLines, collapseReplacer, collectTextUsages, createDisplayListBuilder, createScene, diffDisplayLists, drawOn, drawOnEach, each, estimatingMeasurer, evaluate, filtersToCanvasFilter, flatten, followPath, fontString, formatDisplayDiff, fromTRS, getLayoutEngine, glow, hachureLines, highlight, invert, matEquals, matchTokenRun, meshRasterSize, motionPath, multiply, parseDisplaySnapshot, pathFromSegs, pathLength, pointAtLength, quantize, rasterizeMesh, requireLayoutEngine, resolveAnchor, resolveSketch, revealSchedule, roughen, roundedRectSegs, segmentGraphemes, segmentWords, serializeDisplayList, setDefaultMeasurer, setLayoutEngine, sketchStrokes, textCursor, tokenHighlight, typewriter, validateFilters, validateHachure, validateSceneFonts, validateSketch, withDeterminismGuards };
|
package/dist/layout.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { i as setLayoutEngine, j as fallbackMeasurer, n as getLayoutEngine, r as requireLayoutEngine, s as Group, t as LayoutEngineMissingError } from "./layoutEngine.js";
|
|
2
2
|
import { computed, signal } from "@glissade/core";
|
|
3
3
|
//#region src/layout.ts
|
|
4
4
|
/**
|
package/dist/layoutEngine.js
CHANGED
|
@@ -73,17 +73,30 @@ function matEquals(a, b) {
|
|
|
73
73
|
* - ArrayBuffer / typed-array views collapse to a `ab:<len>` / `view:<len>`
|
|
74
74
|
* length marker (opaque binary never belongs in a structural key).
|
|
75
75
|
* - Functions drop (JSON would already drop them; explicit for parity).
|
|
76
|
+
* - Non-finite numbers (`NaN`/`Infinity`/`-Infinity`) collapse to DISTINCT
|
|
77
|
+
* string sentinels. `JSON.stringify` natively serializes all three to the
|
|
78
|
+
* SAME token (`null`), which would collide the cacheKey of two DisplayLists
|
|
79
|
+
* that differ only in WHICH non-finite value reaches a draw field — a stale
|
|
80
|
+
* raster + an `auditCacheCold` false-OK. The distinct sentinels keep them
|
|
81
|
+
* apart. This does NOT touch FINITE numbers (the common path): only the
|
|
82
|
+
* three non-finite inputs change, so the §3.5 cacheKey bytes for every real
|
|
83
|
+
* (finite) list are byte-identical — pinned by the regression guard.
|
|
76
84
|
*
|
|
77
85
|
* NOTE: `-0` is intentionally NOT normalized here. The matrix layer
|
|
78
86
|
* (`matrix.ts`) already normalizes `-0 → 0` at the source, and adding a `-0`
|
|
79
87
|
* pass to THIS replacer would change the cacheKey bytes for any list that ever
|
|
80
|
-
* carried a raw `-0` — silently invalidating the cache cluster-wide.
|
|
81
|
-
*
|
|
88
|
+
* carried a raw `-0` — silently invalidating the cache cluster-wide. (`-0` is
|
|
89
|
+
* finite, so the non-finite branch never touches it.) Byte preservation wins;
|
|
90
|
+
* the regression guard pins the exact key.
|
|
82
91
|
*/
|
|
83
92
|
function collapseReplacer(_key, value) {
|
|
84
93
|
if (value instanceof ArrayBuffer) return `ab:${value.byteLength}`;
|
|
85
94
|
if (ArrayBuffer.isView(value)) return `view:${value.byteLength}`;
|
|
86
95
|
if (typeof value === "function") return void 0;
|
|
96
|
+
if (typeof value === "number" && !Number.isFinite(value)) {
|
|
97
|
+
if (Number.isNaN(value)) return "NaN";
|
|
98
|
+
return value > 0 ? "Infinity" : "-Infinity";
|
|
99
|
+
}
|
|
87
100
|
return value;
|
|
88
101
|
}
|
|
89
102
|
/** A flat, stable JSON value for one command with its resource ids INLINED to content. */
|
|
@@ -2004,4 +2017,4 @@ function requireLayoutEngine() {
|
|
|
2004
2017
|
return engine;
|
|
2005
2018
|
}
|
|
2006
2019
|
//#endregion
|
|
2007
|
-
export {
|
|
2020
|
+
export { multiply as $, estimatingMeasurer as A, validateFilters as B, sketchStrokes as C, resolveAnchor as D, Node as E, setDefaultMeasurer as F, formatDisplayDiff as G, DlSnapshotError as H, FilterValidationError as I, IDENTITY as J, parseDisplaySnapshot as K, createDisplayListBuilder as L, quantize as M, segmentGraphemes as N, MEASURE_QUANTUM_PX as O, segmentWords as P, matEquals as Q, filtersToCanvasFilter as R, roughen as S, validateSketch as T, collapseReplacer as U, DL_SNAPSHOT_VERSION as V, diffDisplayLists as W, fromTRS as X, applyToPoint as Y, invert as Z, arcLength as _, Circle as a, hashStr as b, ImageNode as c, Text as d, Video as f, SketchValidationError as g, roundedRectSegs as h, setLayoutEngine as i, fallbackMeasurer as j, breakLines as k, Path as l, revealSchedule as m, getLayoutEngine as n, Custom as o, pathFromSegs as p, serializeDisplayList as q, requireLayoutEngine as r, Group as s, LayoutEngineMissingError as t, Rect as u, flatten as v, validateHachure as w, resolveSketch as x, hachureLines as y, glow as z };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@glissade/scene",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.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": {
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
],
|
|
24
24
|
"dependencies": {
|
|
25
25
|
"yoga-layout": "^3.2.1",
|
|
26
|
-
"@glissade/core": "0.
|
|
26
|
+
"@glissade/core": "0.13.0-pre.1"
|
|
27
27
|
},
|
|
28
28
|
"repository": {
|
|
29
29
|
"type": "git",
|