@deck.gl/layers
Version:
deck.gl core layers
1,529 lines (1,464 loc) • 207 kB
JavaScript
"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,