@loaders.gl/tiles 5.0.0-alpha.10 → 5.0.0-alpha.11

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.
Files changed (41) hide show
  1. package/dist/dist.dev.js +8975 -8931
  2. package/dist/dist.min.js +3 -3
  3. package/dist/index.cjs +122 -57
  4. package/dist/index.cjs.map +2 -2
  5. package/dist/index.d.ts +1 -1
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js.map +1 -1
  8. package/dist/spatial/i3s-spatial-transformer.d.ts +16 -0
  9. package/dist/spatial/i3s-spatial-transformer.d.ts.map +1 -1
  10. package/dist/spatial/i3s-spatial-transformer.js +45 -0
  11. package/dist/spatial/i3s-spatial-transformer.js.map +1 -1
  12. package/dist/spatial/spatial-coordinate-transformer.d.ts.map +1 -1
  13. package/dist/spatial/spatial-coordinate-transformer.js +8 -8
  14. package/dist/spatial/spatial-coordinate-transformer.js.map +1 -1
  15. package/dist/spatial/spatial-resource-registry.d.ts +3 -0
  16. package/dist/spatial/spatial-resource-registry.d.ts.map +1 -1
  17. package/dist/spatial/spatial-resource-registry.js +12 -3
  18. package/dist/spatial/spatial-resource-registry.js.map +1 -1
  19. package/dist/spatial/tiles-3d-spatial-transformer.d.ts.map +1 -1
  20. package/dist/spatial/tiles-3d-spatial-transformer.js +0 -12
  21. package/dist/spatial/tiles-3d-spatial-transformer.js.map +1 -1
  22. package/dist/tileset-3d/common/tileset-3d.d.ts +8 -1
  23. package/dist/tileset-3d/common/tileset-3d.d.ts.map +1 -1
  24. package/dist/tileset-3d/common/tileset-3d.js +42 -9
  25. package/dist/tileset-3d/common/tileset-3d.js.map +1 -1
  26. package/dist/tileset-3d/common/tileset-traverser.d.ts.map +1 -1
  27. package/dist/tileset-3d/common/tileset-traverser.js +2 -3
  28. package/dist/tileset-3d/common/tileset-traverser.js.map +1 -1
  29. package/dist/tileset-3d/format-3d-tiles/tileset-3d-traverser.d.ts +1 -0
  30. package/dist/tileset-3d/format-3d-tiles/tileset-3d-traverser.d.ts.map +1 -1
  31. package/dist/tileset-3d/format-3d-tiles/tileset-3d-traverser.js +12 -4
  32. package/dist/tileset-3d/format-3d-tiles/tileset-3d-traverser.js.map +1 -1
  33. package/package.json +11 -11
  34. package/src/index.ts +2 -1
  35. package/src/spatial/i3s-spatial-transformer.ts +64 -0
  36. package/src/spatial/spatial-coordinate-transformer.ts +9 -8
  37. package/src/spatial/spatial-resource-registry.ts +14 -3
  38. package/src/spatial/tiles-3d-spatial-transformer.ts +0 -12
  39. package/src/tileset-3d/common/tileset-3d.ts +54 -12
  40. package/src/tileset-3d/common/tileset-traverser.ts +2 -4
  41. package/src/tileset-3d/format-3d-tiles/tileset-3d-traverser.ts +16 -4
