@expofp/renderer 2.0.0 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -408,8 +408,6 @@ export declare type RenderableDefCollection<T = RenderableDef> = T extends Rende
408
408
  * Main class for rendering the content of a {@link SceneDef}
409
409
  */
410
410
  export declare class Renderer {
411
- /** Whether to log debug information */
412
- readonly debugLog: boolean;
413
411
  /** {@link HTMLCanvasElement} that this renderer is rendering to */
414
412
  readonly canvas: HTMLCanvasElement;
415
413
  private ui?;
@@ -523,8 +521,6 @@ export declare interface RendererOptions {
523
521
  canvas: HTMLCanvasElement;
524
522
  /** Optional {@link WebGLRenderingContext} to use (e.g. for Mapbox GL JS) */
525
523
  gl?: WebGLRenderingContext;
526
- /** Whether to log debug information */
527
- debugLog?: boolean;
528
524
  /** Optional partial {@link UI} configuration */
529
525
  ui?: Partial<UI>;
530
526
  }
package/dist/index.js CHANGED
@@ -5,9 +5,11 @@ var _a;
5
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
- import { converter, parse } from "culori";
11
+ import { extend, colord } from "colord";
12
+ import namesPlugin from "colord/plugins/names";
11
13
  import { RAD2DEG, DEG2RAD as DEG2RAD$1 } from "three/src/math/MathUtils.js";
12
14
  import { EventManager, Rotate, Pan } from "mjolnir.js";
13
15
  const floatsPerMember = 32;
@@ -366,6 +368,35 @@ const dimColorFrag = (
366
368
  return vec4(m * col.a, col.a);
367
369
  }`
368
370
  );
371
+ function createLogger(namespace) {
372
+ const fullNamespace = namespace ? `renderer:${namespace}` : "renderer";
373
+ const info = createLog(fullNamespace);
374
+ info.log = console.info.bind(console);
375
+ const debug = info.extend("debug");
376
+ debug.log = console.debug.bind(console);
377
+ const warn = info.extend("warn");
378
+ warn.log = console.warn.bind(console);
379
+ const error = info.extend("error");
380
+ error.log = console.error.bind(console);
381
+ return {
382
+ debug,
383
+ info,
384
+ warn,
385
+ error
386
+ };
387
+ }
388
+ function printTree(object, logger2) {
389
+ if (!logger2.enabled) return;
390
+ const isGroup = object.isGroup;
391
+ const name = object.name.split(":").at(-1);
392
+ if (isGroup) {
393
+ console.groupCollapsed(name);
394
+ object.children.forEach((child) => printTree(child, logger2));
395
+ console.groupEnd();
396
+ } else {
397
+ logger2.log(`${name}<${object.type}>, render order: ${object.renderOrder}`);
398
+ }
399
+ }
369
400
  function isObject(item) {
370
401
  return !!item && typeof item === "object" && !Array.isArray(item);
371
402
  }
@@ -394,7 +425,7 @@ function getSquareTextureSize(capacity, pixelsPerInstance) {
394
425
  }
395
426
  function getSquareTextureInfo(arrayType, channels, pixelsPerInstance, capacity) {
396
427
  if (channels === 3) {
397
- console.warn('"channels" cannot be 3. Set to 4. More info: https://github.com/mrdoob/three.js/pull/23228');
428
+ logger$b.debug('"channels" cannot be 3. Set to 4. More info: https://github.com/mrdoob/three.js/pull/23228');
398
429
  channels = 4;
399
430
  }
400
431
  const size = getSquareTextureSize(capacity, pixelsPerInstance);
@@ -416,6 +447,7 @@ function getSquareTextureInfo(arrayType, channels, pixelsPerInstance, capacity)
416
447
  }
417
448
  return { array, size, type, format };
418
449
  }
450
+ const logger$b = createLogger("SquareDataTexture");
419
451
  class SquareDataTexture extends DataTexture {
420
452
  /**
421
453
  * @param arrayType The constructor for the TypedArray.
@@ -453,7 +485,7 @@ class SquareDataTexture extends DataTexture {
453
485
  setUniformAt(id, name, value) {
454
486
  const schema = this.uniformMap.get(name);
455
487
  if (!schema) {
456
- console.warn(`SquareDataTexture.setUniformAt: uniform ${name} not found`);
488
+ logger$b.debug(`setUniformAt: uniform ${name} not found`);
457
489
  return;
458
490
  }
459
491
  const { offset, size } = schema;
@@ -474,7 +506,7 @@ class SquareDataTexture extends DataTexture {
474
506
  getUniformAt(id, name, target) {
475
507
  const schema = this.uniformMap.get(name);
476
508
  if (!schema) {
477
- console.warn(`SquareDataTexture.getUniformAt: uniform ${name} not found`);
509
+ logger$b.debug(`getUniformAt: uniform ${name} not found`);
478
510
  return 0;
479
511
  }
480
512
  const { offset, size } = schema;
@@ -620,6 +652,7 @@ class SquareDataTexture extends DataTexture {
620
652
  }
621
653
  const componentsArray = ["r", "g", "b", "a"];
622
654
  const batchIdName = "batchId";
655
+ const logger$a = createLogger("BatchedMesh");
623
656
  const _BatchedMesh = class _BatchedMesh extends BatchedMesh$1 {
624
657
  /**
625
658
  * @param instanceCount the max number of individual geometries planned to be added.
@@ -699,7 +732,7 @@ const _BatchedMesh = class _BatchedMesh extends BatchedMesh$1 {
699
732
  if (_BatchedMesh.useMultiDraw) return;
700
733
  const batchCount = this.batchCount;
701
734
  const gl = renderer.getContext();
702
- if (geometry.index == null) return console.warn("No index buffer", (_a2 = this.parent) == null ? void 0 : _a2.name);
735
+ if (geometry.index == null) return logger$a.debug("No index buffer", (_a2 = this.parent) == null ? void 0 : _a2.name);
703
736
  const type = this.getIndexType(gl, geometry.index);
704
737
  gl.drawElements(gl.TRIANGLES, batchCount, type, 0);
705
738
  renderer.info.update(batchCount, gl.TRIANGLES, 1);
@@ -714,7 +747,7 @@ const _BatchedMesh = class _BatchedMesh extends BatchedMesh$1 {
714
747
  */
715
748
  getUniformAt(id, name, target) {
716
749
  if (!this.uniformsTexture) {
717
- console.warn(`BatchedMesh.getUniformAt: uniforms texture not initialized`);
750
+ logger$a.debug(`getUniformAt: uniforms texture not initialized`);
718
751
  return 0;
719
752
  }
720
753
  return this.uniformsTexture.getUniformAt(id, name, target);
@@ -727,7 +760,7 @@ const _BatchedMesh = class _BatchedMesh extends BatchedMesh$1 {
727
760
  */
728
761
  setUniformAt(instanceId, name, value) {
729
762
  if (!this.uniformsTexture) {
730
- console.warn(`BatchedMesh.setUniformAt: uniforms texture not initialized`);
763
+ logger$a.debug(`setUniformAt: uniforms texture not initialized`);
731
764
  return;
732
765
  }
733
766
  this.uniformsTexture.setUniformAt(instanceId, name, value);
@@ -1086,12 +1119,14 @@ class RenderableSystem {
1086
1119
  /**
1087
1120
  * @param type readable name of the system's type for debugging
1088
1121
  * @param renderer {@link Renderer}
1122
+ * @param logger {@link Logger}
1089
1123
  */
1090
- constructor(type, renderer) {
1124
+ constructor(type, renderer, logger2) {
1091
1125
  __publicField(this, "mapDefToObject", /* @__PURE__ */ new Map());
1092
1126
  __publicField(this, "mapObjectToDefs", /* @__PURE__ */ new Map());
1093
1127
  this.type = type;
1094
1128
  this.renderer = renderer;
1129
+ this.logger = logger2;
1095
1130
  }
1096
1131
  /**
1097
1132
  * Update a def with its current properties.
@@ -1118,7 +1153,7 @@ class RenderableSystem {
1118
1153
  * @param layerDef {@link TypedLayerDef} layer definition to update
1119
1154
  */
1120
1155
  updateLayer(group, layerDef) {
1121
- if (this.renderer.debugLog) console.log(`Updating ${this.type} layer ${layerDef.name}`, layerDef);
1156
+ this.logger.debug(`Updating layer ${layerDef.name} %O`, layerDef);
1122
1157
  this.updateLayerImpl(group, layerDef);
1123
1158
  }
1124
1159
  /**
@@ -1223,7 +1258,7 @@ class RenderableSystem {
1223
1258
  registerDefObject(def, object, instanceIds) {
1224
1259
  const ids = Array.isArray(instanceIds) ? instanceIds : [instanceIds];
1225
1260
  if (ids.length === 0) {
1226
- console.warn(`[RenderableSystem] Tried to register def with empty instanceIds:`, def);
1261
+ this.logger.debug(`Tried to register def with empty instanceIds %O`, def);
1227
1262
  return;
1228
1263
  }
1229
1264
  this.mapDefToObject.set(def, { object, instanceIds: ids });
@@ -1282,7 +1317,7 @@ class RenderableSystem {
1282
1317
  getObjectInstanceByDef(def) {
1283
1318
  const mapping = this.mapDefToObject.get(def);
1284
1319
  if (!mapping) {
1285
- console.warn(`[RenderableSystem] No object mapping found for def:`, def);
1320
+ this.logger.debug(`No object mapping found for def %O`, def);
1286
1321
  return void 0;
1287
1322
  }
1288
1323
  return mapping;
@@ -1303,13 +1338,14 @@ class RenderableSystem {
1303
1338
  return Array.from(this.mapObjectToDefs.keys());
1304
1339
  }
1305
1340
  }
1341
+ const logger$9 = createLogger("image");
1306
1342
  class ImageSystem extends RenderableSystem {
1307
1343
  /**
1308
1344
  * @param materialSystem {@link MaterialSystem}
1309
1345
  * @param renderer {@link Renderer}
1310
1346
  */
1311
1347
  constructor(materialSystem, renderer) {
1312
- super("image", renderer);
1348
+ super("image", renderer, logger$9);
1313
1349
  /** Textures memory limit in megabytes */
1314
1350
  __publicField(this, "memoryLimitMb");
1315
1351
  __publicField(this, "packer");
@@ -1319,7 +1355,7 @@ class ImageSystem extends RenderableSystem {
1319
1355
  __publicField(this, "scaleMatrix", new Matrix4());
1320
1356
  this.materialSystem = materialSystem;
1321
1357
  const atlasTextureSize = renderer.context.capabilities.maxTextureSize;
1322
- console.log(`Max texture size: ${atlasTextureSize}`);
1358
+ logger$9.debug(`Max texture size: ${atlasTextureSize}`);
1323
1359
  const padding = 1;
1324
1360
  this.packer = new MaxRectsPacker(atlasTextureSize, atlasTextureSize, padding, { pot: false });
1325
1361
  }
@@ -1369,10 +1405,10 @@ class ImageSystem extends RenderableSystem {
1369
1405
  resizeTextures() {
1370
1406
  var _a2;
1371
1407
  if (!this.memoryLimitMb) {
1372
- console.warn("Memory limit is not set, unable to resize textures.");
1408
+ logger$9.debug("Memory limit is not set, unable to resize textures.");
1373
1409
  return;
1374
1410
  }
1375
- console.log(`Resizing textures to fit memory limit: ${this.memoryLimitMb} MB`);
1411
+ logger$9.debug(`Resizing textures to fit memory limit: ${this.memoryLimitMb} MB`);
1376
1412
  const texturesToResize = [];
1377
1413
  let totalResizable = 0;
1378
1414
  let totalNonResizable = 0;
@@ -1389,15 +1425,15 @@ class ImageSystem extends RenderableSystem {
1389
1425
  }
1390
1426
  const budget = this.memoryLimitMb * 1024 * 1024 - totalNonResizable;
1391
1427
  if (budget < 0) {
1392
- console.warn("Memory limit is too low, unable to resize textures.");
1428
+ logger$9.debug("Memory limit is too low, unable to resize textures.");
1393
1429
  return;
1394
1430
  }
1395
1431
  const resizeFactor = Math.sqrt(budget / totalResizable);
1396
1432
  if (resizeFactor >= 1) {
1397
- console.log("Textures are already within the memory limit, no need to resize");
1433
+ logger$9.debug("Textures are already within the memory limit, no need to resize");
1398
1434
  return;
1399
1435
  }
1400
- console.log(`Resize factor: ${resizeFactor}`);
1436
+ logger$9.debug(`Resize factor: ${resizeFactor}`);
1401
1437
  let newTotal = totalNonResizable;
1402
1438
  for (const mesh of texturesToResize) {
1403
1439
  const material = mesh.material;
@@ -1405,14 +1441,14 @@ class ImageSystem extends RenderableSystem {
1405
1441
  const resizedTexture = resizeTexture(texture, resizeFactor);
1406
1442
  const textureDim = `${texture.image.width}x${texture.image.height}`;
1407
1443
  const resizedDim = `${resizedTexture.image.width}x${resizedTexture.image.height}`;
1408
- console.log(`Resized atlas for ${mesh.name || ((_a2 = mesh.parent) == null ? void 0 : _a2.name)}, from ${textureDim} to ${resizedDim}`);
1444
+ logger$9.debug(`Resized atlas for ${mesh.name || ((_a2 = mesh.parent) == null ? void 0 : _a2.name)}, from ${textureDim} to ${resizedDim}`);
1409
1445
  newTotal += getTextureSizeBytes(resizedTexture);
1410
1446
  material.map = resizedTexture;
1411
1447
  material.needsUpdate = true;
1412
1448
  texture.dispose();
1413
1449
  mesh.userData["nonResizable"] = true;
1414
1450
  }
1415
- console.log(`New memory usage after resizing: ${newTotal} bytes`);
1451
+ logger$9.debug(`New memory usage after resizing: ${newTotal} bytes`);
1416
1452
  }
1417
1453
  updateDefImpl(imageDef, mesh, instanceIds) {
1418
1454
  const bounds = imageDef.bounds;
@@ -1441,8 +1477,8 @@ class ImageSystem extends RenderableSystem {
1441
1477
  const boundsArea = boundsWidth * boundsHeight;
1442
1478
  const ratio = sourceArea / boundsArea;
1443
1479
  if (ratio > 1e3) {
1444
- console.log(`Image bounds: ${boundsWidth}x${boundsHeight}`, `Image source: ${sourceWidth}x${sourceHeight}`);
1445
- console.warn(`Image bounds area is ${ratio.toFixed(2)} times smaller than the image.`);
1480
+ logger$9.debug(`Image bounds: ${boundsWidth}x${boundsHeight}`, `Image source: ${sourceWidth}x${sourceHeight}`);
1481
+ logger$9.warn(`Image bounds area is ${ratio.toFixed(2)} times smaller than the image.`);
1446
1482
  }
1447
1483
  const rect = new Rectangle(image.source.width, image.source.height);
1448
1484
  rect.data = [imageWithIndex];
@@ -1453,7 +1489,7 @@ class ImageSystem extends RenderableSystem {
1453
1489
  }
1454
1490
  }
1455
1491
  this.packer.addArray(rectangles);
1456
- this.packer.bins.forEach((bin) => console.log(`Bin: ${bin.width}x${bin.height}, ${bin.rects.length} rectangles`));
1492
+ this.packer.bins.forEach((bin) => logger$9.debug(`Bin: ${bin.width}x${bin.height}, ${bin.rects.length} rectangles`));
1457
1493
  return this.packer.bins;
1458
1494
  }
1459
1495
  }
@@ -1467,7 +1503,7 @@ function createAtlas(bin) {
1467
1503
  ctx.drawImage(rect.data[0].def.source, rect.x, rect.y, rect.width, rect.height);
1468
1504
  }
1469
1505
  const t1 = performance.now();
1470
- console.log(`Create atlas took ${(t1 - t0).toFixed(2)} milliseconds.`);
1506
+ logger$9.debug(`Create atlas took ${(t1 - t0).toFixed(2)} milliseconds.`);
1471
1507
  return createTexture(canvas);
1472
1508
  }
1473
1509
  function resizeTexture(texture, resizeFactor) {
@@ -1490,13 +1526,14 @@ function getTextureSizeBytes(texture) {
1490
1526
  const imageBytes = texture.image.width * texture.image.height * 4 * (texture.generateMipmaps ? 1.33 : 1);
1491
1527
  return Math.ceil(imageBytes);
1492
1528
  }
1529
+ const logger$8 = createLogger("line");
1493
1530
  class LineSystem extends RenderableSystem {
1494
1531
  /**
1495
1532
  * @param materialSystem {@link MaterialSystem}
1496
1533
  * @param renderer {@link Renderer}
1497
1534
  */
1498
1535
  constructor(materialSystem, renderer) {
1499
- super("line", renderer);
1536
+ super("line", renderer, logger$8);
1500
1537
  __publicField(this, "lineColor", new Color());
1501
1538
  this.materialSystem = materialSystem;
1502
1539
  }
@@ -1685,14 +1722,15 @@ class Polygon {
1685
1722
  return this;
1686
1723
  }
1687
1724
  }
1725
+ const logger$7 = createLogger("mesh");
1726
+ extend([namesPlugin]);
1688
1727
  class MeshSystem extends RenderableSystem {
1689
1728
  /**
1690
1729
  * @param materialSystem {@link MaterialSystem}
1691
1730
  * @param renderer {@link Renderer}
1692
1731
  */
1693
1732
  constructor(materialSystem, renderer) {
1694
- super("mesh", renderer);
1695
- __publicField(this, "toRgbConverter", converter("rgb"));
1733
+ super("mesh", renderer, logger$7);
1696
1734
  __publicField(this, "meshColor", new Color());
1697
1735
  this.materialSystem = materialSystem;
1698
1736
  }
@@ -1707,10 +1745,10 @@ class MeshSystem extends RenderableSystem {
1707
1745
  shapes,
1708
1746
  (shapeDef) => {
1709
1747
  var _a2;
1710
- return (((_a2 = mapShapeToNormColor.get(shapeDef)) == null ? void 0 : _a2.alpha) ?? 1) === 1;
1748
+ return (((_a2 = mapShapeToNormColor.get(shapeDef)) == null ? void 0 : _a2.a) ?? 1) === 1;
1711
1749
  }
1712
1750
  );
1713
- const transparentShapesGrouped = groupBy(transparentShapes, (shapeDef) => mapShapeToNormColor.get(shapeDef).alpha);
1751
+ const transparentShapesGrouped = groupBy(transparentShapes, (shapeDef) => mapShapeToNormColor.get(shapeDef).a);
1714
1752
  const group = new Group();
1715
1753
  for (const [opacity, shapes2] of transparentShapesGrouped) {
1716
1754
  const transparentMesh = this.buildBatchedMesh(shapes2, opacity);
@@ -1732,9 +1770,12 @@ class MeshSystem extends RenderableSystem {
1732
1770
  }
1733
1771
  updateDefImpl(shapeDef, mesh, instanceIds) {
1734
1772
  const color = this.normalizeColor(shapeDef.color);
1735
- if (color === void 0) return;
1773
+ if (!color) {
1774
+ logger$7.warn(`Invalid color: ${shapeDef.color} %O`, shapeDef);
1775
+ return;
1776
+ }
1736
1777
  for (const instanceId of instanceIds) {
1737
- mesh.setColorAt(instanceId, this.meshColor.setRGB(color.r, color.g, color.b, SRGBColorSpace));
1778
+ mesh.setColorAt(instanceId, this.meshColor.setRGB(color.r / 255, color.g / 255, color.b / 255, SRGBColorSpace));
1738
1779
  }
1739
1780
  }
1740
1781
  buildBatchedMesh(shapes, opacity = 1) {
@@ -1784,8 +1825,11 @@ class MeshSystem extends RenderableSystem {
1784
1825
  }
1785
1826
  return batchedMesh;
1786
1827
  }
1787
- normalizeColor(color) {
1788
- return typeof color === "string" ? this.toRgbConverter(parse(color)) : this.toRgbConverter(parse(`#${color.toString(16).padStart(6, "0")}`));
1828
+ normalizeColor(colorInput) {
1829
+ const colorString = typeof colorInput === "string" ? colorInput : `#${colorInput.toString(16).padStart(6, "0")}`;
1830
+ const color = colord(colorString);
1831
+ if (!color.isValid()) return void 0;
1832
+ return color.toRgb();
1789
1833
  }
1790
1834
  buildPolygonGeometry(polygon) {
1791
1835
  const geometry = new BufferGeometry().setFromPoints(polygon.vertices).setIndex(polygon.indices.flat());
@@ -1802,13 +1846,14 @@ class MeshSystem extends RenderableSystem {
1802
1846
  });
1803
1847
  }
