@vectojs/three 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/dist/index.js ADDED
@@ -0,0 +1,711 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.ts
31
+ var index_exports = {};
32
+ __export(index_exports, {
33
+ ThreeAdapter: () => ThreeAdapter,
34
+ ThreeRenderer: () => ThreeRenderer,
35
+ WebGLGradient: () => WebGLGradient
36
+ });
37
+ module.exports = __toCommonJS(index_exports);
38
+
39
+ // src/ThreeRenderer.ts
40
+ var import_core = require("@vectojs/core");
41
+ var THREE = __toESM(require("three"));
42
+ var WebGLGradient = class {
43
+ constructor(x0, y0, x1, y1, colorStops) {
44
+ this.x0 = x0;
45
+ this.y0 = y0;
46
+ this.x1 = x1;
47
+ this.y1 = y1;
48
+ this.colorStops = colorStops;
49
+ }
50
+ x0;
51
+ y0;
52
+ x1;
53
+ y1;
54
+ colorStops;
55
+ type = "linear";
56
+ };
57
+ var ThreePath = class {
58
+ shapes = [];
59
+ currentShape = null;
60
+ constructor() {
61
+ this.moveTo(0, 0);
62
+ }
63
+ getShape() {
64
+ if (!this.currentShape) {
65
+ this.currentShape = new THREE.Shape();
66
+ this.shapes.push(this.currentShape);
67
+ }
68
+ return this.currentShape;
69
+ }
70
+ moveTo(x, y) {
71
+ if (this.currentShape && this.currentShape.curves.length > 0) {
72
+ this.currentShape = new THREE.Shape();
73
+ this.shapes.push(this.currentShape);
74
+ }
75
+ this.getShape().moveTo(x, y);
76
+ }
77
+ lineTo(x, y) {
78
+ this.getShape().lineTo(x, y);
79
+ }
80
+ bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y) {
81
+ this.getShape().bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y);
82
+ }
83
+ closePath() {
84
+ this.currentShape?.closePath();
85
+ }
86
+ arc(x, y, radius, startAngle, endAngle, counterclockwise) {
87
+ this.getShape().absarc(x, y, radius, startAngle, endAngle, counterclockwise);
88
+ }
89
+ toShapes() {
90
+ return this.shapes;
91
+ }
92
+ getPoints() {
93
+ const allPoints = [];
94
+ for (const shape of this.shapes) {
95
+ allPoints.push(...shape.getPoints());
96
+ }
97
+ return allPoints;
98
+ }
99
+ };
100
+ var ThreeRenderer = class {
101
+ scene;
102
+ camera;
103
+ renderer;
104
+ width;
105
+ height;
106
+ matrix;
107
+ stack = [];
108
+ globalAlpha = 1;
109
+ alphaStack = [];
110
+ currentPath = null;
111
+ activeObjects = [];
112
+ scissorStack = [];
113
+ constructor(canvas) {
114
+ this.width = window.innerWidth;
115
+ this.height = window.innerHeight;
116
+ this.scene = new THREE.Scene();
117
+ this.camera = new THREE.OrthographicCamera(0, this.width, 0, this.height, 0.1, 1e3);
118
+ this.camera.position.z = 1;
119
+ this.renderer = new THREE.WebGLRenderer({ canvas, alpha: true, antialias: true });
120
+ this.renderer.setSize(this.width, this.height);
121
+ this.renderer.setPixelRatio(window.devicePixelRatio || 1);
122
+ this.matrix = new THREE.Matrix4().identity();
123
+ }
124
+ resize(width, height) {
125
+ this.width = width;
126
+ this.height = height;
127
+ this.renderer.setSize(width, height);
128
+ this.camera.right = width;
129
+ this.camera.bottom = height;
130
+ this.camera.updateProjectionMatrix();
131
+ }
132
+ clear() {
133
+ for (const obj of this.activeObjects) {
134
+ this.scene.remove(obj);
135
+ if (obj instanceof THREE.Mesh || obj instanceof THREE.Line) {
136
+ obj.geometry.dispose();
137
+ if (Array.isArray(obj.material)) {
138
+ for (const mat of obj.material) {
139
+ mat.dispose();
140
+ if (mat.map) mat.map.dispose();
141
+ }
142
+ } else {
143
+ obj.material.dispose();
144
+ if (obj.material.map) {
145
+ obj.material.map.dispose();
146
+ }
147
+ }
148
+ }
149
+ }
150
+ this.activeObjects = [];
151
+ this.renderer.clear();
152
+ this.renderer.setScissorTest(false);
153
+ }
154
+ save() {
155
+ this.stack.push(this.matrix.clone());
156
+ this.alphaStack.push(this.globalAlpha);
157
+ const box = new THREE.Vector4();
158
+ this.renderer.getScissor(box);
159
+ this.scissorStack.push({
160
+ enabled: this.renderer.getScissorTest(),
161
+ box
162
+ });
163
+ }
164
+ restore() {
165
+ if (this.stack.length > 0) this.matrix.copy(this.stack.pop());
166
+ if (this.alphaStack.length > 0) this.globalAlpha = this.alphaStack.pop();
167
+ if (this.scissorStack.length > 0) {
168
+ const state = this.scissorStack.pop();
169
+ this.renderer.setScissorTest(state.enabled);
170
+ this.renderer.setScissor(state.box);
171
+ }
172
+ }
173
+ translate(x, y) {
174
+ const m = new THREE.Matrix4().makeTranslation(x, y, 0);
175
+ this.matrix.multiply(m);
176
+ }
177
+ scale(x, y) {
178
+ const m = new THREE.Matrix4().makeScale(x, y, 1);
179
+ this.matrix.multiply(m);
180
+ }
181
+ rotate(angle) {
182
+ const m = new THREE.Matrix4().makeRotationZ(angle);
183
+ this.matrix.multiply(m);
184
+ }
185
+ setGlobalAlpha(alpha) {
186
+ this.globalAlpha = alpha;
187
+ }
188
+ clip(x, y, width, height) {
189
+ const pos = new THREE.Vector3(x, y, 0).applyMatrix4(this.matrix);
190
+ const size = new THREE.Vector3(width, height, 0).applyMatrix4(this.matrix).sub(new THREE.Vector3(0, 0, 0).applyMatrix4(this.matrix));
191
+ const dpr = window.devicePixelRatio || 1;
192
+ const canvasHeight = this.renderer.domElement.height / dpr;
193
+ this.renderer.setScissor(
194
+ pos.x * dpr,
195
+ (canvasHeight - (pos.y + size.y)) * dpr,
196
+ size.x * dpr,
197
+ size.y * dpr
198
+ );
199
+ this.renderer.setScissorTest(true);
200
+ }
201
+ beginPath() {
202
+ this.currentPath = new ThreePath();
203
+ }
204
+ moveTo(x, y) {
205
+ this.currentPath?.moveTo(x, y);
206
+ }
207
+ lineTo(x, y) {
208
+ this.currentPath?.lineTo(x, y);
209
+ }
210
+ bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y) {
211
+ this.currentPath?.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y);
212
+ }
213
+ closePath() {
214
+ this.currentPath?.closePath();
215
+ }
216
+ arc(x, y, radius, startAngle, endAngle, counterclockwise) {
217
+ this.currentPath?.arc(x, y, radius, startAngle, endAngle, counterclockwise);
218
+ }
219
+ roundRect(x, y, width, height, radii) {
220
+ if (!this.currentPath) return;
221
+ if (width < 0) {
222
+ x += width;
223
+ width = -width;
224
+ }
225
+ if (height < 0) {
226
+ y += height;
227
+ height = -height;
228
+ }
229
+ let r_tl = 0, r_tr = 0, r_br = 0, r_bl = 0;
230
+ if (typeof radii === "number") {
231
+ r_tl = r_tr = r_br = r_bl = radii;
232
+ } else if (Array.isArray(radii)) {
233
+ if (radii.length === 1) {
234
+ r_tl = r_tr = r_br = r_bl = radii[0];
235
+ } else if (radii.length === 2) {
236
+ r_tl = r_br = radii[0];
237
+ r_tr = r_bl = radii[1];
238
+ } else if (radii.length === 3) {
239
+ r_tl = radii[0];
240
+ r_tr = r_bl = radii[1];
241
+ r_br = radii[2];
242
+ } else if (radii.length >= 4) {
243
+ r_tl = radii[0];
244
+ r_tr = radii[1];
245
+ r_br = radii[2];
246
+ r_bl = radii[3];
247
+ }
248
+ }
249
+ const tl_tr = r_tl + r_tr;
250
+ const bl_br = r_bl + r_br;
251
+ const tl_bl = r_tl + r_bl;
252
+ const tr_br = r_tr + r_br;
253
+ let factor = 1;
254
+ if (tl_tr > width) factor = Math.min(factor, width / tl_tr);
255
+ if (bl_br > width) factor = Math.min(factor, width / bl_br);
256
+ if (tl_bl > height) factor = Math.min(factor, height / tl_bl);
257
+ if (tr_br > height) factor = Math.min(factor, height / tr_br);
258
+ if (factor < 1) {
259
+ r_tl *= factor;
260
+ r_tr *= factor;
261
+ r_br *= factor;
262
+ r_bl *= factor;
263
+ }
264
+ this.currentPath.moveTo(x + r_tl, y);
265
+ this.currentPath.lineTo(x + width - r_tr, y);
266
+ this.currentPath.arc(x + width - r_tr, y + r_tr, r_tr, -Math.PI / 2, 0, false);
267
+ this.currentPath.lineTo(x + width, y + height - r_br);
268
+ this.currentPath.arc(x + width - r_br, y + height - r_br, r_br, 0, Math.PI / 2, false);
269
+ this.currentPath.lineTo(x + r_bl, y + height);
270
+ this.currentPath.arc(x + r_bl, y + height - r_bl, r_bl, Math.PI / 2, Math.PI, false);
271
+ this.currentPath.lineTo(x, y + r_tl);
272
+ this.currentPath.arc(x + r_tl, y + r_tl, r_tl, Math.PI, -Math.PI / 2, false);
273
+ }
274
+ drawImage(source, dx, dy, dw, dh) {
275
+ let texture;
276
+ if (source instanceof HTMLImageElement || source instanceof HTMLCanvasElement || typeof ImageBitmap !== "undefined" && source instanceof ImageBitmap) {
277
+ texture = new THREE.CanvasTexture(source);
278
+ } else {
279
+ texture = new THREE.Texture(source);
280
+ texture.needsUpdate = true;
281
+ }
282
+ texture.minFilter = THREE.LinearFilter;
283
+ const material = new THREE.MeshBasicMaterial({
284
+ map: texture,
285
+ transparent: true,
286
+ opacity: this.globalAlpha,
287
+ depthWrite: false
288
+ });
289
+ const geometry = new THREE.PlaneGeometry(dw, dh);
290
+ const mesh = new THREE.Mesh(geometry, material);
291
+ mesh.position.set(dx + dw / 2, dy + dh / 2, 0);
292
+ mesh.applyMatrix4(this.matrix);
293
+ this.scene.add(mesh);
294
+ this.activeObjects.push(mesh);
295
+ }
296
+ fill(colorOrGradient) {
297
+ if (!this.currentPath) return;
298
+ if (colorOrGradient && colorOrGradient.type === "linear") {
299
+ const grad = colorOrGradient;
300
+ const sortedStops = [...grad.colorStops].sort((a, b) => a.stop - b.stop);
301
+ if (sortedStops.length < 2) {
302
+ const fallbackColor = sortedStops.length === 1 ? sortedStops[0].color : "#ffffff";
303
+ this.fillSolidShape(fallbackColor);
304
+ return;
305
+ }
306
+ const finalColors = [];
307
+ const finalStops = [];
308
+ if (sortedStops.length > 8) {
309
+ const lerpColor = (c1, c2, f) => {
310
+ const rgba1 = (0, import_core.parseColorToRGBA)(c1);
311
+ const rgba2 = (0, import_core.parseColorToRGBA)(c2);
312
+ return new THREE.Vector4(
313
+ rgba1[0] + (rgba2[0] - rgba1[0]) * f,
314
+ rgba1[1] + (rgba2[1] - rgba1[1]) * f,
315
+ rgba1[2] + (rgba2[2] - rgba1[2]) * f,
316
+ rgba1[3] + (rgba2[3] - rgba1[3]) * f
317
+ );
318
+ };
319
+ for (let k = 0; k < 8; k++) {
320
+ const t = k / 7;
321
+ if (t <= sortedStops[0].stop) {
322
+ const rgba = (0, import_core.parseColorToRGBA)(sortedStops[0].color);
323
+ finalColors.push(new THREE.Vector4(rgba[0], rgba[1], rgba[2], rgba[3]));
324
+ } else if (t >= sortedStops[sortedStops.length - 1].stop) {
325
+ const rgba = (0, import_core.parseColorToRGBA)(sortedStops[sortedStops.length - 1].color);
326
+ finalColors.push(new THREE.Vector4(rgba[0], rgba[1], rgba[2], rgba[3]));
327
+ } else {
328
+ let i = 0;
329
+ for (let idx = 0; idx < sortedStops.length - 1; idx++) {
330
+ if (t >= sortedStops[idx].stop && t <= sortedStops[idx + 1].stop) {
331
+ i = idx;
332
+ break;
333
+ }
334
+ }
335
+ const gap = sortedStops[i + 1].stop - sortedStops[i].stop;
336
+ const f = gap > 1e-4 ? (t - sortedStops[i].stop) / gap : 0;
337
+ finalColors.push(lerpColor(sortedStops[i].color, sortedStops[i + 1].color, f));
338
+ }
339
+ finalStops.push(t);
340
+ }
341
+ } else {
342
+ for (let i = 0; i < 8; i++) {
343
+ const stopIdx = Math.min(i, sortedStops.length - 1);
344
+ const stop = sortedStops[stopIdx];
345
+ const rgba = (0, import_core.parseColorToRGBA)(stop.color);
346
+ finalColors.push(new THREE.Vector4(rgba[0], rgba[1], rgba[2], rgba[3]));
347
+ finalStops.push(stop.stop);
348
+ }
349
+ }
350
+ const u_grad_start = new THREE.Vector3(grad.x0, grad.y0, 0).applyMatrix4(this.matrix);
351
+ const u_grad_end = new THREE.Vector3(grad.x1, grad.y1, 0).applyMatrix4(this.matrix);
352
+ const shapes = this.currentPath.toShapes();
353
+ for (const shape of shapes) {
354
+ const geometry = new THREE.ShapeGeometry(shape);
355
+ const material = new THREE.ShaderMaterial({
356
+ uniforms: {
357
+ u_grad_start: { value: new THREE.Vector2(u_grad_start.x, u_grad_start.y) },
358
+ u_grad_end: { value: new THREE.Vector2(u_grad_end.x, u_grad_end.y) },
359
+ u_grad_colors: { value: finalColors },
360
+ u_grad_stops: { value: finalStops },
361
+ u_global_alpha: { value: this.globalAlpha }
362
+ },
363
+ vertexShader: `
364
+ varying vec2 v_world_pos;
365
+ void main() {
366
+ vec4 worldPos = modelMatrix * vec4(position, 1.0);
367
+ v_world_pos = worldPos.xy;
368
+ gl_Position = projectionMatrix * viewMatrix * worldPos;
369
+ }
370
+ `,
371
+ fragmentShader: `
372
+ varying vec2 v_world_pos;
373
+ uniform vec2 u_grad_start;
374
+ uniform vec2 u_grad_end;
375
+ uniform vec4 u_grad_colors[8];
376
+ uniform float u_grad_stops[8];
377
+ uniform float u_global_alpha;
378
+
379
+ void main() {
380
+ vec2 d = u_grad_end - u_grad_start;
381
+ float d_len_sq = dot(d, d);
382
+ float t = 0.0;
383
+ if (d_len_sq > 0.0001) {
384
+ t = clamp(dot(v_world_pos - u_grad_start, d) / d_len_sq, 0.0, 1.0);
385
+ }
386
+ vec4 finalColor = u_grad_colors[7];
387
+ if (t <= u_grad_stops[0]) {
388
+ finalColor = u_grad_colors[0];
389
+ } else {
390
+ for (int i = 0; i < 7; i++) {
391
+ if (t >= u_grad_stops[i] && t <= u_grad_stops[i+1]) {
392
+ float gap = u_grad_stops[i+1] - u_grad_stops[i];
393
+ float factor = gap > 0.0001 ? (t - u_grad_stops[i]) / gap : 0.0;
394
+ finalColor = mix(u_grad_colors[i], u_grad_colors[i+1], factor);
395
+ break;
396
+ }
397
+ }
398
+ }
399
+ gl_FragColor = vec4(finalColor.rgb, finalColor.a * u_global_alpha);
400
+ }
401
+ `,
402
+ transparent: true,
403
+ depthWrite: false
404
+ });
405
+ const mesh = new THREE.Mesh(geometry, material);
406
+ mesh.applyMatrix4(this.matrix);
407
+ this.scene.add(mesh);
408
+ this.activeObjects.push(mesh);
409
+ }
410
+ } else {
411
+ this.fillSolidShape(colorOrGradient);
412
+ }
413
+ }
414
+ fillSolidShape(colorOrGradient) {
415
+ if (!this.currentPath) return;
416
+ const color = typeof colorOrGradient === "string" ? colorOrGradient : "#ffffff";
417
+ const shapes = this.currentPath.toShapes();
418
+ for (const shape of shapes) {
419
+ const geometry = new THREE.ShapeGeometry(shape);
420
+ const material = new THREE.MeshBasicMaterial({
421
+ color: new THREE.Color(color),
422
+ transparent: this.globalAlpha < 1,
423
+ opacity: this.globalAlpha,
424
+ depthWrite: false
425
+ });
426
+ const mesh = new THREE.Mesh(geometry, material);
427
+ mesh.applyMatrix4(this.matrix);
428
+ this.scene.add(mesh);
429
+ this.activeObjects.push(mesh);
430
+ }
431
+ }
432
+ stroke(colorOrGradient, lineWidth = 1) {
433
+ if (!this.currentPath) return;
434
+ let color = "#ffffff";
435
+ if (typeof colorOrGradient === "string") {
436
+ color = colorOrGradient;
437
+ } else if (colorOrGradient && colorOrGradient.type === "linear") {
438
+ const grad = colorOrGradient;
439
+ if (grad.colorStops && grad.colorStops.length > 0) {
440
+ color = grad.colorStops[0].color;
441
+ }
442
+ }
443
+ const points = this.currentPath.getPoints();
444
+ if (points.length === 0) return;
445
+ const geometry = new THREE.BufferGeometry().setFromPoints(points);
446
+ const material = new THREE.LineBasicMaterial({
447
+ color: new THREE.Color(color),
448
+ transparent: this.globalAlpha < 1,
449
+ opacity: this.globalAlpha,
450
+ linewidth: lineWidth
451
+ });
452
+ const line = new THREE.Line(geometry, material);
453
+ line.applyMatrix4(this.matrix);
454
+ this.scene.add(line);
455
+ this.activeObjects.push(line);
456
+ }
457
+ fillText(text, x, y, font, color) {
458
+ if (typeof document === "undefined") return;
459
+ const canvas = document.createElement("canvas");
460
+ const ctx = canvas.getContext("2d");
461
+ ctx.font = font;
462
+ const width = Math.max(1, Math.ceil(ctx.measureText(text).width));
463
+ const fontSize = parseInt(font) || 16;
464
+ const height = Math.max(1, Math.ceil(fontSize * 1.5));
465
+ canvas.width = width;
466
+ canvas.height = height;
467
+ ctx.font = font;
468
+ let fillCol = "#ffffff";
469
+ if (typeof color === "string") {
470
+ fillCol = color;
471
+ } else if (color && color.type === "linear") {
472
+ const grad = color;
473
+ if (grad.colorStops && grad.colorStops.length > 0) {
474
+ fillCol = grad.colorStops[0].color;
475
+ }
476
+ }
477
+ ctx.fillStyle = fillCol;
478
+ ctx.textBaseline = "alphabetic";
479
+ ctx.fillText(text, 0, fontSize);
480
+ const texture = new THREE.CanvasTexture(canvas);
481
+ texture.minFilter = THREE.LinearFilter;
482
+ const material = new THREE.MeshBasicMaterial({
483
+ map: texture,
484
+ transparent: true,
485
+ opacity: this.globalAlpha,
486
+ depthWrite: false
487
+ });
488
+ const geometry = new THREE.PlaneGeometry(width, height);
489
+ const mesh = new THREE.Mesh(geometry, material);
490
+ mesh.position.set(x + width / 2, y - height / 2 + fontSize, 0);
491
+ mesh.applyMatrix4(this.matrix);
492
+ this.scene.add(mesh);
493
+ this.activeObjects.push(mesh);
494
+ }
495
+ fillCircle(cx, cy, radius, color, alpha = 1) {
496
+ const geometry = new THREE.CircleGeometry(radius, 32);
497
+ const material = new THREE.MeshBasicMaterial({
498
+ color: new THREE.Color(color),
499
+ transparent: this.globalAlpha * alpha < 1,
500
+ opacity: this.globalAlpha * alpha,
501
+ depthWrite: false
502
+ });
503
+ const mesh = new THREE.Mesh(geometry, material);
504
+ mesh.position.set(cx, cy, 0);
505
+ mesh.applyMatrix4(this.matrix);
506
+ this.scene.add(mesh);
507
+ this.activeObjects.push(mesh);
508
+ }
509
+ flush() {
510
+ this.renderer.render(this.scene, this.camera);
511
+ }
512
+ createLinearGradient(x0, y0, x1, y1, colorStops) {
513
+ return new WebGLGradient(x0, y0, x1, y1, colorStops);
514
+ }
515
+ };
516
+
517
+ // src/ThreeAdapter.ts
518
+ var THREE2 = __toESM(require("three"));
519
+ var import_core2 = require("@vectojs/core");
520
+ var ThreeAdapter = class {
521
+ /** The Three.js CanvasTexture wrapping the offscreen Vecto canvas. */
522
+ texture;
523
+ /** The active VectoJS Scene instance. */
524
+ vectoScene;
525
+ /** The offscreen HTMLCanvasElement on which Vecto draws. */
526
+ canvas;
527
+ /** A pre-built THREE.Mesh with PlaneGeometry and this texture for immediate use. */
528
+ mesh;
529
+ /** Track hover states independently per pointerId for WebXR / Multi-Touch. */
530
+ activePointers = /* @__PURE__ */ new Map();
531
+ constructor(options) {
532
+ this.canvas = options.canvas || (typeof document !== "undefined" ? document.createElement("canvas") : { width: options.width, height: options.height });
533
+ this.canvas.width = options.width;
534
+ this.canvas.height = options.height;
535
+ const sceneOptions = {
536
+ ...options.sceneOptions,
537
+ disableWindowResize: true
538
+ };
539
+ this.vectoScene = new import_core2.Scene(this.canvas, sceneOptions);
540
+ this.vectoScene.resize(options.width, options.height);
541
+ this.texture = new THREE2.CanvasTexture(this.canvas);
542
+ this.texture.minFilter = THREE2.LinearFilter;
543
+ this.texture.magFilter = THREE2.LinearFilter;
544
+ const originalRender = this.vectoScene.render;
545
+ this.vectoScene.render = (renderer, dt, time) => {
546
+ originalRender.call(this.vectoScene, renderer, dt, time);
547
+ this.texture.needsUpdate = true;
548
+ };
549
+ const geometry = new THREE2.PlaneGeometry(1, 1);
550
+ const material = new THREE2.MeshBasicMaterial({
551
+ map: this.texture,
552
+ transparent: true,
553
+ depthWrite: false
554
+ });
555
+ this.mesh = new THREE2.Mesh(geometry, material);
556
+ }
557
+ /**
558
+ * Processes 3D Raycasting intersections and forwards pointer/scroll events.
559
+ * Call this from window/document event listeners passing the raycaster.
560
+ *
561
+ * @param raycaster Three.js Raycaster instance.
562
+ * @param type Pointer event type: 'pointerdown' | 'pointerup' | 'pointermove' | 'wheel' | 'click'.
563
+ * @param originalEvent Optional original DOM Event to forward scroll deltas or button states.
564
+ * @returns true if the ray intersected the VectoJS mesh; false otherwise.
565
+ */
566
+ updateIntersection(raycaster, type, originalEvent) {
567
+ const intersects = raycaster.intersectObject(this.mesh);
568
+ const pointerId = originalEvent instanceof PointerEvent ? originalEvent.pointerId : 1;
569
+ let state = this.activePointers.get(pointerId);
570
+ if (!state) {
571
+ state = { isHovering: false, lastUv: new THREE2.Vector2(), lastTargetId: null };
572
+ this.activePointers.set(pointerId, state);
573
+ }
574
+ if (intersects.length > 0) {
575
+ const hit = intersects[0];
576
+ if (hit.uv) {
577
+ state.lastUv.copy(hit.uv);
578
+ state.isHovering = true;
579
+ this.dispatchAtUv(type, hit.uv, pointerId, originalEvent);
580
+ return true;
581
+ }
582
+ }
583
+ if (state.isHovering) {
584
+ state.isHovering = false;
585
+ this.dispatchAtUv("pointerleave", state.lastUv, pointerId, originalEvent);
586
+ }
587
+ return false;
588
+ }
589
+ /**
590
+ * Dispatches pointer events mapped from UV coordinates [0, 1] to VectoJS entities.
591
+ */
592
+ dispatchAtUv(type, uv, pointerId, originalEvent) {
593
+ const px = uv.x * this.canvas.width;
594
+ const py = (1 - uv.y) * this.canvas.height;
595
+ this.vectoScene.markDirty();
596
+ const hitEntity = this.vectoScene.findEntityAt(px, py);
597
+ const state = this.activePointers.get(pointerId);
598
+ if (state && type === "pointermove") {
599
+ const currentTargetId = hitEntity ? hitEntity.id : null;
600
+ if (currentTargetId !== state.lastTargetId) {
601
+ if (state.lastTargetId) {
602
+ const oldEntity = this.findEntityById(this.vectoScene.getRoot(), state.lastTargetId);
603
+ if (oldEntity) {
604
+ this.dispatchEventToTarget(oldEntity, "pointerleave", px, py, pointerId, originalEvent);
605
+ }
606
+ }
607
+ if (hitEntity) {
608
+ this.dispatchEventToTarget(hitEntity, "hover", px, py, pointerId, originalEvent);
609
+ }
610
+ state.lastTargetId = currentTargetId;
611
+ }
612
+ } else if (state && type === "pointerleave") {
613
+ if (state.lastTargetId) {
614
+ const oldEntity = this.findEntityById(this.vectoScene.getRoot(), state.lastTargetId);
615
+ if (oldEntity) {
616
+ this.dispatchEventToTarget(oldEntity, "pointerleave", px, py, pointerId, originalEvent);
617
+ }
618
+ }
619
+ state.lastTargetId = null;
620
+ }
621
+ if (hitEntity) {
622
+ this.dispatchEventToTarget(hitEntity, type, px, py, pointerId, originalEvent);
623
+ } else {
624
+ const fallbackEvent = this.createDOMEvent(type, px, py, pointerId, originalEvent);
625
+ this.canvas.dispatchEvent(fallbackEvent);
626
+ }
627
+ }
628
+ /**
629
+ * Routes events to the associated A11y DOM element, or Vecto's own event dispatch system.
630
+ */
631
+ dispatchEventToTarget(entity, type, x, y, pointerId, originalEvent) {
632
+ const a11yEl = this.vectoScene.getA11yElement(entity.id);
633
+ if (a11yEl) {
634
+ const domEvent = this.createDOMEvent(type, x, y, pointerId, originalEvent);
635
+ a11yEl.dispatchEvent(domEvent);
636
+ if (type === "pointerdown" && (a11yEl instanceof HTMLInputElement || a11yEl instanceof HTMLTextAreaElement || a11yEl.getAttribute("tabindex") !== null)) {
637
+ a11yEl.focus();
638
+ }
639
+ } else {
640
+ const e = originalEvent instanceof MouseEvent ? originalEvent : void 0;
641
+ const vectoEvent = new import_core2.VectoJSEvent(type, entity, e, type !== "pointerleave");
642
+ entity.dispatchEvent(vectoEvent);
643
+ }
644
+ }
645
+ createDOMEvent(type, x, y, pointerId, originalEvent) {
646
+ if (type === "wheel") {
647
+ const wheelE = originalEvent instanceof WheelEvent ? originalEvent : void 0;
648
+ return new WheelEvent("wheel", {
649
+ clientX: x,
650
+ clientY: y,
651
+ deltaX: wheelE ? wheelE.deltaX : 0,
652
+ deltaY: wheelE ? wheelE.deltaY : 0,
653
+ deltaZ: wheelE ? wheelE.deltaZ : 0,
654
+ deltaMode: wheelE ? wheelE.deltaMode : 0,
655
+ bubbles: true,
656
+ cancelable: true
657
+ });
658
+ }
659
+ return new PointerEvent(type, {
660
+ clientX: x,
661
+ clientY: y,
662
+ button: originalEvent instanceof MouseEvent ? originalEvent.button : 0,
663
+ buttons: originalEvent instanceof MouseEvent ? originalEvent.buttons : 0,
664
+ pointerId,
665
+ bubbles: true,
666
+ cancelable: true
667
+ });
668
+ }
669
+ findEntityById(root, id) {
670
+ if (root.id === id) return root;
671
+ for (const child of root.children) {
672
+ const found = this.findEntityById(child, id);
673
+ if (found) return found;
674
+ }
675
+ return null;
676
+ }
677
+ /**
678
+ * Resizes the offscreen canvas and VectoScene dimensions.
679
+ */
680
+ resize(width, height) {
681
+ this.canvas.width = width;
682
+ this.canvas.height = height;
683
+ this.vectoScene.resize(width, height);
684
+ this.texture.needsUpdate = true;
685
+ }
686
+ /**
687
+ * Disposes of Three.js textures, geometries, and VectoJS scenes to prevent memory leaks.
688
+ */
689
+ dispose() {
690
+ this.texture.dispose();
691
+ this.mesh.geometry.dispose();
692
+ if (Array.isArray(this.mesh.material)) {
693
+ for (const mat of this.mesh.material) mat.dispose();
694
+ } else {
695
+ this.mesh.material.dispose();
696
+ }
697
+ if (this.mesh.parent) {
698
+ this.mesh.parent.remove(this.mesh);
699
+ }
700
+ this.vectoScene.destroy();
701
+ this.activePointers.clear();
702
+ this.canvas.width = 0;
703
+ this.canvas.height = 0;
704
+ }
705
+ };
706
+ // Annotate the CommonJS export names for ESM import in node:
707
+ 0 && (module.exports = {
708
+ ThreeAdapter,
709
+ ThreeRenderer,
710
+ WebGLGradient
711
+ });