@vectojs/core 1.19.0 → 1.21.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/dist/{chunk-IA3KW4CG.js → chunk-AGP4VLF4.js} +75 -25
- package/dist/{chunk-XWBVBXFZ.mjs → chunk-FRMLD4PP.mjs} +50 -0
- package/dist/{chunk-L4SWVP2H.js → chunk-GKSCJ6AF.js} +201 -9
- package/dist/{chunk-QS3CUV7H.mjs → chunk-RTENOAYT.mjs} +192 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +542 -228
- package/dist/index.mjs +335 -21
- package/dist/renderer/CanvasRenderer.d.ts +2 -0
- package/dist/renderer/GlyphRasterAtlas.d.ts +186 -0
- package/dist/renderer/IRenderer.d.ts +31 -0
- package/dist/renderer/index.d.ts +1 -0
- package/dist/renderer.js +4 -2
- package/dist/renderer.mjs +3 -1
- package/dist/text.js +2 -2
- package/dist/text.mjs +1 -1
- package/dist/tree/Entity.d.ts +144 -0
- package/dist/tree/Scene.d.ts +179 -0
- package/package.json +1 -1
|
@@ -253,20 +253,38 @@ var Entity = (_class2 = class {
|
|
|
253
253
|
* nodes, so on-top components stay clickable.
|
|
254
254
|
*/
|
|
255
255
|
__init33() {this.a11yFullViewport = false}
|
|
256
|
+
/**
|
|
257
|
+
* Hide this entity AND its whole subtree from the accessibility/automation
|
|
258
|
+
* projection, regardless of each node's own `interactive` flag.
|
|
259
|
+
*
|
|
260
|
+
* For a container that is logically closed while still mounted — an `Overlay`
|
|
261
|
+
* after `hide()`, a collapsed panel kept in the tree for its transition. Setting
|
|
262
|
+
* `interactive = false` on the container alone is not enough: the projection walk
|
|
263
|
+
* still descends, and any still-interactive child is re-created on the next
|
|
264
|
+
* frame. Measured before this existed: after `Popover.hide()` the popover's own
|
|
265
|
+
* element was gone while its button stayed projected with `tabIndex: 0` and a
|
|
266
|
+
* live box, so a keyboard user could Tab into a hidden popover.
|
|
267
|
+
*
|
|
268
|
+
* Deliberately NOT inferred from `opacity`: `Overlay.hide()` springs opacity
|
|
269
|
+
* toward 0, so mid-transition it reads nonzero (~0.26 when measured) and an
|
|
270
|
+
* `=== 0` test never fires; a threshold would instead silently un-project a
|
|
271
|
+
* faint-but-live control.
|
|
272
|
+
*/
|
|
273
|
+
__init34() {this.a11yHidden = false}
|
|
256
274
|
/**
|
|
257
275
|
* Clip this node's children to its local box (`[0,0]–[width,height]`) while
|
|
258
276
|
* rendering. Combined with translating a content child, this is how
|
|
259
277
|
* scroll/overflow containers (e.g. `ScrollView`) keep their content inside a
|
|
260
278
|
* fixed viewport. Off by default (children render unclipped). Canvas2D only.
|
|
261
279
|
*/
|
|
262
|
-
|
|
280
|
+
__init35() {this.clipChildren = false}
|
|
263
281
|
// Lazily allocated (see _drivers above). Most entities never register a
|
|
264
282
|
// listener or an imperative animate() tween.
|
|
265
|
-
|
|
283
|
+
__init36() {this.listeners = null}
|
|
266
284
|
/** Capture-phase listeners (fired root→target before bubble). */
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
constructor(id) {;_class2.prototype.__init3.call(this);_class2.prototype.__init4.call(this);_class2.prototype.__init5.call(this);_class2.prototype.__init6.call(this);_class2.prototype.__init7.call(this);_class2.prototype.__init8.call(this);_class2.prototype.__init9.call(this);_class2.prototype.__init10.call(this);_class2.prototype.__init11.call(this);_class2.prototype.__init12.call(this);_class2.prototype.__init13.call(this);_class2.prototype.__init14.call(this);_class2.prototype.__init15.call(this);_class2.prototype.__init16.call(this);_class2.prototype.__init17.call(this);_class2.prototype.__init18.call(this);_class2.prototype.__init19.call(this);_class2.prototype.__init20.call(this);_class2.prototype.__init21.call(this);_class2.prototype.__init22.call(this);_class2.prototype.__init23.call(this);_class2.prototype.__init24.call(this);_class2.prototype.__init25.call(this);_class2.prototype.__init26.call(this);_class2.prototype.__init27.call(this);_class2.prototype.__init28.call(this);_class2.prototype.__init29.call(this);_class2.prototype.__init30.call(this);_class2.prototype.__init31.call(this);_class2.prototype.__init32.call(this);_class2.prototype.__init33.call(this);_class2.prototype.__init34.call(this);_class2.prototype.__init35.call(this);_class2.prototype.__init36.call(this);_class2.prototype.__init37.call(this);
|
|
285
|
+
__init37() {this.captureListeners = null}
|
|
286
|
+
__init38() {this.animations = null}
|
|
287
|
+
constructor(id) {;_class2.prototype.__init3.call(this);_class2.prototype.__init4.call(this);_class2.prototype.__init5.call(this);_class2.prototype.__init6.call(this);_class2.prototype.__init7.call(this);_class2.prototype.__init8.call(this);_class2.prototype.__init9.call(this);_class2.prototype.__init10.call(this);_class2.prototype.__init11.call(this);_class2.prototype.__init12.call(this);_class2.prototype.__init13.call(this);_class2.prototype.__init14.call(this);_class2.prototype.__init15.call(this);_class2.prototype.__init16.call(this);_class2.prototype.__init17.call(this);_class2.prototype.__init18.call(this);_class2.prototype.__init19.call(this);_class2.prototype.__init20.call(this);_class2.prototype.__init21.call(this);_class2.prototype.__init22.call(this);_class2.prototype.__init23.call(this);_class2.prototype.__init24.call(this);_class2.prototype.__init25.call(this);_class2.prototype.__init26.call(this);_class2.prototype.__init27.call(this);_class2.prototype.__init28.call(this);_class2.prototype.__init29.call(this);_class2.prototype.__init30.call(this);_class2.prototype.__init31.call(this);_class2.prototype.__init32.call(this);_class2.prototype.__init33.call(this);_class2.prototype.__init34.call(this);_class2.prototype.__init35.call(this);_class2.prototype.__init36.call(this);_class2.prototype.__init37.call(this);_class2.prototype.__init38.call(this);
|
|
270
288
|
this.id = id || `entity_${Math.random().toString(36).substring(2, 9)}`;
|
|
271
289
|
}
|
|
272
290
|
/**
|
|
@@ -946,6 +964,38 @@ var Entity = (_class2 = class {
|
|
|
946
964
|
*
|
|
947
965
|
* @returns The {@link A11yAttributes} for this entity's shadow node.
|
|
948
966
|
*/
|
|
967
|
+
/**
|
|
968
|
+
* Describe this entity's own debug surface for DevTools.
|
|
969
|
+
*
|
|
970
|
+
* Returns `null` by default, meaning "nothing beyond the generic `Entity`
|
|
971
|
+
* fields the inspector already shows". Override in a component to expose the
|
|
972
|
+
* state that makes it inspectable — see {@link DevtoolsDescriptor}.
|
|
973
|
+
*
|
|
974
|
+
* Called only while a panel is inspecting this entity, never per frame, so it
|
|
975
|
+
* may compute values that would be too expensive to track continuously.
|
|
976
|
+
*
|
|
977
|
+
* @returns A descriptor, or `null` to opt out.
|
|
978
|
+
*/
|
|
979
|
+
getDevtoolsDescriptor() {
|
|
980
|
+
return null;
|
|
981
|
+
}
|
|
982
|
+
/**
|
|
983
|
+
* Which of a child's properties this entity computes during layout.
|
|
984
|
+
*
|
|
985
|
+
* Returns an empty array by default, meaning "this entity does not position its
|
|
986
|
+
* children". A container that lays out children — `Stack`, `Table`, `Tabs` —
|
|
987
|
+
* overrides it so tooling can mark those values as parent-owned: editing `x` on
|
|
988
|
+
* a `Stack` child is reverted by the next layout, and knowing that in advance is
|
|
989
|
+
* the difference between a confusing tool and a correct one.
|
|
990
|
+
*
|
|
991
|
+
* @param child - The child being asked about. Containers whose control depends
|
|
992
|
+
* on the child (a `Table` cell versus its header) can answer per child.
|
|
993
|
+
* @returns Property names this entity overwrites on that child.
|
|
994
|
+
*/
|
|
995
|
+
getLayoutControlledProperties(child) {
|
|
996
|
+
void child;
|
|
997
|
+
return [];
|
|
998
|
+
}
|
|
949
999
|
getA11yAttributes() {
|
|
950
1000
|
return {};
|
|
951
1001
|
}
|
|
@@ -1041,15 +1091,15 @@ var MSDFTextEntity = (_class3 = class extends Entity {
|
|
|
1041
1091
|
// joined by soft hyphens (U+00AD); the worker then treats those as break
|
|
1042
1092
|
// opportunities. `text` keeps the original string for a11y/content
|
|
1043
1093
|
// projection; `layoutText` is the soft-hyphen-annotated string sent to layout.
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1094
|
+
__init39() {this.hyphenator = null}
|
|
1095
|
+
__init40() {this.layoutText = ""}
|
|
1096
|
+
__init41() {this.text = ""}
|
|
1097
|
+
__init42() {this.lastRenderedSeqId = 0}
|
|
1098
|
+
__init43() {this.rgbColorCache = /* @__PURE__ */ new Map()}
|
|
1099
|
+
__init44() {this.fontStringCache = []}
|
|
1100
|
+
__init45() {this.layoutResult = null}
|
|
1051
1101
|
constructor(text, options) {
|
|
1052
|
-
super();_class3.prototype.
|
|
1102
|
+
super();_class3.prototype.__init39.call(this);_class3.prototype.__init40.call(this);_class3.prototype.__init41.call(this);_class3.prototype.__init42.call(this);_class3.prototype.__init43.call(this);_class3.prototype.__init44.call(this);_class3.prototype.__init45.call(this);;
|
|
1053
1103
|
this.font = options.font;
|
|
1054
1104
|
this.texture = options.texture;
|
|
1055
1105
|
this.fallbackFont = _nullishCoalesce(options.fallbackFont, () => ( "sans-serif"));
|
|
@@ -1269,19 +1319,19 @@ function readSvgAttribute(source, name) {
|
|
|
1269
1319
|
return null;
|
|
1270
1320
|
}
|
|
1271
1321
|
var SVGEntity = (_class4 = class extends Entity {
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1322
|
+
__init46() {this.svgSource = ""}
|
|
1323
|
+
__init47() {this.imageBitmap = null}
|
|
1324
|
+
__init48() {this.imageElement = null}
|
|
1325
|
+
__init49() {this.blobURL = null}
|
|
1326
|
+
__init50() {this.currentImg = null}
|
|
1327
|
+
__init51() {this.lodTimeout = null}
|
|
1328
|
+
__init52() {this.cachedDoc = null}
|
|
1329
|
+
__init53() {this.baseWidth = 100}
|
|
1330
|
+
__init54() {this.baseHeight = 100}
|
|
1331
|
+
__init55() {this.lastRasterizedScale = 1}
|
|
1332
|
+
__init56() {this.targetScale = 1}
|
|
1283
1333
|
constructor(svgSource, id) {
|
|
1284
|
-
super(id);_class4.prototype.
|
|
1334
|
+
super(id);_class4.prototype.__init46.call(this);_class4.prototype.__init47.call(this);_class4.prototype.__init48.call(this);_class4.prototype.__init49.call(this);_class4.prototype.__init50.call(this);_class4.prototype.__init51.call(this);_class4.prototype.__init52.call(this);_class4.prototype.__init53.call(this);_class4.prototype.__init54.call(this);_class4.prototype.__init55.call(this);_class4.prototype.__init56.call(this);;
|
|
1285
1335
|
this.setSVGSource(svgSource);
|
|
1286
1336
|
}
|
|
1287
1337
|
setSVGSource(svgSource) {
|
|
@@ -251,6 +251,24 @@ var Entity = class {
|
|
|
251
251
|
* nodes, so on-top components stay clickable.
|
|
252
252
|
*/
|
|
253
253
|
a11yFullViewport = false;
|
|
254
|
+
/**
|
|
255
|
+
* Hide this entity AND its whole subtree from the accessibility/automation
|
|
256
|
+
* projection, regardless of each node's own `interactive` flag.
|
|
257
|
+
*
|
|
258
|
+
* For a container that is logically closed while still mounted — an `Overlay`
|
|
259
|
+
* after `hide()`, a collapsed panel kept in the tree for its transition. Setting
|
|
260
|
+
* `interactive = false` on the container alone is not enough: the projection walk
|
|
261
|
+
* still descends, and any still-interactive child is re-created on the next
|
|
262
|
+
* frame. Measured before this existed: after `Popover.hide()` the popover's own
|
|
263
|
+
* element was gone while its button stayed projected with `tabIndex: 0` and a
|
|
264
|
+
* live box, so a keyboard user could Tab into a hidden popover.
|
|
265
|
+
*
|
|
266
|
+
* Deliberately NOT inferred from `opacity`: `Overlay.hide()` springs opacity
|
|
267
|
+
* toward 0, so mid-transition it reads nonzero (~0.26 when measured) and an
|
|
268
|
+
* `=== 0` test never fires; a threshold would instead silently un-project a
|
|
269
|
+
* faint-but-live control.
|
|
270
|
+
*/
|
|
271
|
+
a11yHidden = false;
|
|
254
272
|
/**
|
|
255
273
|
* Clip this node's children to its local box (`[0,0]–[width,height]`) while
|
|
256
274
|
* rendering. Combined with translating a content child, this is how
|
|
@@ -944,6 +962,38 @@ var Entity = class {
|
|
|
944
962
|
*
|
|
945
963
|
* @returns The {@link A11yAttributes} for this entity's shadow node.
|
|
946
964
|
*/
|
|
965
|
+
/**
|
|
966
|
+
* Describe this entity's own debug surface for DevTools.
|
|
967
|
+
*
|
|
968
|
+
* Returns `null` by default, meaning "nothing beyond the generic `Entity`
|
|
969
|
+
* fields the inspector already shows". Override in a component to expose the
|
|
970
|
+
* state that makes it inspectable — see {@link DevtoolsDescriptor}.
|
|
971
|
+
*
|
|
972
|
+
* Called only while a panel is inspecting this entity, never per frame, so it
|
|
973
|
+
* may compute values that would be too expensive to track continuously.
|
|
974
|
+
*
|
|
975
|
+
* @returns A descriptor, or `null` to opt out.
|
|
976
|
+
*/
|
|
977
|
+
getDevtoolsDescriptor() {
|
|
978
|
+
return null;
|
|
979
|
+
}
|
|
980
|
+
/**
|
|
981
|
+
* Which of a child's properties this entity computes during layout.
|
|
982
|
+
*
|
|
983
|
+
* Returns an empty array by default, meaning "this entity does not position its
|
|
984
|
+
* children". A container that lays out children — `Stack`, `Table`, `Tabs` —
|
|
985
|
+
* overrides it so tooling can mark those values as parent-owned: editing `x` on
|
|
986
|
+
* a `Stack` child is reverted by the next layout, and knowing that in advance is
|
|
987
|
+
* the difference between a confusing tool and a correct one.
|
|
988
|
+
*
|
|
989
|
+
* @param child - The child being asked about. Containers whose control depends
|
|
990
|
+
* on the child (a `Table` cell versus its header) can answer per child.
|
|
991
|
+
* @returns Property names this entity overwrites on that child.
|
|
992
|
+
*/
|
|
993
|
+
getLayoutControlledProperties(child) {
|
|
994
|
+
void child;
|
|
995
|
+
return [];
|
|
996
|
+
}
|
|
947
997
|
getA11yAttributes() {
|
|
948
998
|
return {};
|
|
949
999
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; } var _class; var _class2; var _class3; var _class4;const __vecto_cjs_url=require("url").pathToFileURL(__filename).href;
|
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; } var _class; var _class2; var _class3; var _class4; var _class5;const __vecto_cjs_url=require("url").pathToFileURL(__filename).href;
|
|
2
2
|
|
|
3
3
|
// src/renderer/CanvasRenderer.ts
|
|
4
4
|
var TWO_PI = Math.PI * 2;
|
|
@@ -212,6 +212,11 @@ var CanvasRenderer = (_class = class _CanvasRenderer {
|
|
|
212
212
|
this.ctx.drawImage(source, dx, dy, dw, dh);
|
|
213
213
|
}
|
|
214
214
|
/** @inheritdoc */
|
|
215
|
+
drawImageRect(source, sx, sy, sw, sh, dx, dy, dw, dh) {
|
|
216
|
+
this.flush();
|
|
217
|
+
this.ctx.drawImage(source, sx, sy, sw, sh, dx, dy, dw, dh);
|
|
218
|
+
}
|
|
219
|
+
/** @inheritdoc */
|
|
215
220
|
fillCircle(cx, cy, radius, color, alpha = 1) {
|
|
216
221
|
if (this.batchActive && (color !== this.batchColor || alpha !== this.batchAlpha)) {
|
|
217
222
|
this.flush();
|
|
@@ -1809,15 +1814,201 @@ var WebGPUParticleSystemManager = (_class3 = class {
|
|
|
1809
1814
|
}
|
|
1810
1815
|
}, _class3);
|
|
1811
1816
|
|
|
1817
|
+
// src/renderer/GlyphRasterAtlas.ts
|
|
1818
|
+
var HARD_MAX_SIZE = 8192;
|
|
1819
|
+
var PAD = 2;
|
|
1820
|
+
var GlyphRasterAtlas = (_class4 = class {
|
|
1821
|
+
__init35() {this.slots = /* @__PURE__ */ new Map()}
|
|
1822
|
+
|
|
1823
|
+
|
|
1824
|
+
__init36() {this.canvas = null}
|
|
1825
|
+
__init37() {this.ctx = null}
|
|
1826
|
+
/**
|
|
1827
|
+
* Shelf packing: glyphs land left-to-right on a row, then a new row starts.
|
|
1828
|
+
* A monospace grid produces near-uniform widths, so shelves waste very little
|
|
1829
|
+
* and cost one comparison per insert — a real 2D packer would buy nothing here.
|
|
1830
|
+
*/
|
|
1831
|
+
__init38() {this.penX = PAD}
|
|
1832
|
+
__init39() {this.penY = PAD}
|
|
1833
|
+
__init40() {this.rowHeight = 0}
|
|
1834
|
+
__init41() {this._hits = 0}
|
|
1835
|
+
__init42() {this._misses = 0}
|
|
1836
|
+
__init43() {this._resets = 0}
|
|
1837
|
+
constructor(options = {}) {;_class4.prototype.__init35.call(this);_class4.prototype.__init36.call(this);_class4.prototype.__init37.call(this);_class4.prototype.__init38.call(this);_class4.prototype.__init39.call(this);_class4.prototype.__init40.call(this);_class4.prototype.__init41.call(this);_class4.prototype.__init42.call(this);_class4.prototype.__init43.call(this);
|
|
1838
|
+
this.dpr = Math.max(1, _nullishCoalesce(options.dpr, () => ( 1)));
|
|
1839
|
+
this.maxSize = Math.min(HARD_MAX_SIZE, Math.max(256, _nullishCoalesce(options.maxSize, () => ( 2048))));
|
|
1840
|
+
}
|
|
1841
|
+
/** Live instrumentation snapshot. */
|
|
1842
|
+
get stats() {
|
|
1843
|
+
return {
|
|
1844
|
+
hits: this._hits,
|
|
1845
|
+
misses: this._misses,
|
|
1846
|
+
size: this.slots.size,
|
|
1847
|
+
resets: this._resets
|
|
1848
|
+
};
|
|
1849
|
+
}
|
|
1850
|
+
/**
|
|
1851
|
+
* The atlas canvas, to pass as the blit source.
|
|
1852
|
+
*
|
|
1853
|
+
* `null` until the first successful {@link get}, and in any non-DOM context.
|
|
1854
|
+
*/
|
|
1855
|
+
get source() {
|
|
1856
|
+
return this.canvas;
|
|
1857
|
+
}
|
|
1858
|
+
ensureCanvas() {
|
|
1859
|
+
if (this.ctx) return true;
|
|
1860
|
+
if (typeof document === "undefined") return false;
|
|
1861
|
+
const canvas = document.createElement("canvas");
|
|
1862
|
+
canvas.width = this.maxSize;
|
|
1863
|
+
canvas.height = this.maxSize;
|
|
1864
|
+
const ctx = canvas.getContext("2d");
|
|
1865
|
+
if (!ctx) return false;
|
|
1866
|
+
this.canvas = canvas;
|
|
1867
|
+
this.ctx = ctx;
|
|
1868
|
+
return true;
|
|
1869
|
+
}
|
|
1870
|
+
/**
|
|
1871
|
+
* Look up a glyph, rasterizing it into the atlas on first request.
|
|
1872
|
+
*
|
|
1873
|
+
* @param font - Full CSS `font` shorthand, used for measuring and painting.
|
|
1874
|
+
* @param color - CSS color baked into the pixels.
|
|
1875
|
+
* @param glyph - A single grapheme cluster. Long strings are rejected
|
|
1876
|
+
* (`null`): they defeat the atlas's fixed-slot packing and belong in
|
|
1877
|
+
* `fillText` or {@link TextRasterCache}.
|
|
1878
|
+
* @returns The slot, or `null` when the caller must fall back to `fillText`
|
|
1879
|
+
* (headless, unrasterizable, or too large to pack).
|
|
1880
|
+
*/
|
|
1881
|
+
get(font, color, glyph) {
|
|
1882
|
+
const key = font + "\0" + color + "\0" + glyph;
|
|
1883
|
+
const existing = this.slots.get(key);
|
|
1884
|
+
if (existing !== void 0) {
|
|
1885
|
+
if (existing) this._hits++;
|
|
1886
|
+
return existing;
|
|
1887
|
+
}
|
|
1888
|
+
this._misses++;
|
|
1889
|
+
if (glyph.length === 0 || glyph.length > 8) {
|
|
1890
|
+
this.slots.set(key, null);
|
|
1891
|
+
return null;
|
|
1892
|
+
}
|
|
1893
|
+
if (!this.ensureCanvas()) return null;
|
|
1894
|
+
const ctx = this.ctx;
|
|
1895
|
+
ctx.font = font;
|
|
1896
|
+
const metrics = ctx.measureText(glyph);
|
|
1897
|
+
const advance = metrics.width;
|
|
1898
|
+
const ascent = metrics.actualBoundingBoxAscent || metrics.fontBoundingBoxAscent || advance * 0.9;
|
|
1899
|
+
const descent = metrics.actualBoundingBoxDescent || metrics.fontBoundingBoxDescent || advance * 0.25;
|
|
1900
|
+
const left = metrics.actualBoundingBoxLeft || 0;
|
|
1901
|
+
const right = metrics.actualBoundingBoxRight || advance;
|
|
1902
|
+
const offsetX = Math.ceil(left) + PAD;
|
|
1903
|
+
const offsetY = Math.ceil(ascent) + PAD;
|
|
1904
|
+
const w = offsetX + Math.ceil(right) + PAD;
|
|
1905
|
+
const h = offsetY + Math.ceil(descent) + PAD;
|
|
1906
|
+
if (!(w > 0) || !(h > 0)) {
|
|
1907
|
+
this.slots.set(key, null);
|
|
1908
|
+
return null;
|
|
1909
|
+
}
|
|
1910
|
+
const dpr = this.dpr;
|
|
1911
|
+
const dw = Math.ceil(w * dpr);
|
|
1912
|
+
const dh = Math.ceil(h * dpr);
|
|
1913
|
+
if (dw + PAD * 2 > this.maxSize || dh + PAD * 2 > this.maxSize) {
|
|
1914
|
+
this.slots.set(key, null);
|
|
1915
|
+
return null;
|
|
1916
|
+
}
|
|
1917
|
+
if (this.penX + dw + PAD > this.maxSize) {
|
|
1918
|
+
this.penX = PAD;
|
|
1919
|
+
this.penY += this.rowHeight + PAD;
|
|
1920
|
+
this.rowHeight = 0;
|
|
1921
|
+
}
|
|
1922
|
+
if (this.penY + dh + PAD > this.maxSize) {
|
|
1923
|
+
this.reset();
|
|
1924
|
+
if (!this.ensureCanvas()) return null;
|
|
1925
|
+
this.ctx.font = font;
|
|
1926
|
+
}
|
|
1927
|
+
const sx = this.penX;
|
|
1928
|
+
const sy = this.penY;
|
|
1929
|
+
const c = this.ctx;
|
|
1930
|
+
c.save();
|
|
1931
|
+
c.translate(sx, sy);
|
|
1932
|
+
if (dpr !== 1) c.scale(dpr, dpr);
|
|
1933
|
+
c.font = font;
|
|
1934
|
+
c.textBaseline = "alphabetic";
|
|
1935
|
+
c.fillStyle = color;
|
|
1936
|
+
c.fillText(glyph, offsetX, offsetY);
|
|
1937
|
+
c.restore();
|
|
1938
|
+
this.penX += dw + PAD;
|
|
1939
|
+
if (dh > this.rowHeight) this.rowHeight = dh;
|
|
1940
|
+
const slot = {
|
|
1941
|
+
sx,
|
|
1942
|
+
sy,
|
|
1943
|
+
sw: dw,
|
|
1944
|
+
sh: dh,
|
|
1945
|
+
w,
|
|
1946
|
+
h,
|
|
1947
|
+
offsetX,
|
|
1948
|
+
offsetY,
|
|
1949
|
+
glyph,
|
|
1950
|
+
font,
|
|
1951
|
+
advance,
|
|
1952
|
+
left,
|
|
1953
|
+
right
|
|
1954
|
+
};
|
|
1955
|
+
this.slots.set(key, slot);
|
|
1956
|
+
return slot;
|
|
1957
|
+
}
|
|
1958
|
+
/**
|
|
1959
|
+
* Find the slot occupying a source position, or `null`.
|
|
1960
|
+
*
|
|
1961
|
+
* The inverse of {@link get}: it maps a blit back to the glyph it drew. Exists
|
|
1962
|
+
* for instrumentation — a test or devtool that traces `drawImage` calls sees
|
|
1963
|
+
* only a source rect, and needs this to recover which cluster was painted and
|
|
1964
|
+
* with what metrics. Linear over resident slots, so it is a diagnostic, not a
|
|
1965
|
+
* per-frame call.
|
|
1966
|
+
*/
|
|
1967
|
+
slotAt(sx, sy) {
|
|
1968
|
+
for (const slot of this.slots.values()) {
|
|
1969
|
+
if (slot && slot.sx === sx && slot.sy === sy) return slot;
|
|
1970
|
+
}
|
|
1971
|
+
return null;
|
|
1972
|
+
}
|
|
1973
|
+
/**
|
|
1974
|
+
* Drop every glyph and reuse the canvas.
|
|
1975
|
+
*
|
|
1976
|
+
* Call after a font or theme change: slots are keyed by `(font, color, glyph)`
|
|
1977
|
+
* so stale entries are never *returned* wrongly, but they do occupy space.
|
|
1978
|
+
*/
|
|
1979
|
+
reset() {
|
|
1980
|
+
this.slots.clear();
|
|
1981
|
+
this.penX = PAD;
|
|
1982
|
+
this.penY = PAD;
|
|
1983
|
+
this.rowHeight = 0;
|
|
1984
|
+
this._resets++;
|
|
1985
|
+
if (this.ctx && this.canvas) {
|
|
1986
|
+
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
|
|
1987
|
+
}
|
|
1988
|
+
}
|
|
1989
|
+
/** Release the backing canvas and all slots. */
|
|
1990
|
+
destroy() {
|
|
1991
|
+
this.slots.clear();
|
|
1992
|
+
this.canvas = null;
|
|
1993
|
+
this.ctx = null;
|
|
1994
|
+
this.penX = PAD;
|
|
1995
|
+
this.penY = PAD;
|
|
1996
|
+
this.rowHeight = 0;
|
|
1997
|
+
this._hits = 0;
|
|
1998
|
+
this._misses = 0;
|
|
1999
|
+
this._resets = 0;
|
|
2000
|
+
}
|
|
2001
|
+
}, _class4);
|
|
2002
|
+
|
|
1812
2003
|
// src/renderer/TextRasterCache.ts
|
|
1813
|
-
var TextRasterCache = (
|
|
1814
|
-
|
|
2004
|
+
var TextRasterCache = (_class5 = class {
|
|
2005
|
+
__init44() {this.cache = /* @__PURE__ */ new Map()}
|
|
1815
2006
|
|
|
1816
2007
|
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
constructor(options = {}) {;
|
|
2008
|
+
__init45() {this.scratchCtx = null}
|
|
2009
|
+
__init46() {this._hits = 0}
|
|
2010
|
+
__init47() {this._misses = 0}
|
|
2011
|
+
constructor(options = {}) {;_class5.prototype.__init44.call(this);_class5.prototype.__init45.call(this);_class5.prototype.__init46.call(this);_class5.prototype.__init47.call(this);
|
|
1821
2012
|
this.maxEntries = Math.max(1, _nullishCoalesce(options.maxEntries, () => ( 4096)));
|
|
1822
2013
|
this.dpr = Math.max(1, _nullishCoalesce(options.dpr, () => ( 1)));
|
|
1823
2014
|
}
|
|
@@ -1892,7 +2083,8 @@ var TextRasterCache = (_class4 = class {
|
|
|
1892
2083
|
this._hits = 0;
|
|
1893
2084
|
this._misses = 0;
|
|
1894
2085
|
}
|
|
1895
|
-
},
|
|
2086
|
+
}, _class5);
|
|
2087
|
+
|
|
1896
2088
|
|
|
1897
2089
|
|
|
1898
2090
|
|
|
@@ -1903,4 +2095,4 @@ var TextRasterCache = (_class4 = class {
|
|
|
1903
2095
|
|
|
1904
2096
|
|
|
1905
2097
|
|
|
1906
|
-
exports.CanvasRenderer = CanvasRenderer; exports.sanitizeUrl = sanitizeUrl; exports.isSafeUrl = isSafeUrl; exports.SVGRenderer = SVGRenderer; exports.parseColorToRGBA = parseColorToRGBA; exports.createWebGLPointRenderer = createWebGLPointRenderer; exports.WebGPUParticleSystemManager = WebGPUParticleSystemManager; exports.TextRasterCache = TextRasterCache;
|
|
2098
|
+
exports.CanvasRenderer = CanvasRenderer; exports.sanitizeUrl = sanitizeUrl; exports.isSafeUrl = isSafeUrl; exports.SVGRenderer = SVGRenderer; exports.parseColorToRGBA = parseColorToRGBA; exports.createWebGLPointRenderer = createWebGLPointRenderer; exports.WebGPUParticleSystemManager = WebGPUParticleSystemManager; exports.GlyphRasterAtlas = GlyphRasterAtlas; exports.TextRasterCache = TextRasterCache;
|
|
@@ -210,6 +210,11 @@ var CanvasRenderer = class _CanvasRenderer {
|
|
|
210
210
|
this.ctx.drawImage(source, dx, dy, dw, dh);
|
|
211
211
|
}
|
|
212
212
|
/** @inheritdoc */
|
|
213
|
+
drawImageRect(source, sx, sy, sw, sh, dx, dy, dw, dh) {
|
|
214
|
+
this.flush();
|
|
215
|
+
this.ctx.drawImage(source, sx, sy, sw, sh, dx, dy, dw, dh);
|
|
216
|
+
}
|
|
217
|
+
/** @inheritdoc */
|
|
213
218
|
fillCircle(cx, cy, radius, color, alpha = 1) {
|
|
214
219
|
if (this.batchActive && (color !== this.batchColor || alpha !== this.batchAlpha)) {
|
|
215
220
|
this.flush();
|
|
@@ -1807,6 +1812,192 @@ var WebGPUParticleSystemManager = class {
|
|
|
1807
1812
|
}
|
|
1808
1813
|
};
|
|
1809
1814
|
|
|
1815
|
+
// src/renderer/GlyphRasterAtlas.ts
|
|
1816
|
+
var HARD_MAX_SIZE = 8192;
|
|
1817
|
+
var PAD = 2;
|
|
1818
|
+
var GlyphRasterAtlas = class {
|
|
1819
|
+
slots = /* @__PURE__ */ new Map();
|
|
1820
|
+
dpr;
|
|
1821
|
+
maxSize;
|
|
1822
|
+
canvas = null;
|
|
1823
|
+
ctx = null;
|
|
1824
|
+
/**
|
|
1825
|
+
* Shelf packing: glyphs land left-to-right on a row, then a new row starts.
|
|
1826
|
+
* A monospace grid produces near-uniform widths, so shelves waste very little
|
|
1827
|
+
* and cost one comparison per insert — a real 2D packer would buy nothing here.
|
|
1828
|
+
*/
|
|
1829
|
+
penX = PAD;
|
|
1830
|
+
penY = PAD;
|
|
1831
|
+
rowHeight = 0;
|
|
1832
|
+
_hits = 0;
|
|
1833
|
+
_misses = 0;
|
|
1834
|
+
_resets = 0;
|
|
1835
|
+
constructor(options = {}) {
|
|
1836
|
+
this.dpr = Math.max(1, options.dpr ?? 1);
|
|
1837
|
+
this.maxSize = Math.min(HARD_MAX_SIZE, Math.max(256, options.maxSize ?? 2048));
|
|
1838
|
+
}
|
|
1839
|
+
/** Live instrumentation snapshot. */
|
|
1840
|
+
get stats() {
|
|
1841
|
+
return {
|
|
1842
|
+
hits: this._hits,
|
|
1843
|
+
misses: this._misses,
|
|
1844
|
+
size: this.slots.size,
|
|
1845
|
+
resets: this._resets
|
|
1846
|
+
};
|
|
1847
|
+
}
|
|
1848
|
+
/**
|
|
1849
|
+
* The atlas canvas, to pass as the blit source.
|
|
1850
|
+
*
|
|
1851
|
+
* `null` until the first successful {@link get}, and in any non-DOM context.
|
|
1852
|
+
*/
|
|
1853
|
+
get source() {
|
|
1854
|
+
return this.canvas;
|
|
1855
|
+
}
|
|
1856
|
+
ensureCanvas() {
|
|
1857
|
+
if (this.ctx) return true;
|
|
1858
|
+
if (typeof document === "undefined") return false;
|
|
1859
|
+
const canvas = document.createElement("canvas");
|
|
1860
|
+
canvas.width = this.maxSize;
|
|
1861
|
+
canvas.height = this.maxSize;
|
|
1862
|
+
const ctx = canvas.getContext("2d");
|
|
1863
|
+
if (!ctx) return false;
|
|
1864
|
+
this.canvas = canvas;
|
|
1865
|
+
this.ctx = ctx;
|
|
1866
|
+
return true;
|
|
1867
|
+
}
|
|
1868
|
+
/**
|
|
1869
|
+
* Look up a glyph, rasterizing it into the atlas on first request.
|
|
1870
|
+
*
|
|
1871
|
+
* @param font - Full CSS `font` shorthand, used for measuring and painting.
|
|
1872
|
+
* @param color - CSS color baked into the pixels.
|
|
1873
|
+
* @param glyph - A single grapheme cluster. Long strings are rejected
|
|
1874
|
+
* (`null`): they defeat the atlas's fixed-slot packing and belong in
|
|
1875
|
+
* `fillText` or {@link TextRasterCache}.
|
|
1876
|
+
* @returns The slot, or `null` when the caller must fall back to `fillText`
|
|
1877
|
+
* (headless, unrasterizable, or too large to pack).
|
|
1878
|
+
*/
|
|
1879
|
+
get(font, color, glyph) {
|
|
1880
|
+
const key = font + "\0" + color + "\0" + glyph;
|
|
1881
|
+
const existing = this.slots.get(key);
|
|
1882
|
+
if (existing !== void 0) {
|
|
1883
|
+
if (existing) this._hits++;
|
|
1884
|
+
return existing;
|
|
1885
|
+
}
|
|
1886
|
+
this._misses++;
|
|
1887
|
+
if (glyph.length === 0 || glyph.length > 8) {
|
|
1888
|
+
this.slots.set(key, null);
|
|
1889
|
+
return null;
|
|
1890
|
+
}
|
|
1891
|
+
if (!this.ensureCanvas()) return null;
|
|
1892
|
+
const ctx = this.ctx;
|
|
1893
|
+
ctx.font = font;
|
|
1894
|
+
const metrics = ctx.measureText(glyph);
|
|
1895
|
+
const advance = metrics.width;
|
|
1896
|
+
const ascent = metrics.actualBoundingBoxAscent || metrics.fontBoundingBoxAscent || advance * 0.9;
|
|
1897
|
+
const descent = metrics.actualBoundingBoxDescent || metrics.fontBoundingBoxDescent || advance * 0.25;
|
|
1898
|
+
const left = metrics.actualBoundingBoxLeft || 0;
|
|
1899
|
+
const right = metrics.actualBoundingBoxRight || advance;
|
|
1900
|
+
const offsetX = Math.ceil(left) + PAD;
|
|
1901
|
+
const offsetY = Math.ceil(ascent) + PAD;
|
|
1902
|
+
const w = offsetX + Math.ceil(right) + PAD;
|
|
1903
|
+
const h = offsetY + Math.ceil(descent) + PAD;
|
|
1904
|
+
if (!(w > 0) || !(h > 0)) {
|
|
1905
|
+
this.slots.set(key, null);
|
|
1906
|
+
return null;
|
|
1907
|
+
}
|
|
1908
|
+
const dpr = this.dpr;
|
|
1909
|
+
const dw = Math.ceil(w * dpr);
|
|
1910
|
+
const dh = Math.ceil(h * dpr);
|
|
1911
|
+
if (dw + PAD * 2 > this.maxSize || dh + PAD * 2 > this.maxSize) {
|
|
1912
|
+
this.slots.set(key, null);
|
|
1913
|
+
return null;
|
|
1914
|
+
}
|
|
1915
|
+
if (this.penX + dw + PAD > this.maxSize) {
|
|
1916
|
+
this.penX = PAD;
|
|
1917
|
+
this.penY += this.rowHeight + PAD;
|
|
1918
|
+
this.rowHeight = 0;
|
|
1919
|
+
}
|
|
1920
|
+
if (this.penY + dh + PAD > this.maxSize) {
|
|
1921
|
+
this.reset();
|
|
1922
|
+
if (!this.ensureCanvas()) return null;
|
|
1923
|
+
this.ctx.font = font;
|
|
1924
|
+
}
|
|
1925
|
+
const sx = this.penX;
|
|
1926
|
+
const sy = this.penY;
|
|
1927
|
+
const c = this.ctx;
|
|
1928
|
+
c.save();
|
|
1929
|
+
c.translate(sx, sy);
|
|
1930
|
+
if (dpr !== 1) c.scale(dpr, dpr);
|
|
1931
|
+
c.font = font;
|
|
1932
|
+
c.textBaseline = "alphabetic";
|
|
1933
|
+
c.fillStyle = color;
|
|
1934
|
+
c.fillText(glyph, offsetX, offsetY);
|
|
1935
|
+
c.restore();
|
|
1936
|
+
this.penX += dw + PAD;
|
|
1937
|
+
if (dh > this.rowHeight) this.rowHeight = dh;
|
|
1938
|
+
const slot = {
|
|
1939
|
+
sx,
|
|
1940
|
+
sy,
|
|
1941
|
+
sw: dw,
|
|
1942
|
+
sh: dh,
|
|
1943
|
+
w,
|
|
1944
|
+
h,
|
|
1945
|
+
offsetX,
|
|
1946
|
+
offsetY,
|
|
1947
|
+
glyph,
|
|
1948
|
+
font,
|
|
1949
|
+
advance,
|
|
1950
|
+
left,
|
|
1951
|
+
right
|
|
1952
|
+
};
|
|
1953
|
+
this.slots.set(key, slot);
|
|
1954
|
+
return slot;
|
|
1955
|
+
}
|
|
1956
|
+
/**
|
|
1957
|
+
* Find the slot occupying a source position, or `null`.
|
|
1958
|
+
*
|
|
1959
|
+
* The inverse of {@link get}: it maps a blit back to the glyph it drew. Exists
|
|
1960
|
+
* for instrumentation — a test or devtool that traces `drawImage` calls sees
|
|
1961
|
+
* only a source rect, and needs this to recover which cluster was painted and
|
|
1962
|
+
* with what metrics. Linear over resident slots, so it is a diagnostic, not a
|
|
1963
|
+
* per-frame call.
|
|
1964
|
+
*/
|
|
1965
|
+
slotAt(sx, sy) {
|
|
1966
|
+
for (const slot of this.slots.values()) {
|
|
1967
|
+
if (slot && slot.sx === sx && slot.sy === sy) return slot;
|
|
1968
|
+
}
|
|
1969
|
+
return null;
|
|
1970
|
+
}
|
|
1971
|
+
/**
|
|
1972
|
+
* Drop every glyph and reuse the canvas.
|
|
1973
|
+
*
|
|
1974
|
+
* Call after a font or theme change: slots are keyed by `(font, color, glyph)`
|
|
1975
|
+
* so stale entries are never *returned* wrongly, but they do occupy space.
|
|
1976
|
+
*/
|
|
1977
|
+
reset() {
|
|
1978
|
+
this.slots.clear();
|
|
1979
|
+
this.penX = PAD;
|
|
1980
|
+
this.penY = PAD;
|
|
1981
|
+
this.rowHeight = 0;
|
|
1982
|
+
this._resets++;
|
|
1983
|
+
if (this.ctx && this.canvas) {
|
|
1984
|
+
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
|
|
1985
|
+
}
|
|
1986
|
+
}
|
|
1987
|
+
/** Release the backing canvas and all slots. */
|
|
1988
|
+
destroy() {
|
|
1989
|
+
this.slots.clear();
|
|
1990
|
+
this.canvas = null;
|
|
1991
|
+
this.ctx = null;
|
|
1992
|
+
this.penX = PAD;
|
|
1993
|
+
this.penY = PAD;
|
|
1994
|
+
this.rowHeight = 0;
|
|
1995
|
+
this._hits = 0;
|
|
1996
|
+
this._misses = 0;
|
|
1997
|
+
this._resets = 0;
|
|
1998
|
+
}
|
|
1999
|
+
};
|
|
2000
|
+
|
|
1810
2001
|
// src/renderer/TextRasterCache.ts
|
|
1811
2002
|
var TextRasterCache = class {
|
|
1812
2003
|
cache = /* @__PURE__ */ new Map();
|
|
@@ -1900,5 +2091,6 @@ export {
|
|
|
1900
2091
|
parseColorToRGBA,
|
|
1901
2092
|
createWebGLPointRenderer,
|
|
1902
2093
|
WebGPUParticleSystemManager,
|
|
2094
|
+
GlyphRasterAtlas,
|
|
1903
2095
|
TextRasterCache
|
|
1904
2096
|
};
|
package/dist/index.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ export * from './renderer/WebGLPointRenderer';
|
|
|
5
5
|
export * from './renderer/WebGPUParticleSystemManager';
|
|
6
6
|
export * from './renderer/colorParse';
|
|
7
7
|
export * from './renderer/url';
|
|
8
|
+
export * from './renderer/GlyphRasterAtlas';
|
|
8
9
|
export * from './renderer/TextRasterCache';
|
|
9
10
|
export * from './tree/Entity';
|
|
10
11
|
export * from './tree/Scene';
|