UNPKG

@allmaps/render

Version:

Render functions for WebGL and image buffers

790 lines (789 loc) 26.3 kB
import { throttle } from "lodash-es"; import { mergeOptions, pointsAndPointsToLines, lineStringToLines, hexToFractionalOpaqueRgba, subSetArray } from "@allmaps/stdlib"; import { green, pink, blue, black, white } from "@allmaps/tailwind"; import { TriangulatedWarpedMap } from "./TriangulatedWarpedMap.js"; import { WarpedMapEvent, WarpedMapEventType } from "../shared/events.js"; import { createHomogeneousTransform, invertHomogeneousTransform, applyHomogeneousTransform } from "../shared/homogeneous-transform.js"; import { createBuffer } from "../shared/webgl2.js"; import { getTilesAtOtherScaleFactors, tileKey } from "../shared/tiles.js"; const THROTTLE_UPDATE_TEXTURES_WAIT_MS = 200; const THROTTLE_UPDATE_TEXTURES_OPTIONS = { leading: true, trailing: true }; const defaultRenderLineLayerOptions = { viewportSize: 6, color: black, viewportBorderSize: 0, borderColor: white }; const defaultRenderPointLayerOptions = { viewportSize: 16, color: black, viewportBorderSize: 1, borderColor: white }; const defaultWebgl2WarpedMapOptions = { renderGcps: false, renderGcpsColor: blue, renderTransformedGcps: false, renderTransformedGcpsColor: pink, renderVectors: false, renderMask: false, renderMaskSize: 8, renderMaskColor: pink, renderFullMask: false, renderFullMaskSize: 8, renderFullMaskColor: green }; const DEFAULT_OPACITY = 1; const DEFAULT_SATURATION = 1; const TEXTURES_MAX_HIGHER_LOG2_SCALE_FACTOR_DIFF = 5; const TEXTURES_MAX_LOWER_LOG2_SCALE_FACTOR_DIFF = 1; function createWebGL2WarpedMapFactory(gl, mapProgram, linesProgram, pointsProgram) { return (mapId, georeferencedMap, options) => new WebGL2WarpedMap( mapId, georeferencedMap, gl, mapProgram, linesProgram, pointsProgram, options ); } class WebGL2WarpedMap extends TriangulatedWarpedMap { webgl2WarpedMapOptions; gl; mapProgram; linesProgram; pointsProgram; mapVao = null; linesVao = null; pointsVao = null; lineLayers = []; pointLayers = []; // Consider to store cachedTilesByTileKey as a quadtree for faster lookups cachedTilesByTileKey; cachedTilesByTileUrl; cachedTilesForTexture = []; previousCachedTilesForTexture = []; opacity = DEFAULT_OPACITY; saturation = DEFAULT_SATURATION; renderOptions = {}; cachedTilesTextureArray = null; cachedTilesResourceOriginPointsAndDimensionsTexture = null; cachedTilesScaleFactorsTexture = null; // About renderHomogeneousTransform and InvertedRenderHomogeneousTransform: // renderHomogeneousTransform is the product of: // - the viewport's projectedGeoToClipTransform (projected geo coordinates -> clip coordinates) // - the saved invertedRenderHomogeneousTransform (projected clip coordinates -> geo coordinates) // since updateVertexBuffers ('where to draw triangles') run with possibly a different Viewport then renderInternal ('drawing the triangles'), a difference caused by throttling, there needs to be an adjustment. // this adjustment is minimal: indeed, since invertedRenderHomogeneousTransform is set as the inverse of the viewport's projectedGeoToClipTransform in updateVertexBuffers() // this renderHomogeneousTransform is almost the identity transform [1, 0, 0, 1, 0, 0]. invertedRenderHomogeneousTransform; throttledUpdateTextures; /** * Creates an instance of WebGL2WarpedMap. * * @constructor * @param mapId - ID of the map * @param georeferencedMap - Georeferenced map used to construct the WarpedMap * @param gl - WebGL rendering context * @param mapProgram - WebGL program for map * @param options - WarpedMapOptions */ constructor(mapId, georeferencedMap, gl, mapProgram, linesProgram, pointsProgram, options) { super(mapId, georeferencedMap, options); this.cachedTilesByTileKey = /* @__PURE__ */ new Map(); this.cachedTilesByTileUrl = /* @__PURE__ */ new Map(); this.webgl2WarpedMapOptions = mergeOptions( defaultWebgl2WarpedMapOptions, options ); this.gl = gl; this.initializeWebGL(mapProgram, linesProgram, pointsProgram); this.invertedRenderHomogeneousTransform = createHomogeneousTransform(); this.throttledUpdateTextures = throttle( this.updateTextures.bind(this), THROTTLE_UPDATE_TEXTURES_WAIT_MS, THROTTLE_UPDATE_TEXTURES_OPTIONS ); } initializeWebGL(mapProgram, linesProgram, pointsProgram) { this.mapProgram = mapProgram; this.linesProgram = linesProgram; this.pointsProgram = pointsProgram; this.mapVao = this.gl.createVertexArray(); this.linesVao = this.gl.createVertexArray(); this.pointsVao = this.gl.createVertexArray(); this.cachedTilesTextureArray = this.gl.createTexture(); this.cachedTilesScaleFactorsTexture = this.gl.createTexture(); this.cachedTilesResourceOriginPointsAndDimensionsTexture = this.gl.createTexture(); } /** * Update the vertex buffers of this warped map * * @param projectedGeoToClipHomogeneousTransform - Transform from projected geo coordinates to webgl2 coordinates in the [-1, 1] range. Equivalent to OpenLayers' projectionTransform. */ updateVertexBuffers(projectedGeoToClipHomogeneousTransform, partialWebgl2RendererOptions) { this.invertedRenderHomogeneousTransform = invertHomogeneousTransform( projectedGeoToClipHomogeneousTransform ); if (partialWebgl2RendererOptions.renderMaps) { this.updateVertexBuffersMap(projectedGeoToClipHomogeneousTransform); } if (partialWebgl2RendererOptions.renderLines) { this.updateVertexBuffersLines(projectedGeoToClipHomogeneousTransform); } if (partialWebgl2RendererOptions.renderPoints) { this.updateVertexBuffersPoints(projectedGeoToClipHomogeneousTransform); } } /** * Clear textures for this map */ clearTextures() { this.throttledUpdateTextures(); } /** * Add cached tile to the textures of this map and update textures * * @param cachedTile */ addCachedTileAndUpdateTextures(cachedTile) { this.cachedTilesByTileKey.set(cachedTile.tileKey, cachedTile); this.cachedTilesByTileUrl.set(cachedTile.tileUrl, cachedTile); this.throttledUpdateTextures(); } /** * Remove cached tile from the textures of this map and update textures * * @param tileUrl */ removeCachedTileAndUpdateTextures(tileUrl) { const cachedTile = this.cachedTilesByTileUrl.get(tileUrl); if (cachedTile) { this.cachedTilesByTileKey.delete(cachedTile.tileKey); } this.cachedTilesByTileUrl.delete(tileUrl); this.throttledUpdateTextures(); } cancelThrottledFunctions() { this.throttledUpdateTextures.cancel(); } destroy() { this.gl.deleteVertexArray(this.mapVao); this.gl.deleteVertexArray(this.linesVao); this.gl.deleteVertexArray(this.pointsVao); this.gl.deleteTexture(this.cachedTilesTextureArray); this.gl.deleteTexture(this.cachedTilesScaleFactorsTexture); this.gl.deleteTexture( this.cachedTilesResourceOriginPointsAndDimensionsTexture ); this.cancelThrottledFunctions(); super.destroy(); } setLineLayers() { this.lineLayers = []; if (this.webgl2WarpedMapOptions.renderVectors) { this.lineLayers.push({ projectedGeoLines: pointsAndPointsToLines( this.projectedGeoPoints, this.projectedGeoTransformedResourcePoints ), projectedGeoPreviousLines: pointsAndPointsToLines( this.projectedGeoPoints, this.projectedGeoPreviousTransformedResourcePoints ), viewportSize: this.webgl2WarpedMapOptions.renderVectorsSize, color: this.webgl2WarpedMapOptions.renderVectorsColor, viewportBorderSize: this.webgl2WarpedMapOptions.renderVectorsBorderSize, borderColor: this.webgl2WarpedMapOptions.renderVectorsBorderColor }); } if (this.webgl2WarpedMapOptions.renderMask) { this.lineLayers.push({ projectedGeoLines: lineStringToLines( this.projectedGeoTriangulationMask ), projectedGeoPreviousLines: lineStringToLines( this.projectedGeoPreviousTriangulationMask ), viewportSize: this.webgl2WarpedMapOptions.renderMaskSize, color: this.webgl2WarpedMapOptions.renderMaskColor, viewportBorderSize: this.webgl2WarpedMapOptions.renderMaskBorderSize, borderColor: this.webgl2WarpedMapOptions.renderMaskBorderColor }); } if (this.webgl2WarpedMapOptions.renderFullMask) { this.lineLayers.push({ projectedGeoLines: lineStringToLines(this.projectedGeoFullMask), viewportSize: this.webgl2WarpedMapOptions.renderFullMaskSize, color: this.webgl2WarpedMapOptions.renderFullMaskColor, viewportBorderSize: this.webgl2WarpedMapOptions.renderFullMaskBorderSize, borderColor: this.webgl2WarpedMapOptions.renderFullMaskBorderColor }); } } setPointLayers() { this.pointLayers = []; if (this.webgl2WarpedMapOptions.renderGcps) { this.pointLayers.push({ projectedGeoPoints: this.projectedGeoPoints, viewportSize: this.webgl2WarpedMapOptions.renderGcpsSize, color: this.webgl2WarpedMapOptions.renderGcpsColor, viewportBorderSize: this.webgl2WarpedMapOptions.renderGcpsBorderSize, borderColor: this.webgl2WarpedMapOptions.renderGcpsBorderColor }); } if (this.webgl2WarpedMapOptions.renderTransformedGcps) { this.pointLayers.push({ projectedGeoPoints: this.projectedGeoTransformedResourcePoints, projectedGeoPreviousPoints: this.projectedGeoPreviousTransformedResourcePoints, viewportSize: this.webgl2WarpedMapOptions.renderTransformedGcpsSize, color: this.webgl2WarpedMapOptions.renderTransformedGcpsColor, viewportBorderSize: this.webgl2WarpedMapOptions.renderTransformedGcpsBorderSize, borderColor: this.webgl2WarpedMapOptions.renderTransformedGcpsBorderColor }); } } updateVertexBuffersMap(projectedGeoToClipHomogeneousTransform) { if (!this.mapVao) { return; } const gl = this.gl; const program = this.mapProgram; gl.bindVertexArray(this.mapVao); createBuffer( gl, program, new Float32Array(this.resourceTrianglePoints.flat()), 2, "a_resourceTrianglePoint" ); const clipPreviousTrianglePoints = this.projectedGeoPreviousTrianglePoints.map( (point) => applyHomogeneousTransform(projectedGeoToClipHomogeneousTransform, point) ); createBuffer( gl, program, new Float32Array(clipPreviousTrianglePoints.flat()), 2, "a_clipPreviousTrianglePoint" ); const clipTrianglePoints = this.projectedGeoTrianglePoints.map( (point) => applyHomogeneousTransform(projectedGeoToClipHomogeneousTransform, point) ); createBuffer( gl, program, new Float32Array(clipTrianglePoints.flat()), 2, "a_clipTrianglePoint" ); createBuffer( gl, program, new Float32Array(this.previousTrianglePointsDistortion), 1, "a_previousTrianglePointDistortion" ); createBuffer( gl, program, new Float32Array(this.trianglePointsDistortion), 1, "a_trianglePointDistortion" ); const trianglePointsTriangleIndex = new Float32Array( this.resourceTrianglePoints.length ).map((_v, i) => { return i; }); createBuffer( gl, program, trianglePointsTriangleIndex, 1, "a_trianglePointIndex" ); } updateVertexBuffersLines(projectedGeoToClipHomogeneousTransform) { if (!this.linesVao) { return; } const gl = this.gl; const program = this.linesProgram; gl.bindVertexArray(this.linesVao); this.setLineLayers(); const clipSixPoints = this.lineLayers.reduce( (accumulator, lineLayer) => accumulator.concat(lineLayer.projectedGeoLines), [] ).map((projectedGeoLine) => [ projectedGeoLine[0], projectedGeoLine[0], projectedGeoLine[0], projectedGeoLine[1], projectedGeoLine[1], projectedGeoLine[1] ]).flat().map( (point) => applyHomogeneousTransform(projectedGeoToClipHomogeneousTransform, point) ); createBuffer( gl, program, new Float32Array(clipSixPoints.flat()), 2, "a_clipPoint" ); const clipSixOtherPoints = this.lineLayers.reduce( (accumulator, lineLayer) => accumulator.concat(lineLayer.projectedGeoLines), [] ).map((projectedGeoLine) => [ projectedGeoLine[1], projectedGeoLine[1], projectedGeoLine[1], projectedGeoLine[0], projectedGeoLine[0], projectedGeoLine[0] ]).flat().map( (point) => applyHomogeneousTransform(projectedGeoToClipHomogeneousTransform, point) ); createBuffer( gl, program, new Float32Array(clipSixOtherPoints.flat()), 2, "a_clipOtherPoint" ); const clipSixPreviousPoints = this.lineLayers.reduce( (accumulator, lineLayer) => accumulator.concat( lineLayer.projectedGeoPreviousLines || lineLayer.projectedGeoLines ), [] ).map((projectedGeoLine) => [ projectedGeoLine[0], projectedGeoLine[0], projectedGeoLine[0], projectedGeoLine[1], projectedGeoLine[1], projectedGeoLine[1] ]).flat().map( (point) => applyHomogeneousTransform(projectedGeoToClipHomogeneousTransform, point) ); createBuffer( gl, program, new Float32Array(clipSixPreviousPoints.flat()), 2, "a_clipPreviousPoint" ); const clipSixPreviousOtherPoints = this.lineLayers.reduce( (accumulator, lineLayer) => accumulator.concat( lineLayer.projectedGeoPreviousLines || lineLayer.projectedGeoLines ), [] ).map((projectedGeoLine) => [ projectedGeoLine[1], projectedGeoLine[1], projectedGeoLine[1], projectedGeoLine[0], projectedGeoLine[0], projectedGeoLine[0] ]).flat().map( (point) => applyHomogeneousTransform(projectedGeoToClipHomogeneousTransform, point) ); createBuffer( gl, program, new Float32Array(clipSixPreviousOtherPoints.flat()), 2, "a_clipPreviousOtherPoint" ); const sixIsOtherPoints = this.lineLayers.reduce( (accumulator, lineLayer) => accumulator.concat( lineLayer.projectedGeoLines.flatMap((_projectedGeoLine) => [ 0, 0, 1, 0, 0, 1 ]) ), [] ); createBuffer( gl, program, new Float32Array(sixIsOtherPoints), 1, "a_isOtherPoint" ); const sixNormalSigns = this.lineLayers.reduce( (accumulator, lineLayer) => accumulator.concat( lineLayer.projectedGeoLines.flatMap((_projectedGeoLine) => [ 1, -1, 1, 1, -1, 1 ]) ), [] ); createBuffer( gl, program, new Float32Array(sixNormalSigns), 1, "a_normalSign" ); const viewportSizes = this.lineLayers.reduce( (accumulator, lineLayer) => accumulator.concat( lineLayer.projectedGeoLines.flatMap( (_projectedGeoLine) => Array(6).fill( lineLayer.viewportSize ?? defaultRenderLineLayerOptions.viewportSize ) ) ), [] ); createBuffer( gl, program, new Float32Array(viewportSizes), 1, "a_viewportSize" ); const colors = this.lineLayers.reduce( (accumulator, lineLayer) => accumulator.concat( lineLayer.projectedGeoLines.flatMap( (_projectedGeoLine) => Array(6).fill( hexToFractionalOpaqueRgba( lineLayer.color ?? defaultRenderLineLayerOptions.color ) ) ) ), [] ); createBuffer(gl, program, new Float32Array(colors.flat()), 4, "a_color"); const viewportBorderSizes = this.lineLayers.reduce( (accumulator, lineLayer) => accumulator.concat( lineLayer.projectedGeoLines.flatMap( (_projectedGeoLine) => Array(6).fill( lineLayer.viewportBorderSize ?? defaultRenderLineLayerOptions.viewportBorderSize ) ) ), [] ); createBuffer( gl, program, new Float32Array(viewportBorderSizes), 1, "a_viewportBorderSize" ); const borderColors = this.lineLayers.reduce( (accumulator, lineLayer) => accumulator.concat( lineLayer.projectedGeoLines.flatMap( (_projectedGeoLine) => Array(6).fill( hexToFractionalOpaqueRgba( lineLayer.borderColor ?? defaultRenderLineLayerOptions.borderColor ) ) ) ), [] ); createBuffer( gl, program, new Float32Array(borderColors.flat()), 4, "a_borderColor" ); } updateVertexBuffersPoints(projectedGeoToClipHomogeneousTransform) { if (!this.pointsVao) { return; } const gl = this.gl; const program = this.pointsProgram; gl.bindVertexArray(this.pointsVao); this.setPointLayers(); const clipPoints = this.pointLayers.reduce( (accumulator, pointLayer) => accumulator.concat(pointLayer.projectedGeoPoints), [] ).map( (point) => applyHomogeneousTransform(projectedGeoToClipHomogeneousTransform, point) ); createBuffer( gl, program, new Float32Array(clipPoints.flat()), 2, "a_clipPoint" ); const clipPreviousPoints = this.pointLayers.reduce( (accumulator, pointLayer) => accumulator.concat( pointLayer.projectedGeoPreviousPoints || pointLayer.projectedGeoPoints ), [] ).map( (point) => applyHomogeneousTransform(projectedGeoToClipHomogeneousTransform, point) ); createBuffer( gl, program, new Float32Array(clipPreviousPoints.flat()), 2, "a_clipPreviousPoint" ); const viewportSizes = this.pointLayers.reduce( (accumulator, pointLayer) => accumulator.concat( pointLayer.projectedGeoPoints.map( (_projectedGeoPoint) => pointLayer.viewportSize ?? defaultRenderPointLayerOptions.viewportSize ) ), [] ); createBuffer( gl, program, new Float32Array(viewportSizes), 1, "a_viewportSize" ); const colors = this.pointLayers.reduce( (accumulator, pointLayer) => accumulator.concat( pointLayer.projectedGeoPoints.map( (_projectedGeoPoint) => hexToFractionalOpaqueRgba( pointLayer.color ?? defaultRenderPointLayerOptions.color ) ) ), [] ); createBuffer(gl, program, new Float32Array(colors.flat()), 4, "a_color"); const viewportBorderSizes = this.pointLayers.reduce( (accumulator, pointLayer) => accumulator.concat( pointLayer.projectedGeoPoints.map( (_projectedGeoPoint) => pointLayer.viewportBorderSize ?? defaultRenderPointLayerOptions.viewportBorderSize ) ), [] ); createBuffer( gl, program, new Float32Array(viewportBorderSizes), 1, "a_viewportBorderSize" ); const borderColors = this.pointLayers.reduce( (accumulator, pointLayer) => accumulator.concat( pointLayer.projectedGeoPoints.map( (_projectedGeoPoint) => hexToFractionalOpaqueRgba( pointLayer.borderColor ?? defaultRenderPointLayerOptions.borderColor ) ) ), [] ); createBuffer( gl, program, new Float32Array(borderColors.flat()), 4, "a_borderColor" ); } async updateTextures() { const gl = this.gl; this.updateCachedTilesForTextures(); if (this.cachedTilesForTexture.length !== 0 && subSetArray( this.previousCachedTilesForTexture.map( (textureTile) => textureTile.tileUrl ), this.cachedTilesForTexture.map((textureTile) => textureTile.tileUrl) )) { return; } const requiredTextureWidth = Math.max( ...this.parsedImage.tileZoomLevels.map((size) => size.width) ); const requiredTextureHeigt = Math.max( ...this.parsedImage.tileZoomLevels.map((size) => size.height) ); const requiredTextureDepth = this.cachedTilesForTexture.length; gl.pixelStorei(gl.UNPACK_ALIGNMENT, 4); gl.bindTexture(gl.TEXTURE_2D_ARRAY, this.cachedTilesTextureArray); gl.texImage3D( gl.TEXTURE_2D_ARRAY, 0, gl.RGBA, requiredTextureWidth, requiredTextureHeigt, requiredTextureDepth, 0, gl.RGBA, gl.UNSIGNED_BYTE, null ); for (let i = 0; i < this.cachedTilesForTexture.length; i++) { const imageData = this.cachedTilesForTexture[i].data; const pbo = gl.createBuffer(); gl.bindBuffer(gl.PIXEL_UNPACK_BUFFER, pbo); gl.bufferData(gl.PIXEL_UNPACK_BUFFER, imageData.data, gl.STATIC_DRAW); gl.texSubImage3D( gl.TEXTURE_2D_ARRAY, 0, 0, 0, i, imageData.width, imageData.height, 1, gl.RGBA, gl.UNSIGNED_BYTE, 0 ); gl.bindBuffer(gl.PIXEL_UNPACK_BUFFER, null); } gl.texParameteri(gl.TEXTURE_2D_ARRAY, gl.TEXTURE_MIN_FILTER, gl.LINEAR); gl.texParameteri(gl.TEXTURE_2D_ARRAY, gl.TEXTURE_MAG_FILTER, gl.LINEAR); gl.texParameteri(gl.TEXTURE_2D_ARRAY, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); gl.texParameteri(gl.TEXTURE_2D_ARRAY, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); const cachedTilesResourceOriginPointsAndDimensions = this.cachedTilesForTexture.map((textureTile) => { if (textureTile && textureTile.imageRequest && textureTile.imageRequest.region) { return [ textureTile.imageRequest.region.x, textureTile.imageRequest.region.y, textureTile.imageRequest.region.width, textureTile.imageRequest.region.height ]; } }); gl.bindTexture( gl.TEXTURE_2D, this.cachedTilesResourceOriginPointsAndDimensionsTexture ); gl.texImage2D( gl.TEXTURE_2D, 0, gl.R32I, 1, this.cachedTilesForTexture.length * 4, 0, gl.RED_INTEGER, gl.INT, new Int32Array(cachedTilesResourceOriginPointsAndDimensions.flat()) ); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); const cachedTilesScaleFactors = this.cachedTilesForTexture.map( (textureTile) => textureTile.tile.tileZoomLevel.scaleFactor ); gl.bindTexture(gl.TEXTURE_2D, this.cachedTilesScaleFactorsTexture); gl.texImage2D( gl.TEXTURE_2D, 0, gl.R32I, 1, this.cachedTilesForTexture.length, 0, gl.RED_INTEGER, gl.INT, new Int32Array(cachedTilesScaleFactors) ); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); this.dispatchEvent(new WarpedMapEvent(WarpedMapEventType.TEXTURESUPDATED)); } updateCachedTilesForTextures() { const cachedTiles = []; const cachedTilesAtOtherScaleFactors = []; const overviewCachedTiles = []; for (const fetchableTile of this.fetchableTilesForViewport) { const cachedTile = this.cachedTilesByTileUrl.get(fetchableTile.tileUrl); if (cachedTile) { cachedTiles.push(cachedTile); } else { for (const cachedTile2 of this.getCachedTilesAtOtherScaleFactors( fetchableTile.tile )) { cachedTilesAtOtherScaleFactors.push(cachedTile2); } } } for (const fetchableTile of this.overviewFetchableTilesForViewport) { const cachedTile = this.cachedTilesByTileUrl.get(fetchableTile.tileUrl); if (cachedTile) { const tileZoolLevelTilesCount = this.tileZoomLevelForViewport ? this.tileZoomLevelForViewport.rows * this.tileZoomLevelForViewport.columns : void 0; if (cachedTiles.length === 0 || tileZoolLevelTilesCount && cachedTiles.length < tileZoolLevelTilesCount) { overviewCachedTiles.push(cachedTile); } } } let cachedTilesForTextures = [ ...cachedTiles, ...cachedTilesAtOtherScaleFactors, ...overviewCachedTiles ]; const cachedTilesForTexturesByTileUrl = /* @__PURE__ */ new Map(); cachedTilesForTextures.forEach( (cachedTile) => cachedTilesForTexturesByTileUrl.set(cachedTile.tileUrl, cachedTile) ); cachedTilesForTextures = [...cachedTilesForTexturesByTileUrl.values()]; this.previousCachedTilesForTexture = this.cachedTilesForTexture; this.cachedTilesForTexture = cachedTilesForTextures; return; } getCachedTilesAtOtherScaleFactors(tile) { if (this.cachedTilesByTileUrl.size === 0) { return []; } if (!this.tileZoomLevelForViewport) { return []; } const cachedTiles = []; for (tile of getTilesAtOtherScaleFactors( tile, this.parsedImage, this.tileZoomLevelForViewport.scaleFactor, TEXTURES_MAX_LOWER_LOG2_SCALE_FACTOR_DIFF, TEXTURES_MAX_HIGHER_LOG2_SCALE_FACTOR_DIFF, this.tileInCachedTiles.bind(this) // Only consider tiles in cache, )) { const cachedTile = this.tileToCachedTile(tile); if (cachedTile) { cachedTiles.push(cachedTile); } else { throw new Error("Tile supposed to be in cache isn't."); } } return cachedTiles; } // Lookup by tileKey (zoomlevel, row, column) instead of tileUrl // Because computing the tileUrl for every tile is expensive tileToCachedTile(tile) { return this.cachedTilesByTileKey.get(tileKey(tile)); } tileInCachedTiles(tile) { return this.cachedTilesByTileKey.has(tileKey(tile)); } } export { WebGL2WarpedMap, createWebGL2WarpedMapFactory }; //# sourceMappingURL=WebGL2WarpedMap.js.map