@vectojs/three 0.1.2 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -1,6 +1,10 @@
1
1
  // src/ThreeRenderer.ts
2
2
  import { parseColorToRGBA } from "@vectojs/core";
3
3
  import * as THREE from "three";
4
+ function parseThreeColor(value) {
5
+ const [r, g, b, alpha] = parseColorToRGBA(value);
6
+ return { color: new THREE.Color(r, g, b), alpha };
7
+ }
4
8
  var WebGLGradient = class {
5
9
  constructor(x0, y0, x1, y1, colorStops) {
6
10
  this.x0 = x0;
@@ -73,14 +77,16 @@ var ThreeRenderer = class {
73
77
  activeObjects = [];
74
78
  scissorStack = [];
75
79
  constructor(canvas) {
76
- this.width = window.innerWidth;
77
- this.height = window.innerHeight;
80
+ const cw = canvas.width > 0 ? canvas.width : typeof window !== "undefined" ? window.innerWidth : 1024;
81
+ const ch = canvas.height > 0 ? canvas.height : typeof window !== "undefined" ? window.innerHeight : 1024;
82
+ this.width = cw;
83
+ this.height = ch;
78
84
  this.scene = new THREE.Scene();
79
85
  this.camera = new THREE.OrthographicCamera(0, this.width, 0, this.height, 0.1, 1e3);
80
86
  this.camera.position.z = 1;
81
87
  this.renderer = new THREE.WebGLRenderer({ canvas, alpha: true, antialias: true });
82
88
  this.renderer.setSize(this.width, this.height);
83
- this.renderer.setPixelRatio(window.devicePixelRatio || 1);
89
+ this.renderer.setPixelRatio(typeof window !== "undefined" ? window.devicePixelRatio || 1 : 1);
84
90
  this.matrix = new THREE.Matrix4().identity();
85
91
  }
86
92
  resize(width, height) {
@@ -92,26 +98,42 @@ var ThreeRenderer = class {
92
98
  this.camera.updateProjectionMatrix();
93
99
  }
94
100
  clear() {
95
- for (const obj of this.activeObjects) {
96
- this.scene.remove(obj);
97
- if (obj instanceof THREE.Mesh || obj instanceof THREE.Line) {
98
- obj.geometry.dispose();
99
- if (Array.isArray(obj.material)) {
100
- for (const mat of obj.material) {
101
- mat.dispose();
102
- if (mat.map) mat.map.dispose();
103
- }
104
- } else {
105
- obj.material.dispose();
106
- if (obj.material.map) {
107
- obj.material.map.dispose();
108
- }
101
+ this.disposeActiveObjects();
102
+ this.currentPath = null;
103
+ this.matrix.identity();
104
+ this.stack.length = 0;
105
+ this.globalAlpha = 1;
106
+ this.alphaStack.length = 0;
107
+ this.scissorStack.length = 0;
108
+ this.renderer.clear();
109
+ this.renderer.setScissorTest(false);
110
+ }
111
+ disposeActiveObjects() {
112
+ const disposedGeometries = /* @__PURE__ */ new Set();
113
+ const disposedMaterials = /* @__PURE__ */ new Set();
114
+ const disposedTextures = /* @__PURE__ */ new Set();
115
+ for (const object of this.activeObjects) {
116
+ this.scene.remove(object);
117
+ if (!(object instanceof THREE.Mesh || object instanceof THREE.Line)) continue;
118
+ const geometry = object.geometry;
119
+ if (!disposedGeometries.has(geometry)) {
120
+ disposedGeometries.add(geometry);
121
+ geometry.dispose();
122
+ }
123
+ const materials = Array.isArray(object.material) ? object.material : [object.material];
124
+ for (const material of materials) {
125
+ const map = material.map;
126
+ if (map && !map.userData.vectoCached && !disposedTextures.has(map)) {
127
+ disposedTextures.add(map);
128
+ map.dispose();
129
+ }
130
+ if (!disposedMaterials.has(material)) {
131
+ disposedMaterials.add(material);
132
+ material.dispose();
109
133
  }
110
134
  }
111
135
  }
112
- this.activeObjects = [];
113
- this.renderer.clear();
114
- this.renderer.setScissorTest(false);
136
+ this.activeObjects.length = 0;
115
137
  }
116
138
  save() {
117
139
  this.stack.push(this.matrix.clone());
@@ -148,16 +170,33 @@ var ThreeRenderer = class {
148
170
  this.globalAlpha = alpha;
149
171
  }
150
172
  clip(x, y, width, height) {
151
- const pos = new THREE.Vector3(x, y, 0).applyMatrix4(this.matrix);
152
- const size = new THREE.Vector3(width, height, 0).applyMatrix4(this.matrix).sub(new THREE.Vector3(0, 0, 0).applyMatrix4(this.matrix));
153
- const dpr = window.devicePixelRatio || 1;
173
+ const corners = [
174
+ new THREE.Vector3(x, y, 0),
175
+ new THREE.Vector3(x + width, y, 0),
176
+ new THREE.Vector3(x, y + height, 0),
177
+ new THREE.Vector3(x + width, y + height, 0)
178
+ ].map((point) => point.applyMatrix4(this.matrix));
179
+ const minX = Math.min(...corners.map((point) => point.x));
180
+ const minY = Math.min(...corners.map((point) => point.y));
181
+ const maxX = Math.max(...corners.map((point) => point.x));
182
+ const maxY = Math.max(...corners.map((point) => point.y));
183
+ const dpr = this.renderer.getPixelRatio() || 1;
154
184
  const canvasHeight = this.renderer.domElement.height / dpr;
155
- this.renderer.setScissor(
156
- pos.x * dpr,
157
- (canvasHeight - (pos.y + size.y)) * dpr,
158
- size.x * dpr,
159
- size.y * dpr
160
- );
185
+ let scissorX = minX * dpr;
186
+ let scissorY = (canvasHeight - maxY) * dpr;
187
+ let scissorWidth = (maxX - minX) * dpr;
188
+ let scissorHeight = (maxY - minY) * dpr;
189
+ if (this.renderer.getScissorTest()) {
190
+ const current = new THREE.Vector4();
191
+ this.renderer.getScissor(current);
192
+ const right = Math.min(scissorX + scissorWidth, current.x + current.z);
193
+ const top = Math.min(scissorY + scissorHeight, current.y + current.w);
194
+ scissorX = Math.max(scissorX, current.x);
195
+ scissorY = Math.max(scissorY, current.y);
196
+ scissorWidth = Math.max(0, right - scissorX);
197
+ scissorHeight = Math.max(0, top - scissorY);
198
+ }
199
+ this.renderer.setScissor(scissorX, scissorY, scissorWidth, scissorHeight);
161
200
  this.renderer.setScissorTest(true);
162
201
  }
163
202
  beginPath() {
@@ -376,13 +415,15 @@ var ThreeRenderer = class {
376
415
  fillSolidShape(colorOrGradient) {
377
416
  if (!this.currentPath) return;
378
417
  const color = typeof colorOrGradient === "string" ? colorOrGradient : "#ffffff";
418
+ const parsed = parseThreeColor(color);
379
419
  const shapes = this.currentPath.toShapes();
380
420
  for (const shape of shapes) {
381
421
  const geometry = new THREE.ShapeGeometry(shape);
422
+ const opacity = this.globalAlpha * parsed.alpha;
382
423
  const material = new THREE.MeshBasicMaterial({
383
- color: new THREE.Color(color),
384
- transparent: this.globalAlpha < 1,
385
- opacity: this.globalAlpha,
424
+ color: parsed.color,
425
+ transparent: opacity < 1,
426
+ opacity,
386
427
  depthWrite: false
387
428
  });
388
429
  const mesh = new THREE.Mesh(geometry, material);
@@ -402,31 +443,35 @@ var ThreeRenderer = class {
402
443
  color = grad.colorStops[0].color;
403
444
  }
404
445
  }
405
- const points = this.currentPath.getPoints();
406
- if (points.length === 0) return;
407
- const geometry = new THREE.BufferGeometry().setFromPoints(points);
408
- const material = new THREE.LineBasicMaterial({
409
- color: new THREE.Color(color),
410
- transparent: this.globalAlpha < 1,
411
- opacity: this.globalAlpha,
412
- linewidth: lineWidth
413
- });
414
- const line = new THREE.Line(geometry, material);
415
- line.applyMatrix4(this.matrix);
416
- this.scene.add(line);
417
- this.activeObjects.push(line);
446
+ const parsed = parseThreeColor(color);
447
+ const opacity = this.globalAlpha * parsed.alpha;
448
+ for (const shape of this.currentPath.toShapes()) {
449
+ const points = shape.getPoints();
450
+ if (points.length < 2) continue;
451
+ const geometry = new THREE.BufferGeometry().setFromPoints(points);
452
+ const material = new THREE.LineBasicMaterial({
453
+ color: parsed.color,
454
+ transparent: opacity < 1,
455
+ opacity,
456
+ linewidth: lineWidth
457
+ });
458
+ const line = new THREE.Line(geometry, material);
459
+ line.applyMatrix4(this.matrix);
460
+ this.scene.add(line);
461
+ this.activeObjects.push(line);
462
+ }
418
463
  }
464
+ /**
465
+ * Rasterized fillText textures keyed by `font|color|text`, LRU-evicted.
466
+ * HUD-style UIs redraw the same labels every frame; without the cache each
467
+ * call allocated a canvas, rasterized it, and uploaded a fresh GPU texture.
468
+ * Entries are skipped by {@link disposeActiveObjects} (flagged via
469
+ * `userData.vectoCached`) and released on eviction or {@link dispose}.
470
+ */
471
+ textTextureCache = /* @__PURE__ */ new Map();
472
+ textTextureCacheLimit = 256;
419
473
  fillText(text, x, y, font, color) {
420
474
  if (typeof document === "undefined") return;
421
- const canvas = document.createElement("canvas");
422
- const ctx = canvas.getContext("2d");
423
- ctx.font = font;
424
- const width = Math.max(1, Math.ceil(ctx.measureText(text).width));
425
- const fontSize = parseInt(font) || 16;
426
- const height = Math.max(1, Math.ceil(fontSize * 1.5));
427
- canvas.width = width;
428
- canvas.height = height;
429
- ctx.font = font;
430
475
  let fillCol = "#ffffff";
431
476
  if (typeof color === "string") {
432
477
  fillCol = color;
@@ -436,11 +481,36 @@ var ThreeRenderer = class {
436
481
  fillCol = grad.colorStops[0].color;
437
482
  }
438
483
  }
439
- ctx.fillStyle = fillCol;
440
- ctx.textBaseline = "alphabetic";
441
- ctx.fillText(text, 0, fontSize);
442
- const texture = new THREE.CanvasTexture(canvas);
443
- texture.minFilter = THREE.LinearFilter;
484
+ const cacheKey = `${font}|${fillCol}|${text}`;
485
+ let entry = this.textTextureCache.get(cacheKey);
486
+ if (entry) {
487
+ this.textTextureCache.delete(cacheKey);
488
+ this.textTextureCache.set(cacheKey, entry);
489
+ } else {
490
+ const canvas = document.createElement("canvas");
491
+ const ctx = canvas.getContext("2d");
492
+ ctx.font = font;
493
+ const width2 = Math.max(1, Math.ceil(ctx.measureText(text).width));
494
+ const fontSize2 = parseInt(font) || 16;
495
+ const height2 = Math.max(1, Math.ceil(fontSize2 * 1.5));
496
+ canvas.width = width2;
497
+ canvas.height = height2;
498
+ ctx.font = font;
499
+ ctx.fillStyle = fillCol;
500
+ ctx.textBaseline = "alphabetic";
501
+ ctx.fillText(text, 0, fontSize2);
502
+ const texture2 = new THREE.CanvasTexture(canvas);
503
+ texture2.minFilter = THREE.LinearFilter;
504
+ texture2.userData.vectoCached = true;
505
+ entry = { texture: texture2, width: width2, height: height2, fontSize: fontSize2 };
506
+ this.textTextureCache.set(cacheKey, entry);
507
+ while (this.textTextureCache.size > Math.max(1, this.textTextureCacheLimit)) {
508
+ const oldestKey = this.textTextureCache.keys().next().value;
509
+ this.textTextureCache.get(oldestKey).texture.dispose();
510
+ this.textTextureCache.delete(oldestKey);
511
+ }
512
+ }
513
+ const { texture, width, height, fontSize } = entry;
444
514
  const material = new THREE.MeshBasicMaterial({
445
515
  map: texture,
446
516
  transparent: true,
@@ -456,10 +526,12 @@ var ThreeRenderer = class {
456
526
  }
457
527
  fillCircle(cx, cy, radius, color, alpha = 1) {
458
528
  const geometry = new THREE.CircleGeometry(radius, 32);
529
+ const parsed = parseThreeColor(color);
530
+ const opacity = this.globalAlpha * alpha * parsed.alpha;
459
531
  const material = new THREE.MeshBasicMaterial({
460
- color: new THREE.Color(color),
461
- transparent: this.globalAlpha * alpha < 1,
462
- opacity: this.globalAlpha * alpha,
532
+ color: parsed.color,
533
+ transparent: opacity < 1,
534
+ opacity,
463
535
  depthWrite: false
464
536
  });
465
537
  const mesh = new THREE.Mesh(geometry, material);
@@ -468,12 +540,51 @@ var ThreeRenderer = class {
468
540
  this.scene.add(mesh);
469
541
  this.activeObjects.push(mesh);
470
542
  }
543
+ frameDirty = false;
544
+ presentScheduled = false;
545
+ /**
546
+ * The Scene calls flush() around every non-batched node (it commits the
547
+ * Canvas2D circle batch there). Rendering the whole Three scene on each of
548
+ * those calls made a frame cost O(N²) in entity count, so flush() only marks
549
+ * the frame dirty; the actual GL render happens once, in {@link present}.
550
+ *
551
+ * A microtask fallback keeps older `@vectojs/core` Scenes (that never call
552
+ * `present()`) painting: the many same-tick flushes coalesce into one render.
553
+ */
471
554
  flush() {
555
+ this.frameDirty = true;
556
+ if (this.presentScheduled) return;
557
+ this.presentScheduled = true;
558
+ queueMicrotask(() => {
559
+ this.presentScheduled = false;
560
+ if (this.frameDirty && !this.disposed) this.present();
561
+ });
562
+ }
563
+ /** Render the accumulated frame once. Called by Scene at the end of each render pass. */
564
+ present() {
565
+ if (this.disposed) return;
566
+ this.frameDirty = false;
472
567
  this.renderer.render(this.scene, this.camera);
473
568
  }
474
569
  createLinearGradient(x0, y0, x1, y1, colorStops) {
475
570
  return new WebGLGradient(x0, y0, x1, y1, colorStops);
476
571
  }
572
+ disposed = false;
573
+ dispose() {
574
+ if (this.disposed) return;
575
+ this.disposed = true;
576
+ this.disposeActiveObjects();
577
+ for (const entry of this.textTextureCache.values()) {
578
+ entry.texture.dispose();
579
+ }
580
+ this.textTextureCache.clear();
581
+ this.currentPath = null;
582
+ this.stack.length = 0;
583
+ this.alphaStack.length = 0;
584
+ this.scissorStack.length = 0;
585
+ this.renderer.setScissorTest(false);
586
+ this.renderer.dispose();
587
+ }
477
588
  };
478
589
 
479
590
  // src/ThreeAdapter.ts
@@ -490,8 +601,20 @@ var ThreeAdapter = class {
490
601
  mesh;
491
602
  /** Track hover states independently per pointerId for WebXR / Multi-Touch. */
492
603
  activePointers = /* @__PURE__ */ new Map();
604
+ /**
605
+ * Holds the original `vectoScene.render` reference so {@link dispose} can
606
+ * restore it. Without restoring, a later render call would write
607
+ * `needsUpdate` on a disposed (deleted) `CanvasTexture`, which Three.js
608
+ * flags with `"THREE.Texture: trying to use deleted texture"`.
609
+ */
610
+ _originalRender = null;
611
+ /** Tracks whether this adapter owns the canvas it is rendering to. */
612
+ _ownsCanvas;
613
+ _disposed = false;
493
614
  constructor(options) {
494
- this.canvas = options.canvas || (typeof document !== "undefined" ? document.createElement("canvas") : { width: options.width, height: options.height });
615
+ const optCanvas = options.canvas;
616
+ this._ownsCanvas = !optCanvas;
617
+ this.canvas = optCanvas || (typeof document !== "undefined" ? document.createElement("canvas") : { width: options.width, height: options.height });
495
618
  this.canvas.width = options.width;
496
619
  this.canvas.height = options.height;
497
620
  const sceneOptions = {
@@ -503,7 +626,8 @@ var ThreeAdapter = class {
503
626
  this.texture = new THREE2.CanvasTexture(this.canvas);
504
627
  this.texture.minFilter = THREE2.LinearFilter;
505
628
  this.texture.magFilter = THREE2.LinearFilter;
506
- const originalRender = this.vectoScene.render;
629
+ this._originalRender = this.vectoScene.render;
630
+ const originalRender = this._originalRender;
507
631
  this.vectoScene.render = (renderer, dt, time) => {
508
632
  originalRender.call(this.vectoScene, renderer, dt, time);
509
633
  this.texture.needsUpdate = true;
@@ -599,34 +723,51 @@ var ThreeAdapter = class {
599
723
  a11yEl.focus();
600
724
  }
601
725
  } else {
602
- const e = originalEvent instanceof MouseEvent ? originalEvent : void 0;
603
- const vectoEvent = new VectoJSEvent(type, entity, e, type !== "pointerleave");
726
+ const vectoEvent = new VectoJSEvent(type, entity, originalEvent, type !== "pointerleave", {
727
+ x,
728
+ y
729
+ });
604
730
  entity.dispatchEvent(vectoEvent);
605
731
  }
606
732
  }
607
733
  createDOMEvent(type, x, y, pointerId, originalEvent) {
734
+ let event;
608
735
  if (type === "wheel") {
609
736
  const wheelE = originalEvent instanceof WheelEvent ? originalEvent : void 0;
610
- return new WheelEvent("wheel", {
737
+ event = new WheelEvent("wheel", {
611
738
  clientX: x,
612
739
  clientY: y,
613
740
  deltaX: wheelE ? wheelE.deltaX : 0,
614
741
  deltaY: wheelE ? wheelE.deltaY : 0,
615
742
  deltaZ: wheelE ? wheelE.deltaZ : 0,
616
743
  deltaMode: wheelE ? wheelE.deltaMode : 0,
744
+ shiftKey: wheelE ? wheelE.shiftKey : false,
745
+ ctrlKey: wheelE ? wheelE.ctrlKey : false,
746
+ altKey: wheelE ? wheelE.altKey : false,
747
+ metaKey: wheelE ? wheelE.metaKey : false,
748
+ bubbles: true,
749
+ cancelable: true
750
+ });
751
+ } else {
752
+ event = new PointerEvent(type, {
753
+ clientX: x,
754
+ clientY: y,
755
+ button: originalEvent instanceof MouseEvent ? originalEvent.button : 0,
756
+ buttons: originalEvent instanceof MouseEvent ? originalEvent.buttons : 0,
757
+ pointerId,
758
+ shiftKey: originalEvent instanceof MouseEvent ? originalEvent.shiftKey : false,
759
+ ctrlKey: originalEvent instanceof MouseEvent ? originalEvent.ctrlKey : false,
760
+ altKey: originalEvent instanceof MouseEvent ? originalEvent.altKey : false,
761
+ metaKey: originalEvent instanceof MouseEvent ? originalEvent.metaKey : false,
617
762
  bubbles: true,
618
763
  cancelable: true
619
764
  });
620
765
  }
621
- return new PointerEvent(type, {
622
- clientX: x,
623
- clientY: y,
624
- button: originalEvent instanceof MouseEvent ? originalEvent.button : 0,
625
- buttons: originalEvent instanceof MouseEvent ? originalEvent.buttons : 0,
626
- pointerId,
627
- bubbles: true,
628
- cancelable: true
766
+ Object.defineProperties(event, {
767
+ vectoSceneX: { value: x },
768
+ vectoSceneY: { value: y }
629
769
  });
770
+ return event;
630
771
  }
631
772
  findEntityById(root, id) {
632
773
  if (root.id === id) return root;
@@ -649,6 +790,12 @@ var ThreeAdapter = class {
649
790
  * Disposes of Three.js textures, geometries, and VectoJS scenes to prevent memory leaks.
650
791
  */
651
792
  dispose() {
793
+ if (this._disposed) return;
794
+ this._disposed = true;
795
+ if (this._originalRender) {
796
+ this.vectoScene.render = this._originalRender;
797
+ this._originalRender = null;
798
+ }
652
799
  this.texture.dispose();
653
800
  this.mesh.geometry.dispose();
654
801
  if (Array.isArray(this.mesh.material)) {
@@ -661,8 +808,10 @@ var ThreeAdapter = class {
661
808
  }
662
809
  this.vectoScene.destroy();
663
810
  this.activePointers.clear();
664
- this.canvas.width = 0;
665
- this.canvas.height = 0;
811
+ if (this._ownsCanvas) {
812
+ this.canvas.width = 0;
813
+ this.canvas.height = 0;
814
+ }
666
815
  }
667
816
  };
668
817
  export {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vectojs/three",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -19,7 +19,7 @@
19
19
  "dist"
20
20
  ],
21
21
  "scripts": {
22
- "build": "tsup src/index.ts --format cjs,esm --dts --clean",
22
+ "build": "(cd ../core && bun run build) && tsup src/index.ts --format cjs,esm --clean && tsc -p tsconfig.build.json",
23
23
  "test": "vitest run"
24
24
  },
25
25
  "peerDependencies": {
package/dist/index.d.mts DELETED
@@ -1,123 +0,0 @@
1
- import { IRenderer, Scene, SceneOptions } from '@vectojs/core';
2
- import * as THREE from 'three';
3
-
4
- declare class WebGLGradient {
5
- x0: number;
6
- y0: number;
7
- x1: number;
8
- y1: number;
9
- colorStops: {
10
- stop: number;
11
- color: string;
12
- }[];
13
- type: string;
14
- constructor(x0: number, y0: number, x1: number, y1: number, colorStops: {
15
- stop: number;
16
- color: string;
17
- }[]);
18
- }
19
- /**
20
- * WebGL/Three.js implementation of {@link IRenderer}.
21
- *
22
- * Bridges VectoJS's 2D canvas API to Three.js for 3D hardware-accelerated rendering.
23
- */
24
- declare class ThreeRenderer implements IRenderer {
25
- scene: THREE.Scene;
26
- camera: THREE.OrthographicCamera;
27
- renderer: THREE.WebGLRenderer;
28
- private width;
29
- private height;
30
- private matrix;
31
- private stack;
32
- private globalAlpha;
33
- private alphaStack;
34
- private currentPath;
35
- private activeObjects;
36
- private scissorStack;
37
- constructor(canvas: HTMLCanvasElement);
38
- resize(width: number, height: number): void;
39
- clear(): void;
40
- save(): void;
41
- restore(): void;
42
- translate(x: number, y: number): void;
43
- scale(x: number, y: number): void;
44
- rotate(angle: number): void;
45
- setGlobalAlpha(alpha: number): void;
46
- clip(x: number, y: number, width: number, height: number): void;
47
- beginPath(): void;
48
- moveTo(x: number, y: number): void;
49
- lineTo(x: number, y: number): void;
50
- bezierCurveTo(cp1x: number, cp1y: number, cp2x: number, cp2y: number, x: number, y: number): void;
51
- closePath(): void;
52
- arc(x: number, y: number, radius: number, startAngle: number, endAngle: number, counterclockwise?: boolean): void;
53
- roundRect(x: number, y: number, width: number, height: number, radii: number | number[]): void;
54
- drawImage(source: CanvasImageSource, dx: number, dy: number, dw: number, dh: number): void;
55
- fill(colorOrGradient: string | any): void;
56
- private fillSolidShape;
57
- stroke(colorOrGradient: string | any, lineWidth?: number): void;
58
- fillText(text: string, x: number, y: number, font: string, color: string | any): void;
59
- fillCircle(cx: number, cy: number, radius: number, color: string, alpha?: number): void;
60
- flush(): void;
61
- createLinearGradient(x0: number, y0: number, x1: number, y1: number, colorStops: {
62
- stop: number;
63
- color: string;
64
- }[]): any;
65
- }
66
-
67
- interface ThreeAdapterOptions {
68
- /** Physical layout width of the 2D UI canvas. */
69
- width: number;
70
- /** Physical layout height of the 2D UI canvas. */
71
- height: number;
72
- /** Optional pre-existing canvas element. If omitted, a new canvas is created. */
73
- canvas?: HTMLCanvasElement;
74
- /** Options passed to the VectoScene constructor. */
75
- sceneOptions?: SceneOptions;
76
- }
77
- /**
78
- * Adapts a VectoJS Scene into a Three.js CanvasTexture, allowing VectoJS
79
- * components to be rendered in 3D space (e.g. on a plane, screen, or VR dashboard).
80
- */
81
- declare class ThreeAdapter {
82
- /** The Three.js CanvasTexture wrapping the offscreen Vecto canvas. */
83
- texture: THREE.CanvasTexture;
84
- /** The active VectoJS Scene instance. */
85
- vectoScene: Scene;
86
- /** The offscreen HTMLCanvasElement on which Vecto draws. */
87
- canvas: HTMLCanvasElement;
88
- /** A pre-built THREE.Mesh with PlaneGeometry and this texture for immediate use. */
89
- mesh: THREE.Mesh;
90
- /** Track hover states independently per pointerId for WebXR / Multi-Touch. */
91
- private activePointers;
92
- constructor(options: ThreeAdapterOptions);
93
- /**
94
- * Processes 3D Raycasting intersections and forwards pointer/scroll events.
95
- * Call this from window/document event listeners passing the raycaster.
96
- *
97
- * @param raycaster Three.js Raycaster instance.
98
- * @param type Pointer event type: 'pointerdown' | 'pointerup' | 'pointermove' | 'wheel' | 'click'.
99
- * @param originalEvent Optional original DOM Event to forward scroll deltas or button states.
100
- * @returns true if the ray intersected the VectoJS mesh; false otherwise.
101
- */
102
- updateIntersection(raycaster: THREE.Raycaster, type: 'pointerdown' | 'pointerup' | 'pointermove' | 'wheel' | 'click', originalEvent?: Event): boolean;
103
- /**
104
- * Dispatches pointer events mapped from UV coordinates [0, 1] to VectoJS entities.
105
- */
106
- private dispatchAtUv;
107
- /**
108
- * Routes events to the associated A11y DOM element, or Vecto's own event dispatch system.
109
- */
110
- private dispatchEventToTarget;
111
- private createDOMEvent;
112
- private findEntityById;
113
- /**
114
- * Resizes the offscreen canvas and VectoScene dimensions.
115
- */
116
- resize(width: number, height: number): void;
117
- /**
118
- * Disposes of Three.js textures, geometries, and VectoJS scenes to prevent memory leaks.
119
- */
120
- dispose(): void;
121
- }
122
-
123
- export { ThreeAdapter, type ThreeAdapterOptions, ThreeRenderer, WebGLGradient };