package/dist/index.cjs CHANGED
@@ -2797,6 +2797,7 @@ var Tileset3D = class {
2797
2797
  _traverser;
2798
2798
  _cache = new TilesetCache();
2799
2799
  _requestScheduler;
2800
+ /** Drawn fallback instances retained independently for each viewport tree. */
2800
2801
  _heldTiles = /* @__PURE__ */ new Set();
2801
2802
  updatePromise = null;
2802
2803
  tilesetInitializationPromise;
@@ -2820,6 +2821,7 @@ var Tileset3D = class {
2820
2821
  ...suppliedOptions,
2821
2822
  cacheBytes,
2822
2823
  maximumCacheOverflowBytes,
2824
+ skipLevelOfDetail: suppliedOptions.skipLevelOfDetail ?? source.type === TILESET_TYPE.TILES3D,
2823
2825
  memoryAdjustedScreenSpaceError: suppliedOptions.memoryAdjustedScreenSpaceError ?? usesTiles3DCacheDefaults
2824
2826
  };
2825
2827
  this.loadOptions = this.options.loadOptions || {};
@@ -3105,27 +3107,41 @@ var Tileset3D = class {
3105
3107
  this._emptyTiles = this._emptyTiles.concat(frameStateDataValue._emptyTiles);
3106
3108
  }
3107
3109
  this.selectedTiles = this.options.onTraversalComplete(this.selectedTiles);
3108
- const selectedIds = new Set(this.selectedTiles.map((tile) => tile.id));
3110
+ const selectedTiles = new Set(this.selectedTiles);
3109
3111
  const hasUndrawnTiles = this.selectedTiles.some((tile) => !tile.tileDrawn);
3110
3112
  let heldBackCount = 0;
3111
3113
  if (hasUndrawnTiles) {
3112
- for (const tileId of selectedIds) {
3113
- this._heldTiles.add(tileId);
3114
+ const activeRoots = new Set(Object.keys(this.frameStateData).map((viewportId) => this.roots[viewportId]));
3115
+ const descendantReadiness = /* @__PURE__ */ new Map();
3116
+ for (const tile of this.selectedTiles) {
3117
+ for (let ancestor = tile.parent; ancestor; ancestor = ancestor.parent) {
3118
+ descendantReadiness.set(ancestor, (descendantReadiness.get(ancestor) ?? true) && tile.tileDrawn);
3119
+ }
3120
+ }
3121
+ for (const tile of previousSelectedTiles) {
3122
+ if (tile.tileDrawn)
3123
+ this._heldTiles.add(tile);
3114
3124
  }
3115
- for (const tileId of this._heldTiles) {
3116
- if (selectedIds.has(tileId))
3125
+ for (const tile of this._heldTiles) {
3126
+ let root = tile;
3127
+ while (root.parent)
3128
+ root = root.parent;
3129
+ if (!activeRoots.has(root)) {
3130
+ this._heldTiles.delete(tile);
3117
3131
  continue;
3118
- const tile = this._tiles[tileId];
3119
- if (tile && tile.contentAvailable) {
3132
+ }
3133
+ if (selectedTiles.has(tile))
3134
+ continue;
3135
+ if (tile.contentAvailable && !this._hasDrawnReplacement(tile, selectedTiles, descendantReadiness)) {
3120
3136
  tile._selectedFrame = this._frameNumber;
3121
3137
  this.selectedTiles.push(tile);
3122
3138
  heldBackCount++;
3123
3139
  } else {
3124
- this._heldTiles.delete(tileId);
3140
+ this._heldTiles.delete(tile);
3125
3141
  }
3126
3142
  }
3127
3143
  } else {
3128
- this._heldTiles = selectedIds;
3144
+ this._heldTiles = selectedTiles;
3129
3145
  }
3130
3146
  if (heldBackCount > 0) {
3131
3147
  setTimeout(() => {
@@ -3142,6 +3158,19 @@ var Tileset3D = class {
3142
3158
  this.options.onUpdate();
3143
3159
  }
3144
3160
  }
3161
+ /**
3162
+ * Releases fallback geometry once its own selected replacement has drawn.
3163
+ * Unrelated pending branches must not delay this region's handoff. When no related
3164
+ * replacement is selected, retain the fallback until the complete selection has drawn.
3165
+ */
3166
+ _hasDrawnReplacement(tile, selectedTiles, descendantReadiness) {
3167
+ for (let ancestor = tile.parent; ancestor; ancestor = ancestor.parent) {
3168
+ if (selectedTiles.has(ancestor)) {
3169
+ return ancestor.tileDrawn;
3170
+ }
3171
+ }
3172
+ return descendantReadiness.get(tile) === true;
3173
+ }
3145
3174
  _tilesChanged(oldSelectedTiles, selectedTiles) {
3146
3175
  if (oldSelectedTiles.length !== selectedTiles.length) {
3147
3176
  return true;
@@ -4193,9 +4222,6 @@ var TilesetTraverser = class {
4193
4222
  childRefines = child.contentAvailable;
4194
4223
  }
4195
4224
  refines = refines && childRefines;
4196
- if (!refines) {
4197
- return false;
4198
- }
4199
4225
  }
4200
4226
  }
4201
4227
  if (!hasVisibleChild) {
@@ -5253,7 +5279,7 @@ var Tile3D = class {
5253
5279
  };
5254
5280
 
5255
5281
  // dist/tileset-3d/format-3d-tiles/tiles-3d-source.js
5256
- var import_geospatial10 = require("@math.gl/geospatial");
5282
+ var import_geospatial9 = require("@math.gl/geospatial");
5257
5283
  var import_core12 = require("@math.gl/core");
5258
5284
 
5259
5285
  // dist/tileset-3d/format-3d-tiles/tileset-3d-traverser.js
@@ -5309,7 +5335,7 @@ var Tileset3DTraverser = class extends TilesetTraverser {
5309
5335
  if (!tile.hasRenderContent) {
5310
5336
  this.emptyTiles[tile.id] = tile;
5311
5337
  this.loadTile(tile, frameState);
5312
- if (stoppedRefining) {
5338
+ if (stoppedRefining && (!tile.hasEmptyContent || tile.hasChildren)) {
5313
5339
  this.selectDesiredTile(tile, frameState);
5314
5340
  }
5315
5341
  } else if (tile.refine === TILE_REFINEMENT.ADD) {
@@ -5350,7 +5376,7 @@ var Tileset3DTraverser = class extends TilesetTraverser {
5350
5376
  if (child.isVisibleAndInRequestVolume) {
5351
5377
  stack.push(child);
5352
5378
  hasVisibleChild = true;
5353
- } else if (this.options.loadSiblings) {
5379
+ } else if (this.options.loadSiblings && child._inRequestVolume) {
5354
5380
  this.loadTile(child, frameState);
5355
5381
  this.touchTile(child, frameState);
5356
5382
  }
@@ -5430,6 +5456,9 @@ var Tileset3DTraverser = class extends TilesetTraverser {
5430
5456
  while (fallbackTile) {
5431
5457
  if (this.shouldSelectTile(fallbackTile, frameState)) {
5432
5458
  this.selectTile(fallbackTile, frameState);
5459
+ if (fallbackTile !== tile) {
5460
+ this.selectLoadedDescendants(tile, frameState, true);
5461
+ }
5433
5462
  return;
5434
5463
  }
5435
5464
  fallbackTile = fallbackTile.parent;
@@ -5441,8 +5470,9 @@ var Tileset3DTraverser = class extends TilesetTraverser {
5441
5470
  *
5442
5471
  * @param root - Unavailable tile whose descendants are searched.
5443
5472
  * @param frameState - Current culling state.
5473
+ * @param drawnOnly - Reuse drawn detail without starting obsolete GPU work.
5444
5474
  */
5445
- selectLoadedDescendants(root, frameState) {
5475
+ selectLoadedDescendants(root, frameState, drawnOnly = false) {
5446
5476
  const stack = [{ tile: root, depth: 0 }];
5447
5477
  while (stack.length > 0) {
5448
5478
  const { tile, depth } = stack.pop();
@@ -5451,7 +5481,7 @@ var Tileset3DTraverser = class extends TilesetTraverser {
5451
5481
  if (!child.isVisibleAndInRequestVolume) {
5452
5482
  continue;
5453
5483
  }
5454
- if (child.contentAvailable) {
5484
+ if (this.shouldSelectTile(child, frameState) && (!drawnOnly || child.tileDrawn)) {
5455
5485
  this.selectTile(child, frameState);
5456
5486
  this.touchTile(child, frameState);
5457
5487
  } else if (depth < 1) {
@@ -5895,19 +5925,26 @@ function getTilesetSemanticValue(tileset, semantic) {
5895
5925
  // dist/spatial/spatial-coordinate-transformer.js
5896
5926
  var import_core10 = require("@math.gl/core");
5897
5927
  var import_geospatial7 = require("@math.gl/geospatial");
5898
- var import_projection2 = require("@math.gl/projection");
5899
5928
 
5900
5929
  // dist/spatial/spatial-resource-registry.js
5901
5930
  var import_geoid = require("@math.gl/geoid");
5902
5931
  var import_projection = require("@math.gl/projection");
5903
5932
  var geoidModels = /* @__PURE__ */ new Map();
5933
+ var projectionAliases = {};
5934
+ var datumGrids = {};
5935
+ var spatialProjectionEngine = new import_projection.FullProjectionEngine();
5936
+ function getSpatialProjectionEngine() {
5937
+ return spatialProjectionEngine;
5938
+ }
5904
5939
  function registerSpatialCrs(name, definition) {
5905
5940
  validateResourceName(name, "CRS");
5906
- import_projection.Projection.defineProjectionAliases({ [name]: definition });
5941
+ projectionAliases[name] = definition;
5942
+ spatialProjectionEngine = new import_projection.FullProjectionEngine({ aliases: projectionAliases, datumGrids });
5907
5943
  }
5908
5944
  function registerSpatialDatumGrid(name, data) {
5909
5945
  validateResourceName(name, "datum grid");
5910
- import_projection.Projection.registerDatumGrid(name, data);
5946
+ datumGrids[name] = (0, import_projection.parseNTv2Grid)(data);
5947
+ spatialProjectionEngine = new import_projection.FullProjectionEngine({ aliases: projectionAliases, datumGrids });
5911
5948
  }
5912
5949
  function registerGeoidModel(name, geoid) {
5913
5950
  validateResourceName(name, "geoid model");
@@ -5955,6 +5992,7 @@ var SpatialCoordinateTransformer = class {
5955
5992
  */
5956
5993
  constructor(spatialReference, options = {}) {
5957
5994
  this.spatialReference = spatialReference;
5995
+ const projectionEngine = getSpatialProjectionEngine();
5958
5996
  validateTransformRequest(spatialReference);
5959
5997
  const outputCrs = spatialReference.targetCrs || spatialReference.sourceCrs;
5960
5998
  this.sourceIsGeocentric = isWgs84Geocentric(spatialReference.sourceCrs);
@@ -5962,7 +6000,7 @@ var SpatialCoordinateTransformer = class {
5962
6000
  this.sourceIsGeographic = isWgs84Geographic(spatialReference.sourceCrs);
5963
6001
  this.outputIsGeographic = isWgs84Geographic(outputCrs);
5964
6002
  if (spatialReference.sourceCrs && spatialReference.targetCrs && !this.sourceIsGeocentric && !this.outputIsGeocentric) {
5965
- this.horizontalProjection = new import_projection2.Projection({
6003
+ this.horizontalProjection = projectionEngine.createProjection({
5966
6004
  from: getHorizontalProjectionDefinition(spatialReference.sourceCrs),
5967
6005
  to: getHorizontalProjectionDefinition(spatialReference.targetCrs),
5968
6006
  enforceAxis: false
@@ -5970,14 +6008,14 @@ var SpatialCoordinateTransformer = class {
5970
6008
  }
5971
6009
  if (requiresHeightTransformation(spatialReference) || this.sourceIsGeocentric || this.outputIsGeocentric) {
5972
6010
  if (!this.sourceIsGeographic && !this.sourceIsGeocentric) {
5973
- this.geographicProjection = new import_projection2.Projection({
6011
+ this.geographicProjection = projectionEngine.createProjection({
5974
6012
  from: getHorizontalProjectionDefinition(spatialReference.sourceCrs),
5975
6013
  to: WGS84_GEOGRAPHIC_CRS,
5976
6014
  enforceAxis: false
5977
6015
  });
5978
6016
  }
5979
6017
  if (!this.outputIsGeographic && !this.outputIsGeocentric) {
5980
- this.heightOutputProjection = new import_projection2.Projection({
6018
+ this.heightOutputProjection = projectionEngine.createProjection({
5981
6019
  from: WGS84_GEOGRAPHIC_CRS,
5982
6020
  to: getHorizontalProjectionDefinition(outputCrs),
5983
6021
  enforceAxis: false
@@ -6017,7 +6055,7 @@ var SpatialCoordinateTransformer = class {
6017
6055
  return result;
6018
6056
  }
6019
6057
  if (this.horizontalProjection) {
6020
- const projected = this.horizontalProjection.project(result.slice(0, 3));
6058
+ const projected = this.horizontalProjection.projectSync(result.slice(0, 3));
6021
6059
  result.splice(0, projected.length, ...projected);
6022
6060
  }
6023
6061
  return result;
@@ -6081,7 +6119,7 @@ var SpatialCoordinateTransformer = class {
6081
6119
  if (this.sourceIsGeographic) {
6082
6120
  return coordinate.slice(0, 3);
6083
6121
  }
6084
- return this.geographicProjection.project(coordinate.slice(0, 3));
6122
+ return this.geographicProjection.projectSync(coordinate.slice(0, 3));
6085
6123
  }
6086
6124
  /** Convert one conventional WGS84 geographic coordinate to the selected output CRS. */
6087
6125
  fromGeographic(coordinate) {
@@ -6091,7 +6129,7 @@ var SpatialCoordinateTransformer = class {
6091
6129
  if (this.outputIsGeographic) {
6092
6130
  return coordinate.slice(0, 3);
6093
6131
  }
6094
- return this.heightOutputProjection.project(coordinate.slice(0, 3));
6132
+ return this.heightOutputProjection.projectSync(coordinate.slice(0, 3));
6095
6133
  }
6096
6134
  };
6097
6135
  function getFiniteDifferenceJacobian(transformer, position, epsilon) {
@@ -6189,7 +6227,6 @@ function transformHeight(height, geoidUndulation, source, target) {
6189
6227
 
6190
6228
  // dist/spatial/tiles-3d-spatial-transformer.js
6191
6229
  var import_core11 = require("@math.gl/core");
6192
- var import_geospatial8 = require("@math.gl/geospatial");
6193
6230
  var Tiles3DSpatialTransformer = class {
6194
6231
  /** Normalized reference describing the completed operation. */
6195
6232
  spatialReference;
@@ -6228,16 +6265,6 @@ var Tiles3DSpatialTransformer = class {
6228
6265
  }
6229
6266
  /** Transforms one WGS84 region sample into the selected output frame. */
6230
6267
  transformRegionSample(sample) {
6231
- const [longitudeDegrees, latitudeDegrees, height] = sample;
6232
- const targetCrs = this.spatialReference.targetCrs || this.spatialReference.sourceCrs;
6233
- if (targetCrs && getSpatialCoordinateFrame(targetCrs) === "geocentric") {
6234
- const cartesian = import_geospatial8.Ellipsoid.WGS84.cartographicToCartesian([
6235
- longitudeDegrees,
6236
- latitudeDegrees,
6237
- height
6238
- ]);
6239
- return Array.from(cartesian);
6240
- }
6241
6268
  return this.geographicCoordinateTransformer.transformPosition(sample);
6242
6269
  }
6243
6270
  };
@@ -6346,10 +6373,10 @@ var import_s22 = require("@math.gl/dggs/s2");
6346
6373
 
6347
6374
  // dist/tileset-3d/helpers/s2-bounding-volume.js
6348
6375
  var import_s2 = require("@math.gl/dggs/s2");
6349
- var import_geospatial9 = require("@math.gl/geospatial");
6376
+ var import_geospatial8 = require("@math.gl/geospatial");
6350
6377
  function convertS2BoundingVolumeToOBB(s2VolumeInfo) {
6351
6378
  const [[west, south], [east, north]] = (0, import_s2.getS2Bounds)(s2VolumeInfo.token);
6352
- const orientedBoundingBox = (0, import_geospatial9.makeOBBFromRegion)([west, south, east, north, s2VolumeInfo.minimumHeight, s2VolumeInfo.maximumHeight], import_geospatial9.Ellipsoid.WGS84, { units: "degrees" });
6379
+ const orientedBoundingBox = (0, import_geospatial8.makeOBBFromRegion)([west, south, east, north, s2VolumeInfo.minimumHeight, s2VolumeInfo.maximumHeight], import_geospatial8.Ellipsoid.WGS84, { units: "degrees" });
6353
6380
  return [...orientedBoundingBox.center, ...orientedBoundingBox.halfAxes];
6354
6381
  }
6355
6382
 
@@ -7260,9 +7287,9 @@ var Tiles3DSource = class {
7260
7287
  let cartographicCenter;
7261
7288
  if (center && (center[0] !== 0 || center[1] !== 0 || center[2] !== 0)) {
7262
7289
  cartographicCenter = new import_core12.Vector3();
7263
- import_geospatial10.Ellipsoid.WGS84.cartesianToCartographic(center, cartographicCenter);
7290
+ import_geospatial9.Ellipsoid.WGS84.cartesianToCartographic(center, cartographicCenter);
7264
7291
  } else {
7265
- cartographicCenter = new import_core12.Vector3(0, 0, -import_geospatial10.Ellipsoid.WGS84.radii[0]);
7292
+ cartographicCenter = new import_core12.Vector3(0, 0, -import_geospatial9.Ellipsoid.WGS84.radii[0]);
7266
7293
  }
7267
7294
  return {
7268
7295
  asset: this.asset,
@@ -7559,7 +7586,7 @@ function addNestedSpatialReference(nestedTileset, spatialReference) {
7559
7586
  }
7560
7587
 
7561
7588
  // dist/tileset-3d/format-i3s/i3s-source.js
7562
- var import_geospatial12 = require("@math.gl/geospatial");
7589
+ var import_geospatial11 = require("@math.gl/geospatial");
7563
7590
  var import_core14 = require("@math.gl/core");
7564
7591
 
7565
7592
  // dist/tileset-3d/format-i3s/i3s-pending-tiles-register.js
@@ -7745,7 +7772,7 @@ var I3STilesetTraverser = class extends TilesetTraverser {
7745
7772
 
7746
7773
  // dist/spatial/i3s-spatial-transformer.js
7747
7774
  var import_core13 = require("@math.gl/core");
7748
- var import_geospatial11 = require("@math.gl/geospatial");
7775
+ var import_geospatial10 = require("@math.gl/geospatial");
7749
7776
  var I3SSpatialTransformer = class {
7750
7777
  /** Descriptor attached to output after all adapter-owned spatial values have been transformed. */
7751
7778
  spatialReference;
@@ -7924,6 +7951,32 @@ var I3SSpatialTransformer = class {
7924
7951
  const placedPositions = await this.placeSourcePositions(combinedPositions);
7925
7952
  return this.transformPlacedPositions(placedPositions.subarray(0, sourcePositions.length), placedPositions.subarray(sourcePositions.length));
7926
7953
  }
7954
+ /**
7955
+ * Transforms conversion geometry without passing through renderer float32 offsets.
7956
+ * Positions and normals share one batch of elevation samples. Source arrays remain unchanged.
7957
+ * @param sourcePositions - Packed absolute xyz positions in source units.
7958
+ * @param normals - Optional packed normals paired with the positions.
7959
+ * @param normalReferenceFrame - I3S earth-centered or vertex-reference-frame declaration.
7960
+ * @returns Absolute target positions and optional transformed normals.
7961
+ */
7962
+ async transformGeometryAsync(sourcePositions, normals, normalReferenceFrame = "earth-centered") {
7963
+ if (sourcePositions.length % 3 || normals && normals.length !== sourcePositions.length) {
7964
+ throw new Error("Conversion positions and normals must contain matching finite xyz triples");
7965
+ }
7966
+ for (let index = 0; index < sourcePositions.length; index++) {
7967
+ if (!Number.isFinite(sourcePositions[index]) || normals && !Number.isFinite(normals[index])) {
7968
+ throw new Error("Conversion positions and normals must contain matching finite xyz triples");
7969
+ }
7970
+ }
7971
+ if (!["earth-centered", "vertex-reference-frame"].includes(normalReferenceFrame)) {
7972
+ throw new Error("Unsupported I3S normal reference frame");
7973
+ }
7974
+ const placedPositions = await this.placeSourcePositions(sourcePositions);
7975
+ return {
7976
+ positions: this.positionTransformer.transformPositions(placedPositions),
7977
+ normals: normals ? this.transformNormals(normals, placedPositions, normalReferenceFrame) : void 0
7978
+ };
7979
+ }
7927
7980
  /** Transform already placed positions into renderer-relative target coordinates. */
7928
7981
  transformPlacedPositions(sourcePositions, sourceOrigin) {
7929
7982
  const origin = this.transformPlacedPosition(sourceOrigin);
@@ -7961,7 +8014,19 @@ var I3SSpatialTransformer = class {
7961
8014
  * @param normalReferenceFrame - I3S normal frame declaration.
7962
8015
  */
7963
8016
  transformNormals(normals, sourcePositions, normalReferenceFrame = "earth-centered") {
8017
+ if (normals.length !== sourcePositions.length || normals.length % 3) {
8018
+ throw new Error("I3S normals and positions must have matching xyz component counts");
8019
+ }
7964
8020
  const transformedNormals = new Float32Array(normals.length);
8021
+ if (normalReferenceFrame === "earth-centered" && (this.spatialReference.targetCrs || this.spatialReference.sourceCrs) === "EPSG:4978") {
8022
+ for (let index = 0; index < normals.length; index += 3) {
8023
+ const length = Math.hypot(normals[index], normals[index + 1], normals[index + 2]);
8024
+ for (let axis = 0; axis < 3; axis++) {
8025
+ transformedNormals[index + axis] = length ? normals[index + axis] / length : normals[index + axis];
8026
+ }
8027
+ }
8028
+ return transformedNormals;
8029
+ }
7965
8030
  for (let index = 0; index < normals.length; index += 3) {
7966
8031
  const geographic = this.sourceToGeographicTransformer.transformPosition([
7967
8032
  sourcePositions[index],
@@ -8006,12 +8071,12 @@ var I3SSpatialTransformer = class {
8006
8071
  }
8007
8072
  /** Rebuild an I3S bound as a WGS84 ECEF box for generic `Tileset3D` traversal. */
8008
8073
  transformBoundingVolumeToGeocentric(bounds) {
8009
- const cartesianSamples = this.getGeographicBoundSamples(bounds).map((position) => Array.from(import_geospatial11.Ellipsoid.WGS84.cartographicToCartesian(new import_core13.Vector3(position))));
8074
+ const cartesianSamples = this.getGeographicBoundSamples(bounds).map((position) => Array.from(import_geospatial10.Ellipsoid.WGS84.cartographicToCartesian(new import_core13.Vector3(position))));
8010
8075
  return { box: getAxisAlignedBox(cartesianSamples) };
8011
8076
  }
8012
8077
  /** Rebuild an I3S bound as WGS84 ECEF after asynchronous elevation placement. */
8013
8078
  async transformBoundingVolumeToGeocentricAsync(bounds) {
8014
- const cartesianSamples = (await this.getGeographicBoundSamplesAsync(bounds)).map((position) => Array.from(import_geospatial11.Ellipsoid.WGS84.cartographicToCartesian(new import_core13.Vector3(position))));
8079
+ const cartesianSamples = (await this.getGeographicBoundSamplesAsync(bounds)).map((position) => Array.from(import_geospatial10.Ellipsoid.WGS84.cartographicToCartesian(new import_core13.Vector3(position))));
8015
8080
  return { box: getAxisAlignedBox(cartesianSamples) };
8016
8081
  }
8017
8082
  /** Return a WGS84 geographic MBS used by the I3S screen-threshold calculation. */
@@ -8038,7 +8103,7 @@ var I3SSpatialTransformer = class {
8038
8103
  */
8039
8104
  async transformBoundsAsync(bounds) {
8040
8105
  const geographicSamples = await this.getGeographicBoundSamplesAsync(bounds);
8041
- const cartesianSamples = geographicSamples.map((position) => Array.from(import_geospatial11.Ellipsoid.WGS84.cartographicToCartesian(new import_core13.Vector3(position))));
8106
+ const cartesianSamples = geographicSamples.map((position) => Array.from(import_geospatial10.Ellipsoid.WGS84.cartographicToCartesian(new import_core13.Vector3(position))));
8042
8107
  const targetSamples = geographicSamples.map((position) => this.geographicToTargetTransformer.transformPosition(position));
8043
8108
  return {
8044
8109
  boundingVolume: { box: getAxisAlignedBox(cartesianSamples) },
@@ -8161,11 +8226,11 @@ var I3SSpatialTransformer = class {
8161
8226
  if (this.targetCoordinateFrame === "geographic") {
8162
8227
  return ecefVectorToLocalEnu(ecefNormal, geographic).normalize();
8163
8228
  }
8164
- const cartesian = import_geospatial11.Ellipsoid.WGS84.cartographicToCartesian(new import_core13.Vector3(geographic));
8229
+ const cartesian = import_geospatial10.Ellipsoid.WGS84.cartographicToCartesian(new import_core13.Vector3(geographic));
8165
8230
  const jacobianColumns = [];
8166
8231
  for (const direction of getPositiveAxisDirections()) {
8167
- const positiveGeographic = import_geospatial11.Ellipsoid.WGS84.cartesianToCartographic(new import_core13.Vector3(cartesian).add(direction));
8168
- const negativeGeographic = import_geospatial11.Ellipsoid.WGS84.cartesianToCartographic(new import_core13.Vector3(cartesian).subtract(direction));
8232
+ const positiveGeographic = import_geospatial10.Ellipsoid.WGS84.cartesianToCartographic(new import_core13.Vector3(cartesian).add(direction));
8233
+ const negativeGeographic = import_geospatial10.Ellipsoid.WGS84.cartesianToCartographic(new import_core13.Vector3(cartesian).subtract(direction));
8169
8234
  const positiveTarget = this.geographicToTargetTransformer.transformPosition(positiveGeographic);
8170
8235
  const negativeTarget = this.geographicToTargetTransformer.transformPosition(negativeGeographic);
8171
8236
  jacobianColumns.push((positiveTarget[0] - negativeTarget[0]) / 2, (positiveTarget[1] - negativeTarget[1]) / 2, (positiveTarget[2] - negativeTarget[2]) / 2);
@@ -8207,7 +8272,7 @@ function getGeographicBoundingSphere(geographicSamples) {
8207
8272
  if (!geographicSamples.length) {
8208
8273
  throw new Error("I3S spatial transformation requires at least one bound sample");
8209
8274
  }
8210
- const cartesianSamples = geographicSamples.map((position) => import_geospatial11.Ellipsoid.WGS84.cartographicToCartesian(new import_core13.Vector3(position)));
8275
+ const cartesianSamples = geographicSamples.map((position) => import_geospatial10.Ellipsoid.WGS84.cartographicToCartesian(new import_core13.Vector3(position)));
8211
8276
  const cartesianCenter = new import_core13.Vector3();
8212
8277
  for (const sample of cartesianSamples) {
8213
8278
  cartesianCenter.add(sample);
@@ -8217,7 +8282,7 @@ function getGeographicBoundingSphere(geographicSamples) {
8217
8282
  for (const sample of cartesianSamples) {
8218
8283
  radius = Math.max(radius, new import_core13.Vector3(sample).subtract(cartesianCenter).magnitude());
8219
8284
  }
8220
- const geographicCenter = import_geospatial11.Ellipsoid.WGS84.cartesianToCartographic(cartesianCenter);
8285
+ const geographicCenter = import_geospatial10.Ellipsoid.WGS84.cartesianToCartographic(cartesianCenter);
8221
8286
  return [geographicCenter[0], geographicCenter[1], geographicCenter[2], radius];
8222
8287
  }
8223
8288
  function getSourceBoundSamples(bounds, sourceFrame, sourceToGeographicTransformer, sourceVerticalUnitScale) {
@@ -8234,14 +8299,14 @@ function getSourceBoundSamples(bounds, sourceFrame, sourceToGeographicTransforme
8234
8299
  ];
8235
8300
  if (sourceFrame === "geographic") {
8236
8301
  const geographicCenter = sourceToGeographicTransformer.transformPosition(center2);
8237
- const cartesianCenter = import_geospatial11.Ellipsoid.WGS84.cartographicToCartesian(new import_core13.Vector3(geographicCenter));
8302
+ const cartesianCenter = import_geospatial10.Ellipsoid.WGS84.cartographicToCartesian(new import_core13.Vector3(geographicCenter));
8238
8303
  const cartesianAxes = bounds.normalReferenceFrame === "vertex-reference-frame" ? axes.map((axis) => localEnuVectorToEcef(axis, geographicCenter)) : axes;
8239
8304
  return getBoxSigns().map((signs) => {
8240
8305
  const corner = new import_core13.Vector3(cartesianCenter);
8241
8306
  for (let axisIndex = 0; axisIndex < 3; axisIndex++) {
8242
8307
  corner.add(new import_core13.Vector3(cartesianAxes[axisIndex]).scale(signs[axisIndex]));
8243
8308
  }
8244
- return Array.from(import_geospatial11.Ellipsoid.WGS84.cartesianToCartographic(corner));
8309
+ return Array.from(import_geospatial10.Ellipsoid.WGS84.cartesianToCartographic(corner));
8245
8310
  });
8246
8311
  }
8247
8312
  return getBoxSigns().map((signs) => {
@@ -8261,8 +8326,8 @@ function getSourceBoundSamples(bounds, sourceFrame, sourceToGeographicTransforme
8261
8326
  const radius = mbs[3];
8262
8327
  if (sourceFrame === "geographic") {
8263
8328
  const geographicCenter = sourceToGeographicTransformer.transformPosition(center);
8264
- const cartesianCenter = import_geospatial11.Ellipsoid.WGS84.cartographicToCartesian(new import_core13.Vector3(geographicCenter));
8265
- return getAxisDirections().map((direction) => Array.from(import_geospatial11.Ellipsoid.WGS84.cartesianToCartographic(new import_core13.Vector3(cartesianCenter).add(new import_core13.Vector3(direction).scale(radius)))));
8329
+ const cartesianCenter = import_geospatial10.Ellipsoid.WGS84.cartographicToCartesian(new import_core13.Vector3(geographicCenter));
8330
+ return getAxisDirections().map((direction) => Array.from(import_geospatial10.Ellipsoid.WGS84.cartesianToCartographic(new import_core13.Vector3(cartesianCenter).add(new import_core13.Vector3(direction).scale(radius)))));
8266
8331
  }
8267
8332
  return getAxisDirections().map((direction) => Array.from(new import_core13.Vector3(center).add(new import_core13.Vector3(direction).scale(radius))));
8268
8333
  }
@@ -8646,7 +8711,7 @@ var I3SSource = class {
8646
8711
  const { xmin, xmax, ymin, ymax, zmin, zmax } = fullExtent;
8647
8712
  const cartographicCenter = new import_core14.Vector3(xmin + (xmax - xmin) / 2, ymin + (ymax - ymin) / 2, zmin + (zmax - zmin) / 2);
8648
8713
  const cartesianCenter = new import_core14.Vector3();
8649
- import_geospatial12.Ellipsoid.WGS84.cartographicToCartesian(cartographicCenter, cartesianCenter);
8714
+ import_geospatial11.Ellipsoid.WGS84.cartographicToCartesian(cartographicCenter, cartesianCenter);
8650
8715
  return {
8651
8716
  cartographicCenter,
8652
8717
  cartesianCenter,
@@ -8658,7 +8723,7 @@ var I3SSource = class {
8658
8723
  const [xmin, ymin, xmax, ymax] = extent;
8659
8724
  const cartographicCenter = new import_core14.Vector3(xmin + (xmax - xmin) / 2, ymin + (ymax - ymin) / 2, 0);
8660
8725
  const cartesianCenter = new import_core14.Vector3();
8661
- import_geospatial12.Ellipsoid.WGS84.cartographicToCartesian(cartographicCenter, cartesianCenter);
8726
+ import_geospatial11.Ellipsoid.WGS84.cartographicToCartesian(cartographicCenter, cartesianCenter);
8662
8727
  return {
8663
8728
  cartographicCenter,
8664
8729
  cartesianCenter,