@vectojs/core 0.1.0 → 0.2.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/animation/drivers.d.ts +48 -0
- package/dist/animation/easing.d.ts +16 -0
- package/dist/{chunk-M2IZPGOL.mjs → chunk-H3QIE77O.mjs} +316 -12
- package/dist/{chunk-53DAQC3U.js → chunk-LA3FJLP2.js} +369 -65
- package/dist/components/GridTextEntity.d.ts +15 -0
- package/dist/components/SplineEntity.d.ts +144 -0
- package/dist/components/TextEntity.d.ts +35 -0
- package/dist/index.d.ts +26 -577
- package/dist/index.js +121 -136
- package/dist/index.mjs +19 -34
- package/dist/{layout.d.mts → layout/LayoutEngine.d.ts} +15 -70
- package/dist/layout/LayoutWorker.d.ts +23 -0
- package/dist/layout/LayoutWorkerManager.d.ts +22 -0
- package/dist/layout/LayoutWorkerSource.d.ts +1 -0
- package/dist/layout/index.d.ts +3 -0
- package/dist/layout/measure.d.ts +20 -0
- package/dist/math/SpatialHashGrid.d.ts +53 -0
- package/dist/math/SpringPhysics.d.ts +13 -0
- package/dist/renderer/CanvasRenderer.d.ts +81 -0
- package/dist/renderer/IRenderer.d.ts +178 -0
- package/dist/renderer/SVGRenderer.d.ts +69 -0
- package/dist/renderer/WebGLPointRenderer.d.ts +62 -0
- package/dist/renderer/WebGPUParticleSystemManager.d.ts +14 -0
- package/dist/renderer/colorParse.d.ts +17 -0
- package/dist/renderer/index.d.ts +6 -0
- package/dist/text/ArabicShaper.d.ts +10 -0
- package/dist/text/BidiResolver.d.ts +6 -0
- package/dist/{text.d.ts → text/MSDFFont.d.ts} +10 -82
- package/dist/text/MSDFTextEntity.d.ts +30 -0
- package/dist/text/SVGEntity.d.ts +22 -0
- package/dist/text/index.d.ts +5 -0
- package/dist/text.js +2 -2
- package/dist/text.mjs +1 -1
- package/dist/tree/ComputeParticleEntity.d.ts +118 -0
- package/dist/tree/DOMPortalEntity.d.ts +18 -0
- package/dist/{Entity-D-rfAFCf.d.mts → tree/Entity.d.ts} +71 -201
- package/dist/tree/Scene.d.ts +302 -0
- package/package.json +5 -5
- package/dist/Entity-D-rfAFCf.d.ts +0 -572
- package/dist/index-ByBDSmMK.d.mts +0 -365
- package/dist/index-C3Fd_XmG.d.ts +0 -365
- package/dist/index.d.mts +0 -577
- package/dist/layout.d.ts +0 -319
- package/dist/renderer.d.mts +0 -2
- package/dist/renderer.d.ts +0 -2
- package/dist/text.d.mts +0 -201
package/dist/index.mjs
CHANGED
|
@@ -12,12 +12,17 @@ import {
|
|
|
12
12
|
parseColorToRGBA
|
|
13
13
|
} from "./chunk-2Y45S4JK.mjs";
|
|
14
14
|
import {
|
|
15
|
+
Easing,
|
|
15
16
|
Entity,
|
|
16
17
|
MSDFFont,
|
|
17
18
|
MSDFTextEntity,
|
|
18
19
|
SVGEntity,
|
|
19
|
-
|
|
20
|
-
|
|
20
|
+
SpringDriver,
|
|
21
|
+
SpringPhysics,
|
|
22
|
+
TweenDriver,
|
|
23
|
+
VectoJSEvent,
|
|
24
|
+
isTweenConfig
|
|
25
|
+
} from "./chunk-H3QIE77O.mjs";
|
|
21
26
|
import {
|
|
22
27
|
ArabicShaper,
|
|
23
28
|
BidiResolver,
|
|
@@ -313,6 +318,8 @@ var Scene = class _Scene {
|
|
|
313
318
|
*/
|
|
314
319
|
renderMode = "always";
|
|
315
320
|
dirty = true;
|
|
321
|
+
/** Whether to throttle rendering to 2 FPS when the scene is static to save power. */
|
|
322
|
+
autoThrottle = true;
|
|
316
323
|
/**
|
|
317
324
|
* Frame-rate cap (power saving). `0` = uncapped (native refresh). When set,
|
|
318
325
|
* the loop renders at most `maxFPS` times per second; animations still run,
|
|
@@ -323,6 +330,10 @@ var Scene = class _Scene {
|
|
|
323
330
|
respectReducedMotion = true;
|
|
324
331
|
/** Cached media-query list; `.matches` is read live each frame. */
|
|
325
332
|
reducedMotionQuery = null;
|
|
333
|
+
/** True when the OS asks for reduced motion and we respect it. Read by the animation drivers. */
|
|
334
|
+
get prefersReducedMotion() {
|
|
335
|
+
return this.respectReducedMotion && !!this.reducedMotionQuery?.matches;
|
|
336
|
+
}
|
|
326
337
|
/**
|
|
327
338
|
* Throttle interval (ms) for the a11y/automation shadow sync. `0` = every
|
|
328
339
|
* frame. See {@link SceneOptions.a11ySyncInterval}.
|
|
@@ -393,6 +404,7 @@ var Scene = class _Scene {
|
|
|
393
404
|
const isTest = globalProcess && (globalProcess.env?.NODE_ENV === "test" || globalProcess.env?.VITEST === "true");
|
|
394
405
|
this.maxFPS = options.maxFPS ?? (isTest ? 0 : 60);
|
|
395
406
|
this.respectReducedMotion = options.respectReducedMotion ?? true;
|
|
407
|
+
this.autoThrottle = options.autoThrottle ?? true;
|
|
396
408
|
this.particleBackend = options.particleBackend ?? "auto";
|
|
397
409
|
this.a11ySyncInterval = options.a11ySyncInterval ?? 0;
|
|
398
410
|
this.reducedMotionQuery = typeof window !== "undefined" && typeof window.matchMedia === "function" ? window.matchMedia("(prefers-reduced-motion: reduce)") : null;
|
|
@@ -1081,7 +1093,7 @@ var Scene = class _Scene {
|
|
|
1081
1093
|
loop(time) {
|
|
1082
1094
|
if (!this.isRunning) return;
|
|
1083
1095
|
let cap = this.effectiveMaxFPS();
|
|
1084
|
-
const isStatic = !this.dirty && !this.hasAnyPendingAnimation(this.root) && !this.hasAnyPendingAnimation(this.overlayRoot);
|
|
1096
|
+
const isStatic = this.autoThrottle && !this.dirty && !this.hasAnyPendingAnimation(this.root) && !this.hasAnyPendingAnimation(this.overlayRoot);
|
|
1085
1097
|
if (isStatic && this.renderMode === "always" && this.maxFPS > 0) {
|
|
1086
1098
|
cap = Math.min(cap, 2);
|
|
1087
1099
|
}
|
|
@@ -2064,37 +2076,6 @@ var SpatialHashGrid = class {
|
|
|
2064
2076
|
}
|
|
2065
2077
|
};
|
|
2066
2078
|
|
|
2067
|
-
// src/math/SpringPhysics.ts
|
|
2068
|
-
var SpringPhysics = class {
|
|
2069
|
-
value;
|
|
2070
|
-
target;
|
|
2071
|
-
velocity = 0;
|
|
2072
|
-
stiffness = 180;
|
|
2073
|
-
damping = 12;
|
|
2074
|
-
mass = 1;
|
|
2075
|
-
valEpsilon = 5e-3;
|
|
2076
|
-
velEpsilon = 5e-3;
|
|
2077
|
-
constructor(initial) {
|
|
2078
|
-
this.value = initial;
|
|
2079
|
-
this.target = initial;
|
|
2080
|
-
}
|
|
2081
|
-
update(dt) {
|
|
2082
|
-
if (this.isAtRest()) {
|
|
2083
|
-
this.value = this.target;
|
|
2084
|
-
this.velocity = 0;
|
|
2085
|
-
return;
|
|
2086
|
-
}
|
|
2087
|
-
const forceSpring = -this.stiffness * (this.value - this.target);
|
|
2088
|
-
const forceDamping = -this.damping * this.velocity;
|
|
2089
|
-
const acceleration = (forceSpring + forceDamping) / this.mass;
|
|
2090
|
-
this.velocity += acceleration * dt;
|
|
2091
|
-
this.value += this.velocity * dt;
|
|
2092
|
-
}
|
|
2093
|
-
isAtRest() {
|
|
2094
|
-
return Math.abs(this.value - this.target) < this.valEpsilon && Math.abs(this.velocity) < this.velEpsilon;
|
|
2095
|
-
}
|
|
2096
|
-
};
|
|
2097
|
-
|
|
2098
2079
|
// src/tree/DOMPortalEntity.ts
|
|
2099
2080
|
var DOMPortalEntity = class extends Entity {
|
|
2100
2081
|
domElement;
|
|
@@ -2203,6 +2184,7 @@ export {
|
|
|
2203
2184
|
CanvasRenderer,
|
|
2204
2185
|
ComputeParticleEntity,
|
|
2205
2186
|
DOMPortalEntity,
|
|
2187
|
+
Easing,
|
|
2206
2188
|
Entity,
|
|
2207
2189
|
GridTextEntity,
|
|
2208
2190
|
LayoutEngine,
|
|
@@ -2225,13 +2207,16 @@ export {
|
|
|
2225
2207
|
Scene,
|
|
2226
2208
|
SpatialHashGrid,
|
|
2227
2209
|
SplineEntity,
|
|
2210
|
+
SpringDriver,
|
|
2228
2211
|
SpringPhysics,
|
|
2229
2212
|
TextEntity,
|
|
2213
|
+
TweenDriver,
|
|
2230
2214
|
VectoJSEvent,
|
|
2231
2215
|
WebGPUParticleSystemManager,
|
|
2232
2216
|
computeLineSegments,
|
|
2233
2217
|
createCanvasMeasurer,
|
|
2234
2218
|
createWebGLPointRenderer,
|
|
2219
|
+
isTweenConfig,
|
|
2235
2220
|
loadSpline,
|
|
2236
2221
|
parseColorToRGBA,
|
|
2237
2222
|
polySegmentToBezier
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Each entry provides the glyph's pixel `width` at `baseSize`, and an `ast`
|
|
5
5
|
* property holding the raw vector path data used by the renderer.
|
|
6
6
|
*/
|
|
7
|
-
interface GlyphAtlas {
|
|
7
|
+
export interface GlyphAtlas {
|
|
8
8
|
[char: string]: {
|
|
9
9
|
width: number;
|
|
10
10
|
baseSize: number;
|
|
@@ -18,14 +18,14 @@ interface GlyphAtlas {
|
|
|
18
18
|
* Implemented by {@link createCanvasMeasurer} (canvas `measureText`), but kept
|
|
19
19
|
* abstract so callers can supply their own metrics source.
|
|
20
20
|
*/
|
|
21
|
-
interface GlyphMeasurer {
|
|
21
|
+
export interface GlyphMeasurer {
|
|
22
22
|
measure(char: string, fontSize: number): number;
|
|
23
23
|
}
|
|
24
24
|
/**
|
|
25
25
|
* Per-run inline style for rich text ({@link LayoutEngine.prepareRich}). All
|
|
26
26
|
* fields are optional and inherited from the call's base style when omitted.
|
|
27
27
|
*/
|
|
28
|
-
interface TextStyle {
|
|
28
|
+
export interface TextStyle {
|
|
29
29
|
/** Font size in px for this run; overrides the base size (affects width + line height). */
|
|
30
30
|
fontSize?: number;
|
|
31
31
|
/** Fill color, e.g. `'#38bdf8'`. */
|
|
@@ -38,14 +38,14 @@ interface TextStyle {
|
|
|
38
38
|
href?: string;
|
|
39
39
|
}
|
|
40
40
|
/** A run of text sharing one {@link TextStyle}, the input unit of {@link LayoutEngine.prepareRich}. */
|
|
41
|
-
interface StyledSpan {
|
|
41
|
+
export interface StyledSpan {
|
|
42
42
|
text: string;
|
|
43
43
|
style?: TextStyle;
|
|
44
44
|
}
|
|
45
45
|
/**
|
|
46
46
|
* A single positioned glyph produced by {@link LayoutEngine.layoutText}.
|
|
47
47
|
*/
|
|
48
|
-
interface LayoutNode {
|
|
48
|
+
export interface LayoutNode {
|
|
49
49
|
char: string;
|
|
50
50
|
x: number;
|
|
51
51
|
y: number;
|
|
@@ -62,14 +62,14 @@ interface LayoutNode {
|
|
|
62
62
|
* The complete output of a text layout pass — an ordered list of positioned
|
|
63
63
|
* glyphs and the total bounding-box dimensions.
|
|
64
64
|
*/
|
|
65
|
-
interface LayoutResult {
|
|
65
|
+
export interface LayoutResult {
|
|
66
66
|
nodes: LayoutNode[];
|
|
67
67
|
totalWidth: number;
|
|
68
68
|
totalHeight: number;
|
|
69
69
|
fallbackToCanvas?: boolean;
|
|
70
70
|
}
|
|
71
71
|
/** A single measured grapheme (the "cold" half of the cold/hot split). */
|
|
72
|
-
interface PreparedGlyph {
|
|
72
|
+
export interface PreparedGlyph {
|
|
73
73
|
char: string;
|
|
74
74
|
/** Advance width at the prepared `fontSize`. */
|
|
75
75
|
width: number;
|
|
@@ -81,7 +81,7 @@ interface PreparedGlyph {
|
|
|
81
81
|
combining?: string[];
|
|
82
82
|
}
|
|
83
83
|
/** A measured word/segment, ready to be placed without re-measuring. */
|
|
84
|
-
interface PreparedWord {
|
|
84
|
+
export interface PreparedWord {
|
|
85
85
|
glyphs: PreparedGlyph[];
|
|
86
86
|
/** Sum of glyph advances — used for word-level wrap decisions. */
|
|
87
87
|
width: number;
|
|
@@ -90,7 +90,7 @@ interface PreparedWord {
|
|
|
90
90
|
isWhitespace: boolean;
|
|
91
91
|
}
|
|
92
92
|
/** A measured paragraph; `isEmpty` marks a blank line (forced newline). */
|
|
93
|
-
interface PreparedParagraph {
|
|
93
|
+
export interface PreparedParagraph {
|
|
94
94
|
words: PreparedWord[];
|
|
95
95
|
isEmpty: boolean;
|
|
96
96
|
fallbackToCanvas?: boolean;
|
|
@@ -103,7 +103,7 @@ interface PreparedParagraph {
|
|
|
103
103
|
* re-layouts ({@link LayoutEngine.layoutPrepared}) on resize / reposition,
|
|
104
104
|
* avoiding the per-frame `Intl.Segmenter` + measurement cost.
|
|
105
105
|
*/
|
|
106
|
-
interface PreparedText {
|
|
106
|
+
export interface PreparedText {
|
|
107
107
|
paragraphs: PreparedParagraph[];
|
|
108
108
|
fontSize: number;
|
|
109
109
|
fallbackToCanvas?: boolean;
|
|
@@ -113,14 +113,14 @@ interface PreparedText {
|
|
|
113
113
|
* flow around — the v1 of text flow exclusion shapes. A left/right rect acts
|
|
114
114
|
* like a CSS float; a centered rect splits the affected lines in two.
|
|
115
115
|
*/
|
|
116
|
-
interface ExclusionRect {
|
|
116
|
+
export interface ExclusionRect {
|
|
117
117
|
x: number;
|
|
118
118
|
y: number;
|
|
119
119
|
width: number;
|
|
120
120
|
height: number;
|
|
121
121
|
}
|
|
122
122
|
/** A free horizontal interval `[x0, x1)` available for text on one line. */
|
|
123
|
-
interface LineSegment {
|
|
123
|
+
export interface LineSegment {
|
|
124
124
|
x0: number;
|
|
125
125
|
x1: number;
|
|
126
126
|
}
|
|
@@ -133,12 +133,12 @@ interface LineSegment {
|
|
|
133
133
|
*
|
|
134
134
|
* Time O(n log n) in the number of overlapping exclusions; space O(n).
|
|
135
135
|
*/
|
|
136
|
-
declare function computeLineSegments(top: number, bottom: number, maxWidth: number, exclusions: ExclusionRect[]): LineSegment[];
|
|
136
|
+
export declare function computeLineSegments(top: number, bottom: number, maxWidth: number, exclusions: ExclusionRect[]): LineSegment[];
|
|
137
137
|
/**
|
|
138
138
|
* VectoJS Global Layout Engine (Intl.Segmenter)
|
|
139
139
|
* Advanced Typography Engine supporting CJK, Emoji, and Western Graphemes
|
|
140
140
|
*/
|
|
141
|
-
declare class LayoutEngine {
|
|
141
|
+
export declare class LayoutEngine {
|
|
142
142
|
maxWidth: number;
|
|
143
143
|
maxHeight: number;
|
|
144
144
|
preserveLeadingSpaces: boolean;
|
|
@@ -243,7 +243,7 @@ declare class LayoutEngine {
|
|
|
243
243
|
* Pre-allocated buffer for zero-GC layout results.
|
|
244
244
|
* Reuse a single instance across frames by calling reset() before each layout pass.
|
|
245
245
|
*/
|
|
246
|
-
declare class LayoutResultBuffer {
|
|
246
|
+
export declare class LayoutResultBuffer {
|
|
247
247
|
static readonly CAPACITY = 16384;
|
|
248
248
|
/** X positions of each glyph. */
|
|
249
249
|
xs: Float32Array;
|
|
@@ -262,58 +262,3 @@ declare class LayoutResultBuffer {
|
|
|
262
262
|
/** Convert to the standard LayoutResult format (allocates — use sparingly). */
|
|
263
263
|
toLayoutResult(): LayoutResult;
|
|
264
264
|
}
|
|
265
|
-
|
|
266
|
-
/**
|
|
267
|
-
* Create a {@link GlyphMeasurer} backed by a single lazily-created offscreen
|
|
268
|
-
* Canvas 2D context.
|
|
269
|
-
*
|
|
270
|
-
* Each grapheme is measured once at `baseSize` and cached; because canvas
|
|
271
|
-
* `measureText` advance width is linear in font size, later queries at any
|
|
272
|
-
* `fontSize` are derived by pure arithmetic (no re-measure). This gives the
|
|
273
|
-
* {@link LayoutEngine} real per-glyph metrics for text that has no pre-baked
|
|
274
|
-
* vector atlas, fixing the coarse `0.5em` line-breaking fallback.
|
|
275
|
-
*
|
|
276
|
-
* Returns `null` in DOM-free environments (SSR, workers without a canvas) so
|
|
277
|
-
* callers stay portable and the engine keeps its `0.5em` fallback.
|
|
278
|
-
*
|
|
279
|
-
* @param fontFamily - CSS font family used for measurement; should match what
|
|
280
|
-
* the renderer actually draws (e.g. `TextEntity` falls back to `sans-serif`).
|
|
281
|
-
* @param baseSize - Pixel size at which each glyph is measured and cached.
|
|
282
|
-
* @returns A measurer, or `null` when no Canvas 2D context is available.
|
|
283
|
-
*/
|
|
284
|
-
declare function createCanvasMeasurer(fontFamily?: string, baseSize?: number): GlyphMeasurer | null;
|
|
285
|
-
|
|
286
|
-
interface LayoutWorkerResponse {
|
|
287
|
-
id: string;
|
|
288
|
-
seqId: number;
|
|
289
|
-
width: number;
|
|
290
|
-
height: number;
|
|
291
|
-
codePoints: Uint32Array;
|
|
292
|
-
xCoords: Float32Array;
|
|
293
|
-
yCoords: Float32Array;
|
|
294
|
-
packedStyles: Uint32Array;
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
declare class LayoutWorkerManager {
|
|
298
|
-
private static instance;
|
|
299
|
-
private worker;
|
|
300
|
-
private registeredFonts;
|
|
301
|
-
private pendingCallbacks;
|
|
302
|
-
private seqIdCounter;
|
|
303
|
-
private debounceTimers;
|
|
304
|
-
private constructor();
|
|
305
|
-
static getInstance(): LayoutWorkerManager;
|
|
306
|
-
queueLayout(entityId: string, text: string, options: {
|
|
307
|
-
fontId: string;
|
|
308
|
-
fontSize: number;
|
|
309
|
-
maxWidth: number;
|
|
310
|
-
maxHeight: number;
|
|
311
|
-
fontData?: any;
|
|
312
|
-
lineHeight?: number;
|
|
313
|
-
letterSpacing?: number;
|
|
314
|
-
callback: (res: LayoutWorkerResponse) => void;
|
|
315
|
-
}): void;
|
|
316
|
-
cancelLayout(entityId: string): void;
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
export { type ExclusionRect, type GlyphAtlas, type GlyphMeasurer, LayoutEngine, type LayoutNode, type LayoutResult, LayoutResultBuffer, LayoutWorkerManager, type LineSegment, type PreparedGlyph, type PreparedParagraph, type PreparedText, type PreparedWord, type StyledSpan, type TextStyle, computeLineSegments, createCanvasMeasurer };
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { MSDFFontData } from '../text/MSDFFont';
|
|
2
|
+
export interface LayoutWorkerRequest {
|
|
3
|
+
id: string;
|
|
4
|
+
seqId: number;
|
|
5
|
+
text: string;
|
|
6
|
+
fontId: string;
|
|
7
|
+
fontData?: MSDFFontData;
|
|
8
|
+
maxWidth: number;
|
|
9
|
+
maxHeight: number;
|
|
10
|
+
fontSize: number;
|
|
11
|
+
lineHeight?: number;
|
|
12
|
+
letterSpacing?: number;
|
|
13
|
+
}
|
|
14
|
+
export interface LayoutWorkerResponse {
|
|
15
|
+
id: string;
|
|
16
|
+
seqId: number;
|
|
17
|
+
width: number;
|
|
18
|
+
height: number;
|
|
19
|
+
codePoints: Uint32Array;
|
|
20
|
+
xCoords: Float32Array;
|
|
21
|
+
yCoords: Float32Array;
|
|
22
|
+
packedStyles: Uint32Array;
|
|
23
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { LayoutWorkerResponse } from './LayoutWorker';
|
|
2
|
+
export declare class LayoutWorkerManager {
|
|
3
|
+
private static instance;
|
|
4
|
+
private worker;
|
|
5
|
+
private registeredFonts;
|
|
6
|
+
private pendingCallbacks;
|
|
7
|
+
private seqIdCounter;
|
|
8
|
+
private debounceTimers;
|
|
9
|
+
private constructor();
|
|
10
|
+
static getInstance(): LayoutWorkerManager;
|
|
11
|
+
queueLayout(entityId: string, text: string, options: {
|
|
12
|
+
fontId: string;
|
|
13
|
+
fontSize: number;
|
|
14
|
+
maxWidth: number;
|
|
15
|
+
maxHeight: number;
|
|
16
|
+
fontData?: any;
|
|
17
|
+
lineHeight?: number;
|
|
18
|
+
letterSpacing?: number;
|
|
19
|
+
callback: (res: LayoutWorkerResponse) => void;
|
|
20
|
+
}): void;
|
|
21
|
+
cancelLayout(entityId: string): void;
|
|
22
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const WORKER_SOURCE_STRING = "\"use strict\";(()=>{var b=new Map;self.onmessage=x=>{let{id:S,seqId:k,text:A,fontId:i,fontData:c,maxWidth:d,maxHeight:H,fontSize:o,lineHeight:F,letterSpacing:C}=x.data;c&&b.set(i,c);let n=b.get(i);if(!n)return;let f=[],u=[],y=[],l=[],e=0,r=0,h=n.metrics?.ascender??.8,D=n.metrics?.descender??-.2,m=F??o*(h-D),p=Array.from(A);for(let s=0;s<p.length;s++){let a=p[s].codePointAt(0),g=(n.glyphs?.find(w=>w.unicode===a)?.advance??1)*o;e+g>d&&a===32&&(e=0,r++),f.push(a),u.push(e);let M=r*m+h*o;y.push(M),l.push(-256),e+=g+(C??0)}let t={id:S,seqId:k,width:Math.min(e,d),height:(r+1)*m,codePoints:new Uint32Array(f),xCoords:new Float32Array(u),yCoords:new Float32Array(y),packedStyles:new Uint32Array(l)};self.postMessage(t,[t.codePoints.buffer,t.xCoords.buffer,t.yCoords.buffer,t.packedStyles.buffer])};})();\n";
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { GlyphMeasurer } from './LayoutEngine';
|
|
2
|
+
/**
|
|
3
|
+
* Create a {@link GlyphMeasurer} backed by a single lazily-created offscreen
|
|
4
|
+
* Canvas 2D context.
|
|
5
|
+
*
|
|
6
|
+
* Each grapheme is measured once at `baseSize` and cached; because canvas
|
|
7
|
+
* `measureText` advance width is linear in font size, later queries at any
|
|
8
|
+
* `fontSize` are derived by pure arithmetic (no re-measure). This gives the
|
|
9
|
+
* {@link LayoutEngine} real per-glyph metrics for text that has no pre-baked
|
|
10
|
+
* vector atlas, fixing the coarse `0.5em` line-breaking fallback.
|
|
11
|
+
*
|
|
12
|
+
* Returns `null` in DOM-free environments (SSR, workers without a canvas) so
|
|
13
|
+
* callers stay portable and the engine keeps its `0.5em` fallback.
|
|
14
|
+
*
|
|
15
|
+
* @param fontFamily - CSS font family used for measurement; should match what
|
|
16
|
+
* the renderer actually draws (e.g. `TextEntity` falls back to `sans-serif`).
|
|
17
|
+
* @param baseSize - Pixel size at which each glyph is measured and cached.
|
|
18
|
+
* @returns A measurer, or `null` when no Canvas 2D context is available.
|
|
19
|
+
*/
|
|
20
|
+
export declare function createCanvasMeasurer(fontFamily?: string, baseSize?: number): GlyphMeasurer | null;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fixed-cell Spatial Hash Grid for O(1) average-case AABB neighbor queries.
|
|
3
|
+
* Insert entities each frame, then query by AABB to find nearby entity IDs.
|
|
4
|
+
*/
|
|
5
|
+
export declare class SpatialHashGrid {
|
|
6
|
+
private cellSize;
|
|
7
|
+
private grid;
|
|
8
|
+
private entityCells;
|
|
9
|
+
constructor(cellSize?: number);
|
|
10
|
+
private hash;
|
|
11
|
+
private cellsForAABB;
|
|
12
|
+
/**
|
|
13
|
+
* Insert or update an entity's axis-aligned bounding box in the grid.
|
|
14
|
+
*
|
|
15
|
+
* If the entity is already registered its old cell memberships are removed
|
|
16
|
+
* before the new ones are computed, so this method is safe to call every
|
|
17
|
+
* frame.
|
|
18
|
+
*
|
|
19
|
+
* @param id - Unique string identifier for the entity.
|
|
20
|
+
* @param x - Left edge of the AABB in world space.
|
|
21
|
+
* @param y - Top edge of the AABB in world space.
|
|
22
|
+
* @param w - Width of the AABB.
|
|
23
|
+
* @param h - Height of the AABB.
|
|
24
|
+
*/
|
|
25
|
+
insert(id: string, x: number, y: number, w: number, h: number): void;
|
|
26
|
+
/**
|
|
27
|
+
* Remove an entity from all grid cells it currently occupies.
|
|
28
|
+
*
|
|
29
|
+
* Silently does nothing if the entity is not registered.
|
|
30
|
+
*
|
|
31
|
+
* @param id - Unique string identifier of the entity to remove.
|
|
32
|
+
*/
|
|
33
|
+
remove(id: string): void;
|
|
34
|
+
/**
|
|
35
|
+
* Return all entity IDs whose grid cells overlap the given AABB.
|
|
36
|
+
*
|
|
37
|
+
* Time complexity: O(k) where k is the number of cells the query AABB spans
|
|
38
|
+
* plus the number of results — O(1) average for small, similarly-sized entities.
|
|
39
|
+
*
|
|
40
|
+
* @param x - Left edge of the query AABB.
|
|
41
|
+
* @param y - Top edge of the query AABB.
|
|
42
|
+
* @param w - Width of the query AABB.
|
|
43
|
+
* @param h - Height of the query AABB.
|
|
44
|
+
* @returns A `Set` of entity ID strings whose cells intersect the query region.
|
|
45
|
+
*/
|
|
46
|
+
query(x: number, y: number, w: number, h: number): Set<string>;
|
|
47
|
+
/**
|
|
48
|
+
* Clear all cells and entity registrations, resetting the grid to an empty state.
|
|
49
|
+
*
|
|
50
|
+
* Call once per frame before re-inserting all dynamic entities.
|
|
51
|
+
*/
|
|
52
|
+
clear(): void;
|
|
53
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export declare class SpringPhysics {
|
|
2
|
+
value: number;
|
|
3
|
+
target: number;
|
|
4
|
+
velocity: number;
|
|
5
|
+
stiffness: number;
|
|
6
|
+
damping: number;
|
|
7
|
+
mass: number;
|
|
8
|
+
private readonly valEpsilon;
|
|
9
|
+
private readonly velEpsilon;
|
|
10
|
+
constructor(initial: number);
|
|
11
|
+
update(dt: number): void;
|
|
12
|
+
isAtRest(): boolean;
|
|
13
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { IRenderer } from './IRenderer';
|
|
2
|
+
export declare class CanvasRenderer implements IRenderer {
|
|
3
|
+
private ctx;
|
|
4
|
+
private width;
|
|
5
|
+
private height;
|
|
6
|
+
/**
|
|
7
|
+
* Max circles per batched `fill()`. A single Canvas 2D `fill()` over a path is
|
|
8
|
+
* superlinear in sub-path count, so an unbounded batch is *slower* than many
|
|
9
|
+
* small fills at high entity counts. Capping bounds each fill's path
|
|
10
|
+
* complexity while still amortizing per-draw overhead. Tuned via the benchmark.
|
|
11
|
+
*/
|
|
12
|
+
static readonly MAX_BATCH = 64;
|
|
13
|
+
private batchActive;
|
|
14
|
+
private batchColor;
|
|
15
|
+
private batchAlpha;
|
|
16
|
+
private batchCount;
|
|
17
|
+
constructor(canvas: HTMLCanvasElement);
|
|
18
|
+
/**
|
|
19
|
+
* Expose the underlying `CanvasRenderingContext2D` for operations not
|
|
20
|
+
* covered by the {@link IRenderer} interface.
|
|
21
|
+
*
|
|
22
|
+
* @returns The raw 2D rendering context.
|
|
23
|
+
*/
|
|
24
|
+
getContext(): CanvasRenderingContext2D;
|
|
25
|
+
/**
|
|
26
|
+
* Resize the backing canvas buffer and re-apply DPR scaling.
|
|
27
|
+
*
|
|
28
|
+
* Called automatically by {@link Scene} on `window.resize` events.
|
|
29
|
+
*
|
|
30
|
+
* @param width - New logical width in CSS pixels.
|
|
31
|
+
* @param height - New logical height in CSS pixels.
|
|
32
|
+
*/
|
|
33
|
+
resize(width: number, height: number): void;
|
|
34
|
+
/** @inheritdoc */
|
|
35
|
+
clear(): void;
|
|
36
|
+
/** @inheritdoc */
|
|
37
|
+
save(): void;
|
|
38
|
+
/** @inheritdoc */
|
|
39
|
+
restore(): void;
|
|
40
|
+
/** @inheritdoc */
|
|
41
|
+
translate(x: number, y: number): void;
|
|
42
|
+
/** @inheritdoc */
|
|
43
|
+
scale(x: number, y: number): void;
|
|
44
|
+
/** @inheritdoc */
|
|
45
|
+
rotate(angle: number): void;
|
|
46
|
+
/** @inheritdoc */
|
|
47
|
+
setGlobalAlpha(alpha: number): void;
|
|
48
|
+
/** @inheritdoc */
|
|
49
|
+
clip(x: number, y: number, width: number, height: number): void;
|
|
50
|
+
/** @inheritdoc */
|
|
51
|
+
beginPath(): void;
|
|
52
|
+
/** @inheritdoc */
|
|
53
|
+
moveTo(x: number, y: number): void;
|
|
54
|
+
/** @inheritdoc */
|
|
55
|
+
lineTo(x: number, y: number): void;
|
|
56
|
+
/** @inheritdoc */
|
|
57
|
+
bezierCurveTo(cp1x: number, cp1y: number, cp2x: number, cp2y: number, x: number, y: number): void;
|
|
58
|
+
/** @inheritdoc */
|
|
59
|
+
closePath(): void;
|
|
60
|
+
/** @inheritdoc */
|
|
61
|
+
arc(x: number, y: number, radius: number, startAngle: number, endAngle: number, counterclockwise?: boolean): void;
|
|
62
|
+
/** @inheritdoc */
|
|
63
|
+
roundRect(x: number, y: number, width: number, height: number, radii: number | number[]): void;
|
|
64
|
+
/** @inheritdoc */
|
|
65
|
+
drawImage(source: CanvasImageSource, dx: number, dy: number, dw: number, dh: number): void;
|
|
66
|
+
/** @inheritdoc */
|
|
67
|
+
fillCircle(cx: number, cy: number, radius: number, color: string, alpha?: number): void;
|
|
68
|
+
/** @inheritdoc */
|
|
69
|
+
flush(): void;
|
|
70
|
+
/** @inheritdoc */
|
|
71
|
+
fill(color: string | any): void;
|
|
72
|
+
/** @inheritdoc */
|
|
73
|
+
stroke(color: string | any, lineWidth?: number): void;
|
|
74
|
+
/** @inheritdoc */
|
|
75
|
+
fillText(text: string, x: number, y: number, font: string, color: string | any): void;
|
|
76
|
+
/** @inheritdoc */
|
|
77
|
+
createLinearGradient(x0: number, y0: number, x1: number, y1: number, colorStops: {
|
|
78
|
+
stop: number;
|
|
79
|
+
color: string;
|
|
80
|
+
}[]): any;
|
|
81
|
+
}
|