@deck.gl-community/layers
Version:
Add-on layers for deck.gl
4 lines • 86.1 kB
Source Map (JSON)
{
"version": 3,
"sources": ["../src/index.ts", "../src/path-outline-layer/path-outline-layer.ts", "../src/path-outline-layer/outline.ts", "../src/path-marker-layer/path-marker-layer.ts", "../src/path-marker-layer/arrow-2d-geometry.ts", "../src/path-marker-layer/create-path-markers.ts", "../src/path-marker-layer/polyline.ts", "../src/dependency-arrow-layer/dependency-arrow-layer.ts", "../src/dependency-arrow-layer/create-path-markers.ts", "../src/dependency-arrow-layer/geometry-layer.ts", "../src/skybox-layer/skybox-layer.ts", "../src/skybox-layer/cubemap-utils.ts"],
"sourcesContent": ["// deck.gl-community\n// SPDX-License-Identifier: MIT\n// Copyright (c) vis.gl contributors\n\nexport type {PathOutlineLayerProps} from './path-outline-layer/path-outline-layer';\nexport {PathOutlineLayer} from './path-outline-layer/path-outline-layer';\n\nexport type {PathMarkerLayerProps} from './path-marker-layer/path-marker-layer';\nexport {PathMarkerLayer} from './path-marker-layer/path-marker-layer';\n\nexport {\n DependencyArrowLayer,\n PathDirection,\n type DependencyArrowLayerProps,\n type MarkerPlacementsAccessor,\n type MarkerPlacementsAccessorContext,\n type PathDirectionAccessor,\n type PathGeometry,\n type PathMarker\n} from './dependency-arrow-layer/dependency-arrow-layer';\n\nexport type {SkyboxLayerProps} from './skybox-layer/skybox-layer';\nexport {SkyboxLayer} from './skybox-layer/skybox-layer';\n", "// deck.gl-community\n// SPDX-License-Identifier: MIT\n// Copyright (c) vis.gl contributors\n\nimport type {PathLayerProps} from '@deck.gl/layers';\nimport {PathLayer} from '@deck.gl/layers';\nimport type {DefaultProps, LayerContext} from '@deck.gl/core';\nimport {Framebuffer} from '@luma.gl/core';\nimport type {Parameters, RenderPipelineParameters, Texture} from '@luma.gl/core';\nimport {outline} from './outline';\n\n/**\n * Unit literal to shader unit number conversion.\n */\nexport const UNIT = {\n common: 0,\n meters: 1,\n pixels: 2\n};\n\n// TODO - this should be built into assembleShaders\nfunction injectShaderCode({source, code = ''}) {\n const INJECT_CODE = /}[^{}]*$/;\n return source.replace(INJECT_CODE, code.concat('\\n}\\n'));\n}\n\nconst VS_CODE = `\\\n outline_setUV(gl_Position);\n outline_setZLevel(instanceZLevel);\n`;\n\nconst FS_CODE = `\\\n fragColor = outline_filterColor(fragColor);\n`;\n\nconst OUTLINE_SHADOWMAP_PARAMETERS: RenderPipelineParameters = {\n blend: true,\n blendColorSrcFactor: 'one',\n blendColorDstFactor: 'one',\n blendColorOperation: 'max',\n blendAlphaSrcFactor: 'one',\n blendAlphaDstFactor: 'one',\n blendAlphaOperation: 'max',\n depthWriteEnabled: false,\n depthCompare: 'always'\n};\n\nconst OUTLINE_RENDER_PARAMETERS: RenderPipelineParameters = {\n blend: false,\n depthWriteEnabled: false,\n depthCompare: 'always'\n};\n\nexport type PathOutlineLayerProps<DataT> = PathLayerProps<DataT> & {\n dashJustified?: boolean;\n getDashArray?: [number, number] | ((d: DataT) => [number, number] | null);\n getZLevel?: (d: DataT, index: number) => number;\n};\n\nconst defaultProps: DefaultProps<PathOutlineLayerProps<any>> = {\n getZLevel: () => 0\n};\n\nexport class PathOutlineLayer<DataT = any, ExtraPropsT = Record<string, unknown>> extends PathLayer<\n DataT,\n ExtraPropsT & Required<PathOutlineLayerProps<DataT>>\n> {\n static layerName = 'PathOutlineLayer';\n static defaultProps = defaultProps;\n\n state: {\n model?: any;\n pathTesselator: any;\n outlineFramebuffer: Framebuffer;\n outlineEmptyTexture: Texture;\n } = undefined!;\n\n // Override getShaders to inject the outline module\n getShaders() {\n const shaders = super.getShaders();\n return Object.assign({}, shaders, {\n modules: shaders.modules.concat([outline]),\n vs: injectShaderCode({source: shaders.vs, code: VS_CODE}),\n fs: injectShaderCode({source: shaders.fs, code: FS_CODE})\n });\n }\n\n // @ts-expect-error PathLayer is missing LayerContext arg\n initializeState(context: LayerContext) {\n super.initializeState();\n\n const attributeManager = this.getAttributeManager();\n\n if (!attributeManager) {\n throw new Error('PathOutlineLayer requires an attribute manager during initialization.');\n }\n\n // Create an outline \"shadow\" map\n // TODO - we should create a single outlineMap for all layers\n const outlineFramebuffer = context.device.createFramebuffer({\n colorAttachments: [\n context.device.createTexture({\n format: 'rgba8unorm',\n width: 1,\n height: 1,\n mipLevels: 1\n })\n ]\n });\n const outlineEmptyTexture = context.device.createTexture({\n format: 'rgba8unorm',\n width: 1,\n height: 1,\n mipLevels: 1\n });\n\n attributeManager.addInstanced({\n instanceZLevel: {\n size: 1,\n type: 'uint8',\n accessor: 'getZLevel'\n }\n });\n\n this.setState({\n outlineFramebuffer,\n outlineEmptyTexture,\n model: this._getModel()\n });\n }\n\n finalizeState(context: LayerContext) {\n this.state.outlineFramebuffer?.destroy();\n this.state.outlineEmptyTexture?.destroy();\n super.finalizeState(context);\n }\n\n // Override draw to add render module\n draw({parameters = {} as Parameters}: {parameters?: Parameters; uniforms?: unknown}) {\n const model = this.state.model;\n const outlineFramebuffer = this.state.outlineFramebuffer;\n const outlineEmptyTexture = this.state.outlineEmptyTexture;\n\n if (!model || !outlineFramebuffer || !outlineEmptyTexture) {\n return;\n }\n\n const viewport = this.context.viewport;\n const viewportWidth = Math.max(1, Math.ceil(viewport.width));\n const viewportHeight = Math.max(1, Math.ceil(viewport.height));\n\n outlineFramebuffer.resize({width: viewportWidth, height: viewportHeight});\n\n const shadowmapTexture = getFramebufferTexture(outlineFramebuffer);\n\n if (!shadowmapTexture) {\n return;\n }\n\n const {\n jointRounded,\n capRounded,\n billboard,\n miterLimit,\n widthUnits,\n widthScale,\n widthMinPixels,\n widthMaxPixels\n } = this.props;\n\n const basePathProps = {\n jointType: Number(jointRounded),\n capType: Number(capRounded),\n billboard,\n widthUnits: UNIT[widthUnits],\n widthScale,\n miterLimit,\n widthMinPixels,\n widthMaxPixels\n };\n\n // Render the outline shadowmap (based on segment z orders)\n this.setShaderModuleProps({\n outline: {\n outlineEnabled: true,\n outlineRenderShadowmap: true,\n outlineShadowmap: outlineEmptyTexture\n }\n });\n model.shaderInputs.setProps({\n path: {\n ...basePathProps,\n jointType: 0,\n widthScale: widthScale * 1.3\n }\n });\n model.setParameters({...parameters, ...OUTLINE_SHADOWMAP_PARAMETERS});\n const shadowRenderPass = this.context.device.beginRenderPass({\n id: `${this.props.id}-outline-shadowmap`,\n framebuffer: outlineFramebuffer,\n parameters: {viewport: [0, 0, viewportWidth, viewportHeight]},\n clearColor: [0, 0, 0, 0],\n clearDepth: 1,\n clearStencil: 0\n });\n model.draw(shadowRenderPass);\n shadowRenderPass.end();\n\n // Now use the outline shadowmap to render the lines (with outlines)\n this.setShaderModuleProps({\n outline: {\n outlineEnabled: true,\n outlineRenderShadowmap: false,\n outlineShadowmap: shadowmapTexture\n }\n });\n model.shaderInputs.setProps({\n path: basePathProps\n });\n model.setParameters(\n isPickingPass(parameters) ? parameters : {...parameters, ...OUTLINE_RENDER_PARAMETERS}\n );\n model.draw(this.context.renderPass);\n }\n}\n\nfunction isPickingPass(parameters: Parameters): boolean {\n return parameters.blend === true && parameters.blendAlphaSrcFactor === 'constant';\n}\n\nfunction getFramebufferTexture(framebuffer: Framebuffer): Texture | null {\n const colorAttachment = framebuffer.colorAttachments[0];\n\n if (!colorAttachment) {\n return null;\n }\n\n return 'texture' in colorAttachment ? colorAttachment.texture : colorAttachment;\n}\n", "// deck.gl-community\n// SPDX-License-Identifier: MIT\n// Copyright (c) vis.gl contributors\n\nimport type {ShaderModule} from '@luma.gl/shadertools';\n\n/* eslint-disable camelcase */\nconst INITIAL_STATE: Record<string, any> = {\n outlineEnabled: false,\n outlineRenderShadowmap: false,\n outlineShadowmap: null\n};\n\nfunction getUniforms({outlineEnabled, outlineRenderShadowmap, outlineShadowmap} = INITIAL_STATE) {\n const uniforms: Record<string, any> = {};\n if (outlineEnabled !== undefined) {\n // ? 1.0 : 0.0;\n uniforms.outline_uEnabled = outlineEnabled;\n }\n if (outlineRenderShadowmap !== undefined) {\n // ? 1.0 : 0.0;\n uniforms.outline_uRenderOutlines = outlineRenderShadowmap;\n }\n if (outlineShadowmap !== undefined) {\n uniforms.outline_uShadowmap = outlineShadowmap;\n }\n return uniforms;\n}\n\nconst vs = `\\\nin float instanceZLevel;\nout float outline_vzLevel;\nout vec4 outline_vPosition;\n\n// Set the z level for the outline shadowmap rendering\nvoid outline_setZLevel(float zLevel) {\n outline_vzLevel = zLevel;\n}\n\n// Store an adjusted position for texture2DProj\nvoid outline_setUV(vec4 position) {\n // mat4(\n // 0.5, 0.0, 0.0, 0.0,\n // 0.0, 0.5, 0.0, 0.0,\n // 0.0, 0.0, 0.5, 0.0,\n // 0.5, 0.5, 0.5, 1.0\n // ) * position;\n outline_vPosition = vec4(position.xyz * 0.5 + position.w * 0.5, position.w);\n}\n`;\n\nconst fs = `\\\nuniform bool outline_uEnabled;\nuniform bool outline_uRenderOutlines;\nuniform sampler2D outline_uShadowmap;\n\nin float outline_vzLevel;\n// in vec2 outline_vUV;\nin vec4 outline_vPosition;\n\nconst float OUTLINE_Z_LEVEL_ERROR = 0.01;\n\n// Return a darker color in shadowmap\nvec4 outline_filterShadowColor(vec4 color) {\n return vec4(outline_vzLevel / 255., outline_vzLevel / 255., outline_vzLevel / 255., 1.);\n}\n\n// Return a darker color if in shadowmap\nvec4 outline_filterDarkenColor(vec4 color) {\n if (outline_uEnabled) {\n float maxZLevel;\n if (outline_vPosition.q > 0.0) {\n maxZLevel = textureProj(outline_uShadowmap, outline_vPosition).r * 255.;\n } else {\n discard;\n }\n if (maxZLevel < outline_vzLevel + OUTLINE_Z_LEVEL_ERROR) {\n return vec4(color.rgb * 0.5, color.a);\n } else {\n discard;\n }\n }\n return color;\n}\n\n// if enabled and rendering outlines - Render depth to shadowmap\n// if enabled and rendering colors - Return a darker color if in shadowmap\n// if disabled, just return color\nvec4 outline_filterColor(vec4 color) {\n if (outline_uEnabled) {\n return outline_uRenderOutlines ?\n outline_filterShadowColor(color) :\n outline_filterDarkenColor(color);\n }\n return color;\n}\n`;\n\nexport const outline = {\n name: 'outline',\n vs,\n fs,\n getUniforms\n} as const satisfies ShaderModule;\n", "// deck.gl-community\n// SPDX-License-Identifier: MIT\n// Copyright (c) vis.gl contributors\n\nimport type {DefaultProps} from '@deck.gl/core';\nimport {CompositeLayer, COORDINATE_SYSTEM} from '@deck.gl/core';\nimport {ScatterplotLayer} from '@deck.gl/layers';\nimport {SimpleMeshLayer} from '@deck.gl/mesh-layers';\nimport {PathOutlineLayer, PathOutlineLayerProps} from '../path-outline-layer/path-outline-layer';\nimport {Arrow2DGeometry} from './arrow-2d-geometry';\n\nimport {createPathMarkers} from './create-path-markers';\nimport {getClosestPointOnPolyline} from './polyline';\nimport {Vector3} from '@math.gl/core';\n\nconst DISTANCE_FOR_MULTI_ARROWS = 0.1;\nconst ARROW_HEAD_SIZE = 0.2;\nconst ARROW_TAIL_WIDTH = 0.05;\n// const ARROW_CENTER_ADJUST = -0.8;\n\nconst DEFAULT_MARKER_LAYER = SimpleMeshLayer;\n\nexport type PathMarkerLayerProps<DataT> = PathOutlineLayerProps<DataT> & {\n getDirection?: (x) => any;\n getMarkerColor?: (x) => number[];\n getMarkerPercentages?: (x: any, info: any) => number[];\n highlightPoint?: any;\n highlightIndex?: number;\n MarkerLayer?: any;\n markerLayerProps?: any;\n sizeScale?: number;\n fp64?: boolean;\n nebulaLayer?: any;\n};\n\nconst DEFAULT_MARKER_LAYER_PROPS = {\n mesh: new Arrow2DGeometry({headSize: ARROW_HEAD_SIZE, tailWidth: ARROW_TAIL_WIDTH})\n};\n\nconst defaultProps: DefaultProps<PathMarkerLayerProps<any>> = Object.assign(\n {},\n PathOutlineLayer.defaultProps,\n {\n MarkerLayer: DEFAULT_MARKER_LAYER,\n markerLayerProps: DEFAULT_MARKER_LAYER_PROPS,\n\n sizeScale: 100,\n fp64: false,\n\n highlightIndex: -1,\n highlightPoint: null,\n\n getPath: x => x.path,\n getColor: x => x.color,\n getMarkerColor: _x => [0, 0, 0, 255],\n getDirection: x => x.direction,\n getMarkerPercentages: (object, {lineLength}) =>\n lineLength > DISTANCE_FOR_MULTI_ARROWS ? [0.25, 0.5, 0.75] : [0.5]\n }\n);\n\nexport class PathMarkerLayer<\n DataT = any,\n ExtraPropsT = Record<string, unknown>\n> extends CompositeLayer<ExtraPropsT & Required<PathMarkerLayerProps<DataT>>> {\n static layerName = 'PathMarkerLayer';\n static defaultProps = defaultProps;\n\n state: {\n closestPoint: Vector3 | null;\n closestPoints?: {position: Vector3}[];\n markers: any[];\n mesh: Arrow2DGeometry;\n } = undefined!;\n\n initializeState() {\n this.state = {\n markers: [],\n mesh: new Arrow2DGeometry({headSize: ARROW_HEAD_SIZE, tailWidth: ARROW_TAIL_WIDTH}),\n closestPoint: null,\n closestPoints: []\n };\n }\n\n projectFlat(xyz, viewport, coordinateSystem, coordinateOrigin) {\n if (coordinateSystem === COORDINATE_SYSTEM.METER_OFFSETS) {\n const [dx, dy] = viewport.metersToLngLatDelta(xyz);\n const [x, y] = coordinateOrigin;\n return viewport.projectFlat([x + dx, dy + y]);\n } else if (coordinateSystem === COORDINATE_SYSTEM.LNGLAT_OFFSETS) {\n const [dx, dy] = xyz;\n const [x, y] = coordinateOrigin;\n return viewport.projectFlat([x + dx, dy + y]);\n }\n\n return viewport.projectFlat(xyz);\n }\n\n updateState({props, oldProps, changeFlags}) {\n if (changeFlags.dataChanged || changeFlags.updateTriggersChanged) {\n const {\n data,\n getPath,\n getDirection,\n getMarkerColor,\n getMarkerPercentages,\n coordinateSystem,\n coordinateOrigin\n } = this.props;\n\n const {viewport} = this.context;\n const projectFlat = o => this.projectFlat(o, viewport, coordinateSystem, coordinateOrigin);\n this.state.markers = createPathMarkers({\n data,\n getPath,\n getDirection,\n getColor: getMarkerColor,\n getMarkerPercentages,\n projectFlat\n });\n this._recalculateClosestPoint();\n }\n if (changeFlags.propsChanged) {\n if (props.point !== oldProps.point) {\n this._recalculateClosestPoint();\n }\n }\n }\n\n _recalculateClosestPoint() {\n const {highlightPoint, highlightIndex} = this.props;\n if (highlightPoint && highlightIndex >= 0) {\n const object = this.props.data[highlightIndex];\n const points = this.props.getPath(object, null as any);\n const {point} = getClosestPointOnPolyline({points, p: highlightPoint});\n this.state.closestPoints = [{position: point}];\n } else {\n this.state.closestPoints = [];\n }\n }\n\n getPickingInfo({info}) {\n return Object.assign(info, {\n // override object with picked feature\n object: (info.object && info.object.path) || info.object\n });\n }\n\n renderLayers() {\n return [\n new PathOutlineLayer(\n this.props,\n this.getSubLayerProps({\n id: 'paths',\n // Note: data has to be passed explicitly like this to avoid being empty\n data: this.props.data\n })\n ),\n new this.props.MarkerLayer(\n this.getSubLayerProps(\n Object.assign({}, this.props.markerLayerProps, {\n id: 'markers',\n data: this.state.markers,\n getOrientation: x => [0, -x.angle, 0],\n getColor: x => x.color,\n sizeScale: this.props.sizeScale,\n fp64: this.props.fp64,\n pickable: false,\n parameters: {\n blend: false,\n depthTest: false\n }\n })\n )\n ),\n this.state.closestPoints &&\n new ScatterplotLayer({\n id: `${this.props.id}-highlight`,\n data: this.state.closestPoints,\n fp64: this.props.fp64\n })\n ];\n }\n}\n", "// deck.gl-community\n// SPDX-License-Identifier: MIT\n// Copyright (c) vis.gl contributors\n\nimport {Geometry} from '@luma.gl/engine';\n\nexport class Arrow2DGeometry extends Geometry {\n constructor(opts = {}) {\n super(\n Object.assign({}, opts, {\n attributes: getArrowAttributes(opts),\n topology: 'triangle-list' as const\n })\n );\n }\n}\n\nfunction getArrowAttributes({length = 1, headSize = 0.2, tailWidth = 0.05, tailStart = 0.05}) {\n const texCoords = [\n // HEAD\n 0.5,\n 1.0,\n 0,\n 0.5 - headSize / 2,\n 1.0 - headSize,\n 0,\n 0.5 + headSize / 2,\n 1.0 - headSize,\n 0,\n 0.5 - tailWidth / 2,\n tailStart,\n 0,\n 0.5 + tailWidth / 2,\n 1.0 - headSize,\n 0,\n 0.5 + tailWidth / 2,\n tailStart,\n 0,\n 0.5 - tailWidth / 2,\n tailStart,\n 0,\n 0.5 - tailWidth / 2,\n 1.0 - headSize,\n 0,\n 0.5 + tailWidth / 2,\n 1.0 - headSize,\n 0\n ];\n\n 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];\n\n // Center and scale\n const positions = new Array(texCoords.length);\n for (let i = 0; i < texCoords.length / 3; i++) {\n const i3 = i * 3;\n positions[i3 + 0] = (texCoords[i3 + 0] - 0.5) * length;\n positions[i3 + 1] = (texCoords[i3 + 1] - 0.5) * length;\n positions[i3 + 2] = 0;\n }\n return {\n positions: {size: 3, value: new Float32Array(positions)},\n normals: {size: 3, value: new Float32Array(normals)},\n texCoords: {size: 2, value: new Float32Array(texCoords)}\n };\n}\n", "// deck.gl-community\n// SPDX-License-Identifier: MIT\n// Copyright (c) vis.gl contributors\n\nimport {Vector2} from '@math.gl/core';\n\n/** GeoJSON style position coordinate vector */\nexport type Position = [number, number] | [number, number, number];\n\n/** [red, green, blue, alpha] in premultiplied alpha format */\nexport type Color = [number, number, number, number];\n\nexport interface PathMarker {\n position: Position;\n angle: number;\n color: Color;\n object: unknown;\n}\n\nfunction getLineLength(vPoints) {\n // calculate total length\n let lineLength = 0;\n for (let i = 0; i < vPoints.length - 1; i++) {\n lineLength += vPoints[i].distance(vPoints[i + 1]);\n }\n return lineLength;\n}\n\nconst DEFAULT_COLOR = [0, 0, 0, 255];\nconst DEFAULT_DIRECTION = {forward: true, backward: false};\n\nexport function createPathMarkers({\n data,\n getPath = (x, context) => x.path,\n getDirection = x => x.direction,\n getColor = _x => DEFAULT_COLOR,\n getMarkerPercentages = (x, info) => [0.5],\n projectFlat\n}): PathMarker[] {\n const markers: PathMarker[] = [];\n\n for (const object of data) {\n const path = getPath(object, null);\n const direction = getDirection(object) || DEFAULT_DIRECTION;\n const color = getColor(object);\n\n const vPoints = path.map(p => new Vector2(p));\n const vPointsReverse = vPoints.slice(0).reverse();\n\n // calculate total length\n const lineLength = getLineLength(vPoints);\n\n // Ask for where to put markers\n const percentages = getMarkerPercentages(object, {lineLength});\n\n // Create the markers\n for (const percentage of percentages) {\n if (direction.forward) {\n const marker = createMarkerAlongPath({\n path: vPoints,\n percentage,\n lineLength,\n color,\n object,\n projectFlat\n });\n markers.push(marker);\n }\n\n if (direction.backward) {\n const marker = createMarkerAlongPath({\n path: vPointsReverse,\n percentage,\n lineLength,\n color,\n object,\n projectFlat\n });\n markers.push(marker);\n }\n }\n }\n\n return markers;\n}\n\nfunction createMarkerAlongPath({\n path,\n percentage,\n lineLength,\n color,\n object,\n projectFlat\n}): PathMarker {\n const distanceAlong = lineLength * percentage;\n let currentDistance = 0;\n let previousDistance = 0;\n let i = 0;\n for (i = 0; i < path.length - 1; i++) {\n currentDistance += path[i].distance(path[i + 1]);\n if (currentDistance > distanceAlong) {\n break;\n }\n previousDistance = currentDistance;\n }\n\n // If reached the end of the loop without exiting early,\n // undo the final increment to avoid a null-pointer exception\n if (i === path.length - 1) {\n i -= 1;\n }\n\n const vDirection = path[i + 1].clone().subtract(path[i]).normalize();\n const along = distanceAlong - previousDistance;\n const vCenter = vDirection.clone().multiply(new Vector2(along, along)).add(path[i]);\n\n const vDirection2 = new Vector2(projectFlat(path[i + 1])).subtract(projectFlat(path[i]));\n\n const angle = (vDirection2.verticalAngle() * 180) / Math.PI;\n\n return {position: [vCenter.x, vCenter.y, 0], angle, color, object};\n}\n", "// deck.gl-community\n// SPDX-License-Identifier: MIT\n// Copyright (c) vis.gl contributors\n\nimport {Vector3, clamp} from '@math.gl/core';\n\n// Return the closest point on a line segment\nexport function getClosestPointOnLine({p, p1, p2, clampToLine = true}) {\n const lineVector = new Vector3(p2).subtract(p1);\n const pointVector = new Vector3(p).subtract(p1);\n let dotProduct = lineVector.dot(pointVector);\n if (clampToLine) {\n dotProduct = clamp(dotProduct, 0, 1);\n }\n\n return lineVector.lerp(p1, p2, dotProduct);\n}\n\n// Return the closest point on a line segment\nexport function getClosestPointOnPolyline({p, points}) {\n p = new Vector3(p);\n let pClosest: Vector3 | null = null;\n let distanceSquared = Infinity;\n let index = -1;\n for (let i = 0; i < points.length - 1; ++i) {\n const p1 = points[i];\n const p2 = points[i + 1];\n const pClosestOnLine = getClosestPointOnLine({p, p1, p2});\n const distanceToLineSquared = p.distanceSquared(pClosestOnLine);\n if (distanceToLineSquared < distanceSquared) {\n distanceSquared = distanceToLineSquared;\n pClosest = pClosestOnLine;\n index = i;\n }\n }\n return {\n point: pClosest,\n index,\n p1: points[index],\n p2: points[index + 1],\n distanceSquared,\n distance: Math.sqrt(distanceSquared)\n };\n}\n", "// deck.gl-community\n// SPDX-License-Identifier: MIT\n// Copyright (c) vis.gl contributors\n\nimport {CompositeLayer} from '@deck.gl/core';\nimport {ArcLayer, LineLayer, PathLayer} from '@deck.gl/layers';\nimport {Vector3} from '@math.gl/core';\n\nimport {createPathMarkers, PathDirection} from './create-path-markers';\nimport {GeometryLayer} from './geometry-layer';\n\nimport type {\n MarkerPlacementsAccessor,\n PathDirectionAccessor,\n PathGeometry,\n PathMarker\n} from './create-path-markers';\nimport type {\n Accessor,\n Color,\n DefaultProps,\n GetPickingInfoParams,\n Layer,\n LayerDataSource,\n LayerProps,\n Position,\n UpdateParameters\n} from '@deck.gl/core';\nimport type {ArcLayerProps, LineLayerProps, PathLayerProps} from '@deck.gl/layers';\n\nexport {PathDirection};\nexport type {\n MarkerPlacementsAccessor,\n MarkerPlacementsAccessorContext,\n PathDirectionAccessor,\n PathGeometry,\n PathMarker\n} from './create-path-markers';\n\n/** Properties supported by {@link DependencyArrowLayer}. */\nexport type DependencyArrowLayerProps<DataT = unknown> = LayerProps &\n _DependencyArrowLayerProps<DataT>;\n\ntype _DependencyArrowLayerProps<DataT = unknown> = {\n /** Dependency data rendered by the layer. */\n data: LayerDataSource<DataT>;\n /** Dependency routing mode. @defaultValue 'path' */\n mode?: 'path' | 'line' | 'arc';\n\n /** Accessor returning nested or flat dependency path coordinates. */\n getPath?: PathLayerProps<DataT>['getPath'];\n /** Accessor returning dependency line color. */\n getColor?: LineLayerProps<DataT>['getColor'];\n /** Accessor returning dependency line width. */\n getWidth?: LineLayerProps<DataT>['getWidth'];\n /** Units used by dependency line width. */\n widthUnits?: LineLayerProps<DataT>['widthUnits'];\n /** Scale applied to dependency line width. */\n widthScale?: LineLayerProps<DataT>['widthScale'];\n /** Minimum rendered dependency line width in pixels. */\n widthMinPixels?: LineLayerProps<DataT>['widthMinPixels'];\n /** Maximum rendered dependency line width in pixels. */\n widthMaxPixels?: LineLayerProps<DataT>['widthMaxPixels'];\n\n /** Arc segment count used when `mode` is `'arc'`. */\n arcNumSegments?: ArcLayerProps<DataT>['numSegments'];\n /** Accessor returning arc height when `mode` is `'arc'`. */\n getArcHeight?: ArcLayerProps<DataT>['getHeight'];\n /** Accessor returning arc tilt when `mode` is `'arc'`. */\n getArcTilt?: ArcLayerProps<DataT>['getTilt'];\n\n /** Accessor returning marker direction flags. @defaultValue PathDirection.FORWARD */\n getDirection?: PathDirectionAccessor<DataT>;\n /** Marker color accessor; falls back to `getColor` when omitted. */\n getMarkerColor?: Accessor<DataT, Color>;\n /** Accessor returning marker ratios along the path, from 0 at start to 1 at end. */\n getMarkerPlacements?: MarkerPlacementsAccessor<DataT>;\n /** Marker size accessor in marker-local `[width, height]` units. */\n getMarkerSize?: Accessor<DataT, [number, number]>;\n /** Optional point used by callers to identify a highlighted dependency location. */\n highlightPoint?: Position | Vector3 | null;\n /** Optional source datum index used with `highlightPoint`. */\n highlightIndex?: number;\n /** Marker size multiplier. @defaultValue 10 */\n markerSizeScale?: number;\n};\n\nconst defaultProps: DefaultProps<_DependencyArrowLayerProps> = {\n getPath: PathLayer.defaultProps.getPath,\n getColor: LineLayer.defaultProps.getColor,\n getWidth: LineLayer.defaultProps.getWidth,\n widthUnits: LineLayer.defaultProps.widthUnits,\n widthScale: LineLayer.defaultProps.widthScale,\n widthMinPixels: LineLayer.defaultProps.widthMinPixels,\n widthMaxPixels: LineLayer.defaultProps.widthMaxPixels,\n arcNumSegments: ArcLayer.defaultProps.numSegments,\n getArcHeight: ArcLayer.defaultProps.getHeight,\n getArcTilt: ArcLayer.defaultProps.getTilt,\n\n mode: 'path',\n\n markerSizeScale: 10,\n\n highlightIndex: -1,\n highlightPoint: null,\n\n getMarkerColor: {type: 'accessor', value: undefined},\n getDirection: {type: 'accessor', value: PathDirection.FORWARD},\n getMarkerSize: {type: 'accessor', value: [1, 1]},\n getMarkerPlacements: {type: 'accessor', value: [0.5]}\n};\n\n/** Renders paths, lines, or arcs with directional dependency markers. */\nexport class DependencyArrowLayer<\n DataT = any,\n ExtraPropsT = Record<string, unknown>\n> extends CompositeLayer<ExtraPropsT & Required<_DependencyArrowLayerProps<DataT>>> {\n static override layerName = 'DependencyArrowLayer';\n static override defaultProps = defaultProps;\n\n override state: {\n markers: PathMarker<DataT>[];\n } = {\n markers: []\n };\n\n override updateState({props, oldProps, changeFlags}: UpdateParameters<this>) {\n const shouldRebuildMarkers =\n changeFlags.dataChanged ||\n props.mode !== oldProps.mode ||\n props.positionFormat !== oldProps.positionFormat ||\n props.getPath !== oldProps.getPath ||\n props.getDirection !== oldProps.getDirection ||\n props.getMarkerPlacements !== oldProps.getMarkerPlacements ||\n (changeFlags.updateTriggersChanged &&\n (changeFlags.updateTriggersChanged['getPath'] ||\n changeFlags.updateTriggersChanged['getDirection'] ||\n changeFlags.updateTriggersChanged['getMarkerPlacements']));\n\n if (shouldRebuildMarkers) {\n const {data, mode, getPath, getDirection, getMarkerPlacements} = this.props;\n\n this.state.markers = createPathMarkers<DataT>({\n data: data as Iterable<DataT>,\n positionSize: props.positionFormat.length,\n getPath,\n getDirection,\n getMarkerPlacements,\n mode\n });\n }\n }\n\n override getPickingInfo({info}: GetPickingInfoParams) {\n const pickedObject = info.object;\n if (pickedObject && pickedObject.__source) {\n info.object = (pickedObject as PathMarker<DataT>).__source.object;\n }\n return info;\n }\n\n renderLayers() {\n const {\n mode,\n getPath,\n getColor,\n getMarkerColor,\n getMarkerSize,\n markerSizeScale,\n updateTriggers = {}\n } = this.props;\n\n let pathLayer: Layer | null = null;\n if (mode === 'path') {\n pathLayer = new PathLayer(\n this.props,\n this.getSubLayerProps({\n id: 'links-path',\n updateTriggers: {\n getPath: updateTriggers['getPath'],\n getColor: updateTriggers['getColor'],\n getWidth: updateTriggers['getWidth']\n }\n })\n );\n } else {\n const positionSize = this.props.positionFormat.length;\n const sharedProps = {\n ...this.props,\n data: this.props.data,\n getSourcePosition: (datum, info) => {\n const path = getPath(datum, info);\n return getFirstPoint(path, positionSize) ?? [NaN, NaN];\n },\n getTargetPosition: (datum, info) => {\n const path = getPath(datum, info);\n return getLastPoint(path, positionSize) ?? [NaN, NaN];\n }\n } satisfies LineLayerProps<DataT>;\n if (mode === 'arc') {\n pathLayer = new ArcLayer<DataT>(\n sharedProps,\n {\n getSourceColor: this.props.getColor,\n getTargetColor: this.props.getColor,\n numSegments: this.props.arcNumSegments,\n getHeight: this.props.getArcHeight,\n getTilt: this.props.getArcTilt\n },\n this.getSubLayerProps({\n id: 'links-arc',\n updateTriggers: {\n getSourcePosition: updateTriggers['getPath'],\n getTargetPosition: updateTriggers['getPath'],\n getSourceColor: updateTriggers['getColor'],\n getTargetColor: updateTriggers['getColor'],\n getWidth: updateTriggers['getWidth'],\n getHeight: updateTriggers['getArcHeight'],\n getTilt: updateTriggers['getArcTilt']\n }\n })\n );\n } else {\n pathLayer = new LineLayer<DataT>(\n sharedProps,\n this.getSubLayerProps({\n id: 'links-line',\n updateTriggers: {\n getSourcePosition: updateTriggers['getPath'],\n getTargetPosition: updateTriggers['getPath'],\n getColor: updateTriggers['getColor'],\n getWidth: updateTriggers['getWidth']\n }\n })\n );\n }\n }\n\n return [\n pathLayer,\n new GeometryLayer<PathMarker<DataT>>(\n this.getSubLayerProps({\n id: 'arrows',\n updateTriggers: {\n getSize: updateTriggers['getMarkerSize'],\n getColor: getMarkerColor\n ? updateTriggers['getMarkerColor']\n : updateTriggers['getColor'],\n getArcHeight: updateTriggers['getArcHeight'],\n getArcTilt: updateTriggers['getArcTilt']\n }\n }),\n {\n data: this.state.markers,\n sizeUnits: 'pixels',\n sizeScale: markerSizeScale,\n interpolationMode: mode === 'arc' ? 'arc' : 'line',\n getSourcePosition: d => d.source,\n getTargetPosition: d => d.target,\n getPositionRatio: d => d.percentage,\n getSize: this.getSubLayerAccessor(getMarkerSize) as Accessor<\n PathMarker<DataT>,\n [number, number]\n >,\n getColor: this.getSubLayerAccessor(getMarkerColor ?? getColor) as Accessor<\n PathMarker<DataT>,\n Color\n >,\n getArcHeight: this.getSubLayerAccessor(this.props.getArcHeight) as Accessor<\n PathMarker<DataT>,\n number\n >,\n getArcTilt: this.getSubLayerAccessor(this.props.getArcTilt) as Accessor<\n PathMarker<DataT>,\n number\n >,\n getPickingColor: d => this.encodePickingColor(d.__source.index)\n }\n )\n ];\n }\n}\n\nfunction getFirstPoint(path: PathGeometry, size: number): Position | null {\n if (!path || path.length === 0) return null;\n if (Array.isArray(path[0])) {\n return (path as Position[])[0]!;\n }\n return path.slice(0, size) as Position;\n}\n\nfunction getLastPoint(path: PathGeometry, size: number): Position | null {\n if (!path || path.length === 0) return null;\n if (Array.isArray(path[0])) {\n return (path as Position[])[path.length - 1]!;\n }\n const len = Math.floor(path.length / size) * size;\n return path.slice(len - size, len) as Position;\n}\n", "// deck.gl-community\n// SPDX-License-Identifier: MIT\n// Copyright (c) vis.gl contributors\n\nimport {createIterable} from '@deck.gl/core';\nimport {Vector2} from '@math.gl/core';\n\nimport type {Accessor, AccessorContext, AccessorFunction, Position} from '@deck.gl/core';\nimport type {NumericArray} from '@math.gl/core';\n\n/** Path positions accepted by dependency marker helpers. */\nexport type PathGeometry = Position[] | NumericArray;\ntype PathAccessor<DataT> = AccessorFunction<DataT, PathGeometry>;\n/** Accessor context passed while resolving dependency marker placements. */\nexport type MarkerPlacementsAccessorContext<DataT> = AccessorContext<DataT> & {\n /** Total resolved dependency path length. */\n lineLength: number;\n};\n/** Accessor that resolves dependency marker direction for one datum. */\nexport type PathDirectionAccessor<DataT> = Accessor<DataT, PathDirection>;\n/** Accessor that resolves marker placements along one dependency path. */\nexport type MarkerPlacementsAccessor<DataT> =\n | number[]\n | ((datum: DataT, context: MarkerPlacementsAccessorContext<DataT>) => number[]);\n\n/** One marker placement resolved along a dependency path. */\nexport interface PathMarker<DataT> {\n /** Segment start used to orient the marker. */\n source: Position;\n /** Segment end used to orient the marker. */\n target: Position;\n /** Marker ratio within the resolved segment. */\n percentage: number;\n /** Original source datum used for picking and accessor forwarding. */\n __source: {\n /** Original source datum. */\n object: DataT;\n /** Original source datum index. */\n index: number;\n };\n}\n\n/** Directions in which dependency markers may be rendered. */\nexport enum PathDirection {\n /** Do not render dependency markers. */\n NONE = 0,\n /** Render markers from source to target. */\n FORWARD = 1,\n /** Render markers from target to source. */\n BACKWARD = 2,\n /** Render markers in both directions. */\n BOTH = 3\n}\n\n/**\n * Resolves directional marker placements for dependency path data.\n * @param options - Marker resolution options.\n * @param options.data - Dependency path data to inspect.\n * @param options.getPath - Accessor returning nested or flat path coordinates.\n * @param options.positionSize - Number of coordinate values in one flat path position.\n * @param options.getDirection - Accessor returning marker direction flags.\n * @param options.getMarkerPlacements - Accessor returning path ratios for marker placement.\n * @param options.mode - Routing mode used by the dependency line.\n * @returns Marker placements resolved against each dependency path.\n */\nexport function createPathMarkers<DataT>({\n data,\n getPath,\n positionSize,\n getDirection,\n getMarkerPlacements,\n mode\n}: {\n data: Iterable<DataT>;\n getPath: PathAccessor<DataT>;\n positionSize: number;\n getDirection: PathDirectionAccessor<DataT>;\n getMarkerPlacements: MarkerPlacementsAccessor<DataT>;\n mode: 'line' | 'arc' | 'path';\n}): PathMarker<DataT>[] {\n const markers: PathMarker<DataT>[] = [];\n if (!data || typeof data === 'string' || !(Symbol.iterator in Object(data))) {\n return markers;\n }\n\n const {iterable, objectInfo} = createIterable(data) as {\n iterable: Iterable<DataT>;\n objectInfo: MarkerPlacementsAccessorContext<DataT>;\n };\n\n for (const object of iterable) {\n objectInfo.index++;\n const path = normalizePath(getPath(object, objectInfo), positionSize);\n if (path.length < 2) {\n continue;\n }\n if (mode !== 'path') {\n path.splice(1, path.length - 2);\n }\n const direction =\n typeof getDirection === 'function' ? getDirection(object, objectInfo) : getDirection;\n\n // calculate total length\n const lineLength = getLineLength(path);\n if (!Number.isFinite(lineLength) || lineLength <= 0) {\n continue;\n }\n objectInfo.lineLength = lineLength;\n const placements =\n typeof getMarkerPlacements === 'function'\n ? getMarkerPlacements(object, objectInfo)\n : getMarkerPlacements;\n\n const sourceObject = {object, index: objectInfo.index};\n\n // Create the markers\n for (const dir of [PathDirection.FORWARD, PathDirection.BACKWARD]) {\n if (!(direction & dir)) continue;\n if (dir === PathDirection.BACKWARD) {\n path.reverse();\n }\n for (const percentage of placements) {\n const marker =\n mode === 'arc'\n ? ({\n source: path[0] as unknown as Position,\n target: path[1] as unknown as Position,\n percentage,\n __source: sourceObject\n } satisfies PathMarker<DataT>)\n : createMarkerAlongPath({\n path,\n percentage,\n lineLength,\n sourceObject\n });\n markers.push(marker);\n }\n }\n }\n\n return markers;\n}\n\nfunction normalizePath(path: PathGeometry, size: number): Vector2[] {\n if (!path) {\n return [];\n }\n if (Array.isArray(path[0])) {\n return (path as NumericArray[]).map(([x, y]) => new Vector2(x, y));\n }\n const flatPath = path as NumericArray;\n const length = flatPath.length / size;\n const points: Vector2[] = new Array(length);\n for (let i = 0; i < length; i++) {\n points[i] = new Vector2(flatPath[i * size], flatPath[i * size + 1]);\n }\n return points;\n}\n\nfunction getLineLength(vPoints: Vector2[]): number {\n // calculate total length\n let lineLength = 0;\n for (let i = 0; i < vPoints.length - 1; i++) {\n lineLength += vPoints[i]!.distance(vPoints[i + 1]!);\n }\n return lineLength;\n}\n\nfunction createMarkerAlongPath<DataT>({\n path,\n percentage,\n lineLength,\n sourceObject\n}: {\n path: Vector2[];\n percentage: number;\n lineLength: number;\n sourceObject: PathMarker<DataT>['__source'];\n}): PathMarker<DataT> {\n const distanceAlong = lineLength * percentage;\n let currentDistance = 0;\n let previousDistance = 0;\n let i = 0;\n for (i = 0; i < path.length - 1; i++) {\n currentDistance += path[i]!.distance(path[i + 1]!);\n if (currentDistance >= distanceAlong) {\n break;\n }\n previousDistance = currentDistance;\n }\n\n // If reached the end of the loop without exiting early,\n // undo the final increment to avoid a null-pointer exception\n if (i === path.length - 1) {\n i -= 1;\n }\n\n const along = distanceAlong - previousDistance;\n const segmentLength = path[i + 1]!.distance(path[i]!);\n\n return {\n source: path[i] as unknown as Position,\n target: path[i + 1]! as unknown as Position,\n percentage: segmentLength > 0 ? along / segmentLength : 0,\n __source: sourceObject\n };\n}\n", "// deck.gl-community\n// SPDX-License-Identifier: MIT\n// Copyright (c) vis.gl contributors\n\nimport {Layer, picking, project32, UNIT} from '@deck.gl/core';\nimport {Geometry, Model} from '@luma.gl/engine';\n\nimport type {\n Accessor,\n Color,\n DefaultProps,\n LayerProps,\n Position,\n Unit,\n UpdateParameters\n} from '@deck.gl/core';\n\n/** Properties supported by the internal dependency marker geometry layer. */\nexport type GeometryLayerProps<DataT = unknown> = LayerProps & _GeometryLayerProps<DataT>;\n\ntype _GeometryLayerProps<DataT> = {\n /** Units used by marker size. @defaultValue 'common' */\n sizeUnits?: Unit;\n /** Scale applied to marker size. @defaultValue 1 */\n sizeScale?: number;\n\n /** Retained for API compatibility with existing callers. */\n nodeDepth?: unknown;\n\n /** Marker interpolation route. @defaultValue 'line' */\n interpolationMode?: 'line' | 'arc';\n\n /** Accessor returning encoded picking color. */\n getPickingColor?: Accessor<DataT, Color>;\n /** Accessor returning the marker segment start. */\n getSourcePosition?: Accessor<DataT, Position>;\n /** Accessor returning the marker segment end. */\n getTargetPosition?: Accessor<DataT, Position>;\n /** Accessor returning marker position ratio along the segment. */\n getPositionRatio?: Accessor<DataT, number>;\n /** Accessor returning marker bounding box as `[along, width]`. */\n getSize?: Accessor<DataT, [number, number]>;\n /** Accessor returning marker color as `[R, G, B, A?]`. */\n getColor?: Accessor<DataT, Color>;\n\n /** Accessor returning arc height when `interpolationMode` is `'arc'`. */\n getArcHeight?: Accessor<DataT, number>;\n /** Accessor returning arc tilt when `interpolationMode` is `'arc'`. */\n getArcTilt?: Accessor<DataT, number>;\n};\n\ntype GeometryLayerUniformProps = {\n sizeScale: number;\n sizeUnits: number;\n interpolationMode: number;\n};\n\nconst geometryLayerUniforms = {\n name: 'geometryLayer',\n vs: `\\\nuniform geometryLayerUniforms {\n float sizeScale;\n highp int sizeUnits;\n highp int interpolationMode;\n} geometryLayer;\n`,\n fs: `\\\nuniform geometryLayerUniforms {\n float sizeScale;\n highp int sizeUnits;\n highp int interpolationMode;\n} geometryLayer;\n `,\n uniformTypes: {\n sizeScale: 'f32',\n sizeUnits: 'i32',\n interpolationMode: 'i32'\n }\n} as const;\n\nconst defaultProps: DefaultProps<_GeometryLayerProps<any>> = {\n sizeUnits: 'common',\n sizeScale: {type: 'number', min: 0, value: 1},\n\n interpolationMode: 'line',\n\n getPickingColor: {type: 'accessor', value: [0, 0, 0]},\n getSourcePosition: {type: 'accessor', value: (x: any) => x.source},\n getTargetPosition: {type: 'accessor', value: (x: any) => x.target},\n getPositionRatio: {type: 'accessor', value: 1},\n getSize: {type: 'accessor', value: [1, 1]},\n getColor: {type: 'accessor', value: [0, 0, 0, 255]},\n getArcHeight: {type: 'accessor', value: 1},\n getArcTilt: {type: 'accessor', value: 0}\n};\n\nconst vs = `\\\n#version 300 es\n#define SHADER_NAME geometry-layer-vertex-shader\n\nin vec2 positions;\nin vec3 instanceSourcePositions;\nin vec3 instanceSourcePositions64Low;\nin vec3 instanceTargetPositions;\nin vec3 instanceTargetPositions64Low;\nin float instanceRatios;\nin vec2 instanceSizes;\nin vec4 instanceColors;\nin float instanceArcHeights;\nin float instanceArcTilts;\nin vec3 instancePickingColors;\n\nout vec4 vColor;\nout vec2 vPosition;\nflat out vec2 vPixelSize;\n\nconst int GEOMETRY_LINE = 0;\nconst int GEOMETRY_ARC = 1;\n\n// START ARC LAYER VERTEX\n\nfloat paraboloid(float distance, float sourceZ, float targetZ, float ratio) {\n float deltaZ = targetZ - sourceZ;\n float dh = distance * instanceArcHeights;\n if (dh == 0.0) {\n return sourceZ + deltaZ * ratio;\n }\n float unitZ = deltaZ / dh;\n float p2 = unitZ * unitZ + 1.0;\n\n // sqrt does not deal with negative values, manually flip source and target if delta.z < 0\n float dir = step(deltaZ, 0.0);\n float z0 = mix(sourceZ, targetZ, dir);\n float r = mix(ratio, 1.0 - ratio, dir);\n return sqrt(r * (p2 - r)) * dh + z0;\n}\n\nvec3 interpolateArc(vec3 source, vec3 target, float ratio) {\n float distance = length(source.xy - target.xy);\n float z = paraboloid(distance, source.z, target.z, ratio);\n\n float tiltAngle = radians(instanceArcTilts);\n vec2 tiltDirection = normalize(target.xy - source.xy);\n vec2 tilt = vec2(-tiltDirection.y, tiltDirection.x) * z * sin(tiltAngle);\n\n return vec3(\n mix(source.xy, target.xy, ratio) + tilt,\n z * cos(tiltAngle)\n );\n}\n\n// END ARC LAYER VERTEX\n\nvec3 interplolatePath(vec3 source, vec3 target, float ratio) {\n if (geometryLayer.interpolationMode == GEOMETRY_ARC) {\n return interpolateArc(source, target, ratio);\n }\n return mix(source, target, ratio);\n}\n\nvoid main(void) {\n geometry.worldPosition = instanceSourcePositions;\n geometry.worldPositionAlt = instanceTargetPositions;\n vPosition = (positions + 1.0) / 2.0;\n geometry.uv = vPosition;\n\n vec3 source = project_position(instanceSourcePositions, instanceSourcePositions64Low);\n vec3 target = project_position(instanceTargetPositions, instanceTargetPositions64Low);\n vec3 curr = interplolatePath(source, target, instanceRatios);\n vec2 normal;\n if (instanceRatios < 0.01) {\n vec3 next = interplolatePath(source, target, instanceRatios + 0.01);\n normal = next.xy - curr.xy;\n } else {\n vec3 prev = interplolatePath(source, target, instanceRatios - 0.01);\n normal = curr.xy - prev.xy;\n }\n\n vec2 scaledSize = instanceSizes * geometryLayer.sizeScale;\n // Anchor the marker at the triangle tip, so ratio 1.0 places the arrowhead on the endpoint.\n vec2 markerPosition = vec2((positions.x - 1.0) / 2.0, positions.y / 2.0);\n vec2 offset = markerPosition * scaledSize;\n float angle = atan(normal.y, normal.x);\n float cosA = cos(angle);\n float sinA = sin(angle);\n offset = vec2(\n offset.x * cosA - offset.y * sinA,\n offset.x * sinA + offset.y * cosA\n );\n vec3 offsetCommon = vec3(offset, 0.);\n if (geometryLayer.sizeUnits == UNIT_PIXELS) {\n offsetCommon.xy = project_pixel_size(offset);\n vPixelSize = scaledSize;\n } else {\n vPixelSize = project_size(scaledSize);\n }\n\n geometry.pickingColor = instancePickingColors;\n geometry.position = vec4(curr + offsetCommon, 0.1);\n gl_Position = project_common_position_to_clipspace(geometry.position);\n DECKGL_FILTER_GL_POSITION(gl_Position, geometry);\n\n vColor = vec4(instanceColors.rgb, instanceColors.a * layer.opacity);\n DECKGL_FILTER_COLOR(vColor, geometry);\n}\n`;\n\nconst fs = `\\\n#version 300 es\n#define SHADER_NAME geometry-layer-fragment-shader\n\nprecision highp float;\n\nin vec4 vColor;\nin vec2 vPosition;\nflat in vec2 vPixelSize;\n\nout vec4 fragColor;\n\nfloat smoothedgeSigned(float signedDistance) {\n float edgeRadius = fwidth(signedDistance);\n return smoothstep(-edgeRadius, edgeRadius, signedDistance);\n}\n\nfloat inTriangle(vec2 bbox, vec2 uv) {\n float w = max(bbox.x, 1.0);\n float h = max(bbox.y, 1.0);\n float d = ((1.0 - abs(1.0 - uv.y * 2.0)) - uv.x) * w;\n return smoothedgeSigned(d);\n}\n\nvoid main(void) {\n geometry.uv = vPosition;\n float inShape = inTriangle(vPixelSize, vPosition);\n\n if (inShape == 0.0) {\n discard;\n }\n\n fragColor = vColor;\n fragColor.a *= inShape;\n DECKGL_FILTER_COLOR(fragColor, geometry);\n}\n`;\n\n/** Renders triangle markers resolved by {@link DependencyArrowLayer}. */\nexport class GeometryLayer<DataT = unknown> extends Layer<Required<_GeometryLayerProps<DataT>>> {\n static override defaultProps = defaultProps;\n static override layerName = 'GeometryLayer';\n\n override state: {\n model?: Model;\n } = {};\n\n override getShaders() {\n return super.getShaders({vs, fs, modules: [project32, picking, geometryLayerUniforms]});\n }\n\n initializeState() {\n this.getAttributeManager()!.addInstanced({\n instanceSourcePositions: {\n size: 3,\n type: 'float64',\n fp64: this.use64bitPositions(),\n transition: true,\n accessor: 'getSourcePosition'\n },\n instanceTargetPositions: {\n size: 3,\n type: 'float64',\n fp64: this.use64bitPositions(),\n transition: true,\n accessor: 'getTargetPosition'\n },\n instanceRatios: {\n size: 1,\n transition: true,\n accessor: 'getPositionRatio'\n },\n instanceArcHeights: {\n size: 1,\n transition: true,\n accessor: 'getArcHeight'\n },\n instanceArcTilts: {\n size: 1,\n transition: true,\n accessor: 'getArcTilt'\n },\n instanceSizes: {\n size: 2,\n transition: true,\n accessor: 'getSize'\n },\n instanceColors: {\n size: 4,\n transition: true,\n type: 'unorm8',\n accessor: 'getColor',\n defaultValue: [0, 0, 0, 255]\n },\n instancePickingColors: {\n size: 3,\n type: 'uint8',\n accessor: 'getPickingColor'\n }\n });\n }\n\n override updateState(params: UpdateParameters<this>) {\n super.updateState(params);\n\n if (params.changeFlags.extensionsChanged) {\n this.state.model?.destroy();\n this.state.model = this._getModel();\n this.getAttributeManager()!.invalidateAll();\n }\n }\n\n override draw() {\n const model = this.state.model;\n if (!model) {\n return;\n }\n\n const {sizeScale, sizeUnits, interpolationMode} = this.props;\n\n const geometryLayerProps: GeometryLayerUniformProps = {\n sizeScale,\n sizeUnits: UNI