@vectojs/core 1.9.1 → 1.10.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.
@@ -7,6 +7,15 @@ var CanvasRenderer = (_class = class _CanvasRenderer {
7
7
 
8
8
 
9
9
 
10
+ /**
11
+ * Cap on the effective device pixel ratio applied by the constructor and
12
+ * {@link resize}. `undefined` (default) uses the real, uncapped
13
+ * `devicePixelRatio` — unchanged from prior versions. Set directly, or via
14
+ * the constructor's third argument; `Scene` (see `SceneOptions.maxDPR`)
15
+ * keeps this in sync on every {@link resize} call, since the real DPR can
16
+ * change at runtime (e.g. a window dragged between displays).
17
+ */
18
+
10
19
  /**
11
20
  * Max circles per batched `fill()`. A single Canvas 2D `fill()` over a path is
12
21
  * superlinear in sub-path count, so an unbounded batch is *slower* than many
@@ -27,9 +36,11 @@ var CanvasRenderer = (_class = class _CanvasRenderer {
27
36
  * fullscreen canvas and sizes to the window — pass this for embedded /
28
37
  * custom-container canvases (the Scene does when `disableWindowResize` is
29
38
  * set) so the canvas's own dimensions aren't clobbered by the window's.
39
+ * @param maxDPR - See {@link maxDPR}.
30
40
  */
31
- constructor(canvas, size) {;_class.prototype.__init.call(this);_class.prototype.__init2.call(this);_class.prototype.__init3.call(this);_class.prototype.__init4.call(this);
32
- const dpr = getDevicePixelRatio();
41
+ constructor(canvas, size, maxDPR) {;_class.prototype.__init.call(this);_class.prototype.__init2.call(this);_class.prototype.__init3.call(this);_class.prototype.__init4.call(this);
42
+ this.maxDPR = maxDPR;
43
+ const dpr = this.effectiveDPR();
33
44
  this.width = _nullishCoalesce(_optionalChain([size, 'optionalAccess', _ => _.width]), () => ( (typeof window !== "undefined" ? window.innerWidth : canvas.width || 0)));
34
45
  this.height = _nullishCoalesce(_optionalChain([size, 'optionalAccess', _2 => _2.height]), () => ( (typeof window !== "undefined" ? window.innerHeight : canvas.height || 0)));
35
46
  canvas.width = this.width * dpr;
@@ -51,6 +62,11 @@ var CanvasRenderer = (_class = class _CanvasRenderer {
51
62
  getContext() {
52
63
  return this.ctx;
53
64
  }
65
+ /** Real `devicePixelRatio`, clamped to {@link maxDPR} when set. */
66
+ effectiveDPR() {
67
+ const real = getDevicePixelRatio();
68
+ return this.maxDPR !== void 0 ? Math.min(real, this.maxDPR) : real;
69
+ }
54
70
  /**
55
71
  * Resize the backing canvas buffer and re-apply DPR scaling.
56
72
  *
@@ -60,7 +76,7 @@ var CanvasRenderer = (_class = class _CanvasRenderer {
60
76
  * @param height - New logical height in CSS pixels.
61
77
  */
62
78
  resize(width, height) {
63
- const dpr = getDevicePixelRatio();
79
+ const dpr = this.effectiveDPR();
64
80
  this.width = width;
65
81
  this.height = height;
66
82
  this.ctx.canvas.width = width * dpr;
@@ -1028,11 +1044,13 @@ function createWebGLPointRenderer(canvas) {
1028
1044
  gl.bindVertexArray(null);
1029
1045
  glyphCount = 0;
1030
1046
  };
1031
- return {
1047
+ const renderer = {
1048
+ maxDPR: void 0,
1032
1049
  resize(width, height) {
1033
1050
  logicalW = width;
1034
1051
  logicalH = height;
1035
- dpr = typeof devicePixelRatio !== "undefined" ? devicePixelRatio || 1 : 1;
1052
+ const realDpr = typeof devicePixelRatio !== "undefined" ? devicePixelRatio || 1 : 1;
1053
+ dpr = renderer.maxDPR !== void 0 ? Math.min(realDpr, renderer.maxDPR) : realDpr;
1036
1054
  canvas.width = Math.round(width * dpr);
1037
1055
  canvas.height = Math.round(height * dpr);
1038
1056
  canvas.style.width = `${width}px`;
@@ -1290,6 +1308,7 @@ function createWebGLPointRenderer(canvas) {
1290
1308
  if (msdfTexture) gl.deleteTexture(msdfTexture);
1291
1309
  }
1292
1310
  };
1311
+ return renderer;
1293
1312
  }
1294
1313
 
1295
1314
  // src/renderer/WebGPUParticleSystemManager.ts
@@ -7,6 +7,15 @@ var CanvasRenderer = class _CanvasRenderer {
7
7
  ctx;
8
8
  width;
9
9
  height;
10
+ /**
11
+ * Cap on the effective device pixel ratio applied by the constructor and
12
+ * {@link resize}. `undefined` (default) uses the real, uncapped
13
+ * `devicePixelRatio` — unchanged from prior versions. Set directly, or via
14
+ * the constructor's third argument; `Scene` (see `SceneOptions.maxDPR`)
15
+ * keeps this in sync on every {@link resize} call, since the real DPR can
16
+ * change at runtime (e.g. a window dragged between displays).
17
+ */
18
+ maxDPR;
10
19
  /**
11
20
  * Max circles per batched `fill()`. A single Canvas 2D `fill()` over a path is
12
21
  * superlinear in sub-path count, so an unbounded batch is *slower* than many
@@ -27,9 +36,11 @@ var CanvasRenderer = class _CanvasRenderer {
27
36
  * fullscreen canvas and sizes to the window — pass this for embedded /
28
37
  * custom-container canvases (the Scene does when `disableWindowResize` is
29
38
  * set) so the canvas's own dimensions aren't clobbered by the window's.
39
+ * @param maxDPR - See {@link maxDPR}.
30
40
  */
31
- constructor(canvas, size) {
32
- const dpr = getDevicePixelRatio();
41
+ constructor(canvas, size, maxDPR) {
42
+ this.maxDPR = maxDPR;
43
+ const dpr = this.effectiveDPR();
33
44
  this.width = size?.width ?? (typeof window !== "undefined" ? window.innerWidth : canvas.width || 0);
34
45
  this.height = size?.height ?? (typeof window !== "undefined" ? window.innerHeight : canvas.height || 0);
35
46
  canvas.width = this.width * dpr;
@@ -51,6 +62,11 @@ var CanvasRenderer = class _CanvasRenderer {
51
62
  getContext() {
52
63
  return this.ctx;
53
64
  }
65
+ /** Real `devicePixelRatio`, clamped to {@link maxDPR} when set. */
66
+ effectiveDPR() {
67
+ const real = getDevicePixelRatio();
68
+ return this.maxDPR !== void 0 ? Math.min(real, this.maxDPR) : real;
69
+ }
54
70
  /**
55
71
  * Resize the backing canvas buffer and re-apply DPR scaling.
56
72
  *
@@ -60,7 +76,7 @@ var CanvasRenderer = class _CanvasRenderer {
60
76
  * @param height - New logical height in CSS pixels.
61
77
  */
62
78
  resize(width, height) {
63
- const dpr = getDevicePixelRatio();
79
+ const dpr = this.effectiveDPR();
64
80
  this.width = width;
65
81
  this.height = height;
66
82
  this.ctx.canvas.width = width * dpr;
@@ -1028,11 +1044,13 @@ function createWebGLPointRenderer(canvas) {
1028
1044
  gl.bindVertexArray(null);
1029
1045
  glyphCount = 0;
1030
1046
  };
1031
- return {
1047
+ const renderer = {
1048
+ maxDPR: void 0,
1032
1049
  resize(width, height) {
1033
1050
  logicalW = width;
1034
1051
  logicalH = height;
1035
- dpr = typeof devicePixelRatio !== "undefined" ? devicePixelRatio || 1 : 1;
1052
+ const realDpr = typeof devicePixelRatio !== "undefined" ? devicePixelRatio || 1 : 1;
1053
+ dpr = renderer.maxDPR !== void 0 ? Math.min(realDpr, renderer.maxDPR) : realDpr;
1036
1054
  canvas.width = Math.round(width * dpr);
1037
1055
  canvas.height = Math.round(height * dpr);
1038
1056
  canvas.style.width = `${width}px`;
@@ -1290,6 +1308,7 @@ function createWebGLPointRenderer(canvas) {
1290
1308
  if (msdfTexture) gl.deleteTexture(msdfTexture);
1291
1309
  }
1292
1310
  };
1311
+ return renderer;
1293
1312
  }
1294
1313
 
1295
1314
  // src/renderer/WebGPUParticleSystemManager.ts
package/dist/index.js CHANGED
@@ -12,7 +12,7 @@ var _chunkBA5HUUDFjs = require('./chunk-BA5HUUDF.js');
12
12
 
13
13
 
14
14
 
15
- var _chunkBPMNCGU7js = require('./chunk-BPMNCGU7.js');
15
+ var _chunkORCPCIJ7js = require('./chunk-ORCPCIJ7.js');
16
16
 
17
17
 
18
18
 
@@ -708,6 +708,8 @@ var Scene = (_class2 = class _Scene {
708
708
 
709
709
 
710
710
  __init44() {this.disableWindowResize = false}
711
+ /** See {@link SceneOptions.maxDPR}. `undefined` = uncapped (real DPR). */
712
+
711
713
  // WebGPU properties
712
714
  __init45() {this.destroyed = false}
713
715
  __init46() {this.device = null}
@@ -737,6 +739,7 @@ var Scene = (_class2 = class _Scene {
737
739
  this.canvas = canvas;
738
740
  this.debugA11y = _nullishCoalesce(options.debugA11y, () => ( false));
739
741
  this.disableWindowResize = _nullishCoalesce(options.disableWindowResize, () => ( false));
742
+ this.maxDPR = options.maxDPR;
740
743
  if (this.disableWindowResize) {
741
744
  const styleWidth = parseInlinePx(_optionalChain([canvas, 'access', _23 => _23.style, 'optionalAccess', _24 => _24.width]));
742
745
  const styleHeight = parseInlinePx(_optionalChain([canvas, 'access', _25 => _25.style, 'optionalAccess', _26 => _26.height]));
@@ -775,9 +778,10 @@ var Scene = (_class2 = class _Scene {
775
778
  if (options.renderer) {
776
779
  this.renderer = options.renderer;
777
780
  } else {
778
- this.renderer = new (0, _chunkBPMNCGU7js.CanvasRenderer)(
781
+ this.renderer = new (0, _chunkORCPCIJ7js.CanvasRenderer)(
779
782
  canvas,
780
- this.disableWindowResize ? { width: this.width, height: this.height } : void 0
783
+ this.disableWindowResize ? { width: this.width, height: this.height } : void 0,
784
+ this.maxDPR
781
785
  );
782
786
  }
783
787
  if (typeof document !== "undefined") {
@@ -903,6 +907,7 @@ var Scene = (_class2 = class _Scene {
903
907
  if (canvas.parentElement) canvas.parentElement.appendChild(gl);
904
908
  const pr = _Scene.webglCreator ? _Scene.webglCreator(gl) : null;
905
909
  if (pr) {
910
+ pr.maxDPR = this.maxDPR;
906
911
  pr.resize(this.width, this.height);
907
912
  this.glCanvas = gl;
908
913
  this.pointRenderer = pr;
@@ -1409,7 +1414,7 @@ var Scene = (_class2 = class _Scene {
1409
1414
  this.syncOptionalAttribute(
1410
1415
  el,
1411
1416
  "href",
1412
- attrs.href === void 0 ? void 0 : _chunkBPMNCGU7js.sanitizeUrl.call(void 0, attrs.href)
1417
+ attrs.href === void 0 ? void 0 : _chunkORCPCIJ7js.sanitizeUrl.call(void 0, attrs.href)
1413
1418
  );
1414
1419
  this.syncOptionalAttribute(el, "target", attrs.target);
1415
1420
  }
@@ -2419,7 +2424,7 @@ var Scene = (_class2 = class _Scene {
2419
2424
  * Export the current scene state to a lightweight, flat SVG XML string.
2420
2425
  */
2421
2426
  toSVG() {
2422
- const renderer = new (0, _chunkBPMNCGU7js.SVGRenderer)(this.width, this.height);
2427
+ const renderer = new (0, _chunkORCPCIJ7js.SVGRenderer)(this.width, this.height);
2423
2428
  this.render(renderer, 0, 0);
2424
2429
  return renderer.toXMLString();
2425
2430
  }
@@ -2431,9 +2436,13 @@ var Scene = (_class2 = class _Scene {
2431
2436
  this.height = height;
2432
2437
  this.contentFontEpoch++;
2433
2438
  if (typeof this.renderer.resize === "function") {
2439
+ if ("maxDPR" in this.renderer) this.renderer.maxDPR = this.maxDPR;
2434
2440
  this.renderer.resize(width, height);
2435
2441
  }
2436
- _optionalChain([this, 'access', _99 => _99.pointRenderer, 'optionalAccess', _100 => _100.resize, 'call', _101 => _101(width, height)]);
2442
+ if (this.pointRenderer) {
2443
+ this.pointRenderer.maxDPR = this.maxDPR;
2444
+ this.pointRenderer.resize(width, height);
2445
+ }
2437
2446
  if (this.gpuCanvas) {
2438
2447
  this.gpuCanvas.width = width;
2439
2448
  this.gpuCanvas.height = height;
@@ -2766,7 +2775,7 @@ var GridTextEntity = (_class4 = class extends _chunkGKLTTGBRjs.Entity {
2766
2775
  updateGrid(ascii) {
2767
2776
  this.grid = ascii;
2768
2777
  this.rows = ascii.length;
2769
- this.cols = _optionalChain([ascii, 'access', _102 => _102[0], 'optionalAccess', _103 => _103.length]) || 0;
2778
+ this.cols = _optionalChain([ascii, 'access', _99 => _99[0], 'optionalAccess', _100 => _100.length]) || 0;
2770
2779
  }
2771
2780
  isPointInside(_globalX, _globalY) {
2772
2781
  return false;
@@ -2876,7 +2885,7 @@ var SplineEntity = (_class5 = class extends _chunkGKLTTGBRjs.Entity {
2876
2885
  this.width = this.bounds.width;
2877
2886
  this.height = this.bounds.height;
2878
2887
  const isGradient = (c) => c !== null && !Array.isArray(c);
2879
- this.containsGradient = (_nullishCoalesce(_optionalChain([this, 'access', _104 => _104.doc, 'access', _105 => _105.equations, 'optionalAccess', _106 => _106.some, 'call', _107 => _107((eq) => isGradient(eq.color_rgb))]), () => ( false))) || (_nullishCoalesce(_optionalChain([this, 'access', _108 => _108.doc, 'access', _109 => _109.paths, 'optionalAccess', _110 => _110.some, 'call', _111 => _111((p) => isGradient(p.color_rgb))]), () => ( false)));
2888
+ this.containsGradient = (_nullishCoalesce(_optionalChain([this, 'access', _101 => _101.doc, 'access', _102 => _102.equations, 'optionalAccess', _103 => _103.some, 'call', _104 => _104((eq) => isGradient(eq.color_rgb))]), () => ( false))) || (_nullishCoalesce(_optionalChain([this, 'access', _105 => _105.doc, 'access', _106 => _106.paths, 'optionalAccess', _107 => _107.some, 'call', _108 => _108((p) => isGradient(p.color_rgb))]), () => ( false)));
2880
2889
  this.interactive = true;
2881
2890
  }
2882
2891
  computeBounds() {
@@ -3296,7 +3305,7 @@ var SpatialHashGrid = (_class6 = class {
3296
3305
  const keys = this.entityCells.get(id);
3297
3306
  if (!keys) return;
3298
3307
  for (const key of keys) {
3299
- _optionalChain([this, 'access', _112 => _112.grid, 'access', _113 => _113.get, 'call', _114 => _114(key), 'optionalAccess', _115 => _115.delete, 'call', _116 => _116(id)]);
3308
+ _optionalChain([this, 'access', _109 => _109.grid, 'access', _110 => _110.get, 'call', _111 => _111(key), 'optionalAccess', _112 => _112.delete, 'call', _113 => _113(id)]);
3300
3309
  }
3301
3310
  this.entityCells.delete(id);
3302
3311
  }
@@ -3407,7 +3416,7 @@ var DOMPortalEntity = (_class7 = class extends _chunkGKLTTGBRjs.Entity {
3407
3416
  if (!local) return false;
3408
3417
  return local.x >= 0 && local.x <= w && local.y >= 0 && local.y <= h;
3409
3418
  }
3410
- add(_child) {
3419
+ add(..._children) {
3411
3420
  console.warn(`DOMPortalEntity (${this.id}) is a leaf node. Child entities are not supported.`);
3412
3421
  return this;
3413
3422
  }
@@ -3432,8 +3441,8 @@ var DOMPortalEntity = (_class7 = class extends _chunkGKLTTGBRjs.Entity {
3432
3441
  }, _class7);
3433
3442
 
3434
3443
  // src/index.ts
3435
- Scene.registerWebGLPointRendererCreator(_chunkBPMNCGU7js.createWebGLPointRenderer);
3436
- Scene.registerWebGPUParticleSystemManager(_chunkBPMNCGU7js.WebGPUParticleSystemManager);
3444
+ Scene.registerWebGLPointRendererCreator(_chunkORCPCIJ7js.createWebGLPointRenderer);
3445
+ Scene.registerWebGPUParticleSystemManager(_chunkORCPCIJ7js.WebGPUParticleSystemManager);
3437
3446
 
3438
3447
 
3439
3448
 
@@ -3484,4 +3493,4 @@ Scene.registerWebGPUParticleSystemManager(_chunkBPMNCGU7js.WebGPUParticleSystemM
3484
3493
 
3485
3494
 
3486
3495
 
3487
- exports.ArabicShaper = _chunk4AR425ARjs.ArabicShaper; exports.BidiResolver = _chunk4AR425ARjs.BidiResolver; exports.CanvasRenderer = _chunkBPMNCGU7js.CanvasRenderer; exports.Circle = Circle; exports.ComputeParticleEntity = ComputeParticleEntity; exports.DOMPortalEntity = DOMPortalEntity; exports.Easing = _chunkGKLTTGBRjs.Easing; exports.Entity = _chunkGKLTTGBRjs.Entity; exports.GridTextEntity = GridTextEntity; exports.Group = Group; exports.LayoutEngine = _chunkBA5HUUDFjs.LayoutEngine; exports.LayoutResultBuffer = _chunkBA5HUUDFjs.LayoutResultBuffer; exports.LayoutWorkerManager = _chunk4AR425ARjs.LayoutWorkerManager; exports.MSDFFont = _chunkGKLTTGBRjs.MSDFFont; exports.MSDFTextEntity = _chunkGKLTTGBRjs.MSDFTextEntity; exports.PARTICLE_OFFSET_LIFE = PARTICLE_OFFSET_LIFE; exports.PARTICLE_OFFSET_ORIGIN_X = PARTICLE_OFFSET_ORIGIN_X; exports.PARTICLE_OFFSET_ORIGIN_Y = PARTICLE_OFFSET_ORIGIN_Y; exports.PARTICLE_OFFSET_POSITION_X = PARTICLE_OFFSET_POSITION_X; exports.PARTICLE_OFFSET_POSITION_Y = PARTICLE_OFFSET_POSITION_Y; exports.PARTICLE_OFFSET_SIZE = PARTICLE_OFFSET_SIZE; exports.PARTICLE_OFFSET_VELOCITY_X = PARTICLE_OFFSET_VELOCITY_X; exports.PARTICLE_OFFSET_VELOCITY_Y = PARTICLE_OFFSET_VELOCITY_Y; exports.PARTICLE_STRIDE_FLOATS = PARTICLE_STRIDE_FLOATS; exports.REDUCED_MOTION_FPS = REDUCED_MOTION_FPS; exports.Rect = Rect; exports.SVGEntity = _chunkGKLTTGBRjs.SVGEntity; exports.SVGRenderer = _chunkBPMNCGU7js.SVGRenderer; exports.Scene = Scene; exports.SpatialHashGrid = SpatialHashGrid; exports.SplineEntity = SplineEntity; exports.SpringDriver = _chunkGKLTTGBRjs.SpringDriver; exports.SpringPhysics = _chunkGKLTTGBRjs.SpringPhysics; exports.TextEntity = TextEntity; exports.TweenDriver = _chunkGKLTTGBRjs.TweenDriver; exports.VectoJSEvent = _chunkGKLTTGBRjs.VectoJSEvent; exports.WebGPUParticleSystemManager = _chunkBPMNCGU7js.WebGPUParticleSystemManager; exports.clearCssLineBoxMetrics = _chunkGKLTTGBRjs.clearCssLineBoxMetrics; exports.computeLineSegments = _chunkBA5HUUDFjs.computeLineSegments; exports.createCanvasMeasurer = _chunkBA5HUUDFjs.createCanvasMeasurer; exports.createWebGLPointRenderer = _chunkBPMNCGU7js.createWebGLPointRenderer; exports.cssLineBoxBaseline = _chunkGKLTTGBRjs.cssLineBoxBaseline; exports.isSafeUrl = _chunkBPMNCGU7js.isSafeUrl; exports.isTweenConfig = _chunkGKLTTGBRjs.isTweenConfig; exports.loadSpline = loadSpline; exports.parseColorToRGBA = _chunkBPMNCGU7js.parseColorToRGBA; exports.polySegmentToBezier = polySegmentToBezier; exports.prepareContentGrid = _chunkGKLTTGBRjs.prepareContentGrid; exports.sanitizeUrl = _chunkBPMNCGU7js.sanitizeUrl;
3496
+ exports.ArabicShaper = _chunk4AR425ARjs.ArabicShaper; exports.BidiResolver = _chunk4AR425ARjs.BidiResolver; exports.CanvasRenderer = _chunkORCPCIJ7js.CanvasRenderer; exports.Circle = Circle; exports.ComputeParticleEntity = ComputeParticleEntity; exports.DOMPortalEntity = DOMPortalEntity; exports.Easing = _chunkGKLTTGBRjs.Easing; exports.Entity = _chunkGKLTTGBRjs.Entity; exports.GridTextEntity = GridTextEntity; exports.Group = Group; exports.LayoutEngine = _chunkBA5HUUDFjs.LayoutEngine; exports.LayoutResultBuffer = _chunkBA5HUUDFjs.LayoutResultBuffer; exports.LayoutWorkerManager = _chunk4AR425ARjs.LayoutWorkerManager; exports.MSDFFont = _chunkGKLTTGBRjs.MSDFFont; exports.MSDFTextEntity = _chunkGKLTTGBRjs.MSDFTextEntity; exports.PARTICLE_OFFSET_LIFE = PARTICLE_OFFSET_LIFE; exports.PARTICLE_OFFSET_ORIGIN_X = PARTICLE_OFFSET_ORIGIN_X; exports.PARTICLE_OFFSET_ORIGIN_Y = PARTICLE_OFFSET_ORIGIN_Y; exports.PARTICLE_OFFSET_POSITION_X = PARTICLE_OFFSET_POSITION_X; exports.PARTICLE_OFFSET_POSITION_Y = PARTICLE_OFFSET_POSITION_Y; exports.PARTICLE_OFFSET_SIZE = PARTICLE_OFFSET_SIZE; exports.PARTICLE_OFFSET_VELOCITY_X = PARTICLE_OFFSET_VELOCITY_X; exports.PARTICLE_OFFSET_VELOCITY_Y = PARTICLE_OFFSET_VELOCITY_Y; exports.PARTICLE_STRIDE_FLOATS = PARTICLE_STRIDE_FLOATS; exports.REDUCED_MOTION_FPS = REDUCED_MOTION_FPS; exports.Rect = Rect; exports.SVGEntity = _chunkGKLTTGBRjs.SVGEntity; exports.SVGRenderer = _chunkORCPCIJ7js.SVGRenderer; exports.Scene = Scene; exports.SpatialHashGrid = SpatialHashGrid; exports.SplineEntity = SplineEntity; exports.SpringDriver = _chunkGKLTTGBRjs.SpringDriver; exports.SpringPhysics = _chunkGKLTTGBRjs.SpringPhysics; exports.TextEntity = TextEntity; exports.TweenDriver = _chunkGKLTTGBRjs.TweenDriver; exports.VectoJSEvent = _chunkGKLTTGBRjs.VectoJSEvent; exports.WebGPUParticleSystemManager = _chunkORCPCIJ7js.WebGPUParticleSystemManager; exports.clearCssLineBoxMetrics = _chunkGKLTTGBRjs.clearCssLineBoxMetrics; exports.computeLineSegments = _chunkBA5HUUDFjs.computeLineSegments; exports.createCanvasMeasurer = _chunkBA5HUUDFjs.createCanvasMeasurer; exports.createWebGLPointRenderer = _chunkORCPCIJ7js.createWebGLPointRenderer; exports.cssLineBoxBaseline = _chunkGKLTTGBRjs.cssLineBoxBaseline; exports.isSafeUrl = _chunkORCPCIJ7js.isSafeUrl; exports.isTweenConfig = _chunkGKLTTGBRjs.isTweenConfig; exports.loadSpline = loadSpline; exports.parseColorToRGBA = _chunkORCPCIJ7js.parseColorToRGBA; exports.polySegmentToBezier = polySegmentToBezier; exports.prepareContentGrid = _chunkGKLTTGBRjs.prepareContentGrid; exports.sanitizeUrl = _chunkORCPCIJ7js.sanitizeUrl;
package/dist/index.mjs CHANGED
@@ -12,7 +12,7 @@ import {
12
12
  isSafeUrl,
13
13
  parseColorToRGBA,
14
14
  sanitizeUrl
15
- } from "./chunk-WT2XZHJQ.mjs";
15
+ } from "./chunk-XZEPHCDZ.mjs";
16
16
  import {
17
17
  Easing,
18
18
  Entity,
@@ -708,6 +708,8 @@ var Scene = class _Scene {
708
708
  width;
709
709
  height;
710
710
  disableWindowResize = false;
711
+ /** See {@link SceneOptions.maxDPR}. `undefined` = uncapped (real DPR). */
712
+ maxDPR;
711
713
  // WebGPU properties
712
714
  destroyed = false;
713
715
  device = null;
@@ -737,6 +739,7 @@ var Scene = class _Scene {
737
739
  this.canvas = canvas;
738
740
  this.debugA11y = options.debugA11y ?? false;
739
741
  this.disableWindowResize = options.disableWindowResize ?? false;
742
+ this.maxDPR = options.maxDPR;
740
743
  if (this.disableWindowResize) {
741
744
  const styleWidth = parseInlinePx(canvas.style?.width);
742
745
  const styleHeight = parseInlinePx(canvas.style?.height);
@@ -777,7 +780,8 @@ var Scene = class _Scene {
777
780
  } else {
778
781
  this.renderer = new CanvasRenderer(
779
782
  canvas,
780
- this.disableWindowResize ? { width: this.width, height: this.height } : void 0
783
+ this.disableWindowResize ? { width: this.width, height: this.height } : void 0,
784
+ this.maxDPR
781
785
  );
782
786
  }
783
787
  if (typeof document !== "undefined") {
@@ -903,6 +907,7 @@ var Scene = class _Scene {
903
907
  if (canvas.parentElement) canvas.parentElement.appendChild(gl);
904
908
  const pr = _Scene.webglCreator ? _Scene.webglCreator(gl) : null;
905
909
  if (pr) {
910
+ pr.maxDPR = this.maxDPR;
906
911
  pr.resize(this.width, this.height);
907
912
  this.glCanvas = gl;
908
913
  this.pointRenderer = pr;
@@ -2431,9 +2436,13 @@ var Scene = class _Scene {
2431
2436
  this.height = height;
2432
2437
  this.contentFontEpoch++;
2433
2438
  if (typeof this.renderer.resize === "function") {
2439
+ if ("maxDPR" in this.renderer) this.renderer.maxDPR = this.maxDPR;
2434
2440
  this.renderer.resize(width, height);
2435
2441
  }
2436
- this.pointRenderer?.resize(width, height);
2442
+ if (this.pointRenderer) {
2443
+ this.pointRenderer.maxDPR = this.maxDPR;
2444
+ this.pointRenderer.resize(width, height);
2445
+ }
2437
2446
  if (this.gpuCanvas) {
2438
2447
  this.gpuCanvas.width = width;
2439
2448
  this.gpuCanvas.height = height;
@@ -3407,7 +3416,7 @@ var DOMPortalEntity = class extends Entity {
3407
3416
  if (!local) return false;
3408
3417
  return local.x >= 0 && local.x <= w && local.y >= 0 && local.y <= h;
3409
3418
  }
3410
- add(_child) {
3419
+ add(..._children) {
3411
3420
  console.warn(`DOMPortalEntity (${this.id}) is a leaf node. Child entities are not supported.`);
3412
3421
  return this;
3413
3422
  }
@@ -3,6 +3,15 @@ export declare class CanvasRenderer implements IRenderer {
3
3
  private ctx;
4
4
  private width;
5
5
  private height;
6
+ /**
7
+ * Cap on the effective device pixel ratio applied by the constructor and
8
+ * {@link resize}. `undefined` (default) uses the real, uncapped
9
+ * `devicePixelRatio` — unchanged from prior versions. Set directly, or via
10
+ * the constructor's third argument; `Scene` (see `SceneOptions.maxDPR`)
11
+ * keeps this in sync on every {@link resize} call, since the real DPR can
12
+ * change at runtime (e.g. a window dragged between displays).
13
+ */
14
+ maxDPR?: number;
6
15
  /**
7
16
  * Max circles per batched `fill()`. A single Canvas 2D `fill()` over a path is
8
17
  * superlinear in sub-path count, so an unbounded batch is *slower* than many
@@ -21,11 +30,12 @@ export declare class CanvasRenderer implements IRenderer {
21
30
  * fullscreen canvas and sizes to the window — pass this for embedded /
22
31
  * custom-container canvases (the Scene does when `disableWindowResize` is
23
32
  * set) so the canvas's own dimensions aren't clobbered by the window's.
33
+ * @param maxDPR - See {@link maxDPR}.
24
34
  */
25
35
  constructor(canvas: HTMLCanvasElement, size?: {
26
36
  width: number;
27
37
  height: number;
28
- });
38
+ }, maxDPR?: number);
29
39
  /**
30
40
  * Expose the underlying `CanvasRenderingContext2D` for operations not
31
41
  * covered by the {@link IRenderer} interface.
@@ -33,6 +43,8 @@ export declare class CanvasRenderer implements IRenderer {
33
43
  * @returns The raw 2D rendering context.
34
44
  */
35
45
  getContext(): CanvasRenderingContext2D;
46
+ /** Real `devicePixelRatio`, clamped to {@link maxDPR} when set. */
47
+ private effectiveDPR;
36
48
  /**
37
49
  * Resize the backing canvas buffer and re-apply DPR scaling.
38
50
  *
@@ -7,6 +7,18 @@
7
7
  export interface PointRenderer {
8
8
  /** Resize the backing buffer + GL viewport to a logical `w × h` (DPR applied). */
9
9
  resize(width: number, height: number): void;
10
+ /**
11
+ * Cap on the effective device pixel ratio applied by {@link resize}.
12
+ * `undefined` (default) uses the real, uncapped `devicePixelRatio`. Set
13
+ * before calling `resize()` for it to take effect on that call (matches
14
+ * {@link import('../tree/Scene').SceneOptions.maxDPR} — `Scene` sets this
15
+ * once at construction and again before every `resize()` call, since a
16
+ * factory function has no other way to receive the option: the WebGL point
17
+ * layer's creator is a plain `(canvas) => PointRenderer` registered once by
18
+ * `@vectojs/core`'s module init, with no room for a per-Scene constructor
19
+ * argument).
20
+ */
21
+ maxDPR?: number;
10
22
  /** Begin a frame: reset the accumulated primitive buffers. */
11
23
  begin(): void;
12
24
  /** Add one circle in world (CSS-pixel) coordinates; `alpha` multiplies the color's. */
package/dist/renderer.js CHANGED
@@ -4,11 +4,11 @@
4
4
 
5
5
 
6
6
 
7
- var _chunkBPMNCGU7js = require('./chunk-BPMNCGU7.js');
7
+ var _chunkORCPCIJ7js = require('./chunk-ORCPCIJ7.js');
8
8
 
9
9
 
10
10
 
11
11
 
12
12
 
13
13
 
14
- exports.CanvasRenderer = _chunkBPMNCGU7js.CanvasRenderer; exports.SVGRenderer = _chunkBPMNCGU7js.SVGRenderer; exports.WebGPUParticleSystemManager = _chunkBPMNCGU7js.WebGPUParticleSystemManager; exports.createWebGLPointRenderer = _chunkBPMNCGU7js.createWebGLPointRenderer; exports.parseColorToRGBA = _chunkBPMNCGU7js.parseColorToRGBA;
14
+ exports.CanvasRenderer = _chunkORCPCIJ7js.CanvasRenderer; exports.SVGRenderer = _chunkORCPCIJ7js.SVGRenderer; exports.WebGPUParticleSystemManager = _chunkORCPCIJ7js.WebGPUParticleSystemManager; exports.createWebGLPointRenderer = _chunkORCPCIJ7js.createWebGLPointRenderer; exports.parseColorToRGBA = _chunkORCPCIJ7js.parseColorToRGBA;
package/dist/renderer.mjs CHANGED
@@ -4,7 +4,7 @@ import {
4
4
  WebGPUParticleSystemManager,
5
5
  createWebGLPointRenderer,
6
6
  parseColorToRGBA
7
- } from "./chunk-WT2XZHJQ.mjs";
7
+ } from "./chunk-XZEPHCDZ.mjs";
8
8
  export {
9
9
  CanvasRenderer,
10
10
  SVGRenderer,
@@ -13,7 +13,7 @@ export declare class DOMPortalEntity extends Entity {
13
13
  lastOpacity: string;
14
14
  constructor(domElement: HTMLElement, width?: number, height?: number, id?: string);
15
15
  isPointInside(globalX: number, globalY: number): boolean;
16
- add(_child: Entity): this;
16
+ add(..._children: Entity[]): this;
17
17
  render(): void;
18
18
  destroy(): void;
19
19
  }
@@ -70,6 +70,21 @@ export interface SceneOptions {
70
70
  * Useful when Vecto is running inside a custom layout container or offscreen canvas.
71
71
  */
72
72
  disableWindowResize?: boolean;
73
+ /**
74
+ * Cap the effective device pixel ratio used to size the Canvas2D and WebGL
75
+ * point-layer backing stores. `undefined` (default) reads the real,
76
+ * uncapped `window.devicePixelRatio` — unchanged from prior versions.
77
+ * Backing-store render cost scales with `logical size × dpr²`, so a
78
+ * full-screen HiDPI scene (`pointBackend: 'webgl'` in particular) can
79
+ * overrun its frame budget on a DPR-3 display while running fine on the
80
+ * DPR-1 dev machine it was tuned on (findings.md, 2026-07-16). `maxDPR: 2`
81
+ * keeps the display retina-crisp (2x already exceeds what most eyes
82
+ * resolve) while roughly halving the backing-store pixel count at DPR 3.
83
+ * Applied at construction and re-applied on every {@link resize} call
84
+ * (including the automatic window-resize listener), since the real DPR
85
+ * can change at runtime (a window dragged between displays).
86
+ */
87
+ maxDPR?: number;
73
88
  /**
74
89
  * Enable automatic throttling to 2 FPS when the scene is static (no active transitions
75
90
  * and not marked dirty) to save power/CPU. Default is `true`.
@@ -196,6 +211,8 @@ export declare class Scene {
196
211
  width: number;
197
212
  height: number;
198
213
  private disableWindowResize;
214
+ /** See {@link SceneOptions.maxDPR}. `undefined` = uncapped (real DPR). */
215
+ maxDPR?: number;
199
216
  private destroyed;
200
217
  private device;
201
218
  private deviceLost;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vectojs/core",
3
- "version": "1.9.1",
3
+ "version": "1.10.0",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },