UNPKG

@deck.gl/layers

Version:

deck.gl core layers

1,529 lines (1,464 loc) 207 kB
"use strict"; var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // dist/index.js var dist_exports = {}; __export(dist_exports, { ArcLayer: () => arc_layer_default, BitmapLayer: () => bitmap_layer_default, ColumnLayer: () => column_layer_default, GeoJsonLayer: () => geojson_layer_default, GridCellLayer: () => grid_cell_layer_default, IconLayer: () => icon_layer_default, LineLayer: () => line_layer_default, PathLayer: () => path_layer_default, PointCloudLayer: () => point_cloud_layer_default, PolygonLayer: () => polygon_layer_default, ScatterplotLayer: () => scatterplot_layer_default, SolidPolygonLayer: () => solid_polygon_layer_default, TextLayer: () => text_layer_default, _MultiIconLayer: () => multi_icon_layer_default, _TextBackgroundLayer: () => text_background_layer_default }); module.exports = __toCommonJS(dist_exports); // dist/arc-layer/arc-layer.js var import_core = require("@deck.gl/core"); var import_engine = require("@luma.gl/engine"); // dist/arc-layer/arc-layer-uniforms.js var uniformBlock = `uniform arcUniforms { bool greatCircle; bool useShortestPath; float numSegments; float widthScale; float widthMinPixels; float widthMaxPixels; highp int widthUnits; } arc; `; var arcUniforms = { name: "arc", vs: uniformBlock, fs: uniformBlock, uniformTypes: { greatCircle: "f32", useShortestPath: "f32", numSegments: "f32", widthScale: "f32", widthMinPixels: "f32", widthMaxPixels: "f32", widthUnits: "i32" } }; // dist/arc-layer/arc-layer-vertex.glsl.js var arc_layer_vertex_glsl_default = `#version 300 es #define SHADER_NAME arc-layer-vertex-shader in vec4 instanceSourceColors; in vec4 instanceTargetColors; in vec3 instanceSourcePositions; in vec3 instanceSourcePositions64Low; in vec3 instanceTargetPositions; in vec3 instanceTargetPositions64Low; in vec3 instancePickingColors; in float instanceWidths; in float instanceHeights; in float instanceTilts; out vec4 vColor; out vec2 uv; out float isValid; float paraboloid(float distance, float sourceZ, float targetZ, float ratio) { float deltaZ = targetZ - sourceZ; float dh = distance * instanceHeights; if (dh == 0.0) { return sourceZ + deltaZ * ratio; } float unitZ = deltaZ / dh; float p2 = unitZ * unitZ + 1.0; float dir = step(deltaZ, 0.0); float z0 = mix(sourceZ, targetZ, dir); float r = mix(ratio, 1.0 - ratio, dir); return sqrt(r * (p2 - r)) * dh + z0; } vec2 getExtrusionOffset(vec2 line_clipspace, float offset_direction, float width) { vec2 dir_screenspace = normalize(line_clipspace * project.viewportSize); dir_screenspace = vec2(-dir_screenspace.y, dir_screenspace.x); return dir_screenspace * offset_direction * width / 2.0; } float getSegmentRatio(float index) { return smoothstep(0.0, 1.0, index / (arc.numSegments - 1.0)); } vec3 interpolateFlat(vec3 source, vec3 target, float segmentRatio) { float distance = length(source.xy - target.xy); float z = paraboloid(distance, source.z, target.z, segmentRatio); float tiltAngle = radians(instanceTilts); vec2 tiltDirection = normalize(target.xy - source.xy); vec2 tilt = vec2(-tiltDirection.y, tiltDirection.x) * z * sin(tiltAngle); return vec3( mix(source.xy, target.xy, segmentRatio) + tilt, z * cos(tiltAngle) ); } float getAngularDist (vec2 source, vec2 target) { vec2 sourceRadians = radians(source); vec2 targetRadians = radians(target); vec2 sin_half_delta = sin((sourceRadians - targetRadians) / 2.0); vec2 shd_sq = sin_half_delta * sin_half_delta; float a = shd_sq.y + cos(sourceRadians.y) * cos(targetRadians.y) * shd_sq.x; return 2.0 * asin(sqrt(a)); } vec3 interpolateGreatCircle(vec3 source, vec3 target, vec3 source3D, vec3 target3D, float angularDist, float t) { vec2 lngLat; if(abs(angularDist - PI) < 0.001) { lngLat = (1.0 - t) * source.xy + t * target.xy; } else { float a = sin((1.0 - t) * angularDist); float b = sin(t * angularDist); vec3 p = source3D.yxz * a + target3D.yxz * b; lngLat = degrees(vec2(atan(p.y, -p.x), atan(p.z, length(p.xy)))); } float z = paraboloid(angularDist * EARTH_RADIUS, source.z, target.z, t); return vec3(lngLat, z); } void main(void) { geometry.worldPosition = instanceSourcePositions; geometry.worldPositionAlt = instanceTargetPositions; float segmentIndex = float(gl_VertexID / 2); float segmentSide = mod(float(gl_VertexID), 2.) == 0. ? -1. : 1.; float segmentRatio = getSegmentRatio(segmentIndex); float prevSegmentRatio = getSegmentRatio(max(0.0, segmentIndex - 1.0)); float nextSegmentRatio = getSegmentRatio(min(arc.numSegments - 1.0, segmentIndex + 1.0)); float indexDir = mix(-1.0, 1.0, step(segmentIndex, 0.0)); isValid = 1.0; uv = vec2(segmentRatio, segmentSide); geometry.uv = uv; geometry.pickingColor = instancePickingColors; vec4 curr; vec4 next; vec3 source; vec3 target; if ((arc.greatCircle || project.projectionMode == PROJECTION_MODE_GLOBE) && project.coordinateSystem == COORDINATE_SYSTEM_LNGLAT) { source = project_globe_(vec3(instanceSourcePositions.xy, 0.0)); target = project_globe_(vec3(instanceTargetPositions.xy, 0.0)); float angularDist = getAngularDist(instanceSourcePositions.xy, instanceTargetPositions.xy); vec3 prevPos = interpolateGreatCircle(instanceSourcePositions, instanceTargetPositions, source, target, angularDist, prevSegmentRatio); vec3 currPos = interpolateGreatCircle(instanceSourcePositions, instanceTargetPositions, source, target, angularDist, segmentRatio); vec3 nextPos = interpolateGreatCircle(instanceSourcePositions, instanceTargetPositions, source, target, angularDist, nextSegmentRatio); if (abs(currPos.x - prevPos.x) > 180.0) { indexDir = -1.0; isValid = 0.0; } else if (abs(currPos.x - nextPos.x) > 180.0) { indexDir = 1.0; isValid = 0.0; } nextPos = indexDir < 0.0 ? prevPos : nextPos; nextSegmentRatio = indexDir < 0.0 ? prevSegmentRatio : nextSegmentRatio; if (isValid == 0.0) { nextPos.x += nextPos.x > 0.0 ? -360.0 : 360.0; float t = ((currPos.x > 0.0 ? 180.0 : -180.0) - currPos.x) / (nextPos.x - currPos.x); currPos = mix(currPos, nextPos, t); segmentRatio = mix(segmentRatio, nextSegmentRatio, t); } vec3 currPos64Low = mix(instanceSourcePositions64Low, instanceTargetPositions64Low, segmentRatio); vec3 nextPos64Low = mix(instanceSourcePositions64Low, instanceTargetPositions64Low, nextSegmentRatio); curr = project_position_to_clipspace(currPos, currPos64Low, vec3(0.0), geometry.position); next = project_position_to_clipspace(nextPos, nextPos64Low, vec3(0.0)); } else { vec3 source_world = instanceSourcePositions; vec3 target_world = instanceTargetPositions; if (arc.useShortestPath) { source_world.x = mod(source_world.x + 180., 360.0) - 180.; target_world.x = mod(target_world.x + 180., 360.0) - 180.; float deltaLng = target_world.x - source_world.x; if (deltaLng > 180.) target_world.x -= 360.; if (deltaLng < -180.) source_world.x -= 360.; } source = project_position(source_world, instanceSourcePositions64Low); target = project_position(target_world, instanceTargetPositions64Low); float antiMeridianX = 0.0; if (arc.useShortestPath) { if (project.projectionMode == PROJECTION_MODE_WEB_MERCATOR_AUTO_OFFSET) { antiMeridianX = -(project.coordinateOrigin.x + 180.) / 360. * TILE_SIZE; } float thresholdRatio = (antiMeridianX - source.x) / (target.x - source.x); if (prevSegmentRatio <= thresholdRatio && nextSegmentRatio > thresholdRatio) { isValid = 0.0; indexDir = sign(segmentRatio - thresholdRatio); segmentRatio = thresholdRatio; } } nextSegmentRatio = indexDir < 0.0 ? prevSegmentRatio : nextSegmentRatio; vec3 currPos = interpolateFlat(source, target, segmentRatio); vec3 nextPos = interpolateFlat(source, target, nextSegmentRatio); if (arc.useShortestPath) { if (nextPos.x < antiMeridianX) { currPos.x += TILE_SIZE; nextPos.x += TILE_SIZE; } } curr = project_common_position_to_clipspace(vec4(currPos, 1.0)); next = project_common_position_to_clipspace(vec4(nextPos, 1.0)); geometry.position = vec4(currPos, 1.0); } float widthPixels = clamp( project_size_to_pixel(instanceWidths * arc.widthScale, arc.widthUnits), arc.widthMinPixels, arc.widthMaxPixels ); vec3 offset = vec3( getExtrusionOffset((next.xy - curr.xy) * indexDir, segmentSide, widthPixels), 0.0); DECKGL_FILTER_SIZE(offset, geometry); DECKGL_FILTER_GL_POSITION(curr, geometry); gl_Position = curr + vec4(project_pixel_size_to_clipspace(offset.xy), 0.0, 0.0); vec4 color = mix(instanceSourceColors, instanceTargetColors, segmentRatio); vColor = vec4(color.rgb, color.a * layer.opacity); DECKGL_FILTER_COLOR(vColor, geometry); } `; // dist/arc-layer/arc-layer-fragment.glsl.js var arc_layer_fragment_glsl_default = `#version 300 es #define SHADER_NAME arc-layer-fragment-shader precision highp float; in vec4 vColor; in vec2 uv; in float isValid; out vec4 fragColor; void main(void) { if (isValid == 0.0) { discard; } fragColor = vColor; geometry.uv = uv; DECKGL_FILTER_COLOR(fragColor, geometry); } `; // dist/arc-layer/arc-layer.js var DEFAULT_COLOR = [0, 0, 0, 255]; var defaultProps = { getSourcePosition: { type: "accessor", value: (x) => x.sourcePosition }, getTargetPosition: { type: "accessor", value: (x) => x.targetPosition }, getSourceColor: { type: "accessor", value: DEFAULT_COLOR }, getTargetColor: { type: "accessor", value: DEFAULT_COLOR }, getWidth: { type: "accessor", value: 1 }, getHeight: { type: "accessor", value: 1 }, getTilt: { type: "accessor", value: 0 }, greatCircle: false, numSegments: { type: "number", value: 50, min: 1 }, widthUnits: "pixels", widthScale: { type: "number", value: 1, min: 0 }, widthMinPixels: { type: "number", value: 0, min: 0 }, widthMaxPixels: { type: "number", value: Number.MAX_SAFE_INTEGER, min: 0 } }; var ArcLayer = class extends import_core.Layer { getBounds() { var _a; return (_a = this.getAttributeManager()) == null ? void 0 : _a.getBounds([ "instanceSourcePositions", "instanceTargetPositions" ]); } getShaders() { return super.getShaders({ vs: arc_layer_vertex_glsl_default, fs: arc_layer_fragment_glsl_default, modules: [import_core.project32, import_core.picking, arcUniforms] }); } // This layer has its own wrapLongitude logic get wrapLongitude() { return false; } initializeState() { const attributeManager = this.getAttributeManager(); attributeManager.addInstanced({ instanceSourcePositions: { size: 3, type: "float64", fp64: this.use64bitPositions(), transition: true, accessor: "getSourcePosition" }, instanceTargetPositions: { size: 3, type: "float64", fp64: this.use64bitPositions(), transition: true, accessor: "getTargetPosition" }, instanceSourceColors: { size: this.props.colorFormat.length, type: "unorm8", transition: true, accessor: "getSourceColor", defaultValue: DEFAULT_COLOR }, instanceTargetColors: { size: this.props.colorFormat.length, type: "unorm8", transition: true, accessor: "getTargetColor", defaultValue: DEFAULT_COLOR }, instanceWidths: { size: 1, transition: true, accessor: "getWidth", defaultValue: 1 }, instanceHeights: { size: 1, transition: true, accessor: "getHeight", defaultValue: 1 }, instanceTilts: { size: 1, transition: true, accessor: "getTilt", defaultValue: 0 } }); } updateState(params) { var _a; super.updateState(params); if (params.changeFlags.extensionsChanged) { (_a = this.state.model) == null ? void 0 : _a.destroy(); this.state.model = this._getModel(); this.getAttributeManager().invalidateAll(); } } draw({ uniforms }) { const { widthUnits, widthScale, widthMinPixels, widthMaxPixels, greatCircle, wrapLongitude, numSegments } = this.props; const arcProps = { numSegments, widthUnits: import_core.UNIT[widthUnits], widthScale, widthMinPixels, widthMaxPixels, greatCircle, useShortestPath: wrapLongitude }; const model = this.state.model; model.shaderInputs.setProps({ arc: arcProps }); model.setVertexCount(numSegments * 2); model.draw(this.context.renderPass); } _getModel() { return new import_engine.Model(this.context.device, { ...this.getShaders(), id: this.props.id, bufferLayout: this.getAttributeManager().getBufferLayouts(), topology: "triangle-strip", isInstanced: true }); } }; ArcLayer.layerName = "ArcLayer"; ArcLayer.defaultProps = defaultProps; var arc_layer_default = ArcLayer; // dist/bitmap-layer/bitmap-layer.js var import_core3 = require("@deck.gl/core"); var import_engine2 = require("@luma.gl/engine"); var import_web_mercator = require("@math.gl/web-mercator"); // dist/bitmap-layer/create-mesh.js var import_core2 = require("@math.gl/core"); var DEFAULT_INDICES = new Uint32Array([0, 2, 1, 0, 3, 2]); var DEFAULT_TEX_COORDS = new Float32Array([0, 1, 0, 0, 1, 0, 1, 1]); function createMesh(bounds, resolution) { if (!resolution) { return createQuad(bounds); } const maxXSpan = Math.max(Math.abs(bounds[0][0] - bounds[3][0]), Math.abs(bounds[1][0] - bounds[2][0])); const maxYSpan = Math.max(Math.abs(bounds[1][1] - bounds[0][1]), Math.abs(bounds[2][1] - bounds[3][1])); const uCount = Math.ceil(maxXSpan / resolution) + 1; const vCount = Math.ceil(maxYSpan / resolution) + 1; const vertexCount = (uCount - 1) * (vCount - 1) * 6; const indices = new Uint32Array(vertexCount); const texCoords = new Float32Array(uCount * vCount * 2); const positions = new Float64Array(uCount * vCount * 3); let vertex = 0; let index = 0; for (let u = 0; u < uCount; u++) { const ut = u / (uCount - 1); for (let v = 0; v < vCount; v++) { const vt = v / (vCount - 1); const p = interpolateQuad(bounds, ut, vt); positions[vertex * 3 + 0] = p[0]; positions[vertex * 3 + 1] = p[1]; positions[vertex * 3 + 2] = p[2] || 0; texCoords[vertex * 2 + 0] = ut; texCoords[vertex * 2 + 1] = 1 - vt; if (u > 0 && v > 0) { indices[index++] = vertex - vCount; indices[index++] = vertex - vCount - 1; indices[index++] = vertex - 1; indices[index++] = vertex - vCount; indices[index++] = vertex - 1; indices[index++] = vertex; } vertex++; } } return { vertexCount, positions, indices, texCoords }; } function createQuad(bounds) { const positions = new Float64Array(12); for (let i = 0; i < bounds.length; i++) { positions[i * 3 + 0] = bounds[i][0]; positions[i * 3 + 1] = bounds[i][1]; positions[i * 3 + 2] = bounds[i][2] || 0; } return { vertexCount: 6, positions, indices: DEFAULT_INDICES, texCoords: DEFAULT_TEX_COORDS }; } function interpolateQuad(quad, ut, vt) { return (0, import_core2.lerp)((0, import_core2.lerp)(quad[0], quad[1], vt), (0, import_core2.lerp)(quad[3], quad[2], vt), ut); } // dist/bitmap-layer/bitmap-layer-uniforms.js var uniformBlock2 = `uniform bitmapUniforms { vec4 bounds; float coordinateConversion; float desaturate; vec3 tintColor; vec4 transparentColor; } bitmap; `; var bitmapUniforms = { name: "bitmap", vs: uniformBlock2, fs: uniformBlock2, uniformTypes: { bounds: "vec4<f32>", coordinateConversion: "f32", desaturate: "f32", tintColor: "vec3<f32>", transparentColor: "vec4<f32>" } }; // dist/bitmap-layer/bitmap-layer-vertex.js var bitmap_layer_vertex_default = `#version 300 es #define SHADER_NAME bitmap-layer-vertex-shader in vec2 texCoords; in vec3 positions; in vec3 positions64Low; out vec2 vTexCoord; out vec2 vTexPos; const vec3 pickingColor = vec3(1.0, 0.0, 0.0); void main(void) { geometry.worldPosition = positions; geometry.uv = texCoords; geometry.pickingColor = pickingColor; gl_Position = project_position_to_clipspace(positions, positions64Low, vec3(0.0), geometry.position); DECKGL_FILTER_GL_POSITION(gl_Position, geometry); vTexCoord = texCoords; if (bitmap.coordinateConversion < -0.5) { vTexPos = geometry.position.xy + project.commonOrigin.xy; } else if (bitmap.coordinateConversion > 0.5) { vTexPos = geometry.worldPosition.xy; } vec4 color = vec4(0.0); DECKGL_FILTER_COLOR(color, geometry); } `; // dist/bitmap-layer/bitmap-layer-fragment.js var packUVsIntoRGB = ` vec3 packUVsIntoRGB(vec2 uv) { // Extract the top 8 bits. We want values to be truncated down so we can add a fraction vec2 uv8bit = floor(uv * 256.); // Calculate the normalized remainders of u and v parts that do not fit into 8 bits // Scale and clamp to 0-1 range vec2 uvFraction = fract(uv * 256.); vec2 uvFraction4bit = floor(uvFraction * 16.); // Remainder can be encoded in blue channel, encode as 4 bits for pixel coordinates float fractions = uvFraction4bit.x + uvFraction4bit.y * 16.; return vec3(uv8bit, fractions) / 255.; } `; var bitmap_layer_fragment_default = `#version 300 es #define SHADER_NAME bitmap-layer-fragment-shader #ifdef GL_ES precision highp float; #endif uniform sampler2D bitmapTexture; in vec2 vTexCoord; in vec2 vTexPos; out vec4 fragColor; /* projection utils */ const float TILE_SIZE = 512.0; const float PI = 3.1415926536; const float WORLD_SCALE = TILE_SIZE / PI / 2.0; // from degrees to Web Mercator vec2 lnglat_to_mercator(vec2 lnglat) { float x = lnglat.x; float y = clamp(lnglat.y, -89.9, 89.9); return vec2( radians(x) + PI, PI + log(tan(PI * 0.25 + radians(y) * 0.5)) ) * WORLD_SCALE; } // from Web Mercator to degrees vec2 mercator_to_lnglat(vec2 xy) { xy /= WORLD_SCALE; return degrees(vec2( xy.x - PI, atan(exp(xy.y - PI)) * 2.0 - PI * 0.5 )); } /* End projection utils */ // apply desaturation vec3 color_desaturate(vec3 color) { float luminance = (color.r + color.g + color.b) * 0.333333333; return mix(color, vec3(luminance), bitmap.desaturate); } // apply tint vec3 color_tint(vec3 color) { return color * bitmap.tintColor; } // blend with background color vec4 apply_opacity(vec3 color, float alpha) { if (bitmap.transparentColor.a == 0.0) { return vec4(color, alpha); } float blendedAlpha = alpha + bitmap.transparentColor.a * (1.0 - alpha); float highLightRatio = alpha / blendedAlpha; vec3 blendedRGB = mix(bitmap.transparentColor.rgb, color, highLightRatio); return vec4(blendedRGB, blendedAlpha); } vec2 getUV(vec2 pos) { return vec2( (pos.x - bitmap.bounds[0]) / (bitmap.bounds[2] - bitmap.bounds[0]), (pos.y - bitmap.bounds[3]) / (bitmap.bounds[1] - bitmap.bounds[3]) ); } ${packUVsIntoRGB} void main(void) { vec2 uv = vTexCoord; if (bitmap.coordinateConversion < -0.5) { vec2 lnglat = mercator_to_lnglat(vTexPos); uv = getUV(lnglat); } else if (bitmap.coordinateConversion > 0.5) { vec2 commonPos = lnglat_to_mercator(vTexPos); uv = getUV(commonPos); } vec4 bitmapColor = texture(bitmapTexture, uv); fragColor = apply_opacity(color_tint(color_desaturate(bitmapColor.rgb)), bitmapColor.a * layer.opacity); geometry.uv = uv; DECKGL_FILTER_COLOR(fragColor, geometry); if (bool(picking.isActive) && !bool(picking.isAttribute)) { // Since instance information is not used, we can use picking color for pixel index fragColor.rgb = packUVsIntoRGB(uv); } } `; // dist/bitmap-layer/bitmap-layer.js var defaultProps2 = { image: { type: "image", value: null, async: true }, bounds: { type: "array", value: [1, 0, 0, 1], compare: true }, _imageCoordinateSystem: import_core3.COORDINATE_SYSTEM.DEFAULT, desaturate: { type: "number", min: 0, max: 1, value: 0 }, // More context: because of the blending mode we're using for ground imagery, // alpha is not effective when blending the bitmap layers with the base map. // Instead we need to manually dim/blend rgb values with a background color. transparentColor: { type: "color", value: [0, 0, 0, 0] }, tintColor: { type: "color", value: [255, 255, 255] }, textureParameters: { type: "object", ignore: true, value: null } }; var BitmapLayer = class extends import_core3.Layer { getShaders() { return super.getShaders({ vs: bitmap_layer_vertex_default, fs: bitmap_layer_fragment_default, modules: [import_core3.project32, import_core3.picking, bitmapUniforms] }); } initializeState() { const attributeManager = this.getAttributeManager(); attributeManager.remove(["instancePickingColors"]); const noAlloc = true; attributeManager.add({ indices: { size: 1, isIndexed: true, update: (attribute) => attribute.value = this.state.mesh.indices, noAlloc }, positions: { size: 3, type: "float64", fp64: this.use64bitPositions(), update: (attribute) => attribute.value = this.state.mesh.positions, noAlloc }, texCoords: { size: 2, update: (attribute) => attribute.value = this.state.mesh.texCoords, noAlloc } }); } updateState({ props, oldProps, changeFlags }) { var _a; const attributeManager = this.getAttributeManager(); if (changeFlags.extensionsChanged) { (_a = this.state.model) == null ? void 0 : _a.destroy(); this.state.model = this._getModel(); attributeManager.invalidateAll(); } if (props.bounds !== oldProps.bounds) { const oldMesh = this.state.mesh; const mesh = this._createMesh(); this.state.model.setVertexCount(mesh.vertexCount); for (const key in mesh) { if (oldMesh && oldMesh[key] !== mesh[key]) { attributeManager.invalidate(key); } } this.setState({ mesh, ...this._getCoordinateUniforms() }); } else if (props._imageCoordinateSystem !== oldProps._imageCoordinateSystem) { this.setState(this._getCoordinateUniforms()); } } getPickingInfo(params) { const { image } = this.props; const info = params.info; if (!info.color || !image) { info.bitmap = null; return info; } const { width, height } = image; info.index = 0; const uv = unpackUVsFromRGB(info.color); info.bitmap = { size: { width, height }, uv, pixel: [Math.floor(uv[0] * width), Math.floor(uv[1] * height)] }; return info; } // Override base Layer multi-depth picking logic disablePickingIndex() { this.setState({ disablePicking: true }); } restorePickingColors() { this.setState({ disablePicking: false }); } _updateAutoHighlight(info) { super._updateAutoHighlight({ ...info, color: this.encodePickingColor(0) }); } _createMesh() { const { bounds } = this.props; let normalizedBounds = bounds; if (isRectangularBounds(bounds)) { normalizedBounds = [ [bounds[0], bounds[1]], [bounds[0], bounds[3]], [bounds[2], bounds[3]], [bounds[2], bounds[1]] ]; } return createMesh(normalizedBounds, this.context.viewport.resolution); } _getModel() { return new import_engine2.Model(this.context.device, { ...this.getShaders(), id: this.props.id, bufferLayout: this.getAttributeManager().getBufferLayouts(), topology: "triangle-list", isInstanced: false }); } draw(opts) { const { shaderModuleProps } = opts; const { model, coordinateConversion, bounds, disablePicking } = this.state; const { image, desaturate, transparentColor, tintColor } = this.props; if (shaderModuleProps.picking.isActive && disablePicking) { return; } if (image && model) { const bitmapProps = { bitmapTexture: image, bounds, coordinateConversion, desaturate, tintColor: tintColor.slice(0, 3).map((x) => x / 255), transparentColor: transparentColor.map((x) => x / 255) }; model.shaderInputs.setProps({ bitmap: bitmapProps }); model.draw(this.context.renderPass); } } _getCoordinateUniforms() { const { LNGLAT, CARTESIAN, DEFAULT } = import_core3.COORDINATE_SYSTEM; let { _imageCoordinateSystem: imageCoordinateSystem } = this.props; if (imageCoordinateSystem !== DEFAULT) { const { bounds } = this.props; if (!isRectangularBounds(bounds)) { throw new Error("_imageCoordinateSystem only supports rectangular bounds"); } const defaultImageCoordinateSystem = this.context.viewport.resolution ? LNGLAT : CARTESIAN; imageCoordinateSystem = imageCoordinateSystem === LNGLAT ? LNGLAT : CARTESIAN; if (imageCoordinateSystem === LNGLAT && defaultImageCoordinateSystem === CARTESIAN) { return { coordinateConversion: -1, bounds }; } if (imageCoordinateSystem === CARTESIAN && defaultImageCoordinateSystem === LNGLAT) { const bottomLeft = (0, import_web_mercator.lngLatToWorld)([bounds[0], bounds[1]]); const topRight = (0, import_web_mercator.lngLatToWorld)([bounds[2], bounds[3]]); return { coordinateConversion: 1, bounds: [bottomLeft[0], bottomLeft[1], topRight[0], topRight[1]] }; } } return { coordinateConversion: 0, bounds: [0, 0, 0, 0] }; } }; BitmapLayer.layerName = "BitmapLayer"; BitmapLayer.defaultProps = defaultProps2; var bitmap_layer_default = BitmapLayer; function unpackUVsFromRGB(color) { const [u, v, fracUV] = color; const vFrac = (fracUV & 240) / 256; const uFrac = (fracUV & 15) / 16; return [(u + uFrac) / 256, (v + vFrac) / 256]; } function isRectangularBounds(bounds) { return Number.isFinite(bounds[0]); } // dist/icon-layer/icon-layer.js var import_core6 = require("@deck.gl/core"); var import_engine3 = require("@luma.gl/engine"); // dist/icon-layer/icon-layer-uniforms.js var uniformBlock3 = `uniform iconUniforms { float sizeScale; vec2 iconsTextureDim; float sizeMinPixels; float sizeMaxPixels; bool billboard; highp int sizeUnits; float alphaCutoff; } icon; `; var iconUniforms = { name: "icon", vs: uniformBlock3, fs: uniformBlock3, uniformTypes: { sizeScale: "f32", iconsTextureDim: "vec2<f32>", sizeMinPixels: "f32", sizeMaxPixels: "f32", billboard: "f32", sizeUnits: "i32", alphaCutoff: "f32" } }; // dist/icon-layer/icon-layer-vertex.glsl.js var icon_layer_vertex_glsl_default = `#version 300 es #define SHADER_NAME icon-layer-vertex-shader in vec2 positions; in vec3 instancePositions; in vec3 instancePositions64Low; in float instanceSizes; in float instanceAngles; in vec4 instanceColors; in vec3 instancePickingColors; in vec4 instanceIconFrames; in float instanceColorModes; in vec2 instanceOffsets; in vec2 instancePixelOffset; out float vColorMode; out vec4 vColor; out vec2 vTextureCoords; out vec2 uv; vec2 rotate_by_angle(vec2 vertex, float angle) { float angle_radian = angle * PI / 180.0; float cos_angle = cos(angle_radian); float sin_angle = sin(angle_radian); mat2 rotationMatrix = mat2(cos_angle, -sin_angle, sin_angle, cos_angle); return rotationMatrix * vertex; } void main(void) { geometry.worldPosition = instancePositions; geometry.uv = positions; geometry.pickingColor = instancePickingColors; uv = positions; vec2 iconSize = instanceIconFrames.zw; float sizePixels = clamp( project_size_to_pixel(instanceSizes * icon.sizeScale, icon.sizeUnits), icon.sizeMinPixels, icon.sizeMaxPixels ); float instanceScale = iconSize.y == 0.0 ? 0.0 : sizePixels / iconSize.y; vec2 pixelOffset = positions / 2.0 * iconSize + instanceOffsets; pixelOffset = rotate_by_angle(pixelOffset, instanceAngles) * instanceScale; pixelOffset += instancePixelOffset; pixelOffset.y *= -1.0; if (icon.billboard) { gl_Position = project_position_to_clipspace(instancePositions, instancePositions64Low, vec3(0.0), geometry.position); DECKGL_FILTER_GL_POSITION(gl_Position, geometry); vec3 offset = vec3(pixelOffset, 0.0); DECKGL_FILTER_SIZE(offset, geometry); gl_Position.xy += project_pixel_size_to_clipspace(offset.xy); } else { vec3 offset_common = vec3(project_pixel_size(pixelOffset), 0.0); DECKGL_FILTER_SIZE(offset_common, geometry); gl_Position = project_position_to_clipspace(instancePositions, instancePositions64Low, offset_common, geometry.position); DECKGL_FILTER_GL_POSITION(gl_Position, geometry); } vTextureCoords = mix( instanceIconFrames.xy, instanceIconFrames.xy + iconSize, (positions.xy + 1.0) / 2.0 ) / icon.iconsTextureDim; vColor = instanceColors; DECKGL_FILTER_COLOR(vColor, geometry); vColorMode = instanceColorModes; } `; // dist/icon-layer/icon-layer-fragment.glsl.js var icon_layer_fragment_glsl_default = `#version 300 es #define SHADER_NAME icon-layer-fragment-shader precision highp float; uniform sampler2D iconsTexture; in float vColorMode; in vec4 vColor; in vec2 vTextureCoords; in vec2 uv; out vec4 fragColor; void main(void) { geometry.uv = uv; vec4 texColor = texture(iconsTexture, vTextureCoords); vec3 color = mix(texColor.rgb, vColor.rgb, vColorMode); float a = texColor.a * layer.opacity * vColor.a; if (a < icon.alphaCutoff) { discard; } fragColor = vec4(color, a); DECKGL_FILTER_COLOR(fragColor, geometry); } `; // dist/icon-layer/icon-manager.js var import_core4 = require("@loaders.gl/core"); var import_core5 = require("@deck.gl/core"); var DEFAULT_CANVAS_WIDTH = 1024; var DEFAULT_BUFFER = 4; var noop = () => { }; var DEFAULT_SAMPLER_PARAMETERS = { minFilter: "linear", mipmapFilter: "linear", // LINEAR is the default value but explicitly set it here magFilter: "linear", // minimize texture boundary artifacts addressModeU: "clamp-to-edge", addressModeV: "clamp-to-edge" }; var MISSING_ICON = { x: 0, y: 0, width: 0, height: 0 }; function nextPowOfTwo(number) { return Math.pow(2, Math.ceil(Math.log2(number))); } function resizeImage(ctx, imageData, maxWidth, maxHeight) { const resizeRatio = Math.min(maxWidth / imageData.width, maxHeight / imageData.height); const width = Math.floor(imageData.width * resizeRatio); const height = Math.floor(imageData.height * resizeRatio); if (resizeRatio === 1) { return { image: imageData, width, height }; } ctx.canvas.height = height; ctx.canvas.width = width; ctx.clearRect(0, 0, width, height); ctx.drawImage(imageData, 0, 0, imageData.width, imageData.height, 0, 0, width, height); return { image: ctx.canvas, width, height }; } function getIconId(icon) { return icon && (icon.id || icon.url); } function resizeTexture(texture, width, height, sampler) { const { width: oldWidth, height: oldHeight, device } = texture; const newTexture = device.createTexture({ format: "rgba8unorm", width, height, sampler, mipmaps: true }); const commandEncoder = device.createCommandEncoder(); commandEncoder.copyTextureToTexture({ sourceTexture: texture, destinationTexture: newTexture, width: oldWidth, height: oldHeight }); commandEncoder.finish(); texture.destroy(); return newTexture; } function buildRowMapping(mapping, columns, yOffset) { for (let i = 0; i < columns.length; i++) { const { icon, xOffset } = columns[i]; const id = getIconId(icon); mapping[id] = { ...icon, x: xOffset, y: yOffset }; } } function buildMapping({ icons, buffer, mapping = {}, xOffset = 0, yOffset = 0, rowHeight = 0, canvasWidth }) { let columns = []; for (let i = 0; i < icons.length; i++) { const icon = icons[i]; const id = getIconId(icon); if (!mapping[id]) { const { height, width } = icon; if (xOffset + width + buffer > canvasWidth) { buildRowMapping(mapping, columns, yOffset); xOffset = 0; yOffset = rowHeight + yOffset + buffer; rowHeight = 0; columns = []; } columns.push({ icon, xOffset }); xOffset = xOffset + width + buffer; rowHeight = Math.max(rowHeight, height); } } if (columns.length > 0) { buildRowMapping(mapping, columns, yOffset); } return { mapping, rowHeight, xOffset, yOffset, canvasWidth, canvasHeight: nextPowOfTwo(rowHeight + yOffset + buffer) }; } function getDiffIcons(data, getIcon, cachedIcons) { if (!data || !getIcon) { return null; } cachedIcons = cachedIcons || {}; const icons = {}; const { iterable, objectInfo } = (0, import_core5.createIterable)(data); for (const object of iterable) { objectInfo.index++; const icon = getIcon(object, objectInfo); const id = getIconId(icon); if (!icon) { throw new Error("Icon is missing."); } if (!icon.url) { throw new Error("Icon url is missing."); } if (!icons[id] && (!cachedIcons[id] || icon.url !== cachedIcons[id].url)) { icons[id] = { ...icon, source: object, sourceIndex: objectInfo.index }; } } return icons; } var IconManager = class { constructor(device, { onUpdate = noop, onError = noop }) { this._loadOptions = null; this._texture = null; this._externalTexture = null; this._mapping = {}; this._samplerParameters = null; this._pendingCount = 0; this._autoPacking = false; this._xOffset = 0; this._yOffset = 0; this._rowHeight = 0; this._buffer = DEFAULT_BUFFER; this._canvasWidth = DEFAULT_CANVAS_WIDTH; this._canvasHeight = 0; this._canvas = null; this.device = device; this.onUpdate = onUpdate; this.onError = onError; } finalize() { var _a; (_a = this._texture) == null ? void 0 : _a.delete(); } getTexture() { return this._texture || this._externalTexture; } getIconMapping(icon) { const id = this._autoPacking ? getIconId(icon) : icon; return this._mapping[id] || MISSING_ICON; } setProps({ loadOptions, autoPacking, iconAtlas, iconMapping, textureParameters }) { var _a; if (loadOptions) { this._loadOptions = loadOptions; } if (autoPacking !== void 0) { this._autoPacking = autoPacking; } if (iconMapping) { this._mapping = iconMapping; } if (iconAtlas) { (_a = this._texture) == null ? void 0 : _a.delete(); this._texture = null; this._externalTexture = iconAtlas; } if (textureParameters) { this._samplerParameters = textureParameters; } } get isLoaded() { return this._pendingCount === 0; } packIcons(data, getIcon) { if (!this._autoPacking || typeof document === "undefined") { return; } const icons = Object.values(getDiffIcons(data, getIcon, this._mapping) || {}); if (icons.length > 0) { const { mapping, xOffset, yOffset, rowHeight, canvasHeight } = buildMapping({ icons, buffer: this._buffer, canvasWidth: this._canvasWidth, mapping: this._mapping, rowHeight: this._rowHeight, xOffset: this._xOffset, yOffset: this._yOffset }); this._rowHeight = rowHeight; this._mapping = mapping; this._xOffset = xOffset; this._yOffset = yOffset; this._canvasHeight = canvasHeight; if (!this._texture) { this._texture = this.device.createTexture({ format: "rgba8unorm", width: this._canvasWidth, height: this._canvasHeight, sampler: this._samplerParameters || DEFAULT_SAMPLER_PARAMETERS, mipmaps: true }); } if (this._texture.height !== this._canvasHeight) { this._texture = resizeTexture(this._texture, this._canvasWidth, this._canvasHeight, this._samplerParameters || DEFAULT_SAMPLER_PARAMETERS); } this.onUpdate(); this._canvas = this._canvas || document.createElement("canvas"); this._loadIcons(icons); } } _loadIcons(icons) { const ctx = this._canvas.getContext("2d", { willReadFrequently: true }); for (const icon of icons) { this._pendingCount++; (0, import_core4.load)(icon.url, this._loadOptions).then((imageData) => { var _a; const id = getIconId(icon); const iconDef = this._mapping[id]; const { x, y, width: maxWidth, height: maxHeight } = iconDef; const { image, width, height } = resizeImage(ctx, imageData, maxWidth, maxHeight); (_a = this._texture) == null ? void 0 : _a.copyExternalImage({ image, x: x + (maxWidth - width) / 2, y: y + (maxHeight - height) / 2, width, height }); iconDef.width = width; iconDef.height = height; this._texture.generateMipmap(); this.onUpdate(); }).catch((error) => { this.onError({ url: icon.url, source: icon.source, sourceIndex: icon.sourceIndex, loadOptions: this._loadOptions, error }); }).finally(() => { this._pendingCount--; }); } } }; // dist/icon-layer/icon-layer.js var DEFAULT_COLOR2 = [0, 0, 0, 255]; var defaultProps3 = { iconAtlas: { type: "image", value: null, async: true }, iconMapping: { type: "object", value: {}, async: true }, sizeScale: { type: "number", value: 1, min: 0 }, billboard: true, sizeUnits: "pixels", sizeMinPixels: { type: "number", min: 0, value: 0 }, // min point radius in pixels sizeMaxPixels: { type: "number", min: 0, value: Number.MAX_SAFE_INTEGER }, // max point radius in pixels alphaCutoff: { type: "number", value: 0.05, min: 0, max: 1 }, getPosition: { type: "accessor", value: (x) => x.position }, getIcon: { type: "accessor", value: (x) => x.icon }, getColor: { type: "accessor", value: DEFAULT_COLOR2 }, getSize: { type: "accessor", value: 1 }, getAngle: { type: "accessor", value: 0 }, getPixelOffset: { type: "accessor", value: [0, 0] }, onIconError: { type: "function", value: null, optional: true }, textureParameters: { type: "object", ignore: true, value: null } }; var IconLayer = class extends import_core6.Layer { getShaders() { return super.getShaders({ vs: icon_layer_vertex_glsl_default, fs: icon_layer_fragment_glsl_default, modules: [import_core6.project32, import_core6.picking, iconUniforms] }); } initializeState() { this.state = { iconManager: new IconManager(this.context.device, { onUpdate: this._onUpdate.bind(this), onError: this._onError.bind(this) }) }; const attributeManager = this.getAttributeManager(); attributeManager.addInstanced({ instancePositions: { size: 3, type: "float64", fp64: this.use64bitPositions(), transition: true, accessor: "getPosition" }, instanceSizes: { size: 1, transition: true, accessor: "getSize", defaultValue: 1 }, instanceOffsets: { size: 2, accessor: "getIcon", // eslint-disable-next-line @typescript-eslint/unbound-method transform: this.getInstanceOffset }, instanceIconFrames: { size: 4, accessor: "getIcon", // eslint-disable-next-line @typescript-eslint/unbound-method transform: this.getInstanceIconFrame }, instanceColorModes: { size: 1, type: "uint8", accessor: "getIcon", // eslint-disable-next-line @typescript-eslint/unbound-method transform: this.getInstanceColorMode }, instanceColors: { size: this.props.colorFormat.length, type: "unorm8", transition: true, accessor: "getColor", defaultValue: DEFAULT_COLOR2 }, instanceAngles: { size: 1, transition: true, accessor: "getAngle" }, instancePixelOffset: { size: 2, transition: true, accessor: "getPixelOffset" } }); } /* eslint-disable max-statements, complexity */ updateState(params) { var _a; super.updateState(params); const { props, oldProps, changeFlags } = params; const attributeManager = this.getAttributeManager(); const { iconAtlas, iconMapping, data, getIcon, textureParameters } = props; const { iconManager } = this.state; if (typeof iconAtlas === "string") { return; } const prePacked = iconAtlas || this.internalState.isAsyncPropLoading("iconAtlas"); iconManager.setProps({ loadOptions: props.loadOptions, autoPacking: !prePacked, iconAtlas, iconMapping: prePacked ? iconMapping : null, textureParameters }); if (prePacked) { if (oldProps.iconMapping !== props.iconMapping) { attributeManager.invalidate("getIcon"); } } else if (changeFlags.dataChanged || changeFlags.updateTriggersChanged && (changeFlags.updateTriggersChanged.all || changeFlags.updateTriggersChanged.getIcon)) { iconManager.packIcons(data, getIcon); } if (changeFlags.extensionsChanged) { (_a = this.state.model) == null ? void 0 : _a.destroy(); this.state.model = this._getModel(); attributeManager.invalidateAll(); } } /* eslint-enable max-statements, complexity */ get isLoaded() { return super.isLoaded && this.state.iconManager.isLoaded; } finalizeState(context) { super.finalizeState(context); this.state.iconManager.finalize(); } draw({ uniforms }) { const { sizeScale, sizeMinPixels, sizeMaxPixels, sizeUnits, billboard, alphaCutoff } = this.props; const { iconManager } = this.state; const iconsTexture = iconManager.getTexture(); if (iconsTexture) { const model = this.state.model; const iconProps = { iconsTexture, iconsTextureDim: [iconsTexture.width, iconsTexture.height], sizeUnits: import_core6.UNIT[sizeUnits], sizeScale, sizeMinPixels, sizeMaxPixels, billboard, alphaCutoff }; model.shaderInputs.setProps({ icon: iconProps }); model.draw(this.context.renderPass); } } _getModel() { const positions = [-1, -1, 1, -1, -1, 1, 1, 1]; return new import_engine3.Model(this.context.device, { ...this.getShaders(), id: this.props.id, bufferLayout: this.getAttributeManager().getBufferLayouts(), geometry: new import_engine3.Geometry({ topology: "triangle-strip", attributes: { // The size must be explicitly passed here otherwise luma.gl // will default to assuming that positions are 3D (x,y,z) positions: { size: 2, value: new Float32Array(positions) } } }), isInstanced: true }); } _onUpdate() { this.setNeedsRedraw(); } _onError(evt) { var _a; const onIconError = (_a = this.getCurrentLayer()) == null ? void 0 : _a.props.onIconError; if (onIconError) { onIconError(evt); } else { import_core6.log.error(evt.error.message)(); } } getInstanceOffset(icon) { const { width, height, anchorX = width / 2, anchorY = height / 2 } = this.state.iconManager.getIconMapping(icon); return [width / 2 - anchorX, height / 2 - anchorY]; } getInstanceColorMode(icon) { const mapping = this.state.iconManager.getIconMapping(icon); return mapping.mask ? 1 : 0; } getInstanceIconFrame(icon) { const { x, y, width, height } = this.state.iconManager.getIconMapping(icon); return [x, y, width, height]; } }; IconLayer.defaultProps = defaultProps3; IconLayer.layerName = "IconLayer"; var icon_layer_default = IconLayer; // dist/line-layer/line-layer.js var import_core7 = require("@deck.gl/core"); var import_engine4 = require("@luma.gl/engine"); // dist/line-layer/line-layer-uniforms.js var uniformBlockWGSL = ( /* wgsl */ `struct LineUniforms { widthScale: f32, widthMinPixels: f32, widthMaxPixels: f32, useShortestPath: f32, widthUnits: i32, }; @group(0) @binding(1) var<uniform> line: LineUniforms; ` ); var uniformBlockGLSL = ( /* glsl */ `uniform lineUniforms { float widthScale; float widthMinPixels; float widthMaxPixels; float useShortestPath; highp int widthUnits; } line; ` ); var lineUniforms = { name: "line", source: uniformBlockWGSL, vs: uniformBlockGLSL, fs: uniformBlockGLSL, uniformTypes: { widthScale: "f32", widthMinPixels: "f32", widthMaxPixels: "f32", useShortestPath: "f32", widthUnits: "i32" } }; // dist/line-layer/line-layer.wgsl.js var shaderWGSL = ( /* wgsl */ `// TODO(ibgreen): Hack for Layer uniforms (move to new "color" module?) struct LayerUniforms { opacity: f32, }; var<private> layer: LayerUniforms = LayerUniforms(1.0); // @group(0) @binding(1) var<uniform> layer: LayerUniforms; // ---------- Helper Structures & Functions ---------- // Placeholder filter functions. fn deckgl_filter_size(offset: vec3<f32>, geometry: Geometry) -> vec3<f32> { return offset; } fn deckgl_filter_gl_position(p: vec4<f32>, geometry: Geometry) -> vec4<f32> { return p; } fn deckgl_filter_color(color: vec4<f32>, geometry: Geometry) -> vec4<f32> { return color; } // Compute an extrusion offset given a line direction (in clipspace), // an offset direction (-1 or 1), and a width in pixels. // Assumes a uniform "project" with a viewportSize field is available. fn getExtrusionOffset(line_clipspace: vec2<f32>, offset_direction: f32, width: f32) -> vec2<f32> { // project.viewportSize should be provided as a uniform (not shown here) let dir_screenspace = normalize(line_clipspace * project.viewportSize); // Rotate by 90\xB0: (x,y) becomes (-y,x) let rotated = vec2<f32>(-dir_screenspace.y, dir_screenspace.x); return rotated * offset_direction * width / 2.0; } // Splits the line between two points at a given x coordinate. // Interpolates the y and z components. fn splitLine(a: vec3<f32>, b: vec3<f32>, x: f32) -> vec3<f32> { let t: f32 = (x - a.x) / (b.x - a.x); return vec3<f32>(x, a.yz + t * (b.yz - a.yz)); } // ---------- Uniforms & Global Structures ---------- // Uniforms for line, layer, and project are assumed to be defined elsewhere. // For example: // // @group(0) @binding(0) // var<uniform> line: LineUniform; // // struct LayerUniform { // opacity: f32, // }; // @group(0) @binding(1) // var<uniform> layer: LayerUniform; // // struct ProjectUniform { // viewportSize: vec2<f32>, // }; // @group(0) @binding(2) // var<uniform> project: ProjectUniform; // ---------- Vertex Output Structure ---------- struct Varyings { @builtin(position) gl_Position: vec4<f32>, @location(0) vColor: vec4<f32>, @location(1) uv: vec2<f32>, }; // ---------- Vertex Shader Entry Point ---------- @vertex fn vertexMain( @location(0) positions: vec3<f32>, @location(1) instanceSourcePositions: vec3<f32>, @location(2) instanceTargetPositions: vec3<f32>, @location(3) instanceSourcePositions64Low: vec3<f32>, @location(4) instanceTargetPositions64Low: vec3<f32>, @location(5) instanceColors: vec4<f32>, @location(6) instancePickingColors: vec3<f32>, @location(7) instanceWidths: f32 ) -> Varyings { var geometry: Geometry; geometry.worldPosition = instanceSourcePositions; geometry.worldPositionAlt = instanceTargetPositions; var source_world: vec3<f32> = instanceSourcePositions; var target_world: vec3<f32> = instanceTargetPositions; var source_world_64low: vec3<f32> = instanceSourcePositions64Low; var target_world_64low: vec3<f32> = instanceTargetPositions64Low; // Apply shortest-path adjustments if needed. if (line.useShortestPath > 0.5 || line.useShortestPath < -0.5) { source_world.x = (source_world.x + 180.0 % 360.0) - 180.0; target_world.x = (target_world.x + 180.0 % 360.0) - 180.0; let deltaLng: f32 = target_world.x - source_world.x; if (deltaLng * line.useShortestPath > 180.0) { source_world.x = source_world.x + 360.0 * line.useShortestPath; source_world = splitLine(source_world, target_world, 180.0 * line.useShortestPath); source_world_64low = vec3<f32>(0.0, 0.0, 0.0); } else if (deltaLng * line.useShortestPath < -180.0) { target_world.x = target_world.x + 360.0 * line.useShortestPath; target_world = splitLine(source_world, target_world, 180.0 * line.useShortestPath); target_world_64low = vec3<f32>(0.0, 0.0, 0.0); } else if (line.useShortestPath < 0.0) { var abortOut: Varyings; abortOut.gl_Position = vec4<f32>(0.0); abortOut.vColor = vec4<f32>(0.0); abortOut.uv = vec2<f32>(0.0); return abortOut; } } // Project Pos and target positions to clip space. let sourceResult = project_position_to_clipspace_and_commonspace(source_world, source_world_64low, vec3<f32>(0.0)); let targetResult = project_position_to_clipspace_and_commonspace(target_world, target_world_64low,