@expofp/renderer 1.5.0 → 2.0.1
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.d.ts +90 -36
- package/dist/index.js +734 -379
- package/package.json +3 -1
package/dist/index.js
CHANGED
|
@@ -2,9 +2,10 @@ var __defProp = Object.defineProperty;
|
|
|
2
2
|
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
3
3
|
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
4
4
|
var _a;
|
|
5
|
-
import { Color, Matrix4, Vector3, DataTexture, RGBAFormat, FloatType, RedFormat, UnsignedIntType, IntType, RGBAIntegerFormat, RGFormat, RGIntegerFormat, RedIntegerFormat, BatchedMesh as BatchedMesh$1, BufferAttribute, StreamDrawUsage, Vector4, AlwaysDepth, DoubleSide, MeshBasicMaterial, Texture, Group, PlaneGeometry, SRGBColorSpace, Vector2, Mesh, LessEqualDepth, Quaternion, BufferGeometry, LinearSRGBColorSpace, Plane, Raycaster, Sphere, Box3, Spherical, PerspectiveCamera,
|
|
5
|
+
import { Color, Matrix4, Vector3, DataTexture, RGBAFormat, FloatType, RedFormat, UnsignedIntType, IntType, RGBAIntegerFormat, RGFormat, RGIntegerFormat, RedIntegerFormat, BatchedMesh as BatchedMesh$1, BufferAttribute, StreamDrawUsage, Vector4, AlwaysDepth, DoubleSide, MeshBasicMaterial, Texture, Group, PlaneGeometry, SRGBColorSpace, Vector2, Mesh, LessEqualDepth, Quaternion, BufferGeometry, LinearSRGBColorSpace, Plane, Raycaster, Sphere, Box3, Spherical, PerspectiveCamera, Camera, Scene, MathUtils, Clock, WebGLRenderer } from "three";
|
|
6
6
|
import { traverseAncestorsGenerator } from "three/examples/jsm/utils/SceneUtils.js";
|
|
7
7
|
import { BatchedText as BatchedText$1, Text as Text$1 } from "troika-three-text";
|
|
8
|
+
import createLog from "debug";
|
|
8
9
|
import { LineMaterial, LineSegmentsGeometry } from "three/examples/jsm/Addons.js";
|
|
9
10
|
import { MaxRectsPacker, Rectangle } from "maxrects-packer";
|
|
10
11
|
import { converter, parse } from "culori";
|
|
@@ -336,7 +337,7 @@ const dimColorVertexImpl = (
|
|
|
336
337
|
`
|
|
337
338
|
void setDimAmount() {
|
|
338
339
|
float instanceDim = 0.;
|
|
339
|
-
#ifdef USE_BATCH_UNIFORMS
|
|
340
|
+
#ifdef USE_BATCH_UNIFORMS
|
|
340
341
|
instanceDim = batch_skipDimInstance;
|
|
341
342
|
#endif
|
|
342
343
|
#ifdef TROIKA_DERIVED_MATERIAL_1
|
|
@@ -366,6 +367,34 @@ const dimColorFrag = (
|
|
|
366
367
|
return vec4(m * col.a, col.a);
|
|
367
368
|
}`
|
|
368
369
|
);
|
|
370
|
+
function createLogger(namespace) {
|
|
371
|
+
const info = createLog(namespace);
|
|
372
|
+
info.log = console.info.bind(console);
|
|
373
|
+
const debug = info.extend("debug");
|
|
374
|
+
debug.log = console.debug.bind(console);
|
|
375
|
+
const warn = info.extend("warn");
|
|
376
|
+
warn.log = console.warn.bind(console);
|
|
377
|
+
const error = info.extend("error");
|
|
378
|
+
error.log = console.error.bind(console);
|
|
379
|
+
return {
|
|
380
|
+
debug,
|
|
381
|
+
info,
|
|
382
|
+
warn,
|
|
383
|
+
error
|
|
384
|
+
};
|
|
385
|
+
}
|
|
386
|
+
function printTree(object, logger2) {
|
|
387
|
+
if (!logger2.enabled) return;
|
|
388
|
+
const isGroup = object.isGroup;
|
|
389
|
+
const name = object.name.split(":").at(-1);
|
|
390
|
+
if (isGroup) {
|
|
391
|
+
console.groupCollapsed(name);
|
|
392
|
+
object.children.forEach((child) => printTree(child, logger2));
|
|
393
|
+
console.groupEnd();
|
|
394
|
+
} else {
|
|
395
|
+
logger2.log(`${name}<${object.type}>, render order: ${object.renderOrder}`);
|
|
396
|
+
}
|
|
397
|
+
}
|
|
369
398
|
function isObject(item) {
|
|
370
399
|
return !!item && typeof item === "object" && !Array.isArray(item);
|
|
371
400
|
}
|
|
@@ -394,7 +423,7 @@ function getSquareTextureSize(capacity, pixelsPerInstance) {
|
|
|
394
423
|
}
|
|
395
424
|
function getSquareTextureInfo(arrayType, channels, pixelsPerInstance, capacity) {
|
|
396
425
|
if (channels === 3) {
|
|
397
|
-
|
|
426
|
+
logger$b.debug('"channels" cannot be 3. Set to 4. More info: https://github.com/mrdoob/three.js/pull/23228');
|
|
398
427
|
channels = 4;
|
|
399
428
|
}
|
|
400
429
|
const size = getSquareTextureSize(capacity, pixelsPerInstance);
|
|
@@ -416,6 +445,7 @@ function getSquareTextureInfo(arrayType, channels, pixelsPerInstance, capacity)
|
|
|
416
445
|
}
|
|
417
446
|
return { array, size, type, format };
|
|
418
447
|
}
|
|
448
|
+
const logger$b = createLogger("SquareDataTexture");
|
|
419
449
|
class SquareDataTexture extends DataTexture {
|
|
420
450
|
/**
|
|
421
451
|
* @param arrayType The constructor for the TypedArray.
|
|
@@ -453,7 +483,7 @@ class SquareDataTexture extends DataTexture {
|
|
|
453
483
|
setUniformAt(id, name, value) {
|
|
454
484
|
const schema = this.uniformMap.get(name);
|
|
455
485
|
if (!schema) {
|
|
456
|
-
|
|
486
|
+
logger$b.debug(`setUniformAt: uniform ${name} not found`);
|
|
457
487
|
return;
|
|
458
488
|
}
|
|
459
489
|
const { offset, size } = schema;
|
|
@@ -474,7 +504,7 @@ class SquareDataTexture extends DataTexture {
|
|
|
474
504
|
getUniformAt(id, name, target) {
|
|
475
505
|
const schema = this.uniformMap.get(name);
|
|
476
506
|
if (!schema) {
|
|
477
|
-
|
|
507
|
+
logger$b.debug(`getUniformAt: uniform ${name} not found`);
|
|
478
508
|
return 0;
|
|
479
509
|
}
|
|
480
510
|
const { offset, size } = schema;
|
|
@@ -620,6 +650,7 @@ class SquareDataTexture extends DataTexture {
|
|
|
620
650
|
}
|
|
621
651
|
const componentsArray = ["r", "g", "b", "a"];
|
|
622
652
|
const batchIdName = "batchId";
|
|
653
|
+
const logger$a = createLogger("BatchedMesh");
|
|
623
654
|
const _BatchedMesh = class _BatchedMesh extends BatchedMesh$1 {
|
|
624
655
|
/**
|
|
625
656
|
* @param instanceCount the max number of individual geometries planned to be added.
|
|
@@ -699,7 +730,7 @@ const _BatchedMesh = class _BatchedMesh extends BatchedMesh$1 {
|
|
|
699
730
|
if (_BatchedMesh.useMultiDraw) return;
|
|
700
731
|
const batchCount = this.batchCount;
|
|
701
732
|
const gl = renderer.getContext();
|
|
702
|
-
if (geometry.index == null) return
|
|
733
|
+
if (geometry.index == null) return logger$a.debug("No index buffer", (_a2 = this.parent) == null ? void 0 : _a2.name);
|
|
703
734
|
const type = this.getIndexType(gl, geometry.index);
|
|
704
735
|
gl.drawElements(gl.TRIANGLES, batchCount, type, 0);
|
|
705
736
|
renderer.info.update(batchCount, gl.TRIANGLES, 1);
|
|
@@ -714,7 +745,7 @@ const _BatchedMesh = class _BatchedMesh extends BatchedMesh$1 {
|
|
|
714
745
|
*/
|
|
715
746
|
getUniformAt(id, name, target) {
|
|
716
747
|
if (!this.uniformsTexture) {
|
|
717
|
-
|
|
748
|
+
logger$a.debug(`getUniformAt: uniforms texture not initialized`);
|
|
718
749
|
return 0;
|
|
719
750
|
}
|
|
720
751
|
return this.uniformsTexture.getUniformAt(id, name, target);
|
|
@@ -727,7 +758,7 @@ const _BatchedMesh = class _BatchedMesh extends BatchedMesh$1 {
|
|
|
727
758
|
*/
|
|
728
759
|
setUniformAt(instanceId, name, value) {
|
|
729
760
|
if (!this.uniformsTexture) {
|
|
730
|
-
|
|
761
|
+
logger$a.debug(`setUniformAt: uniforms texture not initialized`);
|
|
731
762
|
return;
|
|
732
763
|
}
|
|
733
764
|
this.uniformsTexture.setUniformAt(instanceId, name, value);
|
|
@@ -747,7 +778,7 @@ const _BatchedMesh = class _BatchedMesh extends BatchedMesh$1 {
|
|
|
747
778
|
}
|
|
748
779
|
dispose() {
|
|
749
780
|
var _a2;
|
|
750
|
-
this.geometry.setIndex(this.indexBuffer
|
|
781
|
+
if (this.indexBuffer) this.geometry.setIndex(this.indexBuffer);
|
|
751
782
|
super.dispose();
|
|
752
783
|
this.uniformSchema = {};
|
|
753
784
|
(_a2 = this.uniformsTexture) == null ? void 0 : _a2.dispose();
|
|
@@ -1082,29 +1113,18 @@ function isVisible(object) {
|
|
|
1082
1113
|
if (!object.visible) return false;
|
|
1083
1114
|
return [...traverseAncestorsGenerator(object)].every((obj) => obj.visible);
|
|
1084
1115
|
}
|
|
1085
|
-
function printTree(object, fullName = false) {
|
|
1086
|
-
object.traverse((obj) => {
|
|
1087
|
-
let s = "";
|
|
1088
|
-
let obj2 = obj;
|
|
1089
|
-
while (obj2 !== object) {
|
|
1090
|
-
s = "|___ " + s;
|
|
1091
|
-
obj2 = (obj2 == null ? void 0 : obj2.parent) ?? null;
|
|
1092
|
-
}
|
|
1093
|
-
const renderOrder = obj.isGroup ? "" : `, RO: ${obj.renderOrder}`;
|
|
1094
|
-
const name = fullName ? obj.name : obj.name.split(":").at(-1);
|
|
1095
|
-
console.log(`${s}${name}<${obj.type}>${renderOrder}`);
|
|
1096
|
-
});
|
|
1097
|
-
}
|
|
1098
1116
|
class RenderableSystem {
|
|
1099
1117
|
/**
|
|
1100
1118
|
* @param type readable name of the system's type for debugging
|
|
1101
1119
|
* @param renderer {@link Renderer}
|
|
1120
|
+
* @param logger {@link Logger}
|
|
1102
1121
|
*/
|
|
1103
|
-
constructor(type, renderer) {
|
|
1122
|
+
constructor(type, renderer, logger2) {
|
|
1104
1123
|
__publicField(this, "mapDefToObject", /* @__PURE__ */ new Map());
|
|
1105
1124
|
__publicField(this, "mapObjectToDefs", /* @__PURE__ */ new Map());
|
|
1106
1125
|
this.type = type;
|
|
1107
1126
|
this.renderer = renderer;
|
|
1127
|
+
this.logger = logger2;
|
|
1108
1128
|
}
|
|
1109
1129
|
/**
|
|
1110
1130
|
* Update a def with its current properties.
|
|
@@ -1131,7 +1151,7 @@ class RenderableSystem {
|
|
|
1131
1151
|
* @param layerDef {@link TypedLayerDef} layer definition to update
|
|
1132
1152
|
*/
|
|
1133
1153
|
updateLayer(group, layerDef) {
|
|
1134
|
-
|
|
1154
|
+
this.logger.debug(`Updating layer ${layerDef.name} %O`, layerDef);
|
|
1135
1155
|
this.updateLayerImpl(group, layerDef);
|
|
1136
1156
|
}
|
|
1137
1157
|
/**
|
|
@@ -1236,7 +1256,7 @@ class RenderableSystem {
|
|
|
1236
1256
|
registerDefObject(def, object, instanceIds) {
|
|
1237
1257
|
const ids = Array.isArray(instanceIds) ? instanceIds : [instanceIds];
|
|
1238
1258
|
if (ids.length === 0) {
|
|
1239
|
-
|
|
1259
|
+
this.logger.debug(`Tried to register def with empty instanceIds %O`, def);
|
|
1240
1260
|
return;
|
|
1241
1261
|
}
|
|
1242
1262
|
this.mapDefToObject.set(def, { object, instanceIds: ids });
|
|
@@ -1295,7 +1315,7 @@ class RenderableSystem {
|
|
|
1295
1315
|
getObjectInstanceByDef(def) {
|
|
1296
1316
|
const mapping = this.mapDefToObject.get(def);
|
|
1297
1317
|
if (!mapping) {
|
|
1298
|
-
|
|
1318
|
+
this.logger.debug(`No object mapping found for def %O`, def);
|
|
1299
1319
|
return void 0;
|
|
1300
1320
|
}
|
|
1301
1321
|
return mapping;
|
|
@@ -1316,13 +1336,14 @@ class RenderableSystem {
|
|
|
1316
1336
|
return Array.from(this.mapObjectToDefs.keys());
|
|
1317
1337
|
}
|
|
1318
1338
|
}
|
|
1339
|
+
const logger$9 = createLogger("image");
|
|
1319
1340
|
class ImageSystem extends RenderableSystem {
|
|
1320
1341
|
/**
|
|
1321
1342
|
* @param materialSystem {@link MaterialSystem}
|
|
1322
1343
|
* @param renderer {@link Renderer}
|
|
1323
1344
|
*/
|
|
1324
1345
|
constructor(materialSystem, renderer) {
|
|
1325
|
-
super("image", renderer);
|
|
1346
|
+
super("image", renderer, logger$9);
|
|
1326
1347
|
/** Textures memory limit in megabytes */
|
|
1327
1348
|
__publicField(this, "memoryLimitMb");
|
|
1328
1349
|
__publicField(this, "packer");
|
|
@@ -1332,7 +1353,7 @@ class ImageSystem extends RenderableSystem {
|
|
|
1332
1353
|
__publicField(this, "scaleMatrix", new Matrix4());
|
|
1333
1354
|
this.materialSystem = materialSystem;
|
|
1334
1355
|
const atlasTextureSize = renderer.context.capabilities.maxTextureSize;
|
|
1335
|
-
|
|
1356
|
+
logger$9.debug(`Max texture size: ${atlasTextureSize}`);
|
|
1336
1357
|
const padding = 1;
|
|
1337
1358
|
this.packer = new MaxRectsPacker(atlasTextureSize, atlasTextureSize, padding, { pot: false });
|
|
1338
1359
|
}
|
|
@@ -1382,10 +1403,10 @@ class ImageSystem extends RenderableSystem {
|
|
|
1382
1403
|
resizeTextures() {
|
|
1383
1404
|
var _a2;
|
|
1384
1405
|
if (!this.memoryLimitMb) {
|
|
1385
|
-
|
|
1406
|
+
logger$9.debug("Memory limit is not set, unable to resize textures.");
|
|
1386
1407
|
return;
|
|
1387
1408
|
}
|
|
1388
|
-
|
|
1409
|
+
logger$9.debug(`Resizing textures to fit memory limit: ${this.memoryLimitMb} MB`);
|
|
1389
1410
|
const texturesToResize = [];
|
|
1390
1411
|
let totalResizable = 0;
|
|
1391
1412
|
let totalNonResizable = 0;
|
|
@@ -1402,15 +1423,15 @@ class ImageSystem extends RenderableSystem {
|
|
|
1402
1423
|
}
|
|
1403
1424
|
const budget = this.memoryLimitMb * 1024 * 1024 - totalNonResizable;
|
|
1404
1425
|
if (budget < 0) {
|
|
1405
|
-
|
|
1426
|
+
logger$9.debug("Memory limit is too low, unable to resize textures.");
|
|
1406
1427
|
return;
|
|
1407
1428
|
}
|
|
1408
1429
|
const resizeFactor = Math.sqrt(budget / totalResizable);
|
|
1409
1430
|
if (resizeFactor >= 1) {
|
|
1410
|
-
|
|
1431
|
+
logger$9.debug("Textures are already within the memory limit, no need to resize");
|
|
1411
1432
|
return;
|
|
1412
1433
|
}
|
|
1413
|
-
|
|
1434
|
+
logger$9.debug(`Resize factor: ${resizeFactor}`);
|
|
1414
1435
|
let newTotal = totalNonResizable;
|
|
1415
1436
|
for (const mesh of texturesToResize) {
|
|
1416
1437
|
const material = mesh.material;
|
|
@@ -1418,14 +1439,14 @@ class ImageSystem extends RenderableSystem {
|
|
|
1418
1439
|
const resizedTexture = resizeTexture(texture, resizeFactor);
|
|
1419
1440
|
const textureDim = `${texture.image.width}x${texture.image.height}`;
|
|
1420
1441
|
const resizedDim = `${resizedTexture.image.width}x${resizedTexture.image.height}`;
|
|
1421
|
-
|
|
1442
|
+
logger$9.debug(`Resized atlas for ${mesh.name || ((_a2 = mesh.parent) == null ? void 0 : _a2.name)}, from ${textureDim} to ${resizedDim}`);
|
|
1422
1443
|
newTotal += getTextureSizeBytes(resizedTexture);
|
|
1423
1444
|
material.map = resizedTexture;
|
|
1424
1445
|
material.needsUpdate = true;
|
|
1425
1446
|
texture.dispose();
|
|
1426
1447
|
mesh.userData["nonResizable"] = true;
|
|
1427
1448
|
}
|
|
1428
|
-
|
|
1449
|
+
logger$9.debug(`New memory usage after resizing: ${newTotal} bytes`);
|
|
1429
1450
|
}
|
|
1430
1451
|
updateDefImpl(imageDef, mesh, instanceIds) {
|
|
1431
1452
|
const bounds = imageDef.bounds;
|
|
@@ -1454,8 +1475,8 @@ class ImageSystem extends RenderableSystem {
|
|
|
1454
1475
|
const boundsArea = boundsWidth * boundsHeight;
|
|
1455
1476
|
const ratio = sourceArea / boundsArea;
|
|
1456
1477
|
if (ratio > 1e3) {
|
|
1457
|
-
|
|
1458
|
-
|
|
1478
|
+
logger$9.debug(`Image bounds: ${boundsWidth}x${boundsHeight}`, `Image source: ${sourceWidth}x${sourceHeight}`);
|
|
1479
|
+
logger$9.warn(`Image bounds area is ${ratio.toFixed(2)} times smaller than the image.`);
|
|
1459
1480
|
}
|
|
1460
1481
|
const rect = new Rectangle(image.source.width, image.source.height);
|
|
1461
1482
|
rect.data = [imageWithIndex];
|
|
@@ -1466,7 +1487,7 @@ class ImageSystem extends RenderableSystem {
|
|
|
1466
1487
|
}
|
|
1467
1488
|
}
|
|
1468
1489
|
this.packer.addArray(rectangles);
|
|
1469
|
-
this.packer.bins.forEach((bin) =>
|
|
1490
|
+
this.packer.bins.forEach((bin) => logger$9.debug(`Bin: ${bin.width}x${bin.height}, ${bin.rects.length} rectangles`));
|
|
1470
1491
|
return this.packer.bins;
|
|
1471
1492
|
}
|
|
1472
1493
|
}
|
|
@@ -1480,7 +1501,7 @@ function createAtlas(bin) {
|
|
|
1480
1501
|
ctx.drawImage(rect.data[0].def.source, rect.x, rect.y, rect.width, rect.height);
|
|
1481
1502
|
}
|
|
1482
1503
|
const t1 = performance.now();
|
|
1483
|
-
|
|
1504
|
+
logger$9.debug(`Create atlas took ${(t1 - t0).toFixed(2)} milliseconds.`);
|
|
1484
1505
|
return createTexture(canvas);
|
|
1485
1506
|
}
|
|
1486
1507
|
function resizeTexture(texture, resizeFactor) {
|
|
@@ -1503,13 +1524,14 @@ function getTextureSizeBytes(texture) {
|
|
|
1503
1524
|
const imageBytes = texture.image.width * texture.image.height * 4 * (texture.generateMipmaps ? 1.33 : 1);
|
|
1504
1525
|
return Math.ceil(imageBytes);
|
|
1505
1526
|
}
|
|
1527
|
+
const logger$8 = createLogger("line");
|
|
1506
1528
|
class LineSystem extends RenderableSystem {
|
|
1507
1529
|
/**
|
|
1508
1530
|
* @param materialSystem {@link MaterialSystem}
|
|
1509
1531
|
* @param renderer {@link Renderer}
|
|
1510
1532
|
*/
|
|
1511
1533
|
constructor(materialSystem, renderer) {
|
|
1512
|
-
super("line", renderer);
|
|
1534
|
+
super("line", renderer, logger$8);
|
|
1513
1535
|
__publicField(this, "lineColor", new Color());
|
|
1514
1536
|
this.materialSystem = materialSystem;
|
|
1515
1537
|
}
|
|
@@ -1698,13 +1720,14 @@ class Polygon {
|
|
|
1698
1720
|
return this;
|
|
1699
1721
|
}
|
|
1700
1722
|
}
|
|
1723
|
+
const logger$7 = createLogger("mesh");
|
|
1701
1724
|
class MeshSystem extends RenderableSystem {
|
|
1702
1725
|
/**
|
|
1703
1726
|
* @param materialSystem {@link MaterialSystem}
|
|
1704
1727
|
* @param renderer {@link Renderer}
|
|
1705
1728
|
*/
|
|
1706
1729
|
constructor(materialSystem, renderer) {
|
|
1707
|
-
super("mesh", renderer);
|
|
1730
|
+
super("mesh", renderer, logger$7);
|
|
1708
1731
|
__publicField(this, "toRgbConverter", converter("rgb"));
|
|
1709
1732
|
__publicField(this, "meshColor", new Color());
|
|
1710
1733
|
this.materialSystem = materialSystem;
|
|
@@ -1815,16 +1838,18 @@ class MeshSystem extends RenderableSystem {
|
|
|
1815
1838
|
});
|
|
1816
1839
|
}
|
|
1817
1840
|
}
|
|
1841
|
+
const logger$6 = createLogger("text");
|
|
1818
1842
|
class TextSystem extends RenderableSystem {
|
|
1819
1843
|
/**
|
|
1820
1844
|
* @param materialSystem {@link MaterialSystem}
|
|
1821
1845
|
* @param renderer {@link Renderer}
|
|
1822
1846
|
*/
|
|
1823
1847
|
constructor(materialSystem, renderer) {
|
|
1824
|
-
super("text", renderer);
|
|
1848
|
+
super("text", renderer, logger$6);
|
|
1825
1849
|
__publicField(this, "initialTextScale", new Vector2(1, -1));
|
|
1826
1850
|
__publicField(this, "textColor", new Color());
|
|
1827
1851
|
__publicField(this, "pendingUpdates", /* @__PURE__ */ new Map());
|
|
1852
|
+
__publicField(this, "sdfAtlases", /* @__PURE__ */ new Set());
|
|
1828
1853
|
__publicField(this, "alignmentOffset", new Vector2());
|
|
1829
1854
|
__publicField(this, "alignmentDirection", new Vector2());
|
|
1830
1855
|
__publicField(this, "localPosition", new Vector2());
|
|
@@ -1834,6 +1859,10 @@ class TextSystem extends RenderableSystem {
|
|
|
1834
1859
|
__publicField(this, "localToMax", new Vector2());
|
|
1835
1860
|
this.materialSystem = materialSystem;
|
|
1836
1861
|
}
|
|
1862
|
+
dispose() {
|
|
1863
|
+
super.dispose();
|
|
1864
|
+
this.sdfAtlases.forEach((texture) => texture.dispose());
|
|
1865
|
+
}
|
|
1837
1866
|
buildLayer(layer) {
|
|
1838
1867
|
const group = new Group();
|
|
1839
1868
|
const batchedText = this.buildBatchedText(layer);
|
|
@@ -1911,7 +1940,12 @@ class TextSystem extends RenderableSystem {
|
|
|
1911
1940
|
for (const { textDef, instanceIds } of mappingData) {
|
|
1912
1941
|
this.registerDefObject(textDef, batchedText, instanceIds);
|
|
1913
1942
|
}
|
|
1914
|
-
batchedText.addEventListener("synccomplete", () =>
|
|
1943
|
+
batchedText.addEventListener("synccomplete", () => {
|
|
1944
|
+
var _a2;
|
|
1945
|
+
const sdfTexture = (_a2 = batchedText.textRenderInfo) == null ? void 0 : _a2.sdfTexture;
|
|
1946
|
+
if (sdfTexture) this.sdfAtlases.add(sdfTexture);
|
|
1947
|
+
this.renderer.update();
|
|
1948
|
+
});
|
|
1915
1949
|
return batchedText;
|
|
1916
1950
|
}
|
|
1917
1951
|
// TODO: Simplify
|
|
@@ -1999,6 +2033,7 @@ function setAnchorsAndAlignment(text, alignment) {
|
|
|
1999
2033
|
text.anchorY = alignment.vertical === "bottom" ? "bottom" : "top";
|
|
2000
2034
|
text.textAlign = alignment.text ?? alignment.horizontal;
|
|
2001
2035
|
}
|
|
2036
|
+
const logger$5 = createLogger("layer");
|
|
2002
2037
|
class LayerSystem {
|
|
2003
2038
|
/**
|
|
2004
2039
|
* @param renderer {@link Renderer}
|
|
@@ -2044,6 +2079,7 @@ class LayerSystem {
|
|
|
2044
2079
|
}
|
|
2045
2080
|
/**
|
|
2046
2081
|
* Update the given defs immediately, or queue them for update if update buffering is enabled.
|
|
2082
|
+
* NOTE: Currently update buffering is disabled, as observed performance gains are negligible. Need to revisit this.
|
|
2047
2083
|
* @param defs {@link RenderableDef} array
|
|
2048
2084
|
*/
|
|
2049
2085
|
updateDefs(defs) {
|
|
@@ -2069,12 +2105,8 @@ class LayerSystem {
|
|
|
2069
2105
|
this.updateDef(def);
|
|
2070
2106
|
processed++;
|
|
2071
2107
|
}
|
|
2072
|
-
const took = performance.now() - startTime;
|
|
2073
|
-
if (processed
|
|
2074
|
-
console.log(
|
|
2075
|
-
`LayerSystem: processed ${processed} defs in ${took.toFixed(2)}ms, ${this.pendingDefs.size} remaining`
|
|
2076
|
-
);
|
|
2077
|
-
}
|
|
2108
|
+
const took = (performance.now() - startTime).toFixed(2);
|
|
2109
|
+
if (processed) logger$5.debug(`processed ${processed} defs in ${took}ms, ${this.pendingDefs.size} remaining`);
|
|
2078
2110
|
return processed > 0;
|
|
2079
2111
|
}
|
|
2080
2112
|
/**
|
|
@@ -2084,11 +2116,8 @@ class LayerSystem {
|
|
|
2084
2116
|
*/
|
|
2085
2117
|
buildScene(sceneDef) {
|
|
2086
2118
|
this.initRenderOrder(sceneDef.rootLayer);
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
"Render order",
|
|
2090
|
-
this.layerDefRenderOrder.map((layer) => this.getFullLayerName(layer))
|
|
2091
|
-
);
|
|
2119
|
+
const renderOrder = this.layerDefRenderOrder.map((layer) => this.getFullLayerName(layer));
|
|
2120
|
+
logger$5.debug("Render order %O", renderOrder);
|
|
2092
2121
|
const rootGroup = new Group();
|
|
2093
2122
|
rootGroup.name = sceneDef.rootLayer.name;
|
|
2094
2123
|
this.mapLayerDefsToObjects.set(sceneDef.rootLayer, rootGroup);
|
|
@@ -2097,7 +2126,7 @@ class LayerSystem {
|
|
|
2097
2126
|
rootGroup.add(this.buildLayer(child));
|
|
2098
2127
|
}
|
|
2099
2128
|
this.updateLayer(sceneDef.rootLayer, false);
|
|
2100
|
-
|
|
2129
|
+
printTree(rootGroup, logger$5.debug);
|
|
2101
2130
|
if (sceneDef.memoryLimit) {
|
|
2102
2131
|
this.imageSystem.memoryLimitMb = sceneDef.memoryLimit;
|
|
2103
2132
|
this.imageSystem.resizeTextures();
|
|
@@ -2141,7 +2170,7 @@ class LayerSystem {
|
|
|
2141
2170
|
}
|
|
2142
2171
|
buildLayer(layerDef, parentPrefix = "") {
|
|
2143
2172
|
const layerFullName = parentPrefix + layerDef.name;
|
|
2144
|
-
|
|
2173
|
+
logger$5.debug(`Building layer ${layerFullName}...`);
|
|
2145
2174
|
let layerObject;
|
|
2146
2175
|
if (isShapeLayer(layerDef)) layerObject = this.meshSystem.buildLayer(layerDef);
|
|
2147
2176
|
else if (isImageLayer(layerDef)) layerObject = this.imageSystem.buildLayer(layerDef);
|
|
@@ -4739,25 +4768,36 @@ const subsetOfTHREE = {
|
|
|
4739
4768
|
Plane
|
|
4740
4769
|
};
|
|
4741
4770
|
CameraControls.install({ THREE: subsetOfTHREE });
|
|
4771
|
+
const logger$4 = createLogger("cameraController");
|
|
4742
4772
|
class CameraController extends CameraControls {
|
|
4743
4773
|
/**
|
|
4744
4774
|
* @param camera {@link PerspectiveCamera} instance
|
|
4745
|
-
* @param renderer {@link Renderer} instance
|
|
4746
4775
|
*/
|
|
4747
|
-
constructor(camera
|
|
4776
|
+
constructor(camera) {
|
|
4748
4777
|
super(camera);
|
|
4749
|
-
this.
|
|
4778
|
+
this.dollyToCursor = true;
|
|
4779
|
+
this.draggingSmoothTime = 0;
|
|
4780
|
+
void this.rotatePolarTo(0, false);
|
|
4781
|
+
this.mouseButtons = {
|
|
4782
|
+
left: CameraController.ACTION.NONE,
|
|
4783
|
+
middle: CameraController.ACTION.NONE,
|
|
4784
|
+
right: CameraController.ACTION.NONE,
|
|
4785
|
+
wheel: CameraController.ACTION.NONE
|
|
4786
|
+
};
|
|
4787
|
+
this.touches = {
|
|
4788
|
+
one: CameraController.ACTION.NONE,
|
|
4789
|
+
two: CameraController.ACTION.NONE,
|
|
4790
|
+
three: CameraController.ACTION.NONE
|
|
4791
|
+
};
|
|
4750
4792
|
}
|
|
4751
4793
|
update(delta) {
|
|
4752
|
-
var _a2;
|
|
4753
4794
|
const needsUpdate = super.update(delta);
|
|
4754
|
-
if (needsUpdate
|
|
4755
|
-
const position = this.camera.position.toArray().map((value) => value.toFixed(2))
|
|
4756
|
-
const target = this._target.toArray().map((value) => value.toFixed(2))
|
|
4757
|
-
const
|
|
4758
|
-
|
|
4759
|
-
|
|
4760
|
-
spherical: [${spherical}]`);
|
|
4795
|
+
if (needsUpdate) {
|
|
4796
|
+
const position = this.camera.position.toArray().map((value) => +value.toFixed(2));
|
|
4797
|
+
const target = this._target.toArray().map((value) => +value.toFixed(2));
|
|
4798
|
+
const { theta, phi, radius } = this._spherical;
|
|
4799
|
+
const spherical = [theta * RAD2DEG, phi * RAD2DEG, radius].map((value) => +value.toFixed(2));
|
|
4800
|
+
logger$4.debug("camera update %O", { position, target, spherical });
|
|
4761
4801
|
}
|
|
4762
4802
|
return needsUpdate;
|
|
4763
4803
|
}
|
|
@@ -4767,31 +4807,30 @@ class CameraSystem {
|
|
|
4767
4807
|
* @param renderer {@link Renderer} instance
|
|
4768
4808
|
*/
|
|
4769
4809
|
constructor(renderer) {
|
|
4770
|
-
/**
|
|
4771
|
-
__publicField(this, "
|
|
4810
|
+
/** {@link PerspectiveCamera} instance. Used to render the scene in internal mode. */
|
|
4811
|
+
__publicField(this, "camera");
|
|
4772
4812
|
/** {@link CameraController} instance. Used to smoothly animate the camera. */
|
|
4773
4813
|
__publicField(this, "controller");
|
|
4774
|
-
|
|
4775
|
-
|
|
4814
|
+
/**
|
|
4815
|
+
* Cached previous viewport height used to preserve zoom across resizes.
|
|
4816
|
+
* Note: we intentionally keep this separate from the derived identity distance.
|
|
4817
|
+
*/
|
|
4818
|
+
__publicField(this, "prevViewportHeightPx");
|
|
4819
|
+
/** [min, max] zoom factors */
|
|
4776
4820
|
__publicField(this, "zoomBounds");
|
|
4821
|
+
/** Default FOV for the camera. Taken from Mapbox GL JS. */
|
|
4822
|
+
__publicField(this, "defaultFov", 36.87);
|
|
4777
4823
|
this.renderer = renderer;
|
|
4778
|
-
const
|
|
4779
|
-
this.
|
|
4824
|
+
const h = renderer.size[1];
|
|
4825
|
+
this.prevViewportHeightPx = h;
|
|
4826
|
+
this.camera = new PerspectiveCamera(this.defaultFov);
|
|
4780
4827
|
this.camera.up.set(0, 0, -1);
|
|
4781
|
-
this.
|
|
4782
|
-
this.camera.position.z = this.zoomIdentityDistance;
|
|
4783
|
-
this.controller = new CameraController(this.camera, renderer);
|
|
4784
|
-
this.controller.polarAngle = 0;
|
|
4828
|
+
this.controller = new CameraController(this.camera);
|
|
4785
4829
|
this.controller.distance = this.zoomIdentityDistance;
|
|
4786
4830
|
}
|
|
4787
|
-
/** Current camera instance. */
|
|
4788
|
-
get currentCamera() {
|
|
4789
|
-
return this.externalCamera ?? this.camera;
|
|
4790
|
-
}
|
|
4791
4831
|
/** Current camera zoom factor. */
|
|
4792
4832
|
get zoomFactor() {
|
|
4793
|
-
|
|
4794
|
-
return distance ? this.zoomIdentityDistance / distance : 1;
|
|
4833
|
+
return this.zoomFactorForHeight(this.renderer.size[1]);
|
|
4795
4834
|
}
|
|
4796
4835
|
/**
|
|
4797
4836
|
* Calculates the camera distance from the scene's plane for a given zoom factor.
|
|
@@ -4799,7 +4838,8 @@ class CameraSystem {
|
|
|
4799
4838
|
* @returns Corresponding camera distance on the Z axis
|
|
4800
4839
|
*/
|
|
4801
4840
|
zoomFactorToDistance(zoomFactor) {
|
|
4802
|
-
|
|
4841
|
+
if (zoomFactor <= 0) return this.zoomIdentityDistance;
|
|
4842
|
+
return this.zoomIdentityDistance / zoomFactor;
|
|
4803
4843
|
}
|
|
4804
4844
|
/**
|
|
4805
4845
|
* Initializes the camera with the given zoom bounds.
|
|
@@ -4813,41 +4853,189 @@ class CameraSystem {
|
|
|
4813
4853
|
updateCamera() {
|
|
4814
4854
|
if (!this.zoomBounds) return;
|
|
4815
4855
|
const [w, h] = this.renderer.size;
|
|
4816
|
-
|
|
4817
|
-
|
|
4818
|
-
const
|
|
4819
|
-
const
|
|
4856
|
+
if (w <= 0 || h <= 0) return;
|
|
4857
|
+
const zoomFactor = this.zoomFactorForHeight(this.prevViewportHeightPx);
|
|
4858
|
+
const newZoomIdentity = this.zoomIdentityDistanceForHeight(h);
|
|
4859
|
+
const maxDistance = Math.abs(newZoomIdentity / this.zoomBounds[0]);
|
|
4860
|
+
const minDistance = Math.abs(newZoomIdentity / this.zoomBounds[1]);
|
|
4820
4861
|
this.camera.aspect = w / (h || 1);
|
|
4821
|
-
this.
|
|
4862
|
+
this.camera.near = 0.01;
|
|
4863
|
+
this.camera.far = Math.max(maxDistance, this.camera.near) * 2;
|
|
4822
4864
|
this.camera.updateProjectionMatrix();
|
|
4823
|
-
this.syncController(minDistance, maxDistance, zoomFactor);
|
|
4824
|
-
}
|
|
4825
|
-
computeCameraClipPlanes(minDistance, maxDistance, nearSafetyFactor = 0.5, farSafetyFactor = 1.5) {
|
|
4826
|
-
const fov = this.camera.fov * DEG2RAD$1;
|
|
4827
|
-
const aspect = this.camera.aspect;
|
|
4828
|
-
const maxPolarAngle = 85 * DEG2RAD$1;
|
|
4829
|
-
const halfFovY = fov / 2;
|
|
4830
|
-
const halfFovX = Math.atan(Math.tan(halfFovY) * aspect);
|
|
4831
|
-
const diagonalFov = 2 * Math.atan(Math.sqrt(Math.tan(halfFovX) ** 2 + Math.tan(halfFovY) ** 2));
|
|
4832
|
-
const minHeight = minDistance * Math.cos(maxPolarAngle);
|
|
4833
|
-
const near = Math.max(0.1, minHeight * nearSafetyFactor);
|
|
4834
|
-
const criticalHeight = minHeight;
|
|
4835
|
-
const horizontalDistToOrbit = minDistance * Math.sin(maxPolarAngle);
|
|
4836
|
-
const distToOrbit = minDistance;
|
|
4837
|
-
const visibleRadiusAtOrbit = distToOrbit * Math.tan(diagonalFov / 2);
|
|
4838
|
-
const planeExtent = Math.max(maxDistance, visibleRadiusAtOrbit);
|
|
4839
|
-
const horizontalDistToFarEdge = horizontalDistToOrbit + planeExtent;
|
|
4840
|
-
const maxViewDistance = Math.sqrt(criticalHeight ** 2 + horizontalDistToFarEdge ** 2);
|
|
4841
|
-
const far = maxViewDistance * farSafetyFactor;
|
|
4842
|
-
this.camera.near = near;
|
|
4843
|
-
this.camera.far = far;
|
|
4844
|
-
if (this.renderer.debugLog) console.log("camera clip planes", near, far);
|
|
4845
|
-
}
|
|
4846
|
-
syncController(minDistance, maxDistance, zoomFactor) {
|
|
4847
|
-
if (this.renderer.debugLog) console.log("syncController", minDistance, maxDistance, zoomFactor);
|
|
4848
4865
|
this.controller.minDistance = minDistance;
|
|
4849
4866
|
this.controller.maxDistance = maxDistance;
|
|
4850
4867
|
void this.controller.dollyTo(this.zoomFactorToDistance(zoomFactor), false);
|
|
4868
|
+
this.prevViewportHeightPx = h;
|
|
4869
|
+
}
|
|
4870
|
+
/**
|
|
4871
|
+
* Distance from the scene plane corresponding to zoomFactor = 1 for the current viewport height.
|
|
4872
|
+
* Derived from camera FOV and renderer height (in pixels).
|
|
4873
|
+
*/
|
|
4874
|
+
get zoomIdentityDistance() {
|
|
4875
|
+
return this.zoomIdentityDistanceForHeight(this.renderer.size[1]);
|
|
4876
|
+
}
|
|
4877
|
+
/**
|
|
4878
|
+
* Calculates the zoom identity distance for a given viewport height.
|
|
4879
|
+
* @param viewportHeightPx Renderer height in pixels
|
|
4880
|
+
* @returns Zoom identity distance
|
|
4881
|
+
*/
|
|
4882
|
+
zoomIdentityDistanceForHeight(viewportHeightPx) {
|
|
4883
|
+
if (viewportHeightPx <= 0) return 0;
|
|
4884
|
+
return viewportHeightPx * 0.5 / Math.tan(this.camera.fov * DEG2RAD$1 / 2);
|
|
4885
|
+
}
|
|
4886
|
+
/**
|
|
4887
|
+
* Calculates the zoom factor for a given viewport height.
|
|
4888
|
+
* @param viewportHeightPx Renderer height in pixels
|
|
4889
|
+
* @returns Zoom factor
|
|
4890
|
+
*/
|
|
4891
|
+
zoomFactorForHeight(viewportHeightPx) {
|
|
4892
|
+
const zid = this.zoomIdentityDistanceForHeight(viewportHeightPx);
|
|
4893
|
+
if (zid === 0) return 1;
|
|
4894
|
+
return zid / (this.controller.distance || zid);
|
|
4895
|
+
}
|
|
4896
|
+
}
|
|
4897
|
+
const logger$3 = createLogger("external");
|
|
4898
|
+
class ExternalSystem {
|
|
4899
|
+
/**
|
|
4900
|
+
* @param pickingSystem {@link PickingSystem} instance
|
|
4901
|
+
*/
|
|
4902
|
+
constructor(pickingSystem) {
|
|
4903
|
+
/** External camera instance */
|
|
4904
|
+
__publicField(this, "camera", new Camera());
|
|
4905
|
+
__publicField(this, "staticTransformMatrix", new Matrix4());
|
|
4906
|
+
__publicField(this, "intersectionPoint", new Vector3());
|
|
4907
|
+
/**
|
|
4908
|
+
* Scratch NDC coordinate used by external ptScale. Kept as a field to avoid allocating a Vector2 each frame.
|
|
4909
|
+
* (This is always screen center: NDC (0,0))
|
|
4910
|
+
*/
|
|
4911
|
+
__publicField(this, "ndcCenter", new Vector2(0, 0));
|
|
4912
|
+
/**
|
|
4913
|
+
* Scratch clip-space vector used when projecting SVG points through the external camera matrix.
|
|
4914
|
+
* Kept as a field to avoid allocating a Vector4 each frame.
|
|
4915
|
+
*/
|
|
4916
|
+
__publicField(this, "clipPoint", new Vector4());
|
|
4917
|
+
/**
|
|
4918
|
+
* Scratch pixel points used by external ptScale estimation.
|
|
4919
|
+
* p0 is the screen center in drawing-buffer pixels; p1/p2 are projected offsets from the anchor point.
|
|
4920
|
+
*/
|
|
4921
|
+
__publicField(this, "px0", new Vector2());
|
|
4922
|
+
__publicField(this, "px1", new Vector2());
|
|
4923
|
+
__publicField(this, "px2", new Vector2());
|
|
4924
|
+
this.pickingSystem = pickingSystem;
|
|
4925
|
+
}
|
|
4926
|
+
/**
|
|
4927
|
+
* Set static part of an svg -> px transform matrix
|
|
4928
|
+
* @param staticTransformMatrix static transform matrix to apply to the scene
|
|
4929
|
+
*/
|
|
4930
|
+
setStaticTransform(staticTransformMatrix) {
|
|
4931
|
+
if (!this.validateMatrix(staticTransformMatrix, "setStaticTransform")) return;
|
|
4932
|
+
this.staticTransformMatrix.fromArray(staticTransformMatrix);
|
|
4933
|
+
}
|
|
4934
|
+
/**
|
|
4935
|
+
* Set dynamic part of an svg -> px transform matrix. Should be called every frame.
|
|
4936
|
+
* @param dynamicTransformMatrix dynamic transform matrix (changes every frame)
|
|
4937
|
+
*/
|
|
4938
|
+
setDynamicTransform(dynamicTransformMatrix) {
|
|
4939
|
+
if (!this.validateMatrix(dynamicTransformMatrix, "setDynamicTransform")) return;
|
|
4940
|
+
this.camera.projectionMatrix.fromArray(dynamicTransformMatrix).multiply(this.staticTransformMatrix);
|
|
4941
|
+
this.camera.projectionMatrixInverse.copy(this.camera.projectionMatrix).invert();
|
|
4942
|
+
}
|
|
4943
|
+
/**
|
|
4944
|
+
* Estimates pixel→SVG scale for external context rendering (e.g. Mapbox).
|
|
4945
|
+
*
|
|
4946
|
+
* In external mode we don't own a camera rig/controller, so we can't derive scale from "camera distance".
|
|
4947
|
+
* Instead, we:
|
|
4948
|
+
* 1) Find the point on the SVG plane that is currently under the screen center.
|
|
4949
|
+
* 2) Measure how much the screen position changes when moving 1 SVG unit in X/Y around that point.
|
|
4950
|
+
* 3) Convert that local plane→screen mapping into a single "zoom-like" scalar that is stable under tilt+rotate.
|
|
4951
|
+
*
|
|
4952
|
+
* This matches internal mode semantics better because the internal orbit target is also kept under screen center.
|
|
4953
|
+
* @param viewportSize Size of the viewport in drawing-buffer pixels
|
|
4954
|
+
* @returns Pixel-to-SVG scale factor (px → svg units)
|
|
4955
|
+
*/
|
|
4956
|
+
pxToSvgScale(viewportSize) {
|
|
4957
|
+
const M = this.camera.projectionMatrix;
|
|
4958
|
+
const [viewportW, viewportH] = viewportSize;
|
|
4959
|
+
if (viewportW <= 0 || viewportH <= 0) return;
|
|
4960
|
+
const intersectionPoint = this.pickingSystem.intersectPlane(this.ndcCenter, this.camera, this.intersectionPoint);
|
|
4961
|
+
if (!intersectionPoint) return;
|
|
4962
|
+
const anchorX = intersectionPoint.x;
|
|
4963
|
+
const anchorY = intersectionPoint.y;
|
|
4964
|
+
const clip = this.clipPoint;
|
|
4965
|
+
const p0 = this.px0.set(viewportW * 0.5, viewportH * 0.5);
|
|
4966
|
+
const p1 = this.px1;
|
|
4967
|
+
const p2 = this.px2;
|
|
4968
|
+
const svgToPixels = (x, y, out) => {
|
|
4969
|
+
clip.set(x, y, 0, 1).applyMatrix4(M);
|
|
4970
|
+
if (clip.w === 0) return false;
|
|
4971
|
+
const ndcX = clip.x / clip.w;
|
|
4972
|
+
const ndcY = clip.y / clip.w;
|
|
4973
|
+
out.set((ndcX + 1) * 0.5 * viewportW, (1 - ndcY) * 0.5 * viewportH);
|
|
4974
|
+
return true;
|
|
4975
|
+
};
|
|
4976
|
+
const svgStep = 1;
|
|
4977
|
+
if (!svgToPixels(anchorX + svgStep, anchorY, p1)) return;
|
|
4978
|
+
if (!svgToPixels(anchorX, anchorY + svgStep, p2)) return;
|
|
4979
|
+
const pxDeltaPerSvgX = p1.sub(p0).divideScalar(svgStep);
|
|
4980
|
+
const pxDeltaPerSvgY = p2.sub(p0).divideScalar(svgStep);
|
|
4981
|
+
const pixelsSqPerSvgX = pxDeltaPerSvgX.dot(pxDeltaPerSvgX);
|
|
4982
|
+
const pixelsSqPerSvgY = pxDeltaPerSvgY.dot(pxDeltaPerSvgY);
|
|
4983
|
+
const pixelsSqCross = pxDeltaPerSvgX.dot(pxDeltaPerSvgY);
|
|
4984
|
+
const sumPixelsSq = pixelsSqPerSvgX + pixelsSqPerSvgY;
|
|
4985
|
+
const areaPixelsSq = pixelsSqPerSvgX * pixelsSqPerSvgY - pixelsSqCross * pixelsSqCross;
|
|
4986
|
+
const maxStretchDiscriminant = Math.max(0, sumPixelsSq * sumPixelsSq - 4 * areaPixelsSq);
|
|
4987
|
+
const maxPixelsSqPerSvg = 0.5 * (sumPixelsSq + Math.sqrt(maxStretchDiscriminant));
|
|
4988
|
+
const pxPerSvg = Math.sqrt(maxPixelsSqPerSvg);
|
|
4989
|
+
if (!Number.isFinite(pxPerSvg) || pxPerSvg <= 0) return;
|
|
4990
|
+
return 1 / pxPerSvg;
|
|
4991
|
+
}
|
|
4992
|
+
validateMatrix(matrix, name) {
|
|
4993
|
+
if (matrix.length !== 16) {
|
|
4994
|
+
logger$3.warn(`${name}: Matrix must be 16 elements long`);
|
|
4995
|
+
return false;
|
|
4996
|
+
}
|
|
4997
|
+
return true;
|
|
4998
|
+
}
|
|
4999
|
+
}
|
|
5000
|
+
class PickingSystem {
|
|
5001
|
+
/** */
|
|
5002
|
+
constructor() {
|
|
5003
|
+
__publicField(this, "raycaster", new Raycaster());
|
|
5004
|
+
__publicField(this, "ndcPoint", new Vector2());
|
|
5005
|
+
__publicField(this, "viewboxPlane", new Plane(new Vector3(0, 0, 1), 0));
|
|
5006
|
+
this.raycaster.layers.set(INTERACTIVE_LAYER);
|
|
5007
|
+
}
|
|
5008
|
+
/**
|
|
5009
|
+
* Gets the objects intersected by the raycaster.
|
|
5010
|
+
* @param ndcCoords raycast point in NDC (normalized device coordinates)
|
|
5011
|
+
* @param scene {@link Scene} instance
|
|
5012
|
+
* @param camera {@link Camera} instance
|
|
5013
|
+
* @returns Array of {@link Intersection} instances
|
|
5014
|
+
*/
|
|
5015
|
+
getIntersectedObjects(ndcCoords, scene, camera) {
|
|
5016
|
+
this.setRaycasterFromCamera(ndcCoords, camera);
|
|
5017
|
+
const intersections = this.raycaster.intersectObject(scene, true);
|
|
5018
|
+
return intersections.filter((i) => isVisible(i.object));
|
|
5019
|
+
}
|
|
5020
|
+
/**
|
|
5021
|
+
* Intersects the xy-plane with the raycaster.
|
|
5022
|
+
* @param ndcCoords raycast point in NDC (normalized device coordinates
|
|
5023
|
+
* @param camera {@link Camera} instance
|
|
5024
|
+
* @param out Output vector
|
|
5025
|
+
* @returns Intersection point in world space or null if no intersection.
|
|
5026
|
+
*/
|
|
5027
|
+
intersectPlane(ndcCoords, camera, out) {
|
|
5028
|
+
this.setRaycasterFromCamera(ndcCoords, camera);
|
|
5029
|
+
return this.raycaster.ray.intersectPlane(this.viewboxPlane, out) ?? void 0;
|
|
5030
|
+
}
|
|
5031
|
+
setRaycasterFromCamera(ndcCoords, camera) {
|
|
5032
|
+
if (camera.isPerspectiveCamera || camera.isOrthographicCamera) {
|
|
5033
|
+
this.ndcPoint.set(ndcCoords.x, ndcCoords.y);
|
|
5034
|
+
this.raycaster.setFromCamera(this.ndcPoint, camera);
|
|
5035
|
+
} else {
|
|
5036
|
+
this.raycaster.ray.origin.set(0, 0, 0).unproject(camera);
|
|
5037
|
+
this.raycaster.ray.direction.set(ndcCoords.x, ndcCoords.y, 1).unproject(camera).sub(this.raycaster.ray.origin).normalize();
|
|
5038
|
+
}
|
|
4851
5039
|
}
|
|
4852
5040
|
}
|
|
4853
5041
|
class SceneSystem {
|
|
@@ -4857,29 +5045,22 @@ class SceneSystem {
|
|
|
4857
5045
|
constructor(renderer) {
|
|
4858
5046
|
/** {@link Scene} instance */
|
|
4859
5047
|
__publicField(this, "scene");
|
|
4860
|
-
/** World matrix -
|
|
5048
|
+
/** World matrix - model → world transform */
|
|
4861
5049
|
__publicField(this, "worldMatrix", new Matrix4());
|
|
4862
|
-
/** Inverse world matrix -
|
|
5050
|
+
/** Inverse world matrix - world → model transform */
|
|
4863
5051
|
__publicField(this, "inverseWorldMatrix", new Matrix4());
|
|
5052
|
+
__publicField(this, "tempVector3", new Vector3());
|
|
4864
5053
|
__publicField(this, "translationMatrix", new Matrix4());
|
|
4865
5054
|
__publicField(this, "scaleMatrix", new Matrix4());
|
|
4866
|
-
__publicField(this, "scaleVector", new Vector3());
|
|
4867
5055
|
__publicField(this, "visibleRectOffsetMatrix", new Matrix4());
|
|
4868
5056
|
__publicField(this, "viewbox");
|
|
4869
5057
|
this.renderer = renderer;
|
|
4870
5058
|
this.scene = new Scene();
|
|
4871
5059
|
this.scene.matrixAutoUpdate = false;
|
|
4872
5060
|
}
|
|
4873
|
-
/** Scene scale factor (
|
|
5061
|
+
/** Scene scale factor (model space to world space) */
|
|
4874
5062
|
get scaleFactor() {
|
|
4875
|
-
this.
|
|
4876
|
-
if (this.scaleVector.z === 1) {
|
|
4877
|
-
return this.scaleVector.x;
|
|
4878
|
-
} else {
|
|
4879
|
-
const perspectiveW = this.scene.matrix.elements[15];
|
|
4880
|
-
const halfViewportWidth = this.renderer.size[0] / 2;
|
|
4881
|
-
return halfViewportWidth * this.scaleVector.x / perspectiveW;
|
|
4882
|
-
}
|
|
5063
|
+
return this.scene.matrix.elements[0];
|
|
4883
5064
|
}
|
|
4884
5065
|
/**
|
|
4885
5066
|
* Initializes the scene with the given SVG viewbox.
|
|
@@ -4889,28 +5070,48 @@ class SceneSystem {
|
|
|
4889
5070
|
this.viewbox = viewbox;
|
|
4890
5071
|
this.updateScene();
|
|
4891
5072
|
}
|
|
4892
|
-
/**
|
|
4893
|
-
* Updates the scene transform when the renderer size changes.
|
|
4894
|
-
*/
|
|
5073
|
+
/** Updates the scene transform from the current viewbox and renderer size. */
|
|
4895
5074
|
updateScene() {
|
|
4896
5075
|
if (!this.viewbox) return;
|
|
5076
|
+
this.composeMatrices(this.viewbox);
|
|
5077
|
+
}
|
|
5078
|
+
/**
|
|
5079
|
+
* Converts a point from model coordinates to world coordinates.
|
|
5080
|
+
* @param modelCoords Point in model coordinates
|
|
5081
|
+
* @param out Output vector
|
|
5082
|
+
* @returns Point in world coordinates
|
|
5083
|
+
*/
|
|
5084
|
+
modelToWorld(modelCoords, out) {
|
|
5085
|
+
const worldPoint = this.tempVector3.set(modelCoords.x, modelCoords.y, 0).applyMatrix4(this.worldMatrix);
|
|
5086
|
+
out.set(worldPoint.x, worldPoint.y, 0);
|
|
5087
|
+
return out;
|
|
5088
|
+
}
|
|
5089
|
+
/**
|
|
5090
|
+
* Converts a point from world coordinates to model coordinates. Z axis is ignored.
|
|
5091
|
+
* @param worldCoords Point in world coordinates
|
|
5092
|
+
* @param out Output vector
|
|
5093
|
+
* @returns Point in SVG coordinates
|
|
5094
|
+
*/
|
|
5095
|
+
worldToModel(worldCoords, out) {
|
|
5096
|
+
const modelPoint = this.tempVector3.copy(worldCoords).applyMatrix4(this.inverseWorldMatrix);
|
|
5097
|
+
out.set(modelPoint.x, modelPoint.y);
|
|
5098
|
+
return out;
|
|
5099
|
+
}
|
|
5100
|
+
composeMatrices(viewbox) {
|
|
4897
5101
|
const dpr = this.renderer.context.getPixelRatio();
|
|
4898
5102
|
const visibleRect = this.renderer.visibleRect;
|
|
4899
|
-
const [viewBoxWidth, viewBoxHeight] =
|
|
5103
|
+
const [viewBoxWidth, viewBoxHeight] = viewbox.size;
|
|
4900
5104
|
const [visibleRectWidth, visibleRectHeight] = (visibleRect == null ? void 0 : visibleRect.size.clone().multiplyScalar(dpr)) ?? this.renderer.size;
|
|
4901
5105
|
const scaleFactor = Math.min(visibleRectWidth / viewBoxWidth, visibleRectHeight / viewBoxHeight);
|
|
4902
|
-
const [centerX, centerY] =
|
|
5106
|
+
const [centerX, centerY] = viewbox.center;
|
|
4903
5107
|
this.translationMatrix.makeTranslation(-centerX, -centerY, 0);
|
|
4904
5108
|
this.scaleMatrix.makeScale(scaleFactor, scaleFactor, 1);
|
|
4905
5109
|
if (visibleRect) {
|
|
4906
5110
|
const visibleRectCenter = visibleRect.center.clone().multiplyScalar(dpr);
|
|
4907
|
-
const canvasCenter =
|
|
5111
|
+
const canvasCenter = { x: this.renderer.size[0] / 2, y: this.renderer.size[1] / 2 };
|
|
4908
5112
|
const offset = visibleRectCenter.sub(canvasCenter);
|
|
4909
5113
|
this.visibleRectOffsetMatrix.makeTranslation(offset.x, offset.y, 0);
|
|
4910
5114
|
}
|
|
4911
|
-
this.composeMatrices();
|
|
4912
|
-
}
|
|
4913
|
-
composeMatrices() {
|
|
4914
5115
|
this.worldMatrix.copy(this.translationMatrix).premultiply(this.scaleMatrix);
|
|
4915
5116
|
if (this.renderer.visibleRect) this.worldMatrix.premultiply(this.visibleRectOffsetMatrix);
|
|
4916
5117
|
this.scene.matrix.copy(this.worldMatrix);
|
|
@@ -4918,25 +5119,25 @@ class SceneSystem {
|
|
|
4918
5119
|
this.scene.matrixWorldNeedsUpdate = true;
|
|
4919
5120
|
}
|
|
4920
5121
|
}
|
|
5122
|
+
const logger$2 = createLogger("viewport");
|
|
4921
5123
|
class ViewportSystem {
|
|
4922
5124
|
/**
|
|
4923
5125
|
* @param renderer {@link Renderer} instance
|
|
4924
5126
|
* @param eventSystem {@link EventSystem} instance
|
|
4925
5127
|
*/
|
|
4926
5128
|
constructor(renderer, eventSystem) {
|
|
5129
|
+
__publicField(this, "pickingSystem");
|
|
5130
|
+
__publicField(this, "externalSystem");
|
|
4927
5131
|
__publicField(this, "sceneSystem");
|
|
4928
5132
|
__publicField(this, "cameraSystem");
|
|
4929
|
-
__publicField(this, "raycaster", new Raycaster());
|
|
4930
|
-
__publicField(this, "intersectionPoint", new Vector3());
|
|
4931
|
-
__publicField(this, "viewboxPlane", new Plane(new Vector3(0, 0, 1), 0));
|
|
4932
5133
|
__publicField(this, "pxToSvgScaleThreshold", 1e-4);
|
|
4933
5134
|
__publicField(this, "prevPxToSvgScale");
|
|
4934
|
-
__publicField(this, "externalStaticTransformMatrix", new Matrix4());
|
|
4935
5135
|
this.renderer = renderer;
|
|
4936
5136
|
this.eventSystem = eventSystem;
|
|
5137
|
+
this.pickingSystem = new PickingSystem();
|
|
4937
5138
|
this.sceneSystem = new SceneSystem(renderer);
|
|
4938
5139
|
this.cameraSystem = new CameraSystem(renderer);
|
|
4939
|
-
this.
|
|
5140
|
+
this.externalSystem = new ExternalSystem(this.pickingSystem);
|
|
4940
5141
|
}
|
|
4941
5142
|
/** {@link Scene} instance */
|
|
4942
5143
|
get scene() {
|
|
@@ -4944,10 +5145,10 @@ class ViewportSystem {
|
|
|
4944
5145
|
}
|
|
4945
5146
|
/** Current {@link Camera} instance */
|
|
4946
5147
|
get camera() {
|
|
4947
|
-
return this.cameraSystem.
|
|
5148
|
+
return this.renderer.isExternalMode ? this.externalSystem.camera : this.cameraSystem.camera;
|
|
4948
5149
|
}
|
|
4949
5150
|
/** {@link CameraController} instance */
|
|
4950
|
-
get
|
|
5151
|
+
get controller() {
|
|
4951
5152
|
return this.cameraSystem.controller;
|
|
4952
5153
|
}
|
|
4953
5154
|
/** Current camera zoom factor. */
|
|
@@ -4960,14 +5161,22 @@ class ViewportSystem {
|
|
|
4960
5161
|
}
|
|
4961
5162
|
/** Pixel to SVG scale factor */
|
|
4962
5163
|
get pxToSvgScale() {
|
|
4963
|
-
return 1 / (this.scaleFactor * this.zoomFactor);
|
|
5164
|
+
return this.renderer.isExternalMode ? this.externalSystem.pxToSvgScale(this.renderer.size) ?? this.prevPxToSvgScale ?? 1 : 1 / (this.scaleFactor * this.zoomFactor);
|
|
5165
|
+
}
|
|
5166
|
+
/**
|
|
5167
|
+
* Get bearing angle between current camera orientation and true north (in radians).
|
|
5168
|
+
* Angle is in range [0, 2π), going clockwise from north.
|
|
5169
|
+
*/
|
|
5170
|
+
get bearing() {
|
|
5171
|
+
const tau = Math.PI * 2;
|
|
5172
|
+
return MathUtils.euclideanModulo(-this.controller.azimuthAngle, tau);
|
|
4964
5173
|
}
|
|
4965
5174
|
/**
|
|
4966
5175
|
* Initializes the viewport and zoom bounds with the given scene definition.
|
|
4967
5176
|
* @param sceneDef {@link SceneDef} scene definition
|
|
4968
5177
|
*/
|
|
4969
5178
|
initViewport(sceneDef) {
|
|
4970
|
-
this.sceneSystem.initScene(sceneDef.viewbox);
|
|
5179
|
+
if (!this.renderer.isExternalMode) this.sceneSystem.initScene(sceneDef.viewbox);
|
|
4971
5180
|
this.cameraSystem.initCamera([0.1, sceneDef.viewbox.size.width > 1e5 ? 100 : 35]);
|
|
4972
5181
|
}
|
|
4973
5182
|
/** Updates the viewport when the renderer size changes. */
|
|
@@ -4981,77 +5190,110 @@ class ViewportSystem {
|
|
|
4981
5190
|
updatePtScale() {
|
|
4982
5191
|
const pxToSvgScale = this.pxToSvgScale;
|
|
4983
5192
|
if (Math.abs(pxToSvgScale - (this.prevPxToSvgScale ?? 0)) < this.pxToSvgScaleThreshold) return;
|
|
4984
|
-
|
|
5193
|
+
logger$2.debug(`pxToSvgScale ${+pxToSvgScale.toFixed(3)}`);
|
|
4985
5194
|
this.eventSystem.emit("viewport:ptscale", pxToSvgScale);
|
|
4986
5195
|
this.prevPxToSvgScale = pxToSvgScale;
|
|
4987
5196
|
}
|
|
4988
5197
|
/**
|
|
4989
5198
|
* Gets the objects intersected by the raycaster.
|
|
4990
|
-
* @param
|
|
5199
|
+
* @param ndcCoords raycast point in NDC (normalized device coordinates)
|
|
4991
5200
|
* @returns Array of {@link Intersection} instances
|
|
4992
5201
|
*/
|
|
4993
|
-
getIntersectedObjects(
|
|
4994
|
-
|
|
4995
|
-
this.raycaster.setFromCamera(normalizedCoords, camera);
|
|
4996
|
-
const intersections = this.raycaster.intersectObject(scene, true).filter((i) => isVisible(i.object));
|
|
4997
|
-
return intersections;
|
|
5202
|
+
getIntersectedObjects(ndcCoords) {
|
|
5203
|
+
return this.pickingSystem.getIntersectedObjects(ndcCoords, this.scene, this.camera);
|
|
4998
5204
|
}
|
|
4999
5205
|
/**
|
|
5000
|
-
* Converts a point from
|
|
5001
|
-
* @param
|
|
5206
|
+
* Converts a point from model coordinates to world coordinates.
|
|
5207
|
+
* @param modelCoords Point in model coordinates
|
|
5208
|
+
* @param out Optional output vector
|
|
5002
5209
|
* @returns Point in world coordinates
|
|
5003
5210
|
*/
|
|
5004
|
-
|
|
5005
|
-
|
|
5006
|
-
svg3D.applyMatrix4(this.sceneSystem.worldMatrix);
|
|
5007
|
-
return new Vector2(svg3D.x, svg3D.y);
|
|
5211
|
+
modelToWorld(modelCoords, out = new Vector3()) {
|
|
5212
|
+
return this.sceneSystem.modelToWorld(modelCoords, out);
|
|
5008
5213
|
}
|
|
5009
5214
|
/**
|
|
5010
|
-
* Converts a point from world coordinates to
|
|
5215
|
+
* Converts a point from world coordinates to model coordinates. Z axis is ignored.
|
|
5011
5216
|
* @param worldCoords Point in world coordinates
|
|
5012
|
-
* @
|
|
5217
|
+
* @param out Optional output vector
|
|
5218
|
+
* @returns Point in model coordinates
|
|
5013
5219
|
*/
|
|
5014
|
-
|
|
5015
|
-
|
|
5016
|
-
return new Vector2(svgCoords.x, svgCoords.y);
|
|
5220
|
+
worldToModel(worldCoords, out = new Vector2()) {
|
|
5221
|
+
return this.sceneSystem.worldToModel(worldCoords, out);
|
|
5017
5222
|
}
|
|
5018
5223
|
/**
|
|
5019
|
-
* Converts a point from screen coordinates to
|
|
5020
|
-
* @param
|
|
5021
|
-
* @param
|
|
5022
|
-
* @returns Point in
|
|
5224
|
+
* Converts a point from screen coordinates to world space.
|
|
5225
|
+
* @param ndcCoords Point in NDC (normalized device coordinates)
|
|
5226
|
+
* @param out Optional output vector
|
|
5227
|
+
* @returns Point in world space
|
|
5023
5228
|
*/
|
|
5024
|
-
|
|
5025
|
-
this.
|
|
5026
|
-
this.raycaster.ray.intersectPlane(this.viewboxPlane, this.intersectionPoint);
|
|
5027
|
-
if (space === "svg") this.intersectionPoint.applyMatrix4(this.sceneSystem.inverseWorldMatrix);
|
|
5028
|
-
return { x: this.intersectionPoint.x, y: this.intersectionPoint.y };
|
|
5229
|
+
ndcToWorld(ndcCoords, out = new Vector3()) {
|
|
5230
|
+
return this.pickingSystem.intersectPlane(ndcCoords, this.camera, out);
|
|
5029
5231
|
}
|
|
5030
5232
|
/**
|
|
5031
|
-
*
|
|
5032
|
-
*
|
|
5033
|
-
* @
|
|
5233
|
+
* Convert canvas coordinates (relative to the canvas's top left corner)
|
|
5234
|
+
* to NDC (normalized device coordinates).
|
|
5235
|
+
* @param point object defining the coordinates relative to the canvas's top left corner
|
|
5236
|
+
* @param out Optional output vector
|
|
5237
|
+
* @returns Point in NDC space
|
|
5034
5238
|
*/
|
|
5035
|
-
|
|
5036
|
-
|
|
5239
|
+
canvasToNDC(point, out = new Vector2()) {
|
|
5240
|
+
const [w, h] = this.renderer.size;
|
|
5241
|
+
if (w <= 0 || h <= 0) {
|
|
5242
|
+
logger$2.warn("canvasToNDC: renderer size is 0");
|
|
5243
|
+
return out.set(0, 0);
|
|
5244
|
+
}
|
|
5245
|
+
const dpr = this.renderer.context.getPixelRatio();
|
|
5246
|
+
const uv = [point.x / (w / dpr), point.y / (h / dpr)];
|
|
5247
|
+
const ndc = [uv[0] * 2 - 1, -uv[1] * 2 + 1];
|
|
5248
|
+
return out.set(MathUtils.clamp(ndc[0], -1, 1), MathUtils.clamp(ndc[1], -1, 1));
|
|
5249
|
+
}
|
|
5250
|
+
/**
|
|
5251
|
+
* Convert canvas coordinates (CSS pixels, relative to canvas top-left) to SVG coordinates.
|
|
5252
|
+
* @param point point in canvas space (CSS pixels)
|
|
5253
|
+
* @returns point in SVG coordinates or undefined if point is outside the SVG plane
|
|
5254
|
+
*/
|
|
5255
|
+
canvasToSvg(point) {
|
|
5256
|
+
const vec2 = new Vector2();
|
|
5257
|
+
const vec3 = new Vector3();
|
|
5258
|
+
const ndcPoint = this.canvasToNDC(point, vec2);
|
|
5259
|
+
const worldPoint = this.ndcToWorld(ndcPoint, vec3);
|
|
5260
|
+
if (!worldPoint) return;
|
|
5261
|
+
return this.worldToModel(worldPoint, vec2);
|
|
5037
5262
|
}
|
|
5038
5263
|
/**
|
|
5039
|
-
*
|
|
5264
|
+
* Set static part of an svg -> px transform matrix
|
|
5040
5265
|
* @param staticTransformMatrix static transform matrix to apply to the scene
|
|
5041
5266
|
*/
|
|
5042
|
-
|
|
5043
|
-
this.
|
|
5044
|
-
this.externalStaticTransformMatrix.fromArray(staticTransformMatrix);
|
|
5267
|
+
setStaticTransform(staticTransformMatrix) {
|
|
5268
|
+
this.externalSystem.setStaticTransform(staticTransformMatrix);
|
|
5045
5269
|
}
|
|
5046
5270
|
/**
|
|
5047
|
-
*
|
|
5048
|
-
* @param dynamicTransformMatrix dynamic transform matrix
|
|
5271
|
+
* Set dynamic part of an svg -> px transform matrix. Should be called every frame.
|
|
5272
|
+
* @param dynamicTransformMatrix dynamic transform matrix (changes every frame)
|
|
5049
5273
|
*/
|
|
5050
|
-
|
|
5051
|
-
this.
|
|
5052
|
-
|
|
5274
|
+
setDynamicTransform(dynamicTransformMatrix) {
|
|
5275
|
+
this.externalSystem.setDynamicTransform(dynamicTransformMatrix);
|
|
5276
|
+
}
|
|
5277
|
+
/**
|
|
5278
|
+
* Calculates the camera distance from the scene's plane for a given zoom factor.
|
|
5279
|
+
* @param zoomFactor Zoom factor
|
|
5280
|
+
* @returns Corresponding camera distance on the Z axis
|
|
5281
|
+
*/
|
|
5282
|
+
zoomFactorToDistance(zoomFactor) {
|
|
5283
|
+
return this.cameraSystem.zoomFactorToDistance(zoomFactor);
|
|
5053
5284
|
}
|
|
5054
5285
|
}
|
|
5286
|
+
function asViewportAPI(viewportSystem) {
|
|
5287
|
+
return {
|
|
5288
|
+
canvasToSvg: viewportSystem.canvasToSvg.bind(viewportSystem),
|
|
5289
|
+
setStaticTransform: viewportSystem.setStaticTransform.bind(viewportSystem),
|
|
5290
|
+
setDynamicTransform: viewportSystem.setDynamicTransform.bind(viewportSystem)
|
|
5291
|
+
};
|
|
5292
|
+
}
|
|
5293
|
+
function eventToCanvas(event) {
|
|
5294
|
+
return { x: event.offsetX, y: event.offsetY };
|
|
5295
|
+
}
|
|
5296
|
+
const logger$1 = createLogger("controls");
|
|
5055
5297
|
class ControlsSystem {
|
|
5056
5298
|
/**
|
|
5057
5299
|
* @param renderer {@link Renderer} instance
|
|
@@ -5063,7 +5305,7 @@ class ControlsSystem {
|
|
|
5063
5305
|
this.renderer = renderer;
|
|
5064
5306
|
this.viewportSystem = viewportSystem;
|
|
5065
5307
|
this.interactionsSystem = interactionsSystem;
|
|
5066
|
-
this.controller = viewportSystem.
|
|
5308
|
+
this.controller = viewportSystem.controller;
|
|
5067
5309
|
}
|
|
5068
5310
|
/** Gesture handlers for camera controls. */
|
|
5069
5311
|
get handlers() {
|
|
@@ -5092,7 +5334,9 @@ class ControlsSystem {
|
|
|
5092
5334
|
const dpr = this.renderer.context.getPixelRatio();
|
|
5093
5335
|
const visibleRect = this.renderer.visibleRect;
|
|
5094
5336
|
const bearingAngle = -this.controller.azimuthAngle;
|
|
5095
|
-
const
|
|
5337
|
+
const worldMin = this.viewportSystem.modelToWorld(rect.min);
|
|
5338
|
+
const worldMax = this.viewportSystem.modelToWorld(rect.max);
|
|
5339
|
+
const worldRect = new Rect(worldMin, worldMax);
|
|
5096
5340
|
const worldPolygon = Polygon.fromRect(worldRect).rotate(bearingAngle, worldRect.center);
|
|
5097
5341
|
const xValues = worldPolygon.vertices.map((p) => p.x);
|
|
5098
5342
|
const yValues = worldPolygon.vertices.map((p) => p.y);
|
|
@@ -5100,7 +5344,11 @@ class ControlsSystem {
|
|
|
5100
5344
|
[Math.min(...xValues), Math.min(...yValues)],
|
|
5101
5345
|
[Math.max(...xValues), Math.max(...yValues)]
|
|
5102
5346
|
);
|
|
5103
|
-
|
|
5347
|
+
if (sourceRect.size.x <= 0 || sourceRect.size.y <= 0) {
|
|
5348
|
+
logger$1.warn("zoomTo: sourceRect size is 0");
|
|
5349
|
+
return;
|
|
5350
|
+
}
|
|
5351
|
+
const targetRect = visibleRect ? new Rect(visibleRect.min.clone().multiplyScalar(dpr), visibleRect.max.clone().multiplyScalar(dpr)) : new Rect([0, 0], [...this.renderer.size]);
|
|
5104
5352
|
if (paddingPercent) targetRect.addPadding(targetRect.size.x * paddingPercent, targetRect.size.y * paddingPercent);
|
|
5105
5353
|
const zoomByWidth = targetRect.size.x / sourceRect.size.x;
|
|
5106
5354
|
const zoomByHeight = targetRect.size.y / sourceRect.size.y;
|
|
@@ -5108,7 +5356,7 @@ class ControlsSystem {
|
|
|
5108
5356
|
const zoom = maxZoom ? Math.min(minZoom, maxZoom) : minZoom;
|
|
5109
5357
|
const translate = sourceRect.center;
|
|
5110
5358
|
if (visibleRect) {
|
|
5111
|
-
const offset = new Vector2(...this.renderer.size).multiplyScalar(0.5).sub(targetRect.center).multiplyScalar(1 / zoom).rotateAround({ x: 0, y: 0 }, bearingAngle);
|
|
5359
|
+
const offset = new Vector2(...this.renderer.size).multiplyScalar(0.5).sub(targetRect.center).multiplyScalar(1 / (zoom || 1)).rotateAround({ x: 0, y: 0 }, bearingAngle);
|
|
5112
5360
|
translate.add(offset);
|
|
5113
5361
|
}
|
|
5114
5362
|
const enableTransition = !immediate;
|
|
@@ -5125,8 +5373,8 @@ class ControlsSystem {
|
|
|
5125
5373
|
* @returns Promise that resolves when the pan animation completes
|
|
5126
5374
|
*/
|
|
5127
5375
|
panBy(x, y, immediate) {
|
|
5128
|
-
const svgOrigin = this.viewportSystem.
|
|
5129
|
-
const svgOffset = this.viewportSystem.
|
|
5376
|
+
const svgOrigin = this.viewportSystem.modelToWorld({ x: 0, y: 0 });
|
|
5377
|
+
const svgOffset = this.viewportSystem.modelToWorld({ x, y });
|
|
5130
5378
|
const worldOffset = new Vector3(svgOffset.x - svgOrigin.x, svgOffset.y - svgOrigin.y, 0);
|
|
5131
5379
|
const currentTarget = this.controller.getTarget(new Vector3());
|
|
5132
5380
|
const newTarget = currentTarget.add(worldOffset);
|
|
@@ -5140,7 +5388,7 @@ class ControlsSystem {
|
|
|
5140
5388
|
* @returns Promise that resolves when the pan animation completes
|
|
5141
5389
|
*/
|
|
5142
5390
|
panTo(x, y, immediate) {
|
|
5143
|
-
const worldCoords = this.viewportSystem.
|
|
5391
|
+
const worldCoords = this.viewportSystem.modelToWorld({ x, y });
|
|
5144
5392
|
return this.controller.moveTo(worldCoords.x, worldCoords.y, 0, !immediate);
|
|
5145
5393
|
}
|
|
5146
5394
|
/**
|
|
@@ -5169,7 +5417,6 @@ class ControlsSystem {
|
|
|
5169
5417
|
const spherical = this.controller.getSpherical(new Spherical());
|
|
5170
5418
|
const azimuthAngle = spherical.theta;
|
|
5171
5419
|
const deltaAngleRad = shortestRotationAngle(targetAngleRad, azimuthAngle);
|
|
5172
|
-
console.log("rollTo", deltaAngleRad * RAD2DEG, targetAngleRad * RAD2DEG, azimuthAngle * RAD2DEG);
|
|
5173
5420
|
return this.rollBy(-deltaAngleRad * RAD2DEG, immediate);
|
|
5174
5421
|
}
|
|
5175
5422
|
/**
|
|
@@ -5238,8 +5485,8 @@ class ControlsSystem {
|
|
|
5238
5485
|
this.controller.setBoundary(void 0);
|
|
5239
5486
|
return;
|
|
5240
5487
|
}
|
|
5241
|
-
const worldMin = this.viewportSystem.
|
|
5242
|
-
const worldMax = this.viewportSystem.
|
|
5488
|
+
const worldMin = this.viewportSystem.modelToWorld(rect.min);
|
|
5489
|
+
const worldMax = this.viewportSystem.modelToWorld(rect.max);
|
|
5243
5490
|
const boundary = new Box3(new Vector3(worldMin.x, worldMin.y, 0), new Vector3(worldMax.x, worldMax.y, 0));
|
|
5244
5491
|
this.controller.setBoundary(boundary);
|
|
5245
5492
|
}
|
|
@@ -5249,9 +5496,9 @@ class ControlsSystem {
|
|
|
5249
5496
|
*/
|
|
5250
5497
|
getCameraState() {
|
|
5251
5498
|
const target = this.controller.getTarget(new Vector3());
|
|
5252
|
-
const center = this.viewportSystem.
|
|
5499
|
+
const center = this.viewportSystem.worldToModel(target);
|
|
5253
5500
|
const zoom = this.viewportSystem.zoomFactor;
|
|
5254
|
-
const roll = this.
|
|
5501
|
+
const roll = this.viewportSystem.bearing * RAD2DEG;
|
|
5255
5502
|
const pitch = this.controller.polarAngle * RAD2DEG;
|
|
5256
5503
|
const ptScale = this.viewportSystem.pxToSvgScale;
|
|
5257
5504
|
return { center, zoom, roll, pitch, ptScale };
|
|
@@ -5348,21 +5595,6 @@ function asEventAPI(system) {
|
|
|
5348
5595
|
clear: system.clear.bind(system)
|
|
5349
5596
|
};
|
|
5350
5597
|
}
|
|
5351
|
-
function clientToCanvas(event, domElement, target) {
|
|
5352
|
-
target = target ?? new Vector2();
|
|
5353
|
-
const { left, top } = domElement.getBoundingClientRect();
|
|
5354
|
-
const clientX = "clientX" in event ? event.clientX : event.x;
|
|
5355
|
-
const clientY = "clientY" in event ? event.clientY : event.y;
|
|
5356
|
-
target.set(clientX - left, clientY - top);
|
|
5357
|
-
return target;
|
|
5358
|
-
}
|
|
5359
|
-
function canvasToNDC(coordinates, canvas, target) {
|
|
5360
|
-
target = target ?? new Vector2();
|
|
5361
|
-
const { width, height } = canvas.getBoundingClientRect();
|
|
5362
|
-
const uv = [coordinates.x / width, coordinates.y / height];
|
|
5363
|
-
target.set(uv[0] * 2 - 1, -uv[1] * 2 + 1);
|
|
5364
|
-
return target;
|
|
5365
|
-
}
|
|
5366
5598
|
class Handler {
|
|
5367
5599
|
/**
|
|
5368
5600
|
* @param viewportSystem The viewport system instance
|
|
@@ -5377,7 +5609,7 @@ class Handler {
|
|
|
5377
5609
|
this.viewportSystem = viewportSystem;
|
|
5378
5610
|
this.domElement = domElement;
|
|
5379
5611
|
this.eventManager = eventManager;
|
|
5380
|
-
this.controller = viewportSystem.
|
|
5612
|
+
this.controller = viewportSystem.controller;
|
|
5381
5613
|
}
|
|
5382
5614
|
/**
|
|
5383
5615
|
* Per-frame update for this handler.
|
|
@@ -5588,13 +5820,15 @@ class PitchHandler extends Handler {
|
|
|
5588
5820
|
__publicField(this, "isValid");
|
|
5589
5821
|
__publicField(this, "firstMove");
|
|
5590
5822
|
__publicField(this, "lastPoints");
|
|
5823
|
+
__publicField(this, "p0", new Vector2());
|
|
5824
|
+
__publicField(this, "p1", new Vector2());
|
|
5591
5825
|
__publicField(this, "prevTwoFingerAction");
|
|
5592
5826
|
__publicField(this, "onPitchStart", (e) => {
|
|
5593
5827
|
const pointers = e.pointers.sort((a, b) => a.pointerId - b.pointerId);
|
|
5594
|
-
|
|
5595
|
-
|
|
5596
|
-
this.lastPoints = [p0, p1];
|
|
5597
|
-
if (this.isVertical(p0.
|
|
5828
|
+
this.p0.copy(eventToCanvas(pointers[0]));
|
|
5829
|
+
this.p1.copy(eventToCanvas(pointers[1]));
|
|
5830
|
+
this.lastPoints = [new Vector2().copy(this.p0), new Vector2().copy(this.p1)];
|
|
5831
|
+
if (this.isVertical(this.p0.sub(this.p1))) {
|
|
5598
5832
|
this.isValid = false;
|
|
5599
5833
|
}
|
|
5600
5834
|
});
|
|
@@ -5609,18 +5843,19 @@ class PitchHandler extends Handler {
|
|
|
5609
5843
|
const lastPoints = this.lastPoints;
|
|
5610
5844
|
if (!lastPoints) return;
|
|
5611
5845
|
const pointers = e.pointers.sort((a, b) => a.pointerId - b.pointerId);
|
|
5612
|
-
|
|
5613
|
-
|
|
5614
|
-
|
|
5615
|
-
|
|
5616
|
-
this.isValid = this.gestureBeginsVertically(
|
|
5846
|
+
this.p0.copy(eventToCanvas(pointers[0]));
|
|
5847
|
+
this.p1.copy(eventToCanvas(pointers[1]));
|
|
5848
|
+
this.p0.sub(lastPoints[0]);
|
|
5849
|
+
this.p1.sub(lastPoints[1]);
|
|
5850
|
+
this.isValid = this.gestureBeginsVertically(this.p0, this.p1, e.timeStamp);
|
|
5617
5851
|
if (!this.isValid) return;
|
|
5618
5852
|
if (this.prevTwoFingerAction === void 0) {
|
|
5619
5853
|
this.prevTwoFingerAction = this.controller.touches.two;
|
|
5620
5854
|
this.controller.touches.two = CameraController.ACTION.NONE;
|
|
5621
5855
|
}
|
|
5622
|
-
this.
|
|
5623
|
-
|
|
5856
|
+
lastPoints[0].add(this.p0);
|
|
5857
|
+
lastPoints[1].add(this.p1);
|
|
5858
|
+
const yDeltaAverage = (this.p0.y + this.p1.y) / 2;
|
|
5624
5859
|
const degreesPerPixelMoved = -0.5;
|
|
5625
5860
|
const deltaAngle = yDeltaAverage * degreesPerPixelMoved * DEG2RAD$1;
|
|
5626
5861
|
void this.controller.rotatePolarTo(this.controller.polarAngle + deltaAngle, false);
|
|
@@ -5665,12 +5900,10 @@ class PitchHandler extends Handler {
|
|
|
5665
5900
|
updatePolarAngles() {
|
|
5666
5901
|
this.controller.minPolarAngle = this.minPitch * DEG2RAD$1;
|
|
5667
5902
|
this.controller.maxPolarAngle = this.maxPitch * DEG2RAD$1;
|
|
5668
|
-
if (this.controller.polarAngle < this.controller.minPolarAngle)
|
|
5903
|
+
if (this.controller.polarAngle < this.controller.minPolarAngle)
|
|
5669
5904
|
void this.controller.rotatePolarTo(this.controller.minPolarAngle, false);
|
|
5670
|
-
|
|
5671
|
-
if (this.controller.polarAngle > this.controller.maxPolarAngle) {
|
|
5905
|
+
if (this.controller.polarAngle > this.controller.maxPolarAngle)
|
|
5672
5906
|
void this.controller.rotatePolarTo(this.controller.maxPolarAngle, false);
|
|
5673
|
-
}
|
|
5674
5907
|
}
|
|
5675
5908
|
gestureBeginsVertically(vectorA, vectorB, timeStamp) {
|
|
5676
5909
|
if (this.isValid !== void 0) return this.isValid;
|
|
@@ -5702,35 +5935,36 @@ class RollHandler extends Handler {
|
|
|
5702
5935
|
__publicField(this, "rotationThreshold", 25);
|
|
5703
5936
|
// Threshold tracking (Mapbox-style)
|
|
5704
5937
|
__publicField(this, "startVector");
|
|
5705
|
-
__publicField(this, "vector");
|
|
5938
|
+
__publicField(this, "vector", new Vector2());
|
|
5939
|
+
__publicField(this, "p0", new Vector2());
|
|
5940
|
+
__publicField(this, "p1", new Vector2());
|
|
5706
5941
|
__publicField(this, "minDiameter", 0);
|
|
5707
5942
|
__publicField(this, "prevAngle", 0);
|
|
5708
5943
|
// Camera and pivot vectors
|
|
5944
|
+
__publicField(this, "pivotNDC", new Vector2());
|
|
5709
5945
|
__publicField(this, "pivotWorld", new Vector3());
|
|
5710
5946
|
__publicField(this, "targetWorld", new Vector3());
|
|
5711
5947
|
__publicField(this, "cameraPosition", new Vector3());
|
|
5712
5948
|
__publicField(this, "cameraForward", new Vector3());
|
|
5713
5949
|
__publicField(this, "rotationMatrix", new Matrix4());
|
|
5714
5950
|
__publicField(this, "onRotateStart", (e) => {
|
|
5715
|
-
console.log("onRotateStart");
|
|
5716
5951
|
const pointers = e.pointers;
|
|
5717
|
-
|
|
5718
|
-
|
|
5719
|
-
this.startVector = p0.sub(p1);
|
|
5952
|
+
this.p0.copy(eventToCanvas(pointers[0]));
|
|
5953
|
+
this.p1.copy(eventToCanvas(pointers[1]));
|
|
5954
|
+
this.startVector = new Vector2().copy(this.p0).sub(this.p1);
|
|
5720
5955
|
this.minDiameter = this.startVector.length();
|
|
5721
5956
|
});
|
|
5722
5957
|
__publicField(this, "onRotateEnd", () => {
|
|
5723
5958
|
this.isRolling = false;
|
|
5724
5959
|
this.startVector = void 0;
|
|
5725
|
-
this.vector = void 0;
|
|
5726
5960
|
this.minDiameter = 0;
|
|
5727
5961
|
});
|
|
5728
5962
|
__publicField(this, "onRotate", (e) => {
|
|
5729
5963
|
const pointers = e.pointers;
|
|
5730
5964
|
if (!this.isRolling) {
|
|
5731
|
-
|
|
5732
|
-
|
|
5733
|
-
this.vector
|
|
5965
|
+
this.p0.copy(eventToCanvas(pointers[0]));
|
|
5966
|
+
this.p1.copy(eventToCanvas(pointers[1]));
|
|
5967
|
+
this.vector.copy(this.p0).sub(this.p1);
|
|
5734
5968
|
if (this.isBelowThreshold(this.vector)) return;
|
|
5735
5969
|
this.isRolling = true;
|
|
5736
5970
|
this.prevAngle = e.rotation;
|
|
@@ -5738,7 +5972,7 @@ class RollHandler extends Handler {
|
|
|
5738
5972
|
const deltaAngle = (e.rotation - this.prevAngle) * -DEG2RAD$1;
|
|
5739
5973
|
this.prevAngle = e.rotation;
|
|
5740
5974
|
if (Math.abs(deltaAngle) < 1e-3) return;
|
|
5741
|
-
this.setPivot(e);
|
|
5975
|
+
if (!this.setPivot(e)) return;
|
|
5742
5976
|
this.rotationMatrix.makeRotationZ(deltaAngle);
|
|
5743
5977
|
this.cameraPosition.sub(this.pivotWorld).applyMatrix4(this.rotationMatrix).add(this.pivotWorld);
|
|
5744
5978
|
this.cameraForward.applyMatrix4(this.rotationMatrix);
|
|
@@ -5786,13 +6020,12 @@ class RollHandler extends Handler {
|
|
|
5786
6020
|
this.eventManager.off("rotateend", this.onRotateEnd);
|
|
5787
6021
|
}
|
|
5788
6022
|
setPivot(e) {
|
|
5789
|
-
|
|
5790
|
-
|
|
5791
|
-
const pivotWorld2D = this.viewportSystem.screenTo("world", pivotNDC);
|
|
5792
|
-
this.pivotWorld.set(pivotWorld2D.x, pivotWorld2D.y, 0);
|
|
6023
|
+
this.viewportSystem.canvasToNDC(e.offsetCenter, this.pivotNDC);
|
|
6024
|
+
if (!this.viewportSystem.ndcToWorld(this.pivotNDC, this.pivotWorld)) return false;
|
|
5793
6025
|
this.controller.getPosition(this.cameraPosition);
|
|
5794
6026
|
this.controller.getTarget(this.targetWorld);
|
|
5795
6027
|
this.cameraForward.copy(this.targetWorld).sub(this.cameraPosition);
|
|
6028
|
+
return true;
|
|
5796
6029
|
}
|
|
5797
6030
|
/**
|
|
5798
6031
|
* Check if rotation is below threshold (Mapbox-style).
|
|
@@ -5812,7 +6045,6 @@ class RollHandler extends Handler {
|
|
|
5812
6045
|
const startVector = this.startVector;
|
|
5813
6046
|
if (!startVector) return false;
|
|
5814
6047
|
const bearingDeltaSinceStart = this.getBearingDelta(vector, startVector);
|
|
5815
|
-
console.log("bearingDeltaSinceStart", vector, startVector);
|
|
5816
6048
|
return Math.abs(bearingDeltaSinceStart) < threshold;
|
|
5817
6049
|
}
|
|
5818
6050
|
/**
|
|
@@ -5824,16 +6056,6 @@ class RollHandler extends Handler {
|
|
|
5824
6056
|
getBearingDelta(a, b) {
|
|
5825
6057
|
return a.angleTo(b) * RAD2DEG;
|
|
5826
6058
|
}
|
|
5827
|
-
/**
|
|
5828
|
-
* Normalize angle to be between -π and π
|
|
5829
|
-
* @param angle Angle in radians
|
|
5830
|
-
* @returns Normalized angle in radians
|
|
5831
|
-
*/
|
|
5832
|
-
normalizeAngle(angle) {
|
|
5833
|
-
while (angle > Math.PI) angle -= 2 * Math.PI;
|
|
5834
|
-
while (angle < -Math.PI) angle += 2 * Math.PI;
|
|
5835
|
-
return angle;
|
|
5836
|
-
}
|
|
5837
6059
|
}
|
|
5838
6060
|
class ZoomHandler extends Handler {
|
|
5839
6061
|
reset(enableTransition = true) {
|
|
@@ -5868,12 +6090,16 @@ class InteractionsSystem {
|
|
|
5868
6090
|
__publicField(this, "handlers");
|
|
5869
6091
|
__publicField(this, "handlerArray");
|
|
5870
6092
|
__publicField(this, "canvas");
|
|
5871
|
-
__publicField(this, "mousePointer", new Vector2());
|
|
5872
6093
|
__publicField(this, "eventManager");
|
|
6094
|
+
__publicField(this, "mousePointerNDC", new Vector2());
|
|
6095
|
+
__publicField(this, "mousePointerWorld", new Vector3());
|
|
6096
|
+
__publicField(this, "mousePointerModel", new Vector2());
|
|
6097
|
+
__publicField(this, "canvasListeners");
|
|
5873
6098
|
__publicField(this, "dragStart");
|
|
5874
6099
|
__publicField(this, "dragThreshold", 15);
|
|
5875
6100
|
__publicField(this, "isDragging", false);
|
|
5876
6101
|
__publicField(this, "prevBearing", 0);
|
|
6102
|
+
this.renderer = renderer;
|
|
5877
6103
|
this.events = events;
|
|
5878
6104
|
this.viewportSystem = viewportSystem;
|
|
5879
6105
|
this.layerSystem = layerSystem;
|
|
@@ -5881,8 +6107,6 @@ class InteractionsSystem {
|
|
|
5881
6107
|
this.eventManager = new EventManager(this.canvas, {
|
|
5882
6108
|
recognizers: [Rotate, [Pan, { event: "pitch", pointers: 2 }, "rotate"]]
|
|
5883
6109
|
});
|
|
5884
|
-
this.configureCameraControls();
|
|
5885
|
-
this.attachCanvasListeners();
|
|
5886
6110
|
const handlers = {
|
|
5887
6111
|
pan: new PanHandler(viewportSystem, this.canvas, this.eventManager),
|
|
5888
6112
|
zoom: new ZoomHandler(viewportSystem, this.canvas, this.eventManager),
|
|
@@ -5891,17 +6115,32 @@ class InteractionsSystem {
|
|
|
5891
6115
|
};
|
|
5892
6116
|
this.handlers = handlers;
|
|
5893
6117
|
this.handlerArray = Object.values(handlers);
|
|
5894
|
-
this.handlers.pan.enable();
|
|
5895
|
-
this.handlers.zoom.enable();
|
|
5896
6118
|
}
|
|
5897
6119
|
/**
|
|
5898
|
-
*
|
|
5899
|
-
*
|
|
6120
|
+
* Initializes the interactions system and attaches event listeners.
|
|
6121
|
+
* Should be called once as part of renderer initialization.
|
|
5900
6122
|
*/
|
|
5901
|
-
|
|
5902
|
-
|
|
5903
|
-
|
|
5904
|
-
|
|
6123
|
+
init() {
|
|
6124
|
+
this.attachCanvasListeners();
|
|
6125
|
+
if (!this.renderer.isExternalMode) {
|
|
6126
|
+
const controller = this.viewportSystem.controller;
|
|
6127
|
+
controller.connect(this.canvas);
|
|
6128
|
+
controller.addEventListener("transitionstart", () => {
|
|
6129
|
+
this.events.emit("navigation:change");
|
|
6130
|
+
});
|
|
6131
|
+
this.handlers.pan.enable();
|
|
6132
|
+
this.handlers.zoom.enable();
|
|
6133
|
+
}
|
|
6134
|
+
}
|
|
6135
|
+
/**
|
|
6136
|
+
* Disposes the interactions system.
|
|
6137
|
+
* WARNING: This method is final and cannot be undone. To re-enable interactions, create a new renderer instance.
|
|
6138
|
+
*/
|
|
6139
|
+
dispose() {
|
|
6140
|
+
for (const handler of this.handlerArray) handler.disable();
|
|
6141
|
+
this.viewportSystem.controller.disconnect();
|
|
6142
|
+
this.detachCanvasListeners();
|
|
6143
|
+
this.eventManager.destroy();
|
|
5905
6144
|
}
|
|
5906
6145
|
/**
|
|
5907
6146
|
* Update camera position and directions.
|
|
@@ -5910,87 +6149,75 @@ class InteractionsSystem {
|
|
|
5910
6149
|
* @returns true if re-rendering is needed
|
|
5911
6150
|
*/
|
|
5912
6151
|
updateControls(delta) {
|
|
5913
|
-
let needsUpdate = this.viewportSystem.
|
|
6152
|
+
let needsUpdate = this.viewportSystem.controller.update(delta);
|
|
5914
6153
|
for (const handler of this.handlerArray) {
|
|
5915
6154
|
if (handler.isEnabled()) {
|
|
5916
6155
|
needsUpdate = handler.update(delta) || needsUpdate;
|
|
5917
6156
|
}
|
|
5918
6157
|
}
|
|
5919
|
-
if (this.bearing !== this.prevBearing) {
|
|
5920
|
-
this.prevBearing = this.bearing;
|
|
5921
|
-
this.events.emit("navigation:roll", this.bearing);
|
|
6158
|
+
if (this.viewportSystem.bearing !== this.prevBearing) {
|
|
6159
|
+
this.prevBearing = this.viewportSystem.bearing;
|
|
6160
|
+
this.events.emit("navigation:roll", this.viewportSystem.bearing);
|
|
5922
6161
|
}
|
|
5923
6162
|
return needsUpdate;
|
|
5924
6163
|
}
|
|
5925
|
-
/** Disconnect the interactions system. */
|
|
5926
|
-
disconnect() {
|
|
5927
|
-
this.viewportSystem.cameraController.disconnect();
|
|
5928
|
-
for (const handler of this.handlerArray) {
|
|
5929
|
-
handler.disable();
|
|
5930
|
-
}
|
|
5931
|
-
}
|
|
5932
|
-
configureCameraControls() {
|
|
5933
|
-
const controller = this.viewportSystem.cameraController;
|
|
5934
|
-
controller.draggingSmoothTime = 0;
|
|
5935
|
-
controller.dollyToCursor = true;
|
|
5936
|
-
controller.mouseButtons = {
|
|
5937
|
-
left: CameraController.ACTION.NONE,
|
|
5938
|
-
middle: CameraController.ACTION.NONE,
|
|
5939
|
-
right: CameraController.ACTION.NONE,
|
|
5940
|
-
wheel: CameraController.ACTION.NONE
|
|
5941
|
-
};
|
|
5942
|
-
controller.touches = {
|
|
5943
|
-
one: CameraController.ACTION.NONE,
|
|
5944
|
-
two: CameraController.ACTION.NONE,
|
|
5945
|
-
three: CameraController.ACTION.NONE
|
|
5946
|
-
};
|
|
5947
|
-
controller.connect(this.canvas);
|
|
5948
|
-
controller.addEventListener("transitionstart", () => {
|
|
5949
|
-
this.events.emit("navigation:change");
|
|
5950
|
-
});
|
|
5951
|
-
}
|
|
5952
6164
|
attachCanvasListeners() {
|
|
5953
|
-
this.
|
|
6165
|
+
if (this.canvasListeners) return;
|
|
6166
|
+
const pointerdown = (event) => {
|
|
5954
6167
|
this.isDragging = false;
|
|
5955
6168
|
this.dragStart = { x: event.offsetX, y: event.offsetY };
|
|
5956
|
-
}
|
|
5957
|
-
|
|
6169
|
+
};
|
|
6170
|
+
const pointerup = (event) => {
|
|
5958
6171
|
if (!this.dragStart) return;
|
|
5959
6172
|
const dX = event.offsetX - this.dragStart.x;
|
|
5960
6173
|
const dY = event.offsetY - this.dragStart.y;
|
|
5961
6174
|
this.dragStart = void 0;
|
|
5962
6175
|
if (dX * dX + dY * dY > this.dragThreshold) this.isDragging = true;
|
|
5963
|
-
}
|
|
6176
|
+
};
|
|
5964
6177
|
const mouseEventsMap = {
|
|
5965
6178
|
mousemove: "pointer:move",
|
|
5966
6179
|
mouseout: "pointer:out",
|
|
5967
6180
|
click: "pointer:click"
|
|
5968
6181
|
};
|
|
5969
6182
|
const mouseEventKeys = Object.keys(mouseEventsMap);
|
|
5970
|
-
|
|
5971
|
-
|
|
5972
|
-
|
|
5973
|
-
|
|
5974
|
-
|
|
5975
|
-
|
|
5976
|
-
|
|
5977
|
-
|
|
5978
|
-
|
|
5979
|
-
|
|
5980
|
-
|
|
5981
|
-
|
|
5982
|
-
|
|
5983
|
-
|
|
6183
|
+
const sharedMouseHandler = (type, event) => {
|
|
6184
|
+
const eventType = mouseEventsMap[type];
|
|
6185
|
+
const isDragging = type === "click" && this.isDragging;
|
|
6186
|
+
const hasListeners = this.events.hasListeners(eventType);
|
|
6187
|
+
if (isDragging || !hasListeners) return;
|
|
6188
|
+
const mousePointer = eventToCanvas(event);
|
|
6189
|
+
this.viewportSystem.canvasToNDC(mousePointer, this.mousePointerNDC);
|
|
6190
|
+
if (!this.viewportSystem.ndcToWorld(this.mousePointerNDC, this.mousePointerWorld)) return;
|
|
6191
|
+
const intersections = this.viewportSystem.getIntersectedObjects(this.mousePointerNDC);
|
|
6192
|
+
const point = this.viewportSystem.worldToModel(this.mousePointerWorld, this.mousePointerModel);
|
|
6193
|
+
const defs = this.layerSystem.getIntersectedDefs(intersections);
|
|
6194
|
+
this.events.emit(eventType, { event, point: { x: point.x, y: point.y }, defs });
|
|
6195
|
+
};
|
|
6196
|
+
const mousemove = (event) => sharedMouseHandler("mousemove", event);
|
|
6197
|
+
const mouseout = (event) => sharedMouseHandler("mouseout", event);
|
|
6198
|
+
const click = (event) => sharedMouseHandler("click", event);
|
|
6199
|
+
const canvasListeners = { pointerdown, pointerup, mousemove, mouseout, click };
|
|
6200
|
+
this.canvas.addEventListener("pointerdown", pointerdown);
|
|
6201
|
+
this.canvas.addEventListener("pointerup", pointerup);
|
|
6202
|
+
mouseEventKeys.forEach((type) => this.canvas.addEventListener(type, canvasListeners[type]));
|
|
6203
|
+
this.canvasListeners = canvasListeners;
|
|
6204
|
+
}
|
|
6205
|
+
detachCanvasListeners() {
|
|
6206
|
+
if (!this.canvasListeners) return;
|
|
6207
|
+
this.canvas.removeEventListener("pointerdown", this.canvasListeners.pointerdown);
|
|
6208
|
+
this.canvas.removeEventListener("pointerup", this.canvasListeners.pointerup);
|
|
6209
|
+
this.canvas.removeEventListener("mousemove", this.canvasListeners.mousemove);
|
|
6210
|
+
this.canvas.removeEventListener("mouseout", this.canvasListeners.mouseout);
|
|
6211
|
+
this.canvas.removeEventListener("click", this.canvasListeners.click);
|
|
6212
|
+
this.canvasListeners = void 0;
|
|
5984
6213
|
}
|
|
5985
6214
|
}
|
|
6215
|
+
const logger = createLogger("renderer");
|
|
5986
6216
|
class Renderer {
|
|
5987
6217
|
/**
|
|
5988
6218
|
* @param opts {@link RendererOptions}
|
|
5989
6219
|
*/
|
|
5990
6220
|
constructor(opts) {
|
|
5991
|
-
/** Whether to log debug information */
|
|
5992
|
-
__publicField(this, "debugLog");
|
|
5993
|
-
//FIXME: Add https://www.npmjs.com/package/debug
|
|
5994
6221
|
/** {@link HTMLCanvasElement} that this renderer is rendering to */
|
|
5995
6222
|
__publicField(this, "canvas");
|
|
5996
6223
|
__publicField(this, "ui");
|
|
@@ -6000,16 +6227,19 @@ class Renderer {
|
|
|
6000
6227
|
__publicField(this, "viewportSystem");
|
|
6001
6228
|
__publicField(this, "interactionsSystem");
|
|
6002
6229
|
__publicField(this, "controlsSystem");
|
|
6230
|
+
__publicField(this, "controlsAPI");
|
|
6231
|
+
__publicField(this, "eventsAPI");
|
|
6232
|
+
__publicField(this, "viewportAPI");
|
|
6003
6233
|
__publicField(this, "clock");
|
|
6004
6234
|
__publicField(this, "renderer");
|
|
6005
|
-
__publicField(this, "
|
|
6235
|
+
__publicField(this, "visibleRectValue");
|
|
6236
|
+
__publicField(this, "memoryInfoExtension", null);
|
|
6237
|
+
__publicField(this, "memoryInfo");
|
|
6238
|
+
__publicField(this, "initialized", false);
|
|
6239
|
+
__publicField(this, "disposed", false);
|
|
6006
6240
|
__publicField(this, "needsRedraw", true);
|
|
6007
|
-
|
|
6008
|
-
__publicField(this, "memoryInfo", "");
|
|
6009
|
-
var _a2, _b;
|
|
6010
|
-
const { canvas, gl, debugLog = false, ui } = opts;
|
|
6241
|
+
const { canvas, gl, ui } = opts;
|
|
6011
6242
|
this.canvas = canvas;
|
|
6012
|
-
this.debugLog = debugLog;
|
|
6013
6243
|
this.ui = ui;
|
|
6014
6244
|
this.gl = gl;
|
|
6015
6245
|
const rendererOptions = {
|
|
@@ -6021,42 +6251,86 @@ class Renderer {
|
|
|
6021
6251
|
this.renderer = new WebGLRenderer(rendererOptions);
|
|
6022
6252
|
this.renderer.setSize(this.canvas.clientWidth, this.canvas.clientHeight, false);
|
|
6023
6253
|
this.renderer.setPixelRatio(window.devicePixelRatio);
|
|
6254
|
+
this.renderer.autoClear = !this.isExternalMode;
|
|
6024
6255
|
this.eventSystem = new EventSystem();
|
|
6025
6256
|
this.viewportSystem = new ViewportSystem(this, this.eventSystem);
|
|
6026
6257
|
this.layerSystem = new LayerSystem(this);
|
|
6027
6258
|
this.interactionsSystem = new InteractionsSystem(this, this.eventSystem, this.viewportSystem, this.layerSystem);
|
|
6028
6259
|
this.controlsSystem = new ControlsSystem(this, this.viewportSystem, this.interactionsSystem);
|
|
6029
|
-
this.memoryInfoExtension = this.renderer.getContext().getExtension("GMAN_webgl_memory");
|
|
6030
6260
|
this.canvas.addEventListener("webglcontextlost", (e) => this.onContextLost(e), false);
|
|
6031
6261
|
this.canvas.addEventListener("webglcontextrestored", (e) => this.onContextRestored(e), false);
|
|
6032
|
-
|
|
6262
|
+
this.initContext(this.renderer.getContext());
|
|
6033
6263
|
BatchedMesh.useMultiDraw = this.renderer.extensions.has("WEBGL_multi_draw");
|
|
6034
6264
|
}
|
|
6035
6265
|
/**
|
|
6036
6266
|
* {@link ControlsAPI} instance for controlling the viewport
|
|
6037
6267
|
*/
|
|
6038
6268
|
get controls() {
|
|
6039
|
-
|
|
6269
|
+
if (this.controlsAPI) return this.controlsAPI;
|
|
6270
|
+
const api = asControlsAPI(this.controlsSystem);
|
|
6271
|
+
const guard = (name) => this.assertInitialized(`controls.${name}`) && this.assertNotDisposed(`controls.${name}`) && this.assertNotExternalMode(`controls.${name}`);
|
|
6272
|
+
this.controlsAPI = {
|
|
6273
|
+
handlers: api.handlers,
|
|
6274
|
+
configure: api.configure,
|
|
6275
|
+
zoomBy: guardFn(guard, api.zoomBy, Promise.resolve()),
|
|
6276
|
+
zoomTo: guardFn(guard, api.zoomTo, Promise.resolve()),
|
|
6277
|
+
panBy: guardFn(guard, api.panBy, Promise.resolve()),
|
|
6278
|
+
panTo: guardFn(guard, api.panTo, Promise.resolve()),
|
|
6279
|
+
rollBy: guardFn(guard, api.rollBy, Promise.resolve()),
|
|
6280
|
+
rollTo: guardFn(guard, api.rollTo, Promise.resolve()),
|
|
6281
|
+
pitchBy: guardFn(guard, api.pitchBy, Promise.resolve()),
|
|
6282
|
+
pitchTo: guardFn(guard, api.pitchTo, Promise.resolve()),
|
|
6283
|
+
resetCamera: guardFn(guard, api.resetCamera, Promise.resolve()),
|
|
6284
|
+
setCameraBounds: guardFn(guard, api.setCameraBounds),
|
|
6285
|
+
getCameraState: guardFn(guard, api.getCameraState, {
|
|
6286
|
+
center: { x: 0, y: 0 },
|
|
6287
|
+
roll: 0,
|
|
6288
|
+
pitch: 0,
|
|
6289
|
+
zoom: 1,
|
|
6290
|
+
ptScale: 1
|
|
6291
|
+
})
|
|
6292
|
+
};
|
|
6293
|
+
return this.controlsAPI;
|
|
6040
6294
|
}
|
|
6041
6295
|
/**
|
|
6042
6296
|
* {@link EventsAPI} instance for subscribing to internal events
|
|
6043
6297
|
*/
|
|
6044
6298
|
get events() {
|
|
6045
|
-
|
|
6299
|
+
if (this.eventsAPI) return this.eventsAPI;
|
|
6300
|
+
this.eventsAPI = asEventAPI(this.eventSystem);
|
|
6301
|
+
return this.eventsAPI;
|
|
6302
|
+
}
|
|
6303
|
+
/**
|
|
6304
|
+
* {@link ViewportAPI} instance for view transforms and external transforms.
|
|
6305
|
+
*/
|
|
6306
|
+
get viewport() {
|
|
6307
|
+
if (this.viewportAPI) return this.viewportAPI;
|
|
6308
|
+
const api = asViewportAPI(this.viewportSystem);
|
|
6309
|
+
const guard = (name) => this.assertInitialized(`viewport.${name}`) && this.assertNotDisposed(`viewport.${name}`);
|
|
6310
|
+
const guardExternal = (name) => guard(name) && this.assertExternalMode(`viewport.${name}`);
|
|
6311
|
+
this.viewportAPI = {
|
|
6312
|
+
canvasToSvg: guardFn(guard, api.canvasToSvg, { x: 0, y: 0 }),
|
|
6313
|
+
setStaticTransform: guardFn(guardExternal, api.setStaticTransform),
|
|
6314
|
+
setDynamicTransform: guardFn(guardExternal, api.setDynamicTransform)
|
|
6315
|
+
};
|
|
6316
|
+
return this.viewportAPI;
|
|
6046
6317
|
}
|
|
6047
6318
|
/**
|
|
6048
6319
|
* Optional sub-rectangle of the viewport that is used for positioning scene's viewbox
|
|
6049
6320
|
*/
|
|
6050
6321
|
get visibleRect() {
|
|
6051
|
-
return this.
|
|
6322
|
+
return this.visibleRectValue;
|
|
6052
6323
|
}
|
|
6053
6324
|
/**
|
|
6054
6325
|
* Optional sub-rectangle of the viewport that is used for positioning scene's viewbox
|
|
6055
6326
|
*/
|
|
6056
6327
|
set visibleRect(rect) {
|
|
6057
|
-
this.
|
|
6058
|
-
this.
|
|
6059
|
-
this.
|
|
6328
|
+
if (!this.assertNotExternalMode("visibleRect")) return;
|
|
6329
|
+
this.visibleRectValue = rect;
|
|
6330
|
+
if (this.initialized && !this.disposed) {
|
|
6331
|
+
this.viewportSystem.updateViewport();
|
|
6332
|
+
this.update();
|
|
6333
|
+
}
|
|
6060
6334
|
}
|
|
6061
6335
|
/**
|
|
6062
6336
|
* Underlying {@link WebGLRenderer} instance
|
|
@@ -6072,64 +6346,75 @@ class Renderer {
|
|
|
6072
6346
|
return [this.canvas.width, this.canvas.height];
|
|
6073
6347
|
}
|
|
6074
6348
|
/**
|
|
6075
|
-
*
|
|
6076
|
-
*
|
|
6349
|
+
* Returns true if the renderer is in external mode, meaning that webgl context is managed outside of the renderer
|
|
6350
|
+
* (for example, when using Mapbox GL JS as a host context). In this mode renderer will not clear the canvas before
|
|
6351
|
+
* rendering, and will not automatically compute scene and camera transformations. Clients are responsible for setting
|
|
6352
|
+
* the matrices manually by using {@link Renderer.viewport} methods.
|
|
6077
6353
|
*/
|
|
6078
|
-
|
|
6079
|
-
|
|
6080
|
-
this.renderer.autoClear = false;
|
|
6081
|
-
this.interactionsSystem.disconnect();
|
|
6082
|
-
this.viewportSystem.setExternalTransform(staticTransformMatrix);
|
|
6354
|
+
get isExternalMode() {
|
|
6355
|
+
return this.gl !== void 0;
|
|
6083
6356
|
}
|
|
6084
6357
|
/**
|
|
6085
|
-
*
|
|
6086
|
-
|
|
6358
|
+
* Returns true if the renderer is initialized, meaning that the viewport and scene have been set up.
|
|
6359
|
+
*/
|
|
6360
|
+
get isInitialized() {
|
|
6361
|
+
return this.initialized;
|
|
6362
|
+
}
|
|
6363
|
+
/**
|
|
6364
|
+
* Returns true if the renderer is disposed, meaning that all WebGL resources have been released.
|
|
6087
6365
|
*/
|
|
6088
|
-
|
|
6089
|
-
this.
|
|
6366
|
+
get isDisposed() {
|
|
6367
|
+
return this.disposed;
|
|
6090
6368
|
}
|
|
6091
6369
|
/**
|
|
6092
|
-
*
|
|
6370
|
+
* Initializes viewport and scene with the given scene definition.
|
|
6371
|
+
* Should be called once on startup. Repeated calls will produce console warnings.
|
|
6093
6372
|
* @param sceneDef {@link SceneDef} to render
|
|
6094
|
-
* @param startLoop whether to start the rendering loop
|
|
6095
6373
|
*/
|
|
6096
|
-
|
|
6097
|
-
this.
|
|
6374
|
+
init(sceneDef) {
|
|
6375
|
+
if (!this.assertNotDisposed("init")) return;
|
|
6376
|
+
if (!this.assertNotInitialized("init")) return;
|
|
6098
6377
|
this.viewportSystem.initViewport(sceneDef);
|
|
6378
|
+
this.interactionsSystem.init();
|
|
6099
6379
|
this.viewportSystem.scene.add(this.layerSystem.buildScene(sceneDef));
|
|
6100
|
-
|
|
6380
|
+
this.initialized = true;
|
|
6381
|
+
logger.info("initialized");
|
|
6382
|
+
}
|
|
6383
|
+
/**
|
|
6384
|
+
* Start the rendering loop
|
|
6385
|
+
*/
|
|
6386
|
+
start() {
|
|
6387
|
+
if (!this.assertNotDisposed("start")) return;
|
|
6388
|
+
if (!this.assertInitialized("start")) return;
|
|
6389
|
+
if (this.clock.running) return;
|
|
6390
|
+
this.clock.start();
|
|
6391
|
+
this.renderer.setAnimationLoop(() => this.render());
|
|
6392
|
+
logger.info("started");
|
|
6101
6393
|
}
|
|
6102
6394
|
/**
|
|
6103
6395
|
* Update the given defs to make them reflect the current state
|
|
6104
6396
|
* @param defs {@link RenderableDef} array to update
|
|
6105
6397
|
*/
|
|
6106
6398
|
update(...defs) {
|
|
6399
|
+
if (!this.assertNotDisposed("update")) return;
|
|
6400
|
+
if (!this.assertInitialized("update")) return;
|
|
6107
6401
|
this.layerSystem.updateDefs(defs);
|
|
6108
6402
|
this.needsRedraw = true;
|
|
6109
6403
|
}
|
|
6110
6404
|
/**
|
|
6111
|
-
*
|
|
6112
|
-
* @param point point in canvas space (relative to the canvas's top left corner), in css pixels
|
|
6113
|
-
* @returns point in SVG space
|
|
6114
|
-
*/
|
|
6115
|
-
screenToSvg(point) {
|
|
6116
|
-
const vector2 = new Vector2(point.x, point.y);
|
|
6117
|
-
canvasToNDC(vector2, this.canvas, vector2);
|
|
6118
|
-
return this.viewportSystem.screenTo("svg", vector2);
|
|
6119
|
-
}
|
|
6120
|
-
/**
|
|
6121
|
-
* Main rendering loop
|
|
6405
|
+
* Render a single frame
|
|
6122
6406
|
*/
|
|
6123
6407
|
render() {
|
|
6124
6408
|
var _a2, _b, _c, _d, _e, _f;
|
|
6409
|
+
if (!this.assertNotDisposed("render")) return;
|
|
6410
|
+
if (!this.assertInitialized("render")) return;
|
|
6125
6411
|
(_b = (_a2 = this.ui) == null ? void 0 : _a2.stats) == null ? void 0 : _b.begin();
|
|
6126
|
-
if (this.
|
|
6412
|
+
if (this.isExternalMode) this.renderer.resetState();
|
|
6127
6413
|
else this.resizeCanvasToDisplaySize();
|
|
6128
6414
|
this.viewportSystem.updatePtScale();
|
|
6129
|
-
const delta = this.clock.getDelta();
|
|
6130
6415
|
const hasDefsUpdated = this.layerSystem.processPendingUpdates();
|
|
6131
|
-
const hasControlsUpdated = this.interactionsSystem.updateControls(
|
|
6132
|
-
const needsRedraw = this.needsRedraw || hasControlsUpdated || hasDefsUpdated || this.ui;
|
|
6416
|
+
const hasControlsUpdated = this.interactionsSystem.updateControls(this.clock.getDelta());
|
|
6417
|
+
const needsRedraw = this.needsRedraw || hasControlsUpdated || hasDefsUpdated || this.isExternalMode || this.ui;
|
|
6133
6418
|
if (needsRedraw) {
|
|
6134
6419
|
this.renderer.render(this.viewportSystem.scene, this.viewportSystem.camera);
|
|
6135
6420
|
this.needsRedraw = false;
|
|
@@ -6144,13 +6429,25 @@ class Renderer {
|
|
|
6144
6429
|
stop() {
|
|
6145
6430
|
this.renderer.setAnimationLoop(null);
|
|
6146
6431
|
this.clock.stop();
|
|
6432
|
+
logger.info("stopped");
|
|
6147
6433
|
}
|
|
6148
6434
|
/**
|
|
6149
|
-
* Dispose all WebGL resources
|
|
6435
|
+
* Dispose all WebGL resources. This calls {@link Renderer.stop} internally.
|
|
6436
|
+
* WARNING: This method is final and cannot be undone. Attempting to use the renderer after calling this method
|
|
6437
|
+
* will result in a console warning and no-op methods. If you need to re-initialize the renderer, create a new instance.
|
|
6150
6438
|
*/
|
|
6151
6439
|
dispose() {
|
|
6440
|
+
if (this.disposed) return;
|
|
6441
|
+
this.stop();
|
|
6442
|
+
this.interactionsSystem.dispose();
|
|
6152
6443
|
this.layerSystem.disposeScene();
|
|
6153
6444
|
this.viewportSystem.scene.clear();
|
|
6445
|
+
this.renderer.clear();
|
|
6446
|
+
this.renderer.info.reset();
|
|
6447
|
+
this.renderer.dispose();
|
|
6448
|
+
this.updateMemoryInfo();
|
|
6449
|
+
this.disposed = true;
|
|
6450
|
+
logger.info("disposed");
|
|
6154
6451
|
}
|
|
6155
6452
|
// https://webgl2fundamentals.org/webgl/lessons/webgl-resizing-the-canvas.html
|
|
6156
6453
|
resizeCanvasToDisplaySize() {
|
|
@@ -6159,7 +6456,7 @@ class Renderer {
|
|
|
6159
6456
|
const displayWidth = Math.floor(width * dpr);
|
|
6160
6457
|
const displayHeight = Math.floor(height * dpr);
|
|
6161
6458
|
if (this.canvas.width !== displayWidth || this.canvas.height !== displayHeight || this.renderer.getPixelRatio() !== dpr) {
|
|
6162
|
-
|
|
6459
|
+
logger.debug("renderer resize", width, height, dpr);
|
|
6163
6460
|
this.renderer.setSize(width, height, false);
|
|
6164
6461
|
this.renderer.setPixelRatio(dpr);
|
|
6165
6462
|
this.viewportSystem.updateViewport();
|
|
@@ -6167,35 +6464,93 @@ class Renderer {
|
|
|
6167
6464
|
}
|
|
6168
6465
|
}
|
|
6169
6466
|
updateMemoryInfo() {
|
|
6170
|
-
var _a2;
|
|
6467
|
+
var _a2, _b, _c, _d;
|
|
6171
6468
|
if (this.memoryInfoExtension && ((_a2 = this.ui) == null ? void 0 : _a2.memoryInfoPanel)) {
|
|
6172
6469
|
const memoryInfo = this.memoryInfoExtension.getMemoryInfo();
|
|
6173
6470
|
memoryInfo.resources["drawCalls"] = this.renderer.info.render.calls;
|
|
6174
|
-
|
|
6175
|
-
|
|
6176
|
-
if (memoryInfoContent !== this.memoryInfo) {
|
|
6471
|
+
if (memoryInfo.memory["texture"] !== ((_b = this.memoryInfo) == null ? void 0 : _b.memory["texture"]) || memoryInfo.memory["buffer"] !== ((_c = this.memoryInfo) == null ? void 0 : _c.memory["buffer"]) || memoryInfo.memory["renderbuffer"] !== ((_d = this.memoryInfo) == null ? void 0 : _d.memory["renderbuffer"])) {
|
|
6472
|
+
const elapsedTime = this.clock.getElapsedTime() * 1e3;
|
|
6177
6473
|
const logMarker = `memoryInfo [${elapsedTime.toFixed(2)}ms since start]`;
|
|
6178
|
-
|
|
6179
|
-
|
|
6180
|
-
this.
|
|
6474
|
+
logger.debug(logMarker, memoryInfo);
|
|
6475
|
+
this.memoryInfo = memoryInfo;
|
|
6476
|
+
this.ui.memoryInfoPanel.textContent = JSON.stringify(memoryInfo, null, 2);
|
|
6181
6477
|
}
|
|
6182
|
-
this.ui.memoryInfoPanel.textContent = JSON.stringify(memoryInfo, null, 2);
|
|
6183
6478
|
}
|
|
6184
6479
|
}
|
|
6185
|
-
// FIXME: Test with mapbox
|
|
6186
6480
|
onContextLost(event) {
|
|
6481
|
+
var _a2, _b;
|
|
6187
6482
|
event.preventDefault();
|
|
6188
|
-
|
|
6189
|
-
this.
|
|
6190
|
-
this.
|
|
6191
|
-
if (
|
|
6483
|
+
logger.debug("webglcontextlost event", event);
|
|
6484
|
+
const stats = (_a2 = this.ui) == null ? void 0 : _a2.stats;
|
|
6485
|
+
const context = this.renderer.getContext();
|
|
6486
|
+
if (stats && "deleteQuery" in context) {
|
|
6487
|
+
const gpuQueries = stats.gpuQueries;
|
|
6488
|
+
for (const queryInfo of gpuQueries) {
|
|
6489
|
+
this.renderer.getContext().deleteQuery(queryInfo.query);
|
|
6490
|
+
}
|
|
6491
|
+
stats.gpuQueries = [];
|
|
6492
|
+
if (stats.gpuPanel) {
|
|
6493
|
+
stats.dom.removeChild((_b = stats.gpuPanel) == null ? void 0 : _b.canvas);
|
|
6494
|
+
stats.gpuPanel = null;
|
|
6495
|
+
stats._panelId--;
|
|
6496
|
+
}
|
|
6497
|
+
}
|
|
6498
|
+
this.stop();
|
|
6192
6499
|
}
|
|
6193
6500
|
onContextRestored(event) {
|
|
6194
6501
|
event.preventDefault();
|
|
6195
|
-
|
|
6196
|
-
this.
|
|
6197
|
-
this.
|
|
6502
|
+
logger.debug("webglcontextrestored event", event);
|
|
6503
|
+
this.initContext(this.renderer.getContext());
|
|
6504
|
+
this.needsRedraw = true;
|
|
6505
|
+
this.start();
|
|
6506
|
+
}
|
|
6507
|
+
initContext(context) {
|
|
6508
|
+
var _a2, _b;
|
|
6509
|
+
this.memoryInfoExtension = context.getExtension("GMAN_webgl_memory");
|
|
6510
|
+
void ((_b = (_a2 = this.ui) == null ? void 0 : _a2.stats) == null ? void 0 : _b.init(context));
|
|
6511
|
+
}
|
|
6512
|
+
assertNotDisposed(funcName) {
|
|
6513
|
+
if (this.disposed) {
|
|
6514
|
+
logger.warn(`[${funcName}]: Renderer is used after being disposed. Please create a new instance.`);
|
|
6515
|
+
return false;
|
|
6516
|
+
}
|
|
6517
|
+
return true;
|
|
6198
6518
|
}
|
|
6519
|
+
assertInitialized(funcName) {
|
|
6520
|
+
if (!this.initialized) {
|
|
6521
|
+
logger.warn(`${funcName}: Renderer is not initialized. Please call init() before using it.`);
|
|
6522
|
+
return false;
|
|
6523
|
+
}
|
|
6524
|
+
return true;
|
|
6525
|
+
}
|
|
6526
|
+
assertNotInitialized(funcName) {
|
|
6527
|
+
if (this.initialized) {
|
|
6528
|
+
logger.warn(`${funcName}: Renderer is already initialized. Please call init() only once.`);
|
|
6529
|
+
return false;
|
|
6530
|
+
}
|
|
6531
|
+
return true;
|
|
6532
|
+
}
|
|
6533
|
+
assertNotExternalMode(funcName) {
|
|
6534
|
+
if (this.isExternalMode) {
|
|
6535
|
+
logger.warn(`${funcName}: This operation is not supported in external mode.`);
|
|
6536
|
+
return false;
|
|
6537
|
+
}
|
|
6538
|
+
return true;
|
|
6539
|
+
}
|
|
6540
|
+
assertExternalMode(funcName) {
|
|
6541
|
+
if (!this.isExternalMode) {
|
|
6542
|
+
logger.warn(`${funcName}: This operation is only supported in external mode.`);
|
|
6543
|
+
return false;
|
|
6544
|
+
}
|
|
6545
|
+
return true;
|
|
6546
|
+
}
|
|
6547
|
+
}
|
|
6548
|
+
function guardFn(guard, fn, ...fallback) {
|
|
6549
|
+
return (...args) => {
|
|
6550
|
+
const name = fn.name.split(" ").at(-1);
|
|
6551
|
+
if (!guard(name)) return fallback[0];
|
|
6552
|
+
return fn(...args);
|
|
6553
|
+
};
|
|
6199
6554
|
}
|
|
6200
6555
|
export {
|
|
6201
6556
|
Polygon,
|