1804
1848
  }
1849
+ const logger$6 = createLogger("text");
1805
1850
  class TextSystem extends RenderableSystem {
1806
1851
  /**
1807
1852
  * @param materialSystem {@link MaterialSystem}
1808
1853
  * @param renderer {@link Renderer}
1809
1854
  */
1810
1855
  constructor(materialSystem, renderer) {
1811
- super("text", renderer);
1856
+ super("text", renderer, logger$6);
1812
1857
  __publicField(this, "initialTextScale", new Vector2(1, -1));
1813
1858
  __publicField(this, "textColor", new Color());
1814
1859
  __publicField(this, "pendingUpdates", /* @__PURE__ */ new Map());
@@ -1996,6 +2041,7 @@ function setAnchorsAndAlignment(text, alignment) {
1996
2041
  text.anchorY = alignment.vertical === "bottom" ? "bottom" : "top";
1997
2042
  text.textAlign = alignment.text ?? alignment.horizontal;
1998
2043
  }
2044
+ const logger$5 = createLogger("layer");
1999
2045
  class LayerSystem {
2000
2046
  /**
2001
2047
  * @param renderer {@link Renderer}
@@ -2067,12 +2113,8 @@ class LayerSystem {
2067
2113
  this.updateDef(def);
2068
2114
  processed++;
2069
2115
  }
2070
- const took = performance.now() - startTime;
2071
- if (processed && this.renderer.debugLog) {
2072
- console.log(
2073
- `LayerSystem: processed ${processed} defs in ${took.toFixed(2)}ms, ${this.pendingDefs.size} remaining`
2074
- );
2075
- }
2116
+ const took = (performance.now() - startTime).toFixed(2);
2117
+ if (processed) logger$5.debug(`processed ${processed} defs in ${took}ms, ${this.pendingDefs.size} remaining`);
2076
2118
  return processed > 0;
2077
2119
  }
2078
2120
  /**
@@ -2082,11 +2124,8 @@ class LayerSystem {
2082
2124
  */
2083
2125
  buildScene(sceneDef) {
2084
2126
  this.initRenderOrder(sceneDef.rootLayer);
2085
- if (this.renderer.debugLog)
2086
- console.log(
2087
- "Render order",
2088
- this.layerDefRenderOrder.map((layer) => this.getFullLayerName(layer))
2089
- );
2127
+ const renderOrder = this.layerDefRenderOrder.map((layer) => this.getFullLayerName(layer));
2128
+ logger$5.debug("Render order %O", renderOrder);
2090
2129
  const rootGroup = new Group();
2091
2130
  rootGroup.name = sceneDef.rootLayer.name;
2092
2131
  this.mapLayerDefsToObjects.set(sceneDef.rootLayer, rootGroup);
@@ -2095,7 +2134,7 @@ class LayerSystem {
2095
2134
  rootGroup.add(this.buildLayer(child));
2096
2135
  }
2097
2136
  this.updateLayer(sceneDef.rootLayer, false);
2098
- if (this.renderer.debugLog) printTree(rootGroup);
2137
+ printTree(rootGroup, logger$5.debug);
2099
2138
  if (sceneDef.memoryLimit) {
2100
2139
  this.imageSystem.memoryLimitMb = sceneDef.memoryLimit;
2101
2140
  this.imageSystem.resizeTextures();
@@ -2139,7 +2178,7 @@ class LayerSystem {
2139
2178
  }
2140
2179
  buildLayer(layerDef, parentPrefix = "") {
2141
2180
  const layerFullName = parentPrefix + layerDef.name;
2142
- console.log(`Building layer ${layerFullName}...`);
2181
+ logger$5.debug(`Building layer ${layerFullName}...`);
2143
2182
  let layerObject;
2144
2183
  if (isShapeLayer(layerDef)) layerObject = this.meshSystem.buildLayer(layerDef);
2145
2184
  else if (isImageLayer(layerDef)) layerObject = this.imageSystem.buildLayer(layerDef);
@@ -2209,19 +2248,6 @@ class LayerSystem {
2209
2248
  return fullName;
2210
2249
  }
2211
2250
  }
2212
- function printTree(object, fullName = false) {
2213
- object.traverse((obj) => {
2214
- let s = "";
2215
- let obj2 = obj;
2216
- while (obj2 !== object) {
2217
- s = "|___ " + s;
2218
- obj2 = (obj2 == null ? void 0 : obj2.parent) ?? null;
2219
- }
2220
- const renderOrder = obj.isGroup ? "" : `, RO: ${obj.renderOrder}`;
2221
- const name = fullName ? obj.name : obj.name.split(":").at(-1);
2222
- console.log(`${s}${name}<${obj.type}>${renderOrder}`);
2223
- });
2224
- }
2225
2251
  /*!
2226
2252
  * camera-controls
2227
2253
  * https://github.com/yomotsu/camera-controls
@@ -4750,14 +4776,13 @@ const subsetOfTHREE = {
4750
4776
  Plane
4751
4777
  };
4752
4778
  CameraControls.install({ THREE: subsetOfTHREE });
4779
+ const logger$4 = createLogger("cameraController");
4753
4780
  class CameraController extends CameraControls {
4754
4781
  /**
4755
4782
  * @param camera {@link PerspectiveCamera} instance
4756
- * @param renderer {@link Renderer} instance
4757
4783
  */
4758
- constructor(camera, renderer) {
4784
+ constructor(camera) {
4759
4785
  super(camera);
4760
- this.renderer = renderer;
4761
4786
  this.dollyToCursor = true;
4762
4787
  this.draggingSmoothTime = 0;
4763
4788
  void this.rotatePolarTo(0, false);
@@ -4774,15 +4799,13 @@ class CameraController extends CameraControls {
4774
4799
  };
4775
4800
  }
4776
4801
  update(delta) {
4777
- var _a2;
4778
4802
  const needsUpdate = super.update(delta);
4779
- if (needsUpdate && ((_a2 = this.renderer) == null ? void 0 : _a2.debugLog)) {
4780
- const position = this.camera.position.toArray().map((value) => value.toFixed(2)).join(", ");
4781
- const target = this._target.toArray().map((value) => value.toFixed(2)).join(", ");
4782
- const spherical = [this._spherical.theta * RAD2DEG, this._spherical.phi * RAD2DEG, this._spherical.radius].map((value) => value.toFixed(2)).join(", ");
4783
- console.log(`position: [${position}]
4784
- target: [${target}]
4785
- spherical: [${spherical}]`);
4803
+ if (needsUpdate) {
4804
+ const position = this.camera.position.toArray().map((value) => +value.toFixed(2));
4805
+ const target = this._target.toArray().map((value) => +value.toFixed(2));
4806
+ const { theta, phi, radius } = this._spherical;
4807
+ const spherical = [theta * RAD2DEG, phi * RAD2DEG, radius].map((value) => +value.toFixed(2));
4808
+ logger$4.debug("camera update %O", { position, target, spherical });
4786
4809
  }
4787
4810
  return needsUpdate;
4788
4811
  }
@@ -4810,7 +4833,7 @@ class CameraSystem {
4810
4833
  this.prevViewportHeightPx = h;
4811
4834
  this.camera = new PerspectiveCamera(this.defaultFov);
4812
4835
  this.camera.up.set(0, 0, -1);
4813
- this.controller = new CameraController(this.camera, this.renderer);
4836
+ this.controller = new CameraController(this.camera);
4814
4837
  this.controller.distance = this.zoomIdentityDistance;
4815
4838
  }
4816
4839
  /** Current camera zoom factor. */
@@ -4823,6 +4846,7 @@ class CameraSystem {
4823
4846
  * @returns Corresponding camera distance on the Z axis
4824
4847
  */
4825
4848
  zoomFactorToDistance(zoomFactor) {
4849
+ if (zoomFactor <= 0) return this.zoomIdentityDistance;
4826
4850
  return this.zoomIdentityDistance / zoomFactor;
4827
4851
  }
4828
4852
  /**
@@ -4878,6 +4902,7 @@ class CameraSystem {
4878
4902
  return zid / (this.controller.distance || zid);
4879
4903
  }
4880
4904
  }
4905
+ const logger$3 = createLogger("external");
4881
4906
  class ExternalSystem {
4882
4907
  /**
4883
4908
  * @param pickingSystem {@link PickingSystem} instance
@@ -4974,7 +4999,7 @@ class ExternalSystem {
4974
4999
  }
4975
5000
  validateMatrix(matrix, name) {
4976
5001
  if (matrix.length !== 16) {
4977
- console.warn(`[ViewportSystem.${name}]: Matrix must be 16 elements long`);
5002
+ logger$3.warn(`${name}: Matrix must be 16 elements long`);
4978
5003
  return false;
4979
5004
  }
4980
5005
  return true;
@@ -5102,6 +5127,7 @@ class SceneSystem {
5102
5127
  this.scene.matrixWorldNeedsUpdate = true;
5103
5128
  }
5104
5129
  }
5130
+ const logger$2 = createLogger("viewport");
5105
5131
  class ViewportSystem {
5106
5132
  /**
5107
5133
  * @param renderer {@link Renderer} instance
@@ -5172,7 +5198,7 @@ class ViewportSystem {
5172
5198
  updatePtScale() {
5173
5199
  const pxToSvgScale = this.pxToSvgScale;
5174
5200
  if (Math.abs(pxToSvgScale - (this.prevPxToSvgScale ?? 0)) < this.pxToSvgScaleThreshold) return;
5175
- if (this.renderer.debugLog) console.log("pxToSvgScale", +pxToSvgScale.toFixed(3));
5201
+ logger$2.debug(`pxToSvgScale ${+pxToSvgScale.toFixed(3)}`);
5176
5202
  this.eventSystem.emit("viewport:ptscale", pxToSvgScale);
5177
5203
  this.prevPxToSvgScale = pxToSvgScale;
5178
5204
  }
@@ -5219,9 +5245,13 @@ class ViewportSystem {
5219
5245
  * @returns Point in NDC space
5220
5246
  */
5221
5247
  canvasToNDC(point, out = new Vector2()) {
5248
+ const [w, h] = this.renderer.size;
5249
+ if (w <= 0 || h <= 0) {
5250
+ logger$2.warn("canvasToNDC: renderer size is 0");
5251
+ return out.set(0, 0);
5252
+ }
5222
5253
  const dpr = this.renderer.context.getPixelRatio();
5223
- const [width, height] = [this.renderer.size[0] / dpr, this.renderer.size[1] / dpr];
5224
- const uv = [point.x / width, point.y / height];
5254
+ const uv = [point.x / (w / dpr), point.y / (h / dpr)];
5225
5255
  const ndc = [uv[0] * 2 - 1, -uv[1] * 2 + 1];
5226
5256
  return out.set(MathUtils.clamp(ndc[0], -1, 1), MathUtils.clamp(ndc[1], -1, 1));
5227
5257
  }
@@ -5271,6 +5301,7 @@ function asViewportAPI(viewportSystem) {
5271
5301
  function eventToCanvas(event) {
5272
5302
  return { x: event.offsetX, y: event.offsetY };
5273
5303
  }
5304
+ const logger$1 = createLogger("controls");
5274
5305
  class ControlsSystem {
5275
5306
  /**
5276
5307
  * @param renderer {@link Renderer} instance
@@ -5321,6 +5352,10 @@ class ControlsSystem {
5321
5352
  [Math.min(...xValues), Math.min(...yValues)],
5322
5353
  [Math.max(...xValues), Math.max(...yValues)]
5323
5354
  );
5355
+ if (sourceRect.size.x <= 0 || sourceRect.size.y <= 0) {
5356
+ logger$1.warn("zoomTo: sourceRect size is 0");
5357
+ return;
5358
+ }
5324
5359
  const targetRect = visibleRect ? new Rect(visibleRect.min.clone().multiplyScalar(dpr), visibleRect.max.clone().multiplyScalar(dpr)) : new Rect([0, 0], [...this.renderer.size]);
5325
5360
  if (paddingPercent) targetRect.addPadding(targetRect.size.x * paddingPercent, targetRect.size.y * paddingPercent);
5326
5361
  const zoomByWidth = targetRect.size.x / sourceRect.size.x;
@@ -5329,7 +5364,7 @@ class ControlsSystem {
5329
5364
  const zoom = maxZoom ? Math.min(minZoom, maxZoom) : minZoom;
5330
5365
  const translate = sourceRect.center;
5331
5366
  if (visibleRect) {
5332
- const offset = new Vector2(...this.renderer.size).multiplyScalar(0.5).sub(targetRect.center).multiplyScalar(1 / zoom).rotateAround({ x: 0, y: 0 }, bearingAngle);
5367
+ const offset = new Vector2(...this.renderer.size).multiplyScalar(0.5).sub(targetRect.center).multiplyScalar(1 / (zoom || 1)).rotateAround({ x: 0, y: 0 }, bearingAngle);
5333
5368
  translate.add(offset);
5334
5369
  }
5335
5370
  const enableTransition = !immediate;
@@ -5390,7 +5425,6 @@ class ControlsSystem {
5390
5425
  const spherical = this.controller.getSpherical(new Spherical());
5391
5426
  const azimuthAngle = spherical.theta;
5392
5427
  const deltaAngleRad = shortestRotationAngle(targetAngleRad, azimuthAngle);
5393
- console.log("rollTo", deltaAngleRad * RAD2DEG, targetAngleRad * RAD2DEG, azimuthAngle * RAD2DEG);
5394
5428
  return this.rollBy(-deltaAngleRad * RAD2DEG, immediate);
5395
5429
  }
5396
5430
  /**
@@ -6186,14 +6220,12 @@ class InteractionsSystem {
6186
6220
  this.canvasListeners = void 0;
6187
6221
  }
6188
6222
  }
6223
+ const logger = createLogger("");
6189
6224
  class Renderer {
6190
6225
  /**
6191
6226
  * @param opts {@link RendererOptions}
6192
6227
  */
6193
6228
  constructor(opts) {
6194
- /** Whether to log debug information */
6195
- __publicField(this, "debugLog");
6196
- //FIXME: Add https://www.npmjs.com/package/debug
6197
6229
  /** {@link HTMLCanvasElement} that this renderer is rendering to */
6198
6230
  __publicField(this, "canvas");
6199
6231
  __publicField(this, "ui");
@@ -6214,9 +6246,8 @@ class Renderer {
6214
6246
  __publicField(this, "initialized", false);
6215
6247
  __publicField(this, "disposed", false);
6216
6248
  __publicField(this, "needsRedraw", true);
6217
- const { canvas, gl, debugLog = false, ui } = opts;
6249
+ const { canvas, gl, ui } = opts;
6218
6250
  this.canvas = canvas;
6219
- this.debugLog = debugLog;
6220
6251
  this.ui = ui;
6221
6252
  this.gl = gl;
6222
6253
  const rendererOptions = {
@@ -6234,6 +6265,8 @@ class Renderer {
6234
6265
  this.layerSystem = new LayerSystem(this);
6235
6266
  this.interactionsSystem = new InteractionsSystem(this, this.eventSystem, this.viewportSystem, this.layerSystem);
6236
6267
  this.controlsSystem = new ControlsSystem(this, this.viewportSystem, this.interactionsSystem);
6268
+ this.canvas.addEventListener("webglcontextlost", (e) => this.onContextLost(e), false);
6269
+ this.canvas.addEventListener("webglcontextrestored", (e) => this.onContextRestored(e), false);
6237
6270
  this.initContext(this.renderer.getContext());
6238
6271
  BatchedMesh.useMultiDraw = this.renderer.extensions.has("WEBGL_multi_draw");
6239
6272
  }
@@ -6353,6 +6386,7 @@ class Renderer {
6353
6386
  this.interactionsSystem.init();
6354
6387
  this.viewportSystem.scene.add(this.layerSystem.buildScene(sceneDef));
6355
6388
  this.initialized = true;
6389
+ logger.info("initialized");
6356
6390
  }
6357
6391
  /**
6358
6392
  * Start the rendering loop
@@ -6363,6 +6397,7 @@ class Renderer {
6363
6397
  if (this.clock.running) return;
6364
6398
  this.clock.start();
6365
6399
  this.renderer.setAnimationLoop(() => this.render());
6400
+ logger.info("started");
6366
6401
  }
6367
6402
  /**
6368
6403
  * Update the given defs to make them reflect the current state
@@ -6402,6 +6437,7 @@ class Renderer {
6402
6437
  stop() {
6403
6438
  this.renderer.setAnimationLoop(null);
6404
6439
  this.clock.stop();
6440
+ logger.info("stopped");
6405
6441
  }
6406
6442
  /**
6407
6443
  * Dispose all WebGL resources. This calls {@link Renderer.stop} internally.
@@ -6419,6 +6455,7 @@ class Renderer {
6419
6455
  this.renderer.dispose();
6420
6456
  this.updateMemoryInfo();
6421
6457
  this.disposed = true;
6458
+ logger.info("disposed");
6422
6459
  }
6423
6460
  // https://webgl2fundamentals.org/webgl/lessons/webgl-resizing-the-canvas.html
6424
6461
  resizeCanvasToDisplaySize() {
@@ -6427,7 +6464,7 @@ class Renderer {
6427
6464
  const displayWidth = Math.floor(width * dpr);
6428
6465
  const displayHeight = Math.floor(height * dpr);
6429
6466
  if (this.canvas.width !== displayWidth || this.canvas.height !== displayHeight || this.renderer.getPixelRatio() !== dpr) {
6430
- if (this.debugLog) console.log("renderer resize", width, height, dpr);
6467
+ logger.debug("renderer resize", width, height, dpr);
6431
6468
  this.renderer.setSize(width, height, false);
6432
6469
  this.renderer.setPixelRatio(dpr);
6433
6470
  this.viewportSystem.updateViewport();
@@ -6442,7 +6479,7 @@ class Renderer {
6442
6479
  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"])) {
6443
6480
  const elapsedTime = this.clock.getElapsedTime() * 1e3;
6444
6481
  const logMarker = `memoryInfo [${elapsedTime.toFixed(2)}ms since start]`;
6445
- if (this.debugLog) console.log(logMarker, memoryInfo);
6482
+ logger.debug(logMarker, memoryInfo);
6446
6483
  this.memoryInfo = memoryInfo;
6447
6484
  this.ui.memoryInfoPanel.textContent = JSON.stringify(memoryInfo, null, 2);
6448
6485
  }
@@ -6451,7 +6488,7 @@ class Renderer {
6451
6488
  onContextLost(event) {
6452
6489
  var _a2, _b;
6453
6490
  event.preventDefault();
6454
- console.log("webglcontextlost event", event);
6491
+ logger.debug("webglcontextlost event", event);
6455
6492
  const stats = (_a2 = this.ui) == null ? void 0 : _a2.stats;
6456
6493
  const context = this.renderer.getContext();
6457
6494
  if (stats && "deleteQuery" in context) {
@@ -6470,7 +6507,7 @@ class Renderer {
6470
6507
  }
6471
6508
  onContextRestored(event) {
6472
6509
  event.preventDefault();
6473
- console.log("webglcontextrestored event", event);
6510
+ logger.debug("webglcontextrestored event", event);
6474
6511
  this.initContext(this.renderer.getContext());
6475
6512
  this.needsRedraw = true;
6476
6513
  this.start();
@@ -6482,35 +6519,35 @@ class Renderer {
6482
6519
  }
6483
6520
  assertNotDisposed(funcName) {
6484
6521
  if (this.disposed) {
6485
- console.warn(`[Renderer.${funcName}]: Renderer is used after being disposed. Please create a new instance.`);
6522
+ logger.warn(`[${funcName}]: Renderer is used after being disposed. Please create a new instance.`);
6486
6523
  return false;
6487
6524
  }
6488
6525
  return true;
6489
6526
  }
6490
6527
  assertInitialized(funcName) {
6491
6528
  if (!this.initialized) {
6492
- console.warn(`[Renderer.${funcName}]: Renderer is not initialized. Please call init() before using it.`);
6529
+ logger.warn(`${funcName}: Renderer is not initialized. Please call init() before using it.`);
6493
6530
  return false;
6494
6531
  }
6495
6532
  return true;
6496
6533
  }
6497
6534
  assertNotInitialized(funcName) {
6498
6535
  if (this.initialized) {
6499
- console.warn(`[Renderer.${funcName}]: Renderer is already initialized. Please call init() only once.`);
6536
+ logger.warn(`${funcName}: Renderer is already initialized. Please call init() only once.`);
6500
6537
  return false;
6501
6538
  }
6502
6539
  return true;
6503
6540
  }
6504
6541
  assertNotExternalMode(funcName) {
6505
6542
  if (this.isExternalMode) {
6506
- console.warn(`[Renderer.${funcName}]: This operation is not supported in external mode.`);
6543
+ logger.warn(`${funcName}: This operation is not supported in external mode.`);
6507
6544
  return false;
6508
6545
  }
6509
6546
  return true;
6510
6547
  }
6511
6548
  assertExternalMode(funcName) {
6512
6549
  if (!this.isExternalMode) {
6513
- console.warn(`[Renderer.${funcName}]: This operation is only supported in external mode.`);
6550
+ logger.warn(`${funcName}: This operation is only supported in external mode.`);
6514
6551
  return false;
6515
6552
  }
6516
6553
  return true;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@expofp/renderer",
3
- "version": "2.0.0",
3
+ "version": "2.1.0",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist"
@@ -10,18 +10,20 @@
10
10
  },
11
11
  "exports": "./dist/index.js",
12
12
  "devDependencies": {
13
- "@types/culori": "^2.1.1",
13
+ "@types/debug": "^4.1.12",
14
14
  "@types/object-hash": "^3.0.6",
15
15
  "@types/three": "^0.174.0",
16
16
  "stats-gl": "^3.6.0",
17
17
  "typescript": "^5.2.2",
18
18
  "vite": "^5.2.0",
19
+ "vite-bundle-analyzer": "^1.3.2",
19
20
  "vite-plugin-dts": "^4.5.4",
20
21
  "vite-plugin-externalize-deps": "^0.9.0"
21
22
  },
22
23
  "dependencies": {
23
24
  "camera-controls": "^3.1.1",
24
- "culori": "^4.0.1",
25
+ "colord": "^2.9.3",
26
+ "debug": "^4.4.3",
25
27
  "maxrects-packer": "^2.7.3",
26
28
  "mjolnir.js": "^3.0.0",
27
29
  "three": "^0.174.0",