@deck.gl-community/layers
Version:
Add-on layers for deck.gl
506 lines (488 loc) • 16.2 kB
JavaScript
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
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 __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var __publicField = (obj, key, value) => {
__defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
return value;
};
// dist/index.js
var dist_exports = {};
__export(dist_exports, {
PathMarkerLayer: () => PathMarkerLayer,
PathOutlineLayer: () => PathOutlineLayer
});
module.exports = __toCommonJS(dist_exports);
// dist/path-outline-layer/path-outline-layer.js
var import_layers = require("@deck.gl/layers");
var import_constants = require("@luma.gl/constants");
// dist/path-outline-layer/outline.js
var INITIAL_STATE = {
outlineEnabled: false,
outlineRenderShadowmap: false,
outlineShadowmap: null
};
function getUniforms({ outlineEnabled, outlineRenderShadowmap, outlineShadowmap } = INITIAL_STATE) {
const uniforms = {};
if (outlineEnabled !== void 0) {
uniforms.outline_uEnabled = outlineEnabled;
}
if (outlineRenderShadowmap !== void 0) {
uniforms.outline_uRenderOutlines = outlineRenderShadowmap;
}
if (outlineShadowmap !== void 0) {
uniforms.outline_uShadowmap = outlineShadowmap;
}
return uniforms;
}
var vs = `#version 300 es
in float instanceZLevel;
out float outline_vzLevel;
out vec4 outline_vPosition;
// Set the z level for the outline shadowmap rendering
void outline_setZLevel(float zLevel) {
outline_vzLevel = zLevel;
}
// Store an adjusted position for texture2DProj
void outline_setUV(vec4 position) {
// mat4(
// 0.5, 0.0, 0.0, 0.0,
// 0.0, 0.5, 0.0, 0.0,
// 0.0, 0.0, 0.5, 0.0,
// 0.5, 0.5, 0.5, 1.0
// ) * position;
outline_vPosition = vec4(position.xyz * 0.5 + position.w * 0.5, position.w);
}
`;
var fs = `uniform bool outline_uEnabled;
uniform bool outline_uRenderOutlines;
uniform sampler2D outline_uShadowmap;
in float outline_vzLevel;
// in vec2 outline_vUV;
in vec4 outline_vPosition;
out vec4 fragColor;
const float OUTLINE_Z_LEVEL_ERROR = 0.01;
// Return a darker color in shadowmap
vec4 outline_filterShadowColor(vec4 color) {
return vec4(outline_vzLevel / 255., outline_vzLevel / 255., outline_vzLevel / 255., 1.);
}
// Return a darker color if in shadowmap
vec4 outline_filterDarkenColor(vec4 color) {
if (outline_uEnabled) {
float maxZLevel;
if (outline_vPosition.q > 0.0) {
maxZLevel = texture2DProj(outline_uShadowmap, outline_vPosition).r * 255.;
} else {
discard;
}
if (maxZLevel < outline_vzLevel + OUTLINE_Z_LEVEL_ERROR) {
vec4(color.rgb * 0.5, color.a);
} else {
discard;
}
}
return color;
}
// if enabled and rendering outlines - Render depth to shadowmap
// if enabled and rendering colors - Return a darker color if in shadowmap
// if disabled, just return color
vec4 outline_filterColor(vec4 color) {
if (outline_uEnabled) {
return outline_uRenderOutlines ?
outline_filterShadowColor(color) :
outline_filterDarkenColor(color);
}
return color;
}
`;
var outline = {
name: "outline",
vs,
fs,
getUniforms
};
// dist/path-outline-layer/path-outline-layer.js
var UNIT = {
common: 0,
meters: 1,
pixels: 2
};
function injectShaderCode({ source, code = "" }) {
const INJECT_CODE = /}[^{}]*$/;
return source.replace(INJECT_CODE, code.concat("\n}\n"));
}
var VS_CODE = ` outline_setUV(gl_Position);
outline_setZLevel(instanceZLevel);
`;
var FS_CODE = ` fragColor = outline_filterColor(fragColor);
`;
var defaultProps = {
getZLevel: () => 0
};
var PathOutlineLayer = class extends import_layers.PathLayer {
state = void 0;
// Override getShaders to inject the outline module
getShaders() {
const shaders = super.getShaders();
return Object.assign({}, shaders, {
modules: shaders.modules.concat([outline]),
vs: injectShaderCode({ source: shaders.vs, code: VS_CODE }),
fs: injectShaderCode({ source: shaders.fs, code: FS_CODE })
});
}
// @ts-expect-error PathLayer is missing LayerContext arg
initializeState(context) {
super.initializeState();
this.setState({
outlineFramebuffer: context.device.createFramebuffer({}),
dummyTexture: context.device.createTexture({})
});
this.state.attributeManager.addInstanced({
instanceZLevel: {
size: 1,
type: 5121,
accessor: "getZLevel"
}
});
}
// Override draw to add render module
draw({ moduleParameters = {}, parameters, uniforms, context }) {
const { jointRounded, capRounded, billboard, miterLimit, widthUnits, widthScale, widthMinPixels, widthMaxPixels } = this.props;
uniforms = Object.assign({}, uniforms, {
jointType: Number(jointRounded),
capType: Number(capRounded),
billboard,
widthUnits: UNIT[widthUnits],
widthScale,
miterLimit,
widthMinPixels,
widthMaxPixels
});
const { outlineFramebuffer, dummyTexture } = this.state;
this.state.model.updateModuleSettings({
outlineEnabled: true,
outlineRenderShadowmap: true,
outlineShadowmap: dummyTexture
});
this.state.model.draw({
uniforms: Object.assign({}, uniforms, {
jointType: 0,
widthScale: this.props.widthScale * 1.3
}),
parameters: {
depthTest: false,
// Biggest value needs to go into buffer
blendEquation: 32776
},
framebuffer: outlineFramebuffer
});
this.state.model.updateModuleSettings({
outlineEnabled: true,
outlineRenderShadowmap: false,
outlineShadowmap: outlineFramebuffer
});
this.state.model.draw({
uniforms: Object.assign({}, uniforms, {
jointType: Number(jointRounded),
capType: Number(capRounded),
widthScale: this.props.widthScale
}),
parameters: {
depthTest: false
}
});
}
};
__publicField(PathOutlineLayer, "layerName", "PathOutlineLayer");
__publicField(PathOutlineLayer, "defaultProps", defaultProps);
// dist/path-marker-layer/path-marker-layer.js
var import_core3 = require("@deck.gl/core");
var import_layers2 = require("@deck.gl/layers");
var import_mesh_layers = require("@deck.gl/mesh-layers");
// dist/path-marker-layer/arrow-2d-geometry.js
var import_engine = require("@luma.gl/engine");
var Arrow2DGeometry = class extends import_engine.Geometry {
constructor(opts = {}) {
super(Object.assign({}, opts, {
attributes: getArrowAttributes(opts),
topology: "triangle-list"
}));
}
};
function getArrowAttributes({ length = 1, headSize = 0.2, tailWidth = 0.05, tailStart = 0.05 }) {
const texCoords = [
// HEAD
0.5,
1,
0,
0.5 - headSize / 2,
1 - headSize,
0,
0.5 + headSize / 2,
1 - headSize,
0,
0.5 - tailWidth / 2,
tailStart,
0,
0.5 + tailWidth / 2,
1 - headSize,
0,
0.5 + tailWidth / 2,
tailStart,
0,
0.5 - tailWidth / 2,
tailStart,
0,
0.5 - tailWidth / 2,
1 - headSize,
0,
0.5 + tailWidth / 2,
1 - headSize,
0
];
const normals = [0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1];
const positions = new Array(texCoords.length);
for (let i = 0; i < texCoords.length / 3; i++) {
const i3 = i * 3;
positions[i3 + 0] = (texCoords[i3 + 0] - 0.5) * length;
positions[i3 + 1] = (texCoords[i3 + 1] - 0.5) * length;
positions[i3 + 2] = 0;
}
return {
positions: { size: 3, value: new Float32Array(positions) },
normals: { size: 3, value: new Float32Array(normals) },
texCoords: { size: 2, value: new Float32Array(texCoords) }
};
}
// dist/path-marker-layer/create-path-markers.js
var import_core = require("@math.gl/core");
function getLineLength(vPoints) {
let lineLength = 0;
for (let i = 0; i < vPoints.length - 1; i++) {
lineLength += vPoints[i].distance(vPoints[i + 1]);
}
return lineLength;
}
var DEFAULT_COLOR = [0, 0, 0, 255];
var DEFAULT_DIRECTION = { forward: true, backward: false };
function createPathMarkers({ data, getPath = (x, context) => x.path, getDirection = (x) => x.direction, getColor = (x) => DEFAULT_COLOR, getMarkerPercentages = (x, info) => [0.5], projectFlat }) {
const markers = [];
for (const object of data) {
const path = getPath(object, null);
const direction = getDirection(object) || DEFAULT_DIRECTION;
const color = getColor(object);
const vPoints = path.map((p) => new import_core.Vector2(p));
const vPointsReverse = vPoints.slice(0).reverse();
const lineLength = getLineLength(vPoints);
const percentages = getMarkerPercentages(object, { lineLength });
for (const percentage of percentages) {
if (direction.forward) {
const marker = createMarkerAlongPath({
path: vPoints,
percentage,
lineLength,
color,
object,
projectFlat
});
markers.push(marker);
}
if (direction.backward) {
const marker = createMarkerAlongPath({
path: vPointsReverse,
percentage,
lineLength,
color,
object,
projectFlat
});
markers.push(marker);
}
}
}
return markers;
}
function createMarkerAlongPath({ path, percentage, lineLength, color, object, projectFlat }) {
const distanceAlong = lineLength * percentage;
let currentDistance = 0;
let previousDistance = 0;
let i = 0;
for (i = 0; i < path.length - 1; i++) {
currentDistance += path[i].distance(path[i + 1]);
if (currentDistance > distanceAlong) {
break;
}
previousDistance = currentDistance;
}
if (i === path.length - 1) {
i -= 1;
}
const vDirection = path[i + 1].clone().subtract(path[i]).normalize();
const along = distanceAlong - previousDistance;
const vCenter = vDirection.clone().multiply(new import_core.Vector2(along, along)).add(path[i]);
const vDirection2 = new import_core.Vector2(projectFlat(path[i + 1])).subtract(projectFlat(path[i]));
const angle = vDirection2.verticalAngle() * 180 / Math.PI;
return { position: [vCenter.x, vCenter.y, 0], angle, color, object };
}
// dist/path-marker-layer/polyline.js
var import_core2 = require("@math.gl/core");
function getClosestPointOnLine({ p, p1, p2, clampToLine = true }) {
const lineVector = new import_core2.Vector3(p2).subtract(p1);
const pointVector = new import_core2.Vector3(p).subtract(p1);
let dotProduct = lineVector.dot(pointVector);
if (clampToLine) {
dotProduct = (0, import_core2.clamp)(dotProduct, 0, 1);
}
return lineVector.lerp(p1, p2, dotProduct);
}
function getClosestPointOnPolyline({ p, points }) {
p = new import_core2.Vector3(p);
let pClosest = null;
let distanceSquared = Infinity;
let index = -1;
for (let i = 0; i < points.length - 1; ++i) {
const p1 = points[i];
const p2 = points[i + 1];
const pClosestOnLine = getClosestPointOnLine({ p, p1, p2 });
const distanceToLineSquared = p.distanceSquared(pClosestOnLine);
if (distanceToLineSquared < distanceSquared) {
distanceSquared = distanceToLineSquared;
pClosest = pClosestOnLine;
index = i;
}
}
return {
point: pClosest,
index,
p1: points[index],
p2: points[index + 1],
distanceSquared,
distance: Math.sqrt(distanceSquared)
};
}
// dist/path-marker-layer/path-marker-layer.js
var DISTANCE_FOR_MULTI_ARROWS = 0.1;
var ARROW_HEAD_SIZE = 0.2;
var ARROW_TAIL_WIDTH = 0.05;
var DEFAULT_MARKER_LAYER = import_mesh_layers.SimpleMeshLayer;
var DEFAULT_MARKER_LAYER_PROPS = {
mesh: new Arrow2DGeometry({ headSize: ARROW_HEAD_SIZE, tailWidth: ARROW_TAIL_WIDTH })
};
var defaultProps2 = Object.assign({}, PathOutlineLayer.defaultProps, {
MarkerLayer: DEFAULT_MARKER_LAYER,
markerLayerProps: DEFAULT_MARKER_LAYER_PROPS,
sizeScale: 100,
fp64: false,
highlightIndex: -1,
highlightPoint: null,
getPath: (x) => x.path,
getColor: (x) => x.color,
getMarkerColor: (x) => [0, 0, 0, 255],
getDirection: (x) => x.direction,
getMarkerPercentages: (object, { lineLength }) => lineLength > DISTANCE_FOR_MULTI_ARROWS ? [0.25, 0.5, 0.75] : [0.5]
});
var PathMarkerLayer = class extends import_core3.CompositeLayer {
state = void 0;
initializeState() {
this.state = {
markers: [],
mesh: new Arrow2DGeometry({ headSize: ARROW_HEAD_SIZE, tailWidth: ARROW_TAIL_WIDTH }),
closestPoint: null,
closestPoints: []
};
}
projectFlat(xyz, viewport, coordinateSystem, coordinateOrigin) {
if (coordinateSystem === import_core3.COORDINATE_SYSTEM.METER_OFFSETS) {
const [dx, dy] = viewport.metersToLngLatDelta(xyz);
const [x, y] = coordinateOrigin;
return viewport.projectFlat([x + dx, dy + y]);
} else if (coordinateSystem === import_core3.COORDINATE_SYSTEM.LNGLAT_OFFSETS) {
const [dx, dy] = xyz;
const [x, y] = coordinateOrigin;
return viewport.projectFlat([x + dx, dy + y]);
}
return viewport.projectFlat(xyz);
}
updateState({ props, oldProps, changeFlags }) {
if (changeFlags.dataChanged || changeFlags.updateTriggersChanged) {
const { data, getPath, getDirection, getMarkerColor, getMarkerPercentages, coordinateSystem, coordinateOrigin } = this.props;
const { viewport } = this.context;
const projectFlat = (o) => this.projectFlat(o, viewport, coordinateSystem, coordinateOrigin);
this.state.markers = createPathMarkers({
data,
getPath,
getDirection,
getColor: getMarkerColor,
getMarkerPercentages,
projectFlat
});
this._recalculateClosestPoint();
}
if (changeFlags.propsChanged) {
if (props.point !== oldProps.point) {
this._recalculateClosestPoint();
}
}
}
_recalculateClosestPoint() {
const { highlightPoint, highlightIndex } = this.props;
if (highlightPoint && highlightIndex >= 0) {
const object = this.props.data[highlightIndex];
const points = this.props.getPath(object, null);
const { point } = getClosestPointOnPolyline({ points, p: highlightPoint });
this.state.closestPoints = [{ position: point }];
} else {
this.state.closestPoints = [];
}
}
getPickingInfo({ info }) {
return Object.assign(info, {
// override object with picked feature
object: info.object && info.object.path || info.object
});
}
renderLayers() {
return [
new PathOutlineLayer(this.props, this.getSubLayerProps({
id: "paths",
// Note: data has to be passed explicitly like this to avoid being empty
data: this.props.data
})),
new this.props.MarkerLayer(this.getSubLayerProps(Object.assign({}, this.props.markerLayerProps, {
id: "markers",
data: this.state.markers,
getOrientation: (x) => [0, -x.angle, 0],
getColor: (x) => x.color,
sizeScale: this.props.sizeScale,
fp64: this.props.fp64,
pickable: false,
parameters: {
blend: false,
depthTest: false
}
}))),
this.state.closestPoints && new import_layers2.ScatterplotLayer({
id: `${this.props.id}-highlight`,
data: this.state.closestPoints,
fp64: this.props.fp64
})
];
}
};
__publicField(PathMarkerLayer, "layerName", "PathMarkerLayer");
__publicField(PathMarkerLayer, "defaultProps", defaultProps2);
//# sourceMappingURL=index.cjs.map