@vectojs/core 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,56 @@
1
+ # @vectojs/core
2
+
3
+ > The Zero-DOM, Canvas-native rendering engine behind **VectoJS** — ECS + Virtual Math Tree,
4
+ > with an accessibility/automation shadow layer.
5
+
6
+ Part of the [VectoJS](https://github.com/vectojs/vectojs) ecosystem.
7
+
8
+ ## What it does
9
+
10
+ `@vectojs/core` renders a whole UI onto one `<canvas>`: layout, hit-testing, animation and
11
+ physics are pure math on a Virtual Math Tree, dispatched to a Canvas 2D (or WebGL2) renderer —
12
+ **no per-element DOM, no reflow, no style recalc**. Interactive entities project a real,
13
+ transparent DOM node through the **`a11yRoot`** shadow layer, so a pure-canvas page stays
14
+ accessible and drivable by assistive tech and AI agents.
15
+
16
+ Includes: `Scene` (render loop + a11y sync), `Entity` (ECS base), `LayoutEngine` (Intl.Segmenter
17
+ with a cold/hot `prepare`/`layoutPrepared` split), `SpatialHashGrid`, `LayoutResultBuffer`
18
+ (zero-GC), `SplineEntity` (native vectomancy math-curve rendering + curve-accurate hit-testing),
19
+ `CanvasRenderer`, and a `WebGLPointRenderer` point/rect batch layer.
20
+
21
+ ## Performance
22
+
23
+ See the [main README](https://github.com/vectojs/vectojs#measured-performance) for measured,
24
+ reproducible numbers (`bun run benchmark` / `bun run compare:dom`). Headline levers: viewport
25
+ culling, on-demand redraw, draw-call batching, a WebGL2 point layer, and a cold/hot text layout
26
+ split (~3.5× faster reflow). No fabricated comparisons — numbers are per-machine and
27
+ complexity-dependent.
28
+
29
+ ## Quick Start
30
+
31
+ ```typescript
32
+ import { Scene, Entity, IRenderer } from '@vectojs/core';
33
+
34
+ class CircleEntity extends Entity {
35
+ isPointInside(x: number, y: number) {
36
+ return Math.hypot(x - this.x, y - this.y) < 50;
37
+ }
38
+ render(r: IRenderer) {
39
+ r.beginPath();
40
+ r.arc(0, 0, 50, 0, Math.PI * 2);
41
+ r.fill('#38bdf8');
42
+ }
43
+ }
44
+
45
+ const canvas = document.querySelector('canvas')!;
46
+ const scene = new Scene(canvas);
47
+ scene.add(new CircleEntity().setPosition(100, 100));
48
+ scene.start();
49
+ ```
50
+
51
+ For high-level accessible components (Button, Input, Card…), see
52
+ [`@vectojs/ui`](https://www.npmjs.com/package/@vectojs/ui).
53
+
54
+ ## License
55
+
56
+ MIT © 2026 Xuepoo
@@ -0,0 +1,572 @@
1
+ /**
2
+ * Renderer abstraction consumed by every {@link Entity}.
3
+ *
4
+ * Implementations wrap a concrete drawing backend (Canvas 2D, WebGL, …) and
5
+ * expose a path-based drawing API. Entities must only depend on `IRenderer`
6
+ * so they remain backend-agnostic.
7
+ *
8
+ * @example
9
+ * // Inside an Entity.render() implementation:
10
+ * render(r: IRenderer) {
11
+ * r.beginPath();
12
+ * r.fill('#38bdf8');
13
+ * }
14
+ */
15
+ interface IRenderer {
16
+ /** Clear the entire drawing surface to transparent / background color. */
17
+ clear(): void;
18
+ /** Push the current transform + state onto the renderer's stack. */
19
+ save(): void;
20
+ /** Pop the last saved transform + state from the renderer's stack. */
21
+ restore(): void;
22
+ /**
23
+ * Apply a translation to the current transform matrix.
24
+ *
25
+ * @param x - Horizontal offset in pixels.
26
+ * @param y - Vertical offset in pixels.
27
+ */
28
+ translate(x: number, y: number): void;
29
+ /**
30
+ * Apply a scale to the current transform matrix.
31
+ *
32
+ * @param x - Horizontal scale factor.
33
+ * @param y - Vertical scale factor.
34
+ */
35
+ scale(x: number, y: number): void;
36
+ /**
37
+ * Apply a clockwise rotation to the current transform matrix.
38
+ *
39
+ * @param angle - Rotation angle in radians.
40
+ */
41
+ rotate(angle: number): void;
42
+ /**
43
+ * Set the global opacity applied to all subsequent draw calls.
44
+ *
45
+ * @param alpha - Opacity in the range `[0, 1]`.
46
+ */
47
+ setGlobalAlpha(alpha: number): void;
48
+ /**
49
+ * Intersect the current clip region with a rectangle. Affects all subsequent
50
+ * draws until the next {@link restore}; wrap in {@link save}/{@link restore}.
51
+ *
52
+ * @param x - Left edge.
53
+ * @param y - Top edge.
54
+ * @param width - Rectangle width.
55
+ * @param height - Rectangle height.
56
+ */
57
+ clip(x: number, y: number, width: number, height: number): void;
58
+ /** Begin a new sub-path, discarding the current path. */
59
+ beginPath(): void;
60
+ /**
61
+ * Move the pen to the given point without drawing a line.
62
+ *
63
+ * @param x - Target X coordinate.
64
+ * @param y - Target Y coordinate.
65
+ */
66
+ moveTo(x: number, y: number): void;
67
+ /**
68
+ * Add a straight line segment from the current pen position to the given point.
69
+ *
70
+ * @param x - Target X coordinate.
71
+ * @param y - Target Y coordinate.
72
+ */
73
+ lineTo(x: number, y: number): void;
74
+ /**
75
+ * Add a cubic Bézier curve to the current path.
76
+ *
77
+ * @param cp1x - X of the first control point.
78
+ * @param cp1y - Y of the first control point.
79
+ * @param cp2x - X of the second control point.
80
+ * @param cp2y - Y of the second control point.
81
+ * @param x - X of the end point.
82
+ * @param y - Y of the end point.
83
+ */
84
+ bezierCurveTo(cp1x: number, cp1y: number, cp2x: number, cp2y: number, x: number, y: number): void;
85
+ /** Close the current sub-path by drawing a line back to its starting point. */
86
+ closePath(): void;
87
+ /**
88
+ * Add a circular arc to the current path.
89
+ *
90
+ * @param x - X of the arc center.
91
+ * @param y - Y of the arc center.
92
+ * @param radius - Arc radius in pixels.
93
+ * @param startAngle - Start angle in radians (0 = 3 o'clock).
94
+ * @param endAngle - End angle in radians.
95
+ * @param counterclockwise - If `true`, draws the arc counter-clockwise.
96
+ */
97
+ arc(x: number, y: number, radius: number, startAngle: number, endAngle: number, counterclockwise?: boolean): void;
98
+ /**
99
+ * Add a rounded rectangle to the current path.
100
+ *
101
+ * @param x - Left edge.
102
+ * @param y - Top edge.
103
+ * @param width - Rectangle width.
104
+ * @param height - Rectangle height.
105
+ * @param radii - Corner radius (uniform) or per-corner array as accepted by `CanvasRenderingContext2D.roundRect()`.
106
+ */
107
+ roundRect(x: number, y: number, width: number, height: number, radii: number | number[]): void;
108
+ /**
109
+ * Draw an image or video frame into the canvas.
110
+ *
111
+ * @param source - The image source (HTMLImageElement, HTMLVideoElement, HTMLCanvasElement, etc.).
112
+ * @param dx - Destination X.
113
+ * @param dy - Destination Y.
114
+ * @param dw - Destination width.
115
+ * @param dh - Destination height.
116
+ */
117
+ drawImage(source: CanvasImageSource, dx: number, dy: number, dw: number, dh: number): void;
118
+ /**
119
+ * Fill the current path with the given color or gradient.
120
+ *
121
+ * @param colorOrGradient - CSS color string or a gradient object.
122
+ */
123
+ fill(colorOrGradient: string | any): void;
124
+ /**
125
+ * Stroke the current path with the given color or gradient.
126
+ *
127
+ * @param colorOrGradient - CSS color string or a gradient object.
128
+ * @param lineWidth - Stroke width in pixels (default: `1`).
129
+ */
130
+ stroke(colorOrGradient: string | any, lineWidth?: number): void;
131
+ /**
132
+ * Render a text string at the given position.
133
+ *
134
+ * @param text - The string to draw.
135
+ * @param x - Left edge of the text baseline.
136
+ * @param y - Baseline Y coordinate.
137
+ * @param font - CSS font shorthand, e.g. `'16px monospace'`.
138
+ * @param color - CSS color string or gradient.
139
+ */
140
+ fillText(text: string, x: number, y: number, font: string, color: string | any): void;
141
+ /**
142
+ * Draw a filled circle through the order-preserving batch.
143
+ *
144
+ * Consecutive calls sharing the same `color` and `alpha` are coalesced into a
145
+ * single path and committed with one `fill()` on {@link flush} (or when the
146
+ * style changes / another draw call intervenes). Coordinates are in the
147
+ * current transform space. This collapses the per-entity
148
+ * `beginPath`/`arc`/`fill` of large point clouds into a handful of draw calls
149
+ * while preserving painter's-order semantics.
150
+ *
151
+ * @param cx - Center X in the current transform space.
152
+ * @param cy - Center Y in the current transform space.
153
+ * @param radius - Circle radius.
154
+ * @param color - CSS color string.
155
+ * @param alpha - Opacity in `[0, 1]` (default `1`).
156
+ */
157
+ fillCircle(cx: number, cy: number, radius: number, color: string, alpha?: number): void;
158
+ /**
159
+ * Commit any pending batched draws (see {@link fillCircle}). Safe to call when
160
+ * no batch is active (no-op). The {@link Scene} flushes at the end of each
161
+ * sibling group and frame.
162
+ */
163
+ flush(): void;
164
+ /**
165
+ * Create a linear gradient between two points with the given color stops.
166
+ *
167
+ * @param x0 - X of the gradient start point.
168
+ * @param y0 - Y of the gradient start point.
169
+ * @param x1 - X of the gradient end point.
170
+ * @param y1 - Y of the gradient end point.
171
+ * @param colorStops - Array of `{ stop, color }` pairs where `stop` is in `[0, 1]`.
172
+ * @returns An opaque gradient object suitable for {@link fill} or {@link stroke}.
173
+ */
174
+ createLinearGradient(x0: number, y0: number, x1: number, y1: number, colorStops: {
175
+ stop: number;
176
+ color: string;
177
+ }[]): any;
178
+ }
179
+
180
+ /**
181
+ * A 2-D coordinate in canvas/world space.
182
+ */
183
+ interface Point {
184
+ x: number;
185
+ y: number;
186
+ }
187
+ /**
188
+ * An axis-aligned bounding box in an entity's local coordinate space.
189
+ *
190
+ * Returned from {@link Entity.getBounds} to enable viewport culling.
191
+ */
192
+ interface Bounds {
193
+ x: number;
194
+ y: number;
195
+ width: number;
196
+ height: number;
197
+ }
198
+ /**
199
+ * Describes an entity that renders as a single filled circle at its local
200
+ * origin, returned from {@link Entity.getBatchCircle} to opt into the renderer's
201
+ * draw-call batching fast-path.
202
+ */
203
+ interface BatchCircle {
204
+ /** Circle radius in the entity's local space. */
205
+ radius: number;
206
+ /** CSS fill color. */
207
+ color: string;
208
+ }
209
+ /**
210
+ * Describes an entity that renders as a single filled rectangle from its local
211
+ * origin, returned from {@link Entity.getBatchRect} to opt into the GPU
212
+ * instanced-rectangle fast-path (WebGL `pointBackend` only).
213
+ */
214
+ interface BatchRect {
215
+ /** Rectangle width in the entity's local space. */
216
+ width: number;
217
+ /** Rectangle height in the entity's local space. */
218
+ height: number;
219
+ /** CSS fill color. */
220
+ color: string;
221
+ }
222
+ /**
223
+ * Semantic attributes an {@link Entity} can project into the accessibility /
224
+ * automation shadow layer maintained by {@link Scene}.
225
+ *
226
+ * Returned from {@link Entity.getA11yAttributes}; consumed by `Scene.syncA11y`
227
+ * to create and label the shadow DOM node (e.g. a real `<button>` or `<a href>`)
228
+ * so the canvas stays accessible and clickable by automation/agents.
229
+ */
230
+ interface A11yAttributes {
231
+ /** Shadow element tag to create. Defaults to `'div'`. */
232
+ tag?: 'div' | 'a' | 'button' | 'img' | 'input' | 'textarea';
233
+ /** ARIA role applied via the `role` attribute. */
234
+ role?: string;
235
+ /** Accessible name applied via `aria-label`. */
236
+ label?: string;
237
+ /** Destination URL; only meaningful for `tag: 'a'`. */
238
+ href?: string;
239
+ /** Image source; only meaningful for `tag: 'img'`. */
240
+ src?: string;
241
+ /** Alternative text; only meaningful for `tag: 'img'`. */
242
+ alt?: string;
243
+ /** Input type (e.g. `'text'`, `'checkbox'`); only meaningful for `tag: 'input'`. */
244
+ inputType?: string;
245
+ /** Placeholder text; only meaningful for `tag: 'input'`. */
246
+ placeholder?: string;
247
+ /** Current value; refreshed each frame for `tag: 'input'` (text fields). */
248
+ value?: string;
249
+ /**
250
+ * Checked state — sets `input.checked` for checkbox inputs and `aria-checked`
251
+ * for `role: 'switch'`/`'checkbox'`. Refreshed each frame.
252
+ */
253
+ checked?: boolean;
254
+ disabled?: boolean;
255
+ expanded?: boolean;
256
+ controls?: string;
257
+ haspopup?: string;
258
+ selected?: boolean;
259
+ activedescendant?: string;
260
+ valuemin?: string;
261
+ valuemax?: string;
262
+ }
263
+ /**
264
+ * Union of all pointer/interaction events that can be emitted by an {@link Entity}.
265
+ */
266
+ type VectoEvent = 'click' | 'hover' | 'pointerdown' | 'pointerup' | 'pointermove' | 'pointerleave' | 'change' | 'focus' | 'blur' | 'wheel' | 'keydown' | 'keyup';
267
+ /** Options for {@link Entity.on} / {@link Entity.off}. */
268
+ interface ListenerOptions {
269
+ /** Register the listener for the capture phase (root→target) instead of bubble. */
270
+ capture?: boolean;
271
+ }
272
+ /**
273
+ * A propagating event dispatched through the entity tree by
274
+ * {@link Entity.dispatchEvent} (DOM-like capture + bubble).
275
+ *
276
+ * It wraps the originating browser event (`nativeEvent`) and adds tree-aware
277
+ * fields: `target` (where it originated), `currentTarget` (the node currently
278
+ * handling it), and `stopPropagation()`. Common native fields (`deltaY`,
279
+ * `clientX`, `key`, …) and `preventDefault()` pass through to `nativeEvent`, so
280
+ * handlers written against the raw DOM event keep working.
281
+ */
282
+ declare class VectoJSEvent<N = unknown> {
283
+ /** The event name. */
284
+ readonly type: VectoEvent;
285
+ /** The entity the event originated on. */
286
+ readonly target: Entity;
287
+ /** The entity whose listeners are currently running (updated per node). */
288
+ currentTarget: Entity;
289
+ /** The wrapped browser event, if any. */
290
+ readonly nativeEvent: N | undefined;
291
+ /** Whether the event bubbles past its target (capture always runs). */
292
+ readonly bubbles: boolean;
293
+ private stopped;
294
+ private stoppedImmediate;
295
+ constructor(type: VectoEvent, target: Entity, nativeEvent?: N, bubbles?: boolean);
296
+ /** Stop the event from reaching the next node in the propagation path. */
297
+ stopPropagation(): void;
298
+ /** Stop propagation AND skip any remaining listeners on the current node. */
299
+ stopImmediatePropagation(): void;
300
+ /** Forward to the native event's `preventDefault` (e.g. stop page scroll). */
301
+ preventDefault(): void;
302
+ /** Whether {@link stopPropagation} has been called. */
303
+ get propagationStopped(): boolean;
304
+ /** Whether {@link stopImmediatePropagation} has been called. */
305
+ get immediatePropagationStopped(): boolean;
306
+ /** Whether the native event's default action was prevented. */
307
+ get defaultPrevented(): boolean;
308
+ /** Native horizontal wheel delta, if this wraps a `WheelEvent`. */
309
+ get deltaX(): number | undefined;
310
+ /** Native vertical wheel delta, if this wraps a `WheelEvent`. */
311
+ get deltaY(): number | undefined;
312
+ /** Native pointer X, if this wraps a pointer/mouse event. */
313
+ get clientX(): number | undefined;
314
+ /** Native pointer Y, if this wraps a pointer/mouse event. */
315
+ get clientY(): number | undefined;
316
+ /** Native key, if this wraps a keyboard event. */
317
+ get key(): string | undefined;
318
+ }
319
+ /**
320
+ * Base class for every node in the Virtual Math Tree (VMT).
321
+ *
322
+ * Subclass `Entity` and implement {@link isPointInside} and {@link render} to
323
+ * create custom drawable objects. Entities form a scene-graph: each node may
324
+ * own child entities, inheriting the parent's transform.
325
+ *
326
+ * @example
327
+ * class CircleEntity extends Entity {
328
+ * isPointInside(x: number, y: number) {
329
+ * return Math.hypot(x - this.x, y - this.y) < 50;
330
+ * }
331
+ * render(r: IRenderer) {
332
+ * r.beginPath();
333
+ * r.fill('#38bdf8');
334
+ * }
335
+ * }
336
+ */
337
+ declare abstract class Entity {
338
+ id: string;
339
+ children: Entity[];
340
+ parent: Entity | null;
341
+ /**
342
+ * Walk up the parent chain to find the scene this entity is currently attached to.
343
+ */
344
+ get scene(): any;
345
+ x: number;
346
+ y: number;
347
+ scaleX: number;
348
+ scaleY: number;
349
+ rotation: number;
350
+ opacity: number;
351
+ isDOMPortal: boolean;
352
+ private _interactive;
353
+ get interactive(): boolean;
354
+ set interactive(val: boolean);
355
+ width: number;
356
+ height: number;
357
+ a11yOffsetX: number;
358
+ a11yOffsetY: number;
359
+ /**
360
+ * Opt in to a viewport-filling accessibility/automation shadow node even when
361
+ * this entity has no intrinsic box (`width`/`height` of `0`). Use for
362
+ * full-screen, boundless interaction surfaces (e.g. an infinite-canvas graph)
363
+ * that need global pointer events. The node is mounted behind all other shadow
364
+ * nodes, so on-top components stay clickable.
365
+ */
366
+ a11yFullViewport: boolean;
367
+ /**
368
+ * Clip this node's children to its local box (`[0,0]–[width,height]`) while
369
+ * rendering. Combined with translating a content child, this is how
370
+ * scroll/overflow containers (e.g. `ScrollView`) keep their content inside a
371
+ * fixed viewport. Off by default (children render unclipped). Canvas2D only.
372
+ */
373
+ clipChildren: boolean;
374
+ protected listeners: Map<VectoEvent, Array<(e: any) => void>>;
375
+ /** Capture-phase listeners (fired root→target before bubble). */
376
+ protected captureListeners: Map<VectoEvent, Array<(e: any) => void>>;
377
+ private animations;
378
+ constructor(id?: string);
379
+ /**
380
+ * Append a child entity to this node's children array.
381
+ *
382
+ * @param child - The entity to add as a child.
383
+ * @returns `this` for method chaining.
384
+ */
385
+ add(child: Entity): this;
386
+ /**
387
+ * Remove a child entity from this node.
388
+ *
389
+ * @param child - The entity to remove.
390
+ * @returns `this` for method chaining.
391
+ */
392
+ remove(child: Entity): this;
393
+ /**
394
+ * Set the local position of this entity.
395
+ *
396
+ * @param x - Horizontal position in local space.
397
+ * @param y - Vertical position in local space.
398
+ * @returns `this` for method chaining.
399
+ * @example entity.setPosition(100, 200);
400
+ */
401
+ setPosition(x: number, y: number): this;
402
+ /**
403
+ * Queue a tween animation toward the specified target property values.
404
+ *
405
+ * Multiple calls chain animations sequentially. Only numeric properties
406
+ * are interpolated; non-numeric values are ignored.
407
+ *
408
+ * @param targetProps - Partial set of numeric properties to tween to.
409
+ * @param durationMs - Duration of the tween in milliseconds.
410
+ * @returns `this` for method chaining.
411
+ * @example entity.animate({ x: 400, opacity: 0 }, 500);
412
+ */
413
+ animate(targetProps: Partial<this>, durationMs: number): this;
414
+ /**
415
+ * Advance the entity's internal state for one frame.
416
+ *
417
+ * Called automatically by the {@link Scene} render loop — override in
418
+ * subclasses to implement custom per-frame logic.
419
+ *
420
+ * @param dt - Elapsed time since the last frame in milliseconds.
421
+ * @param time - Absolute timestamp from `performance.now()`.
422
+ */
423
+ update(_dt: number, time: number): void;
424
+ /**
425
+ * Register a listener for a {@link VectoEvent}.
426
+ *
427
+ * Listeners run in the bubble phase by default; pass `{ capture: true }` for the
428
+ * capture phase (root→target). Bubble listeners also fire for the legacy
429
+ * {@link emit} (direct, self-only) path.
430
+ *
431
+ * @param event - The event name to listen for.
432
+ * @param callback - Handler invoked when the event fires.
433
+ * @param options - `{ capture }` to register for the capture phase.
434
+ * @returns `this` for method chaining.
435
+ * @example entity.on('click', (e) => console.log('clicked', e));
436
+ */
437
+ on(event: VectoEvent, callback: (e: any) => void, options?: ListenerOptions): this;
438
+ /**
439
+ * Remove a previously registered event listener.
440
+ *
441
+ * @param event - The event name to stop listening to.
442
+ * @param callback - The exact handler reference passed to {@link on}.
443
+ * @param options - Must match the phase the listener was registered with.
444
+ * @returns `this` for method chaining.
445
+ */
446
+ off(event: VectoEvent, callback: (e: any) => void, options?: ListenerOptions): this;
447
+ /**
448
+ * Tear down this entity: clear all animations, event listeners, and detach
449
+ * from parent. Call before discarding an entity to prevent memory leaks.
450
+ */
451
+ destroy(): void;
452
+ /**
453
+ * Dispatch a {@link VectoEvent} directly to this entity's bubble-phase listeners
454
+ * only — no tree propagation. Kept for component-internal/self events (e.g. a
455
+ * form control emitting its own `change`); use {@link dispatchEvent} for the
456
+ * capture/bubble path.
457
+ *
458
+ * @param event - The event name to dispatch.
459
+ * @param payload - Arbitrary data forwarded to each listener.
460
+ */
461
+ emit(event: VectoEvent, payload: any): void;
462
+ /** Run one node's listeners for the event, honoring stopImmediatePropagation. */
463
+ private fireListeners;
464
+ /**
465
+ * Dispatch a {@link VectoJSEvent} through the entity tree, DOM-style: a capture
466
+ * phase from the root down to `event.target`, then a bubble phase back up to the
467
+ * root. `event.stopPropagation()` halts the walk; `stopImmediatePropagation()`
468
+ * also skips the remaining listeners on the current node. A non-bubbling event
469
+ * only fires its target in the bubble phase (capture still runs).
470
+ *
471
+ * @param event - The event to propagate (its `target` defines the path).
472
+ */
473
+ dispatchEvent(event: VectoJSEvent): void;
474
+ /**
475
+ * Compute the entity's position in world/canvas space by accumulating
476
+ * local offsets up the scene-graph hierarchy using affine transformations (scale and rotation).
477
+ *
478
+ * @returns World-space {@link Point} for this entity.
479
+ */
480
+ getGlobalPosition(): Point;
481
+ /**
482
+ * Accumulated world scale factors: this entity's own `scaleX`/`scaleY` times
483
+ * those of every ancestor (excluding the scene root). Useful for mapping a
484
+ * world-space point back into local space for hit-testing.
485
+ *
486
+ * @returns The world scale `{ x, y }`.
487
+ */
488
+ getWorldScale(): {
489
+ x: number;
490
+ y: number;
491
+ };
492
+ /**
493
+ * Accumulated world rotation: this entity's own `rotation` plus
494
+ * that of every ancestor (excluding the scene root).
495
+ *
496
+ * @returns The accumulated world rotation in radians.
497
+ */
498
+ getWorldRotation(): number;
499
+ /**
500
+ * Return `true` when the given world-space point lies within this entity's
501
+ * interactive hit area.
502
+ *
503
+ * @param globalX - World-space X coordinate.
504
+ * @param globalY - World-space Y coordinate.
505
+ * @returns Whether the point is inside this entity.
506
+ */
507
+ /**
508
+ * Describe this entity's semantics for the accessibility / automation shadow
509
+ * layer. Override in components to project a real `<button>`, `<a href>`, etc.
510
+ *
511
+ * The default returns `{}`, which `Scene.syncA11y` maps to a plain `div`
512
+ * (preserving the historical behavior of interactive entities).
513
+ *
514
+ * @returns The {@link A11yAttributes} for this entity's shadow node.
515
+ */
516
+ getA11yAttributes(): A11yAttributes;
517
+ /**
518
+ * Local-space axis-aligned bounding box of what this entity's {@link render}
519
+ * draws, used by {@link Scene} for viewport culling.
520
+ *
521
+ * Returns `null` by default, meaning "unknown bounds" — the entity is then
522
+ * never culled (always rendered). Override to return a {@link Bounds} so the
523
+ * scene can skip rendering it when it lies outside the viewport.
524
+ *
525
+ * @returns The local bounds, or `null` to opt out of culling.
526
+ */
527
+ getBounds(): Bounds | null;
528
+ /**
529
+ * Opt into the renderer's draw-call batching fast-path for point-cloud /
530
+ * particle entities that draw as a single filled circle at their local origin.
531
+ *
532
+ * When a leaf entity returns a {@link BatchCircle} and has uniform scale, the
533
+ * {@link Scene} skips its per-entity `save`/`translate`/`scale`/`rotate`/
534
+ * `restore` and {@link render}, emitting the circle through
535
+ * {@link IRenderer.fillCircle} so runs of same-color siblings coalesce into a
536
+ * single `fill()`. Returns `null` by default (normal render path). Read each
537
+ * frame, so an animated color/radius is honored.
538
+ *
539
+ * @returns The circle to batch, or `null` to use the normal {@link render} path.
540
+ */
541
+ getBatchCircle(): BatchCircle | null;
542
+ /**
543
+ * Opt into the GPU instanced-rectangle fast-path for a leaf entity that draws
544
+ * as a single filled rectangle from its local origin. Only used when the
545
+ * {@link Scene} runs a WebGL `pointBackend`; otherwise the entity renders
546
+ * normally via {@link render}. Returns `null` by default. Read each frame.
547
+ *
548
+ * @returns The rectangle to batch, or `null` for the normal render path.
549
+ */
550
+ getBatchRect(): BatchRect | null;
551
+ /**
552
+ * Whether this entity still has a queued/running tween animation.
553
+ *
554
+ * Used by {@link Scene}'s `onDemand` render mode to keep redrawing while an
555
+ * animation is in flight.
556
+ *
557
+ * @returns `true` if at least one animation remains.
558
+ */
559
+ hasPendingAnimations(): boolean;
560
+ abstract isPointInside(globalX: number, globalY: number): boolean;
561
+ /**
562
+ * Draw this entity using the provided renderer.
563
+ *
564
+ * Called each frame after the entity's transform has been pushed onto the
565
+ * renderer's matrix stack.
566
+ *
567
+ * @param renderer - The active renderer instance.
568
+ */
569
+ abstract render(renderer: any): void;
570
+ }
571
+
572
+ export { type A11yAttributes as A, type Bounds as B, Entity as E, type IRenderer as I, type ListenerOptions as L, type Point as P, type VectoEvent as V, type BatchCircle as a, type BatchRect as b, VectoJSEvent as c };