@expofp/renderer 2.2.0 → 2.3.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
@@ -691,6 +691,16 @@ export declare interface ViewportAPI {
691
691
  * @param dynamicTransformMatrix dynamic transform matrix (changes every frame)
692
692
  */
693
693
  setDynamicTransform: (dynamicTransformMatrix: number[]) => void;
694
+ /**
695
+ * Set the maximum zoom factor. Default is 35.
696
+ * @param maxZoom Maximum zoom factor
697
+ */
698
+ setMaxZoom: (maxZoom: number) => void;
699
+ /**
700
+ * Set the minimum zoom factor. Default is 0.1.
701
+ * @param minZoom Minimum zoom factor
702
+ */
703
+ setMinZoom: (minZoom: number) => void;
694
704
  }
695
705
 
696
706
  /** Options for the {@link ControlsAPI.zoomTo} method. */
package/dist/index.js CHANGED
@@ -2,7 +2,7 @@ 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 { DataTexture, FloatType, UnsignedIntType, IntType, RGBAFormat, RGBAIntegerFormat, RGFormat, RGIntegerFormat, RedFormat, RedIntegerFormat, BatchedMesh as BatchedMesh$1, BufferAttribute, StreamDrawUsage, Color, Matrix4, Vector3, Vector4, AlwaysDepth, DoubleSide, MeshBasicMaterial, Texture, Group, PlaneGeometry, SRGBColorSpace, Vector2, Quaternion, BufferGeometry, Mesh, LessEqualDepth, LinearSRGBColorSpace, Plane, Raycaster, Sphere, Box3, Spherical, PerspectiveCamera, Camera, Scene, MathUtils, Clock, WebGLRenderer } from "three";
5
+ import { DataTexture, FloatType, UnsignedIntType, IntType, RGBAFormat, RGBAIntegerFormat, RGFormat, RGIntegerFormat, RedFormat, RedIntegerFormat, BatchedMesh as BatchedMesh$1, BufferAttribute, StreamDrawUsage, Color, Matrix4, Vector3, Frustum, Sphere, Box3, Vector4, AlwaysDepth, DoubleSide, MeshBasicMaterial, Texture, Group, PlaneGeometry, SRGBColorSpace, Vector2, Quaternion, BufferGeometry, Mesh, LessEqualDepth, LinearSRGBColorSpace, Plane, Raycaster, Spherical, PerspectiveCamera, Camera, Scene, MathUtils, Clock, WebGLRenderer } from "three";
6
6
  import { traverseAncestorsGenerator } from "three/examples/jsm/utils/SceneUtils.js";
7
7
  import createLog from "debug";
8
8
  import { BatchedText as BatchedText$1, Text as Text$1 } from "troika-three-text";
@@ -551,9 +551,12 @@ class BatchedText extends BatchedText$1 {
551
551
  // eslint-disable-next-line jsdoc/require-jsdoc
552
552
  constructor() {
553
553
  super();
554
- __publicField(this, "mapInstanceIdToText", /* @__PURE__ */ new Map());
555
554
  __publicField(this, "textArray", []);
556
555
  __publicField(this, "textureNeedsUpdate", false);
556
+ __publicField(this, "boundsNeedsUpdate", false);
557
+ this.addEventListener("synccomplete", () => {
558
+ this.boundsNeedsUpdate = true;
559
+ });
557
560
  }
558
561
  /** Number of texts in the batch */
559
562
  get size() {
@@ -569,7 +572,13 @@ class BatchedText extends BatchedText$1 {
569
572
  * @returns Text object
570
573
  */
571
574
  getText(instanceId) {
572
- return this.mapInstanceIdToText.get(instanceId);
575
+ return this.textArray[instanceId];
576
+ }
577
+ /**
578
+ * Mark this BatchedText as needing bounds update. This should be called when changing text transforms.
579
+ */
580
+ invalidateBounds() {
581
+ this.boundsNeedsUpdate = true;
573
582
  }
574
583
  /**
575
584
  * Set the visibility of the {@link Text} object by instance id.
@@ -583,17 +592,39 @@ class BatchedText extends BatchedText$1 {
583
592
  }
584
593
  addText(text, instanceId) {
585
594
  super.addText(text);
586
- if (instanceId !== void 0) {
587
- this.mapInstanceIdToText.set(instanceId, text);
588
- }
589
- this.textArray.push(text);
595
+ if (instanceId !== void 0) this.textArray[instanceId] = text;
596
+ this.boundsNeedsUpdate = true;
597
+ }
598
+ removeText(text) {
599
+ super.removeText(text);
600
+ this.textArray.splice(this.textArray.indexOf(text), 1);
601
+ this.boundsNeedsUpdate = true;
590
602
  }
591
603
  dispose() {
592
604
  super.dispose();
605
+ this.textArray.length = 0;
593
606
  this.dispatchEvent({ type: "dispose" });
594
607
  }
595
- // TODO: Check performance
596
- _prepareForRender(material) {
608
+ updateBounds() {
609
+ if (this.boundsNeedsUpdate) {
610
+ const bbox = this.geometry.boundingBox.makeEmpty();
611
+ for (const text of this.textArray) {
612
+ if (!text.visible) continue;
613
+ if (text.matrixAutoUpdate) text.updateMatrix();
614
+ _box$1.copy(text.geometry.boundingBox).applyMatrix4(text.matrix);
615
+ bbox.union(_box$1);
616
+ }
617
+ bbox.getBoundingSphere(this.geometry.boundingSphere);
618
+ this.boundsNeedsUpdate = false;
619
+ }
620
+ }
621
+ onBeforeRender(renderer, scene, camera, geometry, material) {
622
+ this.sync();
623
+ if ("isTroikaTextMaterial" in material && material.isTroikaTextMaterial) {
624
+ this.prepareForRender(material, scene, camera);
625
+ }
626
+ }
627
+ prepareForRender(material, scene, camera) {
597
628
  var _a2;
598
629
  const isOutline = material.isTextOutlineMaterial;
599
630
  material.uniforms.uTroikaIsOutline.value = isOutline;
@@ -612,12 +643,15 @@ class BatchedText extends BatchedText$1 {
612
643
  }
613
644
  const texData = texture.image.data;
614
645
  this.textureNeedsUpdate = false;
646
+ _matrix$1.multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse).multiply(scene.matrixWorld);
647
+ _frustum$1.setFromProjectionMatrix(_matrix$1, camera.coordinateSystem);
615
648
  for (const text of this.textArray) {
616
649
  const index = ((_a2 = this._members.get(text)) == null ? void 0 : _a2.index) ?? -1;
617
650
  const textRenderInfo = text.textRenderInfo;
618
651
  if (index < 0 || !textRenderInfo) continue;
619
652
  const startIndex = index * floatsPerMember;
620
- if (!text.visible) {
653
+ const isHidden = !text.visible || !_frustum$1.intersectsSphere(_sphere$1.copy(text.geometry.boundingSphere).applyMatrix4(text.matrix));
654
+ if (isHidden) {
621
655
  for (let i = 0; i < 16; i++) {
622
656
  this.setTexData(startIndex + i, 0, texData);
623
657
  }
@@ -732,7 +766,6 @@ class Text extends Text$1 {
732
766
  blockBounds[3] + pad
733
767
  );
734
768
  }
735
- this.geometry.applyClipRect(uniforms.uTroikaClipRect.value);
736
769
  }
737
770
  uniforms.uTroikaSDFDebug.value = !!this.debugSDF;
738
771
  material.polygonOffset = !!this.depthOffset;
@@ -764,6 +797,10 @@ class Text extends Text$1 {
764
797
  }
765
798
  }
766
799
  }
800
+ const _matrix$1 = new Matrix4();
801
+ const _frustum$1 = new Frustum();
802
+ const _sphere$1 = new Sphere();
803
+ const _box$1 = new Box3();
767
804
  function setDimming(root, dim) {
768
805
  root.userData["uDim"] = dim === void 0 ? void 0 : +dim;
769
806
  }
@@ -1842,7 +1879,8 @@ class MeshSystem extends RenderableSystem {
1842
1879
  }
1843
1880
  updateShape(shapeDef, mesh, instanceId) {
1844
1881
  const geometryId = mesh.getGeometryIdAt(instanceId);
1845
- const expectedShapeType = this.mapInstanceIdToShapeType.get(instanceId);
1882
+ const key = `${mesh.uuid}-${instanceId}`;
1883
+ const expectedShapeType = this.mapInstanceIdToShapeType.get(key);
1846
1884
  const shape = shapeDef.shape;
1847
1885
  const isPolygon = shape instanceof Polygon;
1848
1886
  const isRect = shape instanceof Rect;
@@ -1898,16 +1936,19 @@ class MeshSystem extends RenderableSystem {
1898
1936
  batchedMesh.setCustomSort((list) => this.sortInstances(batchedMesh, list));
1899
1937
  for (const shapeDef of shapes) {
1900
1938
  let instanceId;
1939
+ let type;
1901
1940
  if (shapeDef.shape instanceof Rect && rectGeometryId !== void 0) {
1902
1941
  instanceId = batchedMesh.addInstance(rectGeometryId);
1903
- this.mapInstanceIdToShapeType.set(instanceId, "rect");
1942
+ type = "rect";
1904
1943
  } else {
1905
1944
  const geometry = shapeDefToGeometry.get(shapeDef);
1906
1945
  const { vertices, indices } = countGeometry(geometry);
1907
1946
  const polygonGeometryId = batchedMesh.addGeometry(this.placeholderPolygonGeometry, vertices, indices);
1908
1947
  instanceId = batchedMesh.addInstance(polygonGeometryId);
1909
- this.mapInstanceIdToShapeType.set(instanceId, "polygon");
1948
+ type = "polygon";
1910
1949
  }
1950
+ const key = `${batchedMesh.uuid}-${instanceId}`;
1951
+ this.mapInstanceIdToShapeType.set(key, type);
1911
1952
  this.registerDefObject(shapeDef, batchedMesh, instanceId);
1912
1953
  }
1913
1954
  return batchedMesh;
@@ -1997,19 +2038,30 @@ class TextSystem extends RenderableSystem {
1997
2038
  group.add(newBatchedText);
1998
2039
  }
1999
2040
  updateDefImpl(textDef, mesh, instanceIds) {
2000
- for (const [i, line] of textDef.lines.entries()) {
2041
+ const lines = [];
2042
+ for (let i = 0; i < textDef.lines.length; i++) {
2043
+ const line = textDef.lines[i];
2001
2044
  const text = mesh.getText(instanceIds[i]);
2002
- if (text) {
2003
- text.text = line.text;
2004
- text.color = this.textColor.set(line.color).getHex(LinearSRGBColorSpace);
2005
- text.font = line.fontUrl;
2006
- if (line.stroke) {
2007
- text.outlineColor = line.stroke.color;
2008
- text.outlineWidth = line.stroke.width / line.fontSize;
2009
- }
2045
+ if (line.fontSize === 0) {
2046
+ text.visible = false;
2047
+ continue;
2048
+ }
2049
+ text.visible = true;
2050
+ text.text = line.text;
2051
+ text.color = this.textColor.set(line.color).getHex(LinearSRGBColorSpace);
2052
+ text.font = line.fontUrl;
2053
+ if (line.stroke) {
2054
+ text.outlineColor = line.stroke.color;
2055
+ text.outlineWidth = line.fontSize == 0 ? 0 : line.stroke.width / line.fontSize;
2010
2056
  }
2057
+ const fontSize = line.fontSize;
2058
+ const height = line.text.split("\n").length * text.lineHeight * fontSize;
2059
+ lines.push({ text, fontSize, height });
2060
+ }
2061
+ if (lines.length > 0) {
2062
+ if (textDef.alignment.vertical === "bottom") lines.reverse();
2063
+ this.updateScale(textDef, mesh, lines);
2011
2064
  }
2012
- this.updateScale(textDef);
2013
2065
  }
2014
2066
  buildBatchedText(layer) {
2015
2067
  const textDefs = layer.children;
@@ -2024,6 +2076,7 @@ class TextSystem extends RenderableSystem {
2024
2076
  text.fontSize = 1;
2025
2077
  text.lineHeight = 1.1;
2026
2078
  text.whiteSpace = "nowrap";
2079
+ text.clipRect = [0, 0, 0, 0];
2027
2080
  batchedText.addText(text, instanceId);
2028
2081
  instanceIds.push(instanceId);
2029
2082
  instanceId++;
@@ -2043,9 +2096,8 @@ class TextSystem extends RenderableSystem {
2043
2096
  return batchedText;
2044
2097
  }
2045
2098
  // TODO: Simplify
2046
- updateScale(textDef) {
2099
+ updateScale(textDef, mesh, lines) {
2047
2100
  const dpr = this.renderer.context.getPixelRatio();
2048
- const lines = this.getTextLines(textDef);
2049
2101
  this.calculateStartInBoundsPosition(
2050
2102
  textDef,
2051
2103
  lines,
@@ -2053,41 +2105,18 @@ class TextSystem extends RenderableSystem {
2053
2105
  this.alignmentOffset,
2054
2106
  this.localPosition
2055
2107
  );
2056
- for (const { text, fontSize, height, alignment } of lines) {
2057
- if (!fontSize || !height) {
2058
- text.visible = false;
2059
- continue;
2060
- }
2061
- text.visible = true;
2062
- setAnchorsAndAlignment(text, alignment);
2108
+ for (const { text, fontSize, height } of lines) {
2109
+ setAnchorsAndAlignment(text, textDef.alignment);
2063
2110
  this.worldPosition.copy(this.localPosition).rotateAround({ x: 0, y: 0 }, textDef.bounds.rotation).add(textDef.bounds.center);
2064
2111
  this.textScale.copy(this.initialTextScale).multiplyScalar(fontSize * dpr);
2065
2112
  text.scale.set(this.textScale.x, this.textScale.y, 1);
2066
2113
  text.position.set(this.worldPosition.x, this.worldPosition.y, 0);
2067
2114
  text.rotation.set(0, 0, textDef.bounds.rotation);
2068
- text.clipRect = this.calculateClipRect(
2069
- textDef,
2070
- this.localPosition,
2071
- this.textScale,
2072
- this.localToMin,
2073
- this.localToMax
2074
- );
2115
+ this.calculateClipRect(text, textDef, this.localPosition, this.textScale, this.localToMin, this.localToMax);
2075
2116
  this.localPosition.y += height * dpr;
2117
+ mesh.invalidateBounds();
2076
2118
  }
2077
2119
  }
2078
- getTextLines(textDef) {
2079
- const { object: mesh, instanceIds } = this.getObjectInstanceByDef(textDef);
2080
- const alignment = textDef.alignment;
2081
- const lines = instanceIds.map((instanceId, i) => {
2082
- const text = mesh.getText(instanceId);
2083
- const line = textDef.lines[i];
2084
- const fontSize = line.fontSize;
2085
- const height = fontSize ? text.text.split("\n").length * text.lineHeight * fontSize : 0;
2086
- return { text, fontSize, height, alignment };
2087
- });
2088
- if (alignment.vertical === "bottom") lines.reverse();
2089
- return lines;
2090
- }
2091
2120
  calculateStartInBoundsPosition(textDef, lines, alignmentDirection, alignmentOffset, inBoundsPosition) {
2092
2121
  const padding = textDef.padding;
2093
2122
  const alignment = textDef.alignment;
@@ -2102,10 +2131,13 @@ class TextSystem extends RenderableSystem {
2102
2131
  }
2103
2132
  inBoundsPosition.multiply(alignmentDirection);
2104
2133
  }
2105
- calculateClipRect(textDef, inBoundsPosition, textScale, toMin, toMax) {
2134
+ calculateClipRect(text, textDef, inBoundsPosition, textScale, toMin, toMax) {
2106
2135
  toMin.subVectors(textDef.bounds.min, textDef.bounds.center).multiply(this.initialTextScale).sub(inBoundsPosition).divide(textScale);
2107
2136
  toMax.subVectors(textDef.bounds.max, textDef.bounds.center).multiply(this.initialTextScale).sub(inBoundsPosition).divide(textScale);
2108
- return [toMin.x, toMin.y, toMax.x, toMax.y];
2137
+ text.clipRect[0] = toMin.x;
2138
+ text.clipRect[1] = toMin.y;
2139
+ text.clipRect[2] = toMax.x;
2140
+ text.clipRect[3] = toMax.y;
2109
2141
  }
2110
2142
  }
2111
2143
  function getAlignmentDirection(alignment) {
@@ -2143,7 +2175,8 @@ class LayerSystem {
2143
2175
  __publicField(this, "mapLayerDefToParent", /* @__PURE__ */ new Map());
2144
2176
  __publicField(this, "layerDefRenderOrder", []);
2145
2177
  __publicField(this, "pendingDefs", /* @__PURE__ */ new Set());
2146
- __publicField(this, "useUpdateBuffering", false);
2178
+ __publicField(this, "culledDefs", /* @__PURE__ */ new Set());
2179
+ __publicField(this, "useUpdateBuffering", true);
2147
2180
  this.renderer = renderer;
2148
2181
  this.materialSystem = new MaterialSystem();
2149
2182
  this.meshSystem = new MeshSystem(this.materialSystem, this.renderer);
@@ -2177,30 +2210,57 @@ class LayerSystem {
2177
2210
  */
2178
2211
  updateDefs(defs) {
2179
2212
  for (const def of defs) {
2180
- if (this.useUpdateBuffering) this.pendingDefs.add(def);
2181
- else this.updateDef(def);
2213
+ if (this.useUpdateBuffering) {
2214
+ this.pendingDefs.add(def);
2215
+ this.culledDefs.delete(def);
2216
+ } else this.updateDef(def);
2182
2217
  }
2183
2218
  }
2184
2219
  /**
2185
2220
  * Drain the queued updates within a time budget.
2186
2221
  * Returns true if any def was updated during this call.
2222
+ * @param scene {@link Scene} instance to get the world matrix from
2223
+ * @param camera {@link Camera} instance to do frustum culling against
2187
2224
  * @param timeBudgetMs frame time budget to perform updates in milliseconds
2188
2225
  * @returns true if any def was updated during this call
2189
2226
  */
2190
- processPendingUpdates(timeBudgetMs = 5) {
2227
+ processPendingUpdates(scene, camera, timeBudgetMs = -1) {
2191
2228
  if (!this.useUpdateBuffering) return false;
2192
- if (this.pendingDefs.size === 0) return false;
2229
+ if (this.pendingDefs.size === 0 && this.culledDefs.size === 0) return false;
2193
2230
  const startTime = performance.now();
2194
- let processed = 0;
2195
- while (this.pendingDefs.size && performance.now() - startTime < timeBudgetMs) {
2196
- const def = this.pendingDefs.values().next().value;
2231
+ _matrix.multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse).multiply(scene.matrixWorld);
2232
+ _frustum.setFromProjectionMatrix(_matrix, camera.coordinateSystem);
2233
+ let processedCount = 0;
2234
+ const overBudget = () => timeBudgetMs > 0 && performance.now() - startTime >= timeBudgetMs;
2235
+ for (const def of this.pendingDefs) {
2236
+ if (overBudget()) break;
2237
+ let culled = false;
2197
2238
  this.pendingDefs.delete(def);
2239
+ if (isTextDef(def) || isImageDef(def)) {
2240
+ const bounds = def.bounds;
2241
+ _box.min.set(bounds.min.x, bounds.min.y, 0);
2242
+ _box.max.set(bounds.max.x, bounds.max.y, 0);
2243
+ culled = !_frustum.intersectsBox(_box);
2244
+ }
2245
+ if (culled) this.culledDefs.add(def);
2246
+ else {
2247
+ this.updateDef(def);
2248
+ processedCount++;
2249
+ }
2250
+ }
2251
+ for (const def of this.culledDefs) {
2252
+ if (overBudget()) break;
2253
+ this.culledDefs.delete(def);
2198
2254
  this.updateDef(def);
2199
- processed++;
2255
+ processedCount++;
2200
2256
  }
2201
2257
  const took = (performance.now() - startTime).toFixed(2);
2202
- if (processed) logger$5.debug(`processed ${processed} defs in ${took}ms, ${this.pendingDefs.size} remaining`);
2203
- return processed > 0;
2258
+ if (processedCount > 0) {
2259
+ const culled = this.culledDefs.size;
2260
+ const remaining = this.pendingDefs.size;
2261
+ logger$5.debug(`processed ${processedCount} defs (${culled} culled, ${remaining} remaining) in ${took}ms`);
2262
+ }
2263
+ return processedCount > 0;
2204
2264
  }
2205
2265
  /**
2206
2266
  * Build the scene graph from the given scene definition.
@@ -2333,6 +2393,9 @@ class LayerSystem {
2333
2393
  return fullName;
2334
2394
  }
2335
2395
  }
2396
+ const _matrix = new Matrix4();
2397
+ const _frustum = new Frustum();
2398
+ const _box = new Box3();
2336
2399
  /*!
2337
2400
  * camera-controls
2338
2401
  * https://github.com/yomotsu/camera-controls
@@ -4943,6 +5006,17 @@ class CameraSystem {
4943
5006
  if (zoomFactor <= 0) return this.zoomIdentityDistance;
4944
5007
  return this.zoomIdentityDistance / zoomFactor;
4945
5008
  }
5009
+ /**
5010
+ * Set the zoom factor bounds.
5011
+ * @param minZoom Minimum zoom factor. Default is 0.1.
5012
+ * @param maxZoom Maximum zoom factor. Default is 35.
5013
+ */
5014
+ setZoomBounds(minZoom, maxZoom) {
5015
+ if (!this.zoomBounds) return;
5016
+ if (minZoom) this.zoomBounds[0] = minZoom;
5017
+ if (maxZoom) this.zoomBounds[1] = maxZoom;
5018
+ this.updateCamera();
5019
+ }
4946
5020
  /**
4947
5021
  * Initializes the camera with the given zoom bounds.
4948
5022
  * @param zoomBounds [minZoom, maxZoom]
@@ -5288,7 +5362,7 @@ class ViewportSystem {
5288
5362
  */
5289
5363
  initViewport(sceneDef) {
5290
5364
  if (!this.renderer.isExternalMode) this.sceneSystem.initScene(sceneDef.viewbox);
5291
- this.cameraSystem.initCamera([0.1, sceneDef.viewbox.size.x > 1e5 ? 100 : 35]);
5365
+ this.cameraSystem.initCamera([0.1, sceneDef.viewbox.size.x > 1e4 ? 100 : 35]);
5292
5366
  }
5293
5367
  /** Updates the viewport when the renderer size changes. */
5294
5368
  updateViewport() {
@@ -5393,12 +5467,28 @@ class ViewportSystem {
5393
5467
  zoomFactorToDistance(zoomFactor) {
5394
5468
  return this.cameraSystem.zoomFactorToDistance(zoomFactor);
5395
5469
  }
5470
+ /**
5471
+ * Set the maximum zoom factor. Default is 35.
5472
+ * @param maxZoom Maximum zoom factor
5473
+ */
5474
+ setMaxZoom(maxZoom) {
5475
+ this.cameraSystem.setZoomBounds(void 0, maxZoom);
5476
+ }
5477
+ /**
5478
+ * Set the minimum zoom factor. Default is 0.1.
5479
+ * @param minZoom Minimum zoom factor
5480
+ */
5481
+ setMinZoom(minZoom) {
5482
+ this.cameraSystem.setZoomBounds(minZoom, void 0);
5483
+ }
5396
5484
  }
5397
5485
  function asViewportAPI(viewportSystem) {
5398
5486
  return {
5399
5487
  canvasToSvg: viewportSystem.canvasToSvg.bind(viewportSystem),
5400
5488
  setStaticTransform: viewportSystem.setStaticTransform.bind(viewportSystem),
5401
- setDynamicTransform: viewportSystem.setDynamicTransform.bind(viewportSystem)
5489
+ setDynamicTransform: viewportSystem.setDynamicTransform.bind(viewportSystem),
5490
+ setMaxZoom: viewportSystem.setMaxZoom.bind(viewportSystem),
5491
+ setMinZoom: viewportSystem.setMinZoom.bind(viewportSystem)
5402
5492
  };
5403
5493
  }
5404
5494
  function eventToCanvas(event) {
@@ -6414,10 +6504,13 @@ class Renderer {
6414
6504
  const api = asViewportAPI(this.viewportSystem);
6415
6505
  const guard = (name) => this.assertInitialized(`viewport.${name}`) && this.assertNotDisposed(`viewport.${name}`);
6416
6506
  const guardExternal = (name) => guard(name) && this.assertExternalMode(`viewport.${name}`);
6507
+ const guardNotExternal = (name) => guard(name) && this.assertNotExternalMode(`viewport.${name}`);
6417
6508
  this.viewportAPI = {
6418
6509
  canvasToSvg: guardFn(guard, api.canvasToSvg, { x: 0, y: 0 }),
6419
6510
  setStaticTransform: guardFn(guardExternal, api.setStaticTransform),
6420
- setDynamicTransform: guardFn(guardExternal, api.setDynamicTransform)
6511
+ setDynamicTransform: guardFn(guardExternal, api.setDynamicTransform),
6512
+ setMaxZoom: guardFn(guardNotExternal, api.setMaxZoom),
6513
+ setMinZoom: guardFn(guardNotExternal, api.setMinZoom)
6421
6514
  };
6422
6515
  return this.viewportAPI;
6423
6516
  }
@@ -6517,12 +6610,13 @@ class Renderer {
6517
6610
  (_b = (_a2 = this.ui) == null ? void 0 : _a2.stats) == null ? void 0 : _b.begin();
6518
6611
  if (this.isExternalMode) this.renderer.resetState();
6519
6612
  else this.resizeCanvasToDisplaySize();
6520
- this.viewportSystem.updatePtScale();
6521
- const hasDefsUpdated = this.layerSystem.processPendingUpdates();
6613
+ const { scene, camera } = this.viewportSystem;
6522
6614
  const hasControlsUpdated = this.interactionsSystem.updateControls(this.clock.getDelta());
6615
+ this.viewportSystem.updatePtScale();
6616
+ const hasDefsUpdated = this.layerSystem.processPendingUpdates(scene, camera, 3);
6523
6617
  const needsRedraw = this.needsRedraw || hasControlsUpdated || hasDefsUpdated || this.isExternalMode || this.ui;
6524
6618
  if (needsRedraw) {
6525
- this.renderer.render(this.viewportSystem.scene, this.viewportSystem.camera);
6619
+ this.renderer.render(scene, camera);
6526
6620
  this.needsRedraw = false;
6527
6621
  }
6528
6622
  (_d = (_c = this.ui) == null ? void 0 : _c.stats) == null ? void 0 : _d.end();
@@ -6558,7 +6652,8 @@ class Renderer {
6558
6652
  // https://webgl2fundamentals.org/webgl/lessons/webgl-resizing-the-canvas.html
6559
6653
  resizeCanvasToDisplaySize() {
6560
6654
  const dpr = window.devicePixelRatio;
6561
- const { width, height } = this.canvas.getBoundingClientRect();
6655
+ const width = this.canvas.clientWidth;
6656
+ const height = this.canvas.clientHeight;
6562
6657
  const displayWidth = Math.floor(width * dpr);
6563
6658
  const displayHeight = Math.floor(height * dpr);
6564
6659
  if (this.canvas.width !== displayWidth || this.canvas.height !== displayHeight || this.renderer.getPixelRatio() !== dpr) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@expofp/renderer",
3
- "version": "2.2.0",
3
+ "version": "2.3.0",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist"