@deck.gl/extensions
Version:
Plug-and-play functionalities for deck.gl layers
4 lines • 185 kB
Source Map (JSON)
{
"version": 3,
"sources": ["../src/index.ts", "../src/brushing/brushing-extension.ts", "../src/brushing/shader-module.ts", "../src/data-filter/data-filter-extension.ts", "../src/data-filter/shader-module.ts", "../src/data-filter/aggregator.ts", "../src/fp64/fp64-extension.ts", "../src/fp64/project64.ts", "../src/fp64/project64.glsl.ts", "../src/path-style/path-style-extension.ts", "../src/path-style/shaders.glsl.ts", "../src/fill-style/fill-style-extension.ts", "../src/fill-style/shader-module.ts", "../src/clip/clip-extension.ts", "../src/collision-filter/collision-filter-extension.ts", "../src/collision-filter/shader-module.ts", "../src/collision-filter/collision-filter-effect.ts", "../src/collision-filter/collision-filter-pass.ts", "../src/mask/mask-extension.ts", "../src/mask/shader-module.ts", "../src/mask/mask-effect.ts", "../src/mask/mask-pass.ts", "../src/utils/projection-utils.ts", "../src/terrain/terrain-extension.ts", "../src/terrain/terrain-effect.ts", "../src/terrain/shader-module.ts", "../src/terrain/utils.ts", "../src/terrain/terrain-cover.ts", "../src/terrain/terrain-pass.ts", "../src/terrain/terrain-picking-pass.ts", "../src/terrain/height-map-builder.ts"],
"sourcesContent": ["// deck.gl\n// SPDX-License-Identifier: MIT\n// Copyright (c) vis.gl contributors\n\nexport {default as BrushingExtension} from './brushing/brushing-extension';\nexport {default as DataFilterExtension} from './data-filter/data-filter-extension';\nexport {default as Fp64Extension} from './fp64/fp64-extension';\nexport {default as PathStyleExtension} from './path-style/path-style-extension';\nexport {default as FillStyleExtension} from './fill-style/fill-style-extension';\nexport {default as ClipExtension} from './clip/clip-extension';\nexport {default as CollisionFilterExtension} from './collision-filter/collision-filter-extension';\nexport {default as MaskExtension} from './mask/mask-extension';\nexport {default as _TerrainExtension} from './terrain/terrain-extension';\n\n// Shader module\nexport {default as project64} from './fp64/project64';\n\n// Types\nexport type {BrushingExtensionProps} from './brushing/brushing-extension';\nexport type {\n DataFilterExtensionProps,\n DataFilterExtensionOptions\n} from './data-filter/data-filter-extension';\nexport type {\n PathStyleExtensionProps,\n PathStyleExtensionOptions\n} from './path-style/path-style-extension';\nexport type {\n FillStyleExtensionProps,\n FillStyleExtensionOptions\n} from './fill-style/fill-style-extension';\nexport type {ClipExtensionProps} from './clip/clip-extension';\nexport type {CollisionFilterExtensionProps} from './collision-filter/collision-filter-extension';\nexport type {MaskExtensionProps} from './mask/mask-extension';\nexport type {TerrainExtensionProps} from './terrain/terrain-extension';\nexport type {TerrainModuleProps} from './terrain/shader-module';\n", "// deck.gl\n// SPDX-License-Identifier: MIT\n// Copyright (c) vis.gl contributors\n\nimport {LayerExtension} from '@deck.gl/core';\nimport shaderModule, {BrushingModuleProps} from './shader-module';\n\nimport type {Layer, LayerContext, Accessor} from '@deck.gl/core';\n\nconst defaultProps = {\n getBrushingTarget: {type: 'accessor', value: [0, 0]},\n\n brushingTarget: 'source',\n brushingEnabled: true,\n brushingRadius: 10000\n};\n\nexport type BrushingExtensionProps<DataT = any> = {\n /**\n * Called to retrieve an arbitrary position for each object that it will be filtered by.\n * Only effective if `brushingTarget` is set to `custom`.\n */\n getBrushingTarget?: Accessor<DataT, [number, number]>;\n /**\n * Enable/disable brushing. If brushing is disabled, all objects are rendered.\n * @default true\n */\n brushingEnabled?: boolean;\n /**\n * The position used to filter each object by.\n */\n brushingTarget?: 'source' | 'target' | 'source_target' | 'custom';\n /** The brushing radius centered at the pointer, in meters. If a data object is within this circle, it is rendered; otherwise it is hidden.\n * @default 10000\n */\n brushingRadius?: number;\n};\n\n/** Adds GPU-based data brushing functionalities to layers. It allows the layer to show/hide objects based on the current pointer position. */\nexport default class BrushingExtension extends LayerExtension {\n static defaultProps = defaultProps;\n static extensionName = 'BrushingExtension';\n\n getShaders(): any {\n return {\n modules: [shaderModule]\n };\n }\n\n initializeState(this: Layer<BrushingExtensionProps>, context: LayerContext, extension: this) {\n const attributeManager = this.getAttributeManager();\n if (attributeManager) {\n attributeManager.add({\n brushingTargets: {\n size: 2,\n stepMode: 'dynamic',\n accessor: 'getBrushingTarget'\n }\n });\n }\n\n // Trigger redraw when mouse moves\n const onMouseMove = () => {\n this.getCurrentLayer()?.setNeedsRedraw();\n };\n // TODO - expose this in a better way\n this.state.onMouseMove = onMouseMove;\n if (context.deck) {\n // @ts-expect-error (2446) accessing protected property\n context.deck.eventManager.on({\n pointermove: onMouseMove,\n pointerleave: onMouseMove\n });\n }\n }\n\n finalizeState(this: Layer<BrushingExtensionProps>, context: LayerContext, extension: this) {\n // Remove event listeners\n if (context.deck) {\n const onMouseMove = this.state.onMouseMove as () => void;\n // @ts-expect-error (2446) accessing protected property\n context.deck.eventManager.off({\n pointermove: onMouseMove,\n pointerleave: onMouseMove\n });\n }\n }\n\n draw(this: Layer<BrushingExtensionProps>, params: any, extension: this) {\n const {viewport, mousePosition} = params.context;\n const {brushingEnabled, brushingRadius, brushingTarget} = this.props;\n const brushingProps: BrushingModuleProps = {\n viewport,\n mousePosition,\n brushingEnabled,\n brushingRadius,\n brushingTarget\n };\n this.setShaderModuleProps({brushing: brushingProps});\n }\n}\n", "// deck.gl\n// SPDX-License-Identifier: MIT\n// Copyright (c) vis.gl contributors\n\n/* eslint-disable camelcase */\nimport type {ShaderModule} from '@luma.gl/shadertools';\nimport {project} from '@deck.gl/core';\nimport type {Viewport} from '@deck.gl/core';\n\nimport type {BrushingExtensionProps} from './brushing-extension';\n\nexport type BrushingModuleProps = {\n // From layer context\n viewport: Viewport;\n mousePosition?: {x: number; y: number};\n} & BrushingExtensionProps;\n\ntype BrushingModuleUniforms = {\n enabled?: boolean;\n target?: number;\n mousePos?: [number, number];\n radius?: number;\n};\n\nconst uniformBlock = /* glsl */ `\\\nuniform brushingUniforms {\n bool enabled;\n highp int target;\n vec2 mousePos;\n float radius;\n} brushing;\n`;\n\nconst vertex = /* glsl */ `\n in vec2 brushingTargets;\n\n out float brushing_isVisible;\n\n bool brushing_isPointInRange(vec2 position) {\n if (!brushing.enabled) {\n return true;\n }\n vec2 source_commonspace = project_position(position);\n vec2 target_commonspace = project_position(brushing.mousePos);\n float distance = length((target_commonspace - source_commonspace) / project.commonUnitsPerMeter.xy);\n\n return distance <= brushing.radius;\n }\n\n bool brushing_arePointsInRange(vec2 sourcePos, vec2 targetPos) {\n return brushing_isPointInRange(sourcePos) || brushing_isPointInRange(targetPos);\n }\n\n void brushing_setVisible(bool visible) {\n brushing_isVisible = float(visible);\n }\n`;\n\nconst vs = `\n${uniformBlock}\n${vertex}\n`;\n\nconst fragment = /* glsl */ `\n in float brushing_isVisible;\n`;\n\nconst fs = `\n${uniformBlock}\n${fragment}\n`;\n\nconst TARGET = {\n source: 0,\n target: 1,\n custom: 2,\n source_target: 3\n};\n\nconst inject = {\n 'vs:DECKGL_FILTER_GL_POSITION': /* glsl */ `\n vec2 brushingTarget;\n vec2 brushingSource;\n if (brushing.target == 3) {\n brushingTarget = geometry.worldPositionAlt.xy;\n brushingSource = geometry.worldPosition.xy;\n } else if (brushing.target == 0) {\n brushingTarget = geometry.worldPosition.xy;\n } else if (brushing.target == 1) {\n brushingTarget = geometry.worldPositionAlt.xy;\n } else {\n brushingTarget = brushingTargets;\n }\n bool visible;\n if (brushing.target == 3) {\n visible = brushing_arePointsInRange(brushingSource, brushingTarget);\n } else {\n visible = brushing_isPointInRange(brushingTarget);\n }\n brushing_setVisible(visible);\n `,\n\n 'fs:DECKGL_FILTER_COLOR': `\n if (brushing.enabled && brushing_isVisible < 0.5) {\n discard;\n }\n `\n};\n\nexport default {\n name: 'brushing',\n dependencies: [project],\n vs,\n fs,\n inject,\n getUniforms: (opts?: BrushingModuleProps | {}): BrushingModuleUniforms => {\n if (!opts || !('viewport' in opts)) {\n return {};\n }\n const {\n brushingEnabled = true,\n brushingRadius = 10000,\n brushingTarget = 'source',\n mousePosition,\n viewport\n } = opts;\n return {\n enabled: Boolean(brushingEnabled && mousePosition && viewport.containsPixel(mousePosition)),\n radius: brushingRadius,\n target: TARGET[brushingTarget] || 0,\n mousePos: mousePosition\n ? (viewport.unproject([mousePosition.x - viewport.x, mousePosition.y - viewport.y]) as [\n number,\n number\n ])\n : [0, 0]\n };\n },\n uniformTypes: {\n enabled: 'i32',\n target: 'i32',\n mousePos: 'vec2<f32>',\n radius: 'f32'\n }\n} as ShaderModule<BrushingModuleProps, BrushingModuleUniforms, {}>;\n", "// deck.gl\n// SPDX-License-Identifier: MIT\n// Copyright (c) vis.gl contributors\n\nimport type {Buffer, Framebuffer} from '@luma.gl/core';\nimport type {Model} from '@luma.gl/engine';\nimport type {Layer, LayerContext, Accessor, UpdateParameters} from '@deck.gl/core';\nimport {_deepEqual as deepEqual, LayerExtension, log} from '@deck.gl/core';\nimport {\n CategoryBitMask,\n DataFilterModuleProps,\n Defines,\n dataFilter,\n dataFilter64\n} from './shader-module';\nimport * as aggregator from './aggregator';\nimport {NumberArray4} from '@math.gl/core';\n\nconst defaultProps = {\n getFilterValue: {type: 'accessor', value: 0},\n getFilterCategory: {type: 'accessor', value: 0},\n onFilteredItemsChange: {type: 'function', value: null, optional: true},\n\n filterEnabled: true,\n filterRange: [-1, 1],\n filterSoftRange: null,\n filterCategories: [0],\n filterTransformSize: true,\n filterTransformColor: true\n};\n\ntype FilterCategory = number | string;\n\nexport type DataFilterExtensionProps<DataT = any> = {\n /**\n * Accessor to retrieve the value for each object that it will be filtered by.\n * Returns either a number (if `filterSize: 1`) or an array of numbers.\n */\n getFilterValue?: Accessor<DataT, number | number[]>;\n /**\n * Accessor to retrieve the category (`number | string`) for each object that it will be filtered by.\n * Returns either a single category (if `filterSize: 1`) or an array of categories.\n */\n getFilterCategory?: Accessor<DataT, FilterCategory | FilterCategory[]>;\n /**\n * Enable/disable the data filter. If the data filter is disabled, all objects are rendered.\n * @default true\n */\n filterEnabled?: boolean;\n /**\n * The [min, max] bounds which defines whether an object should be rendered.\n * If an object's filtered value is within the bounds, the object will be rendered; otherwise it will be hidden.\n * @default [-1, 1]\n */\n filterRange?: [number, number] | [number, number][];\n /**\n * If specified, objects will be faded in/out instead of abruptly shown/hidden.\n * When the filtered value is outside of the bounds defined by `filterSoftRange` but still within the bounds defined by `filterRange`, the object will be rendered as \"faded.\"\n * @default null\n */\n filterSoftRange?: [number, number] | [number, number][] | null;\n /**\n * When an object is \"faded\", manipulate its size so that it appears smaller or thinner. Only works if `filterSoftRange` is specified.\n * @default true\n */\n filterTransformSize?: boolean;\n /**\n * When an object is \"faded\", manipulate its opacity so that it appears more translucent. Only works if `filterSoftRange` is specified.\n * @default true\n */\n filterTransformColor?: boolean;\n /**\n * The categories which define whether an object should be rendered.\n * @default []\n */\n filterCategories?: FilterCategory[] | FilterCategory[][];\n /**\n * Only called if the `countItems` option is enabled.\n */\n onFilteredItemsChange?: (evt: {\n /** The id of the source layer. */\n id: string;\n /** The number of data objects that pass the filter. */\n count: number;\n }) => void;\n};\n\nexport type DataFilterExtensionOptions = {\n /**\n * The size of the category filter (number of columns to filter by). The category filter can show/hide data based on 1-4 properties of each object. Set to `0` to disable category filtering.\n * @default 0\n */\n categorySize?: 0 | 1 | 2 | 3 | 4;\n /**\n * The size of the filter (number of columns to filter by). The data filter can show/hide data based on 1-4 numeric properties of each object. Set to `0` to disable numeric filtering.\n * @default 1\n */\n filterSize?: 0 | 1 | 2 | 3 | 4;\n /**\n * Use 64-bit precision instead of 32-bit.\n * @default false\n */\n fp64?: boolean;\n /**\n * If `true`, reports the number of filtered objects with the `onFilteredItemsChange` callback.\n * @default `false`.\n */\n countItems?: boolean;\n};\n\nconst defaultOptions: Required<DataFilterExtensionOptions> = {\n categorySize: 0,\n filterSize: 1,\n fp64: false,\n countItems: false\n};\n\nconst CATEGORY_TYPE_FROM_SIZE = {\n 1: 'uint' as const,\n 2: 'uvec2' as const,\n 3: 'uvec3' as const,\n 4: 'uvec4' as const\n};\nconst DATA_TYPE_FROM_SIZE = {\n 1: 'float' as const,\n 2: 'vec2' as const,\n 3: 'vec3' as const,\n 4: 'vec4' as const\n};\n\n/** Adds GPU-based data filtering functionalities to layers. It allows the layer to show/hide objects based on user-defined properties. */\nexport default class DataFilterExtension extends LayerExtension<\n Required<DataFilterExtensionOptions>\n> {\n static defaultProps = defaultProps;\n static extensionName = 'DataFilterExtension';\n\n constructor(opts: DataFilterExtensionOptions = {}) {\n super({...defaultOptions, ...opts});\n }\n\n getShaders(this: Layer<DataFilterExtensionProps>, extension: this): any {\n const {categorySize, filterSize, fp64} = extension.opts;\n const defines: Defines = {};\n if (categorySize) {\n defines.DATACATEGORY_TYPE = CATEGORY_TYPE_FROM_SIZE[categorySize];\n defines.DATACATEGORY_CHANNELS = categorySize;\n }\n if (filterSize) {\n defines.DATAFILTER_TYPE = DATA_TYPE_FROM_SIZE[filterSize];\n defines.DATAFILTER_DOUBLE = Boolean(fp64);\n }\n\n const module = fp64 ? dataFilter64 : dataFilter;\n module.uniformTypes = module.uniformTypesFromOptions(extension.opts);\n\n return {modules: [module], defines};\n }\n\n initializeState(this: Layer<DataFilterExtensionProps>, context: LayerContext, extension: this) {\n const attributeManager = this.getAttributeManager();\n const {categorySize, filterSize, fp64} = extension.opts;\n\n if (attributeManager) {\n if (filterSize) {\n attributeManager.add({\n filterValues: {\n size: filterSize,\n type: fp64 ? 'float64' : 'float32',\n stepMode: 'dynamic',\n accessor: 'getFilterValue'\n }\n });\n }\n\n if (categorySize) {\n attributeManager.add({\n filterCategoryValues: {\n size: categorySize,\n stepMode: 'dynamic',\n accessor: 'getFilterCategory',\n type: 'uint32',\n transform:\n categorySize === 1\n ? d => extension._getCategoryKey.call(this, d, 0)\n : d => d.map((x, i) => extension._getCategoryKey.call(this, x, i))\n }\n });\n }\n }\n\n const {device} = this.context;\n if (attributeManager && extension.opts.countItems) {\n const useFloatTarget = aggregator.supportsFloatTarget(device);\n // This attribute is needed for variable-width data, e.g. Path, SolidPolygon, Text\n // The vertex shader checks if a vertex has the same \"index\" as the previous vertex\n // so that we only write one count cross multiple vertices of the same object\n attributeManager.add({\n filterVertexIndices: {\n size: useFloatTarget ? 1 : 2,\n vertexOffset: 1,\n type: 'unorm8',\n accessor: (object, {index}) => {\n const i = object && object.__source ? object.__source.index : index;\n return useFloatTarget ? (i + 1) % 255 : [(i + 1) % 255, Math.floor(i / 255) % 255];\n },\n shaderAttributes: {\n filterPrevIndices: {\n vertexOffset: 0\n },\n filterIndices: {\n vertexOffset: 1\n }\n }\n }\n });\n\n const filterFBO = aggregator.getFramebuffer(device, useFloatTarget);\n const filterModel = aggregator.getModel(\n device,\n attributeManager.getBufferLayouts({isInstanced: false}),\n extension.getShaders.call(this, extension),\n useFloatTarget\n );\n this.setState({filterFBO, filterModel});\n }\n }\n\n // eslint-disable-next-line complexity\n updateState(\n this: Layer<DataFilterExtensionProps>,\n {props, oldProps, changeFlags}: UpdateParameters<Layer<DataFilterExtensionProps>>,\n extension: this\n ) {\n const attributeManager = this.getAttributeManager();\n const {categorySize} = extension.opts;\n if (this.state.filterModel) {\n const filterNeedsUpdate =\n // attributeManager must be defined for filterModel to be set\n attributeManager!.attributes.filterValues?.needsUpdate() ||\n attributeManager!.attributes.filterCategoryValues?.needsUpdate() ||\n props.filterEnabled !== oldProps.filterEnabled ||\n props.filterRange !== oldProps.filterRange ||\n props.filterSoftRange !== oldProps.filterSoftRange ||\n props.filterCategories !== oldProps.filterCategories;\n if (filterNeedsUpdate) {\n this.setState({filterNeedsUpdate});\n }\n }\n if (attributeManager?.attributes.filterCategoryValues) {\n // Update bitmask if accessor or selected categories has changed\n const categoryBitMaskNeedsUpdate =\n attributeManager.attributes.filterCategoryValues.needsUpdate() ||\n !deepEqual(props.filterCategories, oldProps.filterCategories, 2);\n if (categoryBitMaskNeedsUpdate) {\n this.setState({categoryBitMask: null});\n }\n\n // Need to recreate category map if categorySize has changed\n const resetCategories = changeFlags.dataChanged;\n if (resetCategories) {\n this.setState({\n categoryMap: Array(categorySize)\n .fill(0)\n .map(() => ({}))\n });\n attributeManager.attributes.filterCategoryValues.setNeedsUpdate('categoryMap');\n }\n }\n }\n\n // eslint-disable-next-line max-statements\n draw(this: Layer<DataFilterExtensionProps>, params: any, extension: this) {\n const filterFBO = this.state.filterFBO as Framebuffer;\n const filterModel = this.state.filterModel as Model;\n const filterNeedsUpdate = this.state.filterNeedsUpdate as boolean;\n\n if (!this.state.categoryBitMask) {\n extension._updateCategoryBitMask.call(this, params, extension);\n }\n\n const {\n onFilteredItemsChange,\n extensions,\n filterEnabled,\n filterRange,\n filterSoftRange,\n filterTransformSize,\n filterTransformColor,\n filterCategories\n } = this.props;\n const dataFilterProps: DataFilterModuleProps = {\n extensions,\n filterEnabled,\n filterRange,\n filterSoftRange,\n filterTransformSize,\n filterTransformColor,\n filterCategories\n };\n if (this.state.categoryBitMask) {\n dataFilterProps.categoryBitMask = this.state.categoryBitMask as CategoryBitMask;\n }\n this.setShaderModuleProps({dataFilter: dataFilterProps});\n\n /* eslint-disable-next-line camelcase */\n if (filterNeedsUpdate && onFilteredItemsChange && filterModel) {\n const attributeManager = this.getAttributeManager()!;\n const {\n attributes: {filterValues, filterCategoryValues, filterVertexIndices}\n } = attributeManager;\n filterModel.setVertexCount(this.getNumInstances());\n\n // @ts-expect-error filterValue and filterVertexIndices should always have buffer value\n const attributes: Record<string, Buffer> = {\n ...filterValues?.getValue(),\n ...filterCategoryValues?.getValue(),\n ...filterVertexIndices?.getValue()\n };\n filterModel.setAttributes(attributes);\n filterModel.shaderInputs.setProps({\n dataFilter: dataFilterProps\n });\n\n const viewport = [0, 0, filterFBO.width, filterFBO.height] as NumberArray4;\n\n const renderPass = filterModel.device.beginRenderPass({\n id: 'data-filter-aggregation',\n framebuffer: filterFBO,\n parameters: {viewport},\n clearColor: [0, 0, 0, 0]\n });\n filterModel.setParameters(aggregator.parameters);\n filterModel.draw(renderPass);\n renderPass.end();\n\n const color = filterModel.device.readPixelsToArrayWebGL(filterFBO);\n let count = 0;\n for (let i = 0; i < color.length; i++) {\n count += color[i];\n }\n onFilteredItemsChange({id: this.id, count});\n\n this.state.filterNeedsUpdate = false;\n }\n }\n\n finalizeState(this: Layer<DataFilterExtensionProps>) {\n const filterFBO = this.state.filterFBO as Framebuffer;\n const filterModel = this.state.filterModel as Model;\n\n // filterFBO.color.delete();\n filterFBO?.destroy();\n filterModel?.destroy();\n }\n\n /**\n * Updates the bitmask used on the GPU to perform the filter based on the\n * `filterCategories` prop. The mapping between categories and bit in the bitmask\n * is performed by `_getCategoryKey()`\n */\n _updateCategoryBitMask(\n this: Layer<DataFilterExtensionProps>,\n params: any,\n extension: this\n ): void {\n const {categorySize} = extension.opts;\n if (!categorySize) return;\n const {filterCategories} = this.props;\n const categoryBitMask: CategoryBitMask = new Uint32Array([0, 0, 0, 0]);\n const categoryFilters = (\n categorySize === 1 ? [filterCategories] : filterCategories\n ) as FilterCategory[][];\n const maxCategories = categorySize === 1 ? 128 : categorySize === 2 ? 64 : 32;\n for (let c = 0; c < categoryFilters.length; c++) {\n const categoryFilter = categoryFilters[c];\n for (const category of categoryFilter) {\n const key = extension._getCategoryKey.call(this, category, c);\n if (key < maxCategories) {\n const channel = c * (maxCategories / 32) + Math.floor(key / 32);\n categoryBitMask[channel] += Math.pow(2, key % 32); // 1 << key fails for key > 30\n } else {\n log.warn(`Exceeded maximum number of categories (${maxCategories})`)();\n }\n }\n }\n this.state.categoryBitMask = categoryBitMask;\n }\n\n /**\n * Returns an index of bit in the bitmask for a given category. If the category has\n * not yet been assigned a bit, a new one is assigned.\n */\n _getCategoryKey(\n this: Layer<DataFilterExtensionProps>,\n category: FilterCategory,\n channel: number\n ) {\n const categoryMap = (this.state.categoryMap as Record<FilterCategory, number>[])[channel];\n if (!(category in categoryMap)) {\n categoryMap[category] = Object.keys(categoryMap).length;\n }\n return categoryMap[category];\n }\n}\n", "// deck.gl\n// SPDX-License-Identifier: MIT\n// Copyright (c) vis.gl contributors\n\nimport type {ShaderModule} from '@luma.gl/shadertools';\nimport type {DataFilterExtensionOptions, DataFilterExtensionProps} from './data-filter-extension';\nimport {UniformFormat} from '@luma.gl/shadertools/dist/types';\n\n/*\n * data filter shader module\n */\nexport type Defines = {\n // Defines passed externally\n /**\n * Primitive type of parameter used for category filtering. If undefined, category filtering disabled.\n */\n DATACATEGORY_TYPE?: 'uint' | 'uvec2' | 'uvec3' | 'uvec4';\n /**\n * Number of category filtering channels. Must match dimension of `DATACATEGORY_TYPE`\n */\n DATACATEGORY_CHANNELS?: 1 | 2 | 3 | 4;\n\n /**\n * Primitive type of parameter used for numeric filtering. If undefined, numeric filtering disabled.\n */\n DATAFILTER_TYPE?: 'float' | 'vec2' | 'vec3' | 'vec4';\n\n /**\n * Enable 64-bit precision in numeric filter.\n */\n DATAFILTER_DOUBLE?: boolean;\n};\n\nconst uniformBlock = /* glsl */ `\\\nuniform dataFilterUniforms {\n bool useSoftMargin;\n bool enabled;\n bool transformSize;\n bool transformColor;\n#ifdef DATAFILTER_TYPE\n DATAFILTER_TYPE min;\n DATAFILTER_TYPE softMin;\n DATAFILTER_TYPE softMax;\n DATAFILTER_TYPE max;\n#ifdef DATAFILTER_DOUBLE\n DATAFILTER_TYPE min64High;\n DATAFILTER_TYPE max64High;\n#endif\n#endif\n#ifdef DATACATEGORY_TYPE\n highp uvec4 categoryBitMask;\n#endif\n} dataFilter;\n`;\n\nconst vertex = /* glsl */ `\n#ifdef DATAFILTER_TYPE\n in DATAFILTER_TYPE filterValues;\n#ifdef DATAFILTER_DOUBLE\n in DATAFILTER_TYPE filterValues64Low;\n#endif\n#endif\n\n#ifdef DATACATEGORY_TYPE\n in DATACATEGORY_TYPE filterCategoryValues;\n#endif\n\nout float dataFilter_value;\n\nfloat dataFilter_reduceValue(float value) {\n return value;\n}\nfloat dataFilter_reduceValue(vec2 value) {\n return min(value.x, value.y);\n}\nfloat dataFilter_reduceValue(vec3 value) {\n return min(min(value.x, value.y), value.z);\n}\nfloat dataFilter_reduceValue(vec4 value) {\n return min(min(value.x, value.y), min(value.z, value.w));\n}\n\n#ifdef DATAFILTER_TYPE\n void dataFilter_setValue(DATAFILTER_TYPE valueFromMin, DATAFILTER_TYPE valueFromMax) {\n if (dataFilter.useSoftMargin) {\n // smoothstep results are undefined if edge0 \u2265 edge1\n // Fallback to ignore filterSoftRange if it is truncated by filterRange\n DATAFILTER_TYPE leftInRange = mix(\n smoothstep(dataFilter.min, dataFilter.softMin, valueFromMin),\n step(dataFilter.min, valueFromMin),\n step(dataFilter.softMin, dataFilter.min)\n );\n DATAFILTER_TYPE rightInRange = mix(\n 1.0 - smoothstep(dataFilter.softMax, dataFilter.max, valueFromMax),\n step(valueFromMax, dataFilter.max),\n step(dataFilter.max, dataFilter.softMax)\n );\n dataFilter_value = dataFilter_reduceValue(leftInRange * rightInRange);\n } else {\n dataFilter_value = dataFilter_reduceValue(\n step(dataFilter.min, valueFromMin) * step(valueFromMax, dataFilter.max)\n );\n }\n }\n#endif\n\n#ifdef DATACATEGORY_TYPE\n void dataFilter_setCategoryValue(DATACATEGORY_TYPE category) {\n #if DATACATEGORY_CHANNELS == 1 // One 128-bit mask\n uint dataFilter_masks = dataFilter.categoryBitMask[category / 32u];\n #elif DATACATEGORY_CHANNELS == 2 // Two 64-bit masks\n uvec2 dataFilter_masks = uvec2(\n dataFilter.categoryBitMask[category.x / 32u],\n dataFilter.categoryBitMask[category.y / 32u + 2u]\n );\n #elif DATACATEGORY_CHANNELS == 3 // Three 32-bit masks\n uvec3 dataFilter_masks = dataFilter.categoryBitMask.xyz;\n #else // Four 32-bit masks\n uvec4 dataFilter_masks = dataFilter.categoryBitMask;\n #endif\n\n // Shift mask and extract relevant bits\n DATACATEGORY_TYPE dataFilter_bits = DATACATEGORY_TYPE(dataFilter_masks) >> (category & 31u);\n dataFilter_bits &= 1u;\n\n #if DATACATEGORY_CHANNELS == 1\n if(dataFilter_bits == 0u) dataFilter_value = 0.0;\n #else\n if(any(equal(dataFilter_bits, DATACATEGORY_TYPE(0u)))) dataFilter_value = 0.0;\n #endif\n }\n#endif\n`;\n\nconst vs = `\n${uniformBlock}\n${vertex}\n`;\n\nconst fragment = /* glsl */ `\nin float dataFilter_value;\n`;\n\nconst fs = `\n${uniformBlock}\n${fragment}\n`;\n\nexport type CategoryBitMask = Uint32Array;\nexport type DataFilterModuleProps = {\n extensions: any[]; // used to detect if layer props are present\n categoryBitMask?: CategoryBitMask;\n} & DataFilterExtensionProps;\n\n/* eslint-disable camelcase */\nfunction getUniforms(opts?: DataFilterModuleProps | {}): Record<string, any> {\n if (!opts || !('extensions' in opts)) {\n return {};\n }\n const {\n filterRange = [-1, 1],\n filterEnabled = true,\n filterTransformSize = true,\n filterTransformColor = true,\n categoryBitMask\n } = opts;\n const filterSoftRange = opts.filterSoftRange || filterRange;\n\n return {\n ...(Number.isFinite(filterRange[0])\n ? {\n min: filterRange[0],\n softMin: filterSoftRange[0],\n softMax: filterSoftRange[1],\n max: filterRange[1]\n }\n : {\n min: filterRange.map(r => r[0]),\n softMin: filterSoftRange.map(r => r[0]),\n softMax: filterSoftRange.map(r => r[1]),\n max: filterRange.map(r => r[1])\n }),\n enabled: filterEnabled,\n useSoftMargin: Boolean(opts.filterSoftRange),\n transformSize: filterEnabled && filterTransformSize,\n transformColor: filterEnabled && filterTransformColor,\n ...(categoryBitMask && {categoryBitMask})\n };\n}\n\nfunction getUniforms64(opts?: DataFilterModuleProps | {}): Record<string, any> {\n if (!opts || !('extensions' in opts)) {\n return {};\n }\n const uniforms = getUniforms(opts);\n if (Number.isFinite(uniforms.min)) {\n const min64High = Math.fround(uniforms.min);\n uniforms.min -= min64High;\n uniforms.softMin -= min64High;\n uniforms.min64High = min64High;\n\n const max64High = Math.fround(uniforms.max);\n uniforms.max -= max64High;\n uniforms.softMax -= max64High;\n uniforms.max64High = max64High;\n } else {\n const min64High = uniforms.min.map(Math.fround);\n uniforms.min = uniforms.min.map((x, i) => x - min64High[i]);\n uniforms.softMin = uniforms.softMin.map((x, i) => x - min64High[i]);\n uniforms.min64High = min64High;\n\n const max64High = uniforms.max.map(Math.fround);\n uniforms.max = uniforms.max.map((x, i) => x - max64High[i]);\n uniforms.softMax = uniforms.softMax.map((x, i) => x - max64High[i]);\n uniforms.max64High = max64High;\n }\n return uniforms;\n}\n\nconst inject = {\n 'vs:#main-start': /* glsl */ `\n dataFilter_value = 1.0;\n if (dataFilter.enabled) {\n #ifdef DATAFILTER_TYPE\n #ifdef DATAFILTER_DOUBLE\n dataFilter_setValue(\n filterValues - dataFilter.min64High + filterValues64Low,\n filterValues - dataFilter.max64High + filterValues64Low\n );\n #else\n dataFilter_setValue(filterValues, filterValues);\n #endif\n #endif\n\n #ifdef DATACATEGORY_TYPE\n dataFilter_setCategoryValue(filterCategoryValues);\n #endif\n }\n `,\n\n 'vs:#main-end': /* glsl */ `\n if (dataFilter_value == 0.0) {\n gl_Position = vec4(0.);\n }\n `,\n\n 'vs:DECKGL_FILTER_SIZE': /* glsl */ `\n if (dataFilter.transformSize) {\n size = size * dataFilter_value;\n }\n `,\n\n 'fs:DECKGL_FILTER_COLOR': /* glsl */ `\n if (dataFilter_value == 0.0) discard;\n if (dataFilter.transformColor) {\n color.a *= dataFilter_value;\n }\n `\n};\n\ntype UniformTypesFunc = (opts: DataFilterExtensionOptions) => any;\nfunction uniformTypesFromOptions(opts: DataFilterExtensionOptions): any {\n const {categorySize, filterSize, fp64} = opts;\n const uniformTypes: Record<string, UniformFormat> = {\n useSoftMargin: 'i32',\n enabled: 'i32',\n transformSize: 'i32',\n transformColor: 'i32'\n };\n\n if (filterSize) {\n const uniformFormat: UniformFormat = filterSize === 1 ? 'f32' : `vec${filterSize}<f32>`;\n uniformTypes.min = uniformFormat;\n uniformTypes.softMin = uniformFormat;\n uniformTypes.softMax = uniformFormat;\n uniformTypes.max = uniformFormat;\n if (fp64) {\n uniformTypes.min64High = uniformFormat;\n uniformTypes.max64High = uniformFormat;\n }\n }\n\n if (categorySize) {\n uniformTypes.categoryBitMask = 'vec4<i32>';\n }\n\n return uniformTypes;\n}\n\nexport const dataFilter: ShaderModule<DataFilterModuleProps> & {\n uniformTypesFromOptions: UniformTypesFunc;\n} = {\n name: 'dataFilter',\n vs,\n fs,\n inject,\n getUniforms,\n uniformTypesFromOptions\n};\n\nexport const dataFilter64: ShaderModule<DataFilterModuleProps> & {\n uniformTypesFromOptions: UniformTypesFunc;\n} = {\n name: 'dataFilter',\n vs,\n fs,\n inject,\n getUniforms: getUniforms64,\n uniformTypesFromOptions\n};\n", "// deck.gl\n// SPDX-License-Identifier: MIT\n// Copyright (c) vis.gl contributors\n\nimport {Device, DeviceFeature, Framebuffer, RenderPipelineParameters} from '@luma.gl/core';\nimport {Model, ModelProps} from '@luma.gl/engine';\n\nconst AGGREGATE_VS = `\\\n#version 300 es\n#define SHADER_NAME data-filter-vertex-shader\n\n#ifdef FLOAT_TARGET\n in float filterIndices;\n in float filterPrevIndices;\n#else\n in vec2 filterIndices;\n in vec2 filterPrevIndices;\n#endif\n\nout vec4 vColor;\nconst float component = 1.0 / 255.0;\n\nvoid main() {\n #ifdef FLOAT_TARGET\n dataFilter_value *= float(filterIndices != filterPrevIndices);\n gl_Position = vec4(0.0, 0.0, 0.0, 1.0);\n vColor = vec4(0.0, 0.0, 0.0, 1.0);\n #else\n // Float texture is not supported: pack result into 4 channels x 256 px x 64px\n dataFilter_value *= float(filterIndices.x != filterPrevIndices.x);\n float col = filterIndices.x;\n float row = filterIndices.y * 4.0;\n float channel = floor(row);\n row = fract(row);\n vColor = component * vec4(bvec4(channel == 0.0, channel == 1.0, channel == 2.0, channel == 3.0));\n gl_Position = vec4(col * 2.0 - 1.0, row * 2.0 - 1.0, 0.0, 1.0);\n #endif\n gl_PointSize = 1.0;\n}\n`;\n\nconst AGGREGATE_FS = `\\\n#version 300 es\n#define SHADER_NAME data-filter-fragment-shader\nprecision highp float;\n\nin vec4 vColor;\n\nout vec4 fragColor;\n\nvoid main() {\n if (dataFilter_value < 0.5) {\n discard;\n }\n fragColor = vColor;\n}\n`;\n\nconst FLOAT_TARGET_FEATURES: DeviceFeature[] = [\n 'float32-renderable-webgl', // ability to render to float texture\n 'texture-blend-float-webgl' // ability to blend when rendering to float texture\n];\n\nexport function supportsFloatTarget(device: Device): boolean {\n return FLOAT_TARGET_FEATURES.every(feature => device.features.has(feature));\n}\n\n// A 1x1 framebuffer object that encodes the total count of filtered items\nexport function getFramebuffer(device: Device, useFloatTarget: boolean): Framebuffer {\n if (useFloatTarget) {\n return device.createFramebuffer({\n width: 1,\n height: 1,\n colorAttachments: [\n device.createTexture({\n format: 'rgba32float',\n mipmaps: false\n })\n ]\n });\n }\n return device.createFramebuffer({\n width: 256,\n height: 64,\n colorAttachments: [device.createTexture({format: 'rgba8unorm', mipmaps: false})]\n });\n}\n\n// Increments the counter based on dataFilter_value\nexport function getModel(\n device: Device,\n bufferLayout: ModelProps['bufferLayout'],\n shaderOptions: any,\n useFloatTarget: boolean\n): Model {\n shaderOptions.defines.NON_INSTANCED_MODEL = 1;\n if (useFloatTarget) {\n shaderOptions.defines.FLOAT_TARGET = 1;\n }\n\n return new Model(device, {\n id: 'data-filter-aggregation-model',\n vertexCount: 1,\n isInstanced: false,\n topology: 'point-list',\n disableWarnings: true,\n vs: AGGREGATE_VS,\n fs: AGGREGATE_FS,\n bufferLayout,\n ...shaderOptions\n });\n}\n\nexport const parameters: RenderPipelineParameters = {\n blend: true,\n blendColorSrcFactor: 'one',\n blendColorDstFactor: 'one',\n blendAlphaSrcFactor: 'one',\n blendAlphaDstFactor: 'one',\n blendColorOperation: 'add',\n blendAlphaOperation: 'add',\n depthCompare: 'never'\n} as const;\n", "// deck.gl\n// SPDX-License-Identifier: MIT\n// Copyright (c) vis.gl contributors\n\nimport {LayerExtension, COORDINATE_SYSTEM} from '@deck.gl/core';\nimport project64 from './project64';\n\nimport type {Layer} from '@deck.gl/core';\n\n/** @deprecated Adds the legacy 64-bit precision to geospatial layers. */\nexport default class Fp64Extension extends LayerExtension {\n static extensionName = 'Fp64Extension';\n\n getShaders(this: Layer): any {\n const {coordinateSystem} = this.props;\n if (\n coordinateSystem !== COORDINATE_SYSTEM.LNGLAT &&\n coordinateSystem !== COORDINATE_SYSTEM.DEFAULT\n ) {\n throw new Error('fp64: coordinateSystem must be LNGLAT');\n }\n\n return {\n modules: [project64]\n };\n }\n\n draw(this: Layer, params: any, extension: this): void {\n const {viewport} = params.context;\n this.setShaderModuleProps({project64: {viewport}});\n }\n}\n", "// deck.gl\n// SPDX-License-Identifier: MIT\n// Copyright (c) vis.gl contributors\n\n/* eslint-disable camelcase */\nimport {fp64} from '@luma.gl/shadertools';\nimport type {ShaderModule} from '@luma.gl/shadertools';\nconst {fp64ify, fp64ifyMatrix4} = fp64;\nimport {project, _memoize as memoize} from '@deck.gl/core';\n\nimport type {Viewport} from '@deck.gl/core';\nimport project64Shader from './project64.glsl';\n\ntype Project64ModuleProps = {\n viewport: Viewport;\n};\n\nexport default {\n name: 'project64',\n dependencies: [project, fp64],\n vs: project64Shader,\n getUniforms,\n uniformTypes: {\n scale: 'vec2<f32>',\n // Cannot pass as vec2[16], so instead split into 2 mat4x4\n viewProjectionMatrix: 'mat4x4<f32>',\n viewProjectionMatrix64Low: 'mat4x4<f32>'\n }\n} as ShaderModule<Project64ModuleProps>;\n\n// TODO - this module should calculate the 64 bit uniforms\n// It is currently done by project to minimize duplicated work\n\nconst getMemoizedUniforms = memoize(calculateUniforms);\n\nfunction getUniforms(opts?: Project64ModuleProps | {}): Record<string, any> {\n if (opts && 'viewport' in opts) {\n const {viewProjectionMatrix, scale} = opts.viewport;\n // We only need to update fp64 uniforms if fp32 projection is being updated\n return getMemoizedUniforms({viewProjectionMatrix, scale});\n }\n return {};\n}\n\nfunction calculateUniforms({\n viewProjectionMatrix,\n scale\n}: {\n viewProjectionMatrix: number[];\n scale: number;\n}) {\n const glViewProjectionMatrixFP64 = fp64ifyMatrix4(viewProjectionMatrix);\n const viewProjectionMatrix64High = new Float32Array(16);\n const viewProjectionMatrix64Low = new Float32Array(16);\n for (let i = 0; i < 4; i++) {\n for (let j = 0; j < 4; j++) {\n // Match order used in project.viewProjectionMatrix\n const from = 4 * i + j;\n const to = 4 * j + i;\n viewProjectionMatrix64High[to] = glViewProjectionMatrixFP64[2 * from];\n viewProjectionMatrix64Low[to] = glViewProjectionMatrixFP64[2 * from + 1];\n }\n }\n return {\n scale: fp64ify(scale),\n viewProjectionMatrix: [...viewProjectionMatrix64High],\n viewProjectionMatrix64Low: [...viewProjectionMatrix64Low]\n };\n}\n", "// deck.gl\n// SPDX-License-Identifier: MIT\n// Copyright (c) vis.gl contributors\n\nexport default `\\\n\nconst vec2 WORLD_SCALE_FP64 = vec2(81.4873275756836, 0.0000032873668232014097);\n\nuniform project64Uniforms {\n vec2 scale;\n mat4 viewProjectionMatrix;\n mat4 viewProjectionMatrix64Low;\n} project64;\n\n// longitude: lnglat_fp64.xy; latitude: lnglat_fp64.zw\nvoid mercatorProject_fp64(vec4 lnglat_fp64, out vec2 out_val[2]) {\n\n#if defined(NVIDIA_FP64_WORKAROUND)\n out_val[0] = sum_fp64(radians_fp64(lnglat_fp64.xy), PI_FP64 * ONE);\n#else\n out_val[0] = sum_fp64(radians_fp64(lnglat_fp64.xy), PI_FP64);\n#endif\n out_val[1] = sum_fp64(PI_FP64,\n log_fp64(tan_fp64(sum_fp64(PI_4_FP64, radians_fp64(lnglat_fp64.zw) / 2.0))));\n return;\n}\n\nvoid project_position_fp64(vec4 position_fp64, out vec2 out_val[2]) {\n vec2 pos_fp64[2];\n mercatorProject_fp64(position_fp64, pos_fp64);\n out_val[0] = mul_fp64(pos_fp64[0], WORLD_SCALE_FP64);\n out_val[1] = mul_fp64(pos_fp64[1], WORLD_SCALE_FP64);\n\n return;\n}\n\nvoid project_position_fp64(vec2 position, vec2 position64xyLow, out vec2 out_val[2]) {\n vec4 position64xy = vec4(\n position.x, position64xyLow.x,\n position.y, position64xyLow.y);\n\n project_position_fp64(position64xy, out_val);\n}\n\nvec4 project_common_position_to_clipspace_fp64(vec2 vertex_pos_modelspace[4]) {\n vec2 vertex_pos_clipspace[4];\n vec2 viewProjectionMatrixFP64[16];\n for (int i = 0; i < 4; i++) {\n for (int j = 0; j < 4; j++) {\n viewProjectionMatrixFP64[4 * i + j] = vec2(\n project64.viewProjectionMatrix[j][i],\n project64.viewProjectionMatrix64Low[j][i]\n );\n } \n }\n mat4_vec4_mul_fp64(viewProjectionMatrixFP64, vertex_pos_modelspace,\n vertex_pos_clipspace);\n return vec4(\n vertex_pos_clipspace[0].x,\n vertex_pos_clipspace[1].x,\n vertex_pos_clipspace[2].x,\n vertex_pos_clipspace[3].x\n );\n}\n\nvec4 project_position_to_clipspace(\n vec3 position, vec3 position64xyLow, vec3 offset, out vec4 commonPosition\n) {\n // This is the local offset to the instance position\n vec2 offset64[4];\n vec4_fp64(vec4(offset, 0.0), offset64);\n\n float z = project_size(position.z);\n\n // Apply web mercator projection (depends on coordinate system imn use)\n vec2 projectedPosition64xy[2];\n project_position_fp64(position.xy, position64xyLow.xy, projectedPosition64xy);\n\n vec2 commonPosition64[4];\n commonPosition64[0] = sum_fp64(offset64[0], projectedPosition64xy[0]);\n commonPosition64[1] = sum_fp64(offset64[1], projectedPosition64xy[1]);\n commonPosition64[2] = sum_fp64(offset64[2], vec2(z, 0.0));\n commonPosition64[3] = vec2(1.0, 0.0);\n\n commonPosition = vec4(projectedPosition64xy[0].x, projectedPosition64xy[1].x, z, 1.0);\n\n return project_common_position_to_clipspace_fp64(commonPosition64);\n}\n\nvec4 project_position_to_clipspace(\n vec3 position, vec3 position64xyLow, vec3 offset\n) {\n vec4 commonPosition;\n return project_position_to_clipspace(\n position, position64xyLow, offset, commonPosition\n );\n}\n`;\n", "// deck.gl\n// SPDX-License-Identifier: MIT\n// Copyright (c) vis.gl contributors\n\nimport {LayerExtension, _mergeShaders as mergeShaders} from '@deck.gl/core';\nimport {vec3} from '@math.gl/core';\nimport {dashShaders, offsetShaders} from './shaders.glsl';\n\nimport type {Layer, LayerContext, Accessor, UpdateParameters} from '@deck.gl/core';\nimport type {ShaderModule} from '@luma.gl/shadertools';\n\nconst defaultProps = {\n getDashArray: {type: 'accessor', value: [0, 0]},\n getOffset: {type: 'accessor', value: 0},\n dashJustified: false,\n dashGapPickable: false\n};\n\ntype PathStyleProps = {\n dashAlignMode: number;\n dashGapPickable: boolean;\n};\n\nexport type PathStyleExtensionProps<DataT = any> = {\n /**\n * Accessor for the dash array to draw each path with: `[dashSize, gapSize]` relative to the width of the path.\n * Requires the `dash` option to be on.\n */\n getDashArray?: Accessor<DataT, [number, number]>;\n /**\n * Accessor for the offset to draw each path with, relative to the width of the path.\n * Negative offset is to the left hand side, and positive offset is to the right hand side.\n * @default 0\n */\n getOffset?: Accessor<DataT, number>;\n /**\n * If `true`, adjust gaps for the dashes to align at both ends.\n * @default false\n */\n dashJustified?: boolean;\n /**\n * If `true`, gaps between solid strokes are pickable. If `false`, only the solid strokes are pickable.\n * @default false\n */\n dashGapPickable?: boolean;\n};\n\nexport type PathStyleExtensionOptions = {\n /**\n * Add capability to render dashed lines.\n * @default false\n */\n dash: boolean;\n /**\n * Add capability to offset lines.\n * @default false\n */\n offset: boolean;\n /**\n * Improve dash rendering quality in certain circumstances. Note that this option introduces additional performance overhead.\n * @default false\n */\n highPrecisionDash: boolean;\n};\n\n/** Adds selected features to the `PathLayer` and composite layers that render the `PathLayer`. */\nexport default class PathStyleExtension extends LayerExtension<PathStyleExtensionOptions> {\n static defaultProps = defaultProps;\n static extensionName = 'PathStyleExtension';\n\n constructor({\n dash = false,\n offset = false,\n highPrecisionDash = false\n }: Partial<PathStyleExtensionOptions> = {}) {\n super({dash: dash || highPrecisionDash, offset, highPrecisionDash});\n }\n\n isEnabled(layer: Layer<PathStyleExtensionProps>): boolean {\n return 'pathTesselator' in layer.state;\n }\n\n getShaders(this: Layer<PathStyleExtensionProps>, extension: this): any {\n if (!extension.isEnabled(this)) {\n return null;\n }\n\n // Merge shader injection\n let result = {} as {inject: Record<string, string>};\n if (extension.opts.dash) {\n result = mergeShaders(result, dashShaders);\n }\n if (extension.opts.offset) {\n result = mergeShaders(result, offsetShaders);\n }\n\n const {inject} = result;\n const pathStyle: ShaderModule<PathStyleProps> = {\n name: 'pathStyle',\n inject,\n uniformTypes: {\n dashAlignMode: 'f32',\n dashGapPickable: 'i32'\n }\n };\n return {\n modules: [pathStyle]\n };\n }\n\n initializeState(this: Layer<PathStyleExtensionProps>, context: LayerContext, extension: this) {\n const attributeManager = this.getAttributeManager();\n if (!attributeManager || !extension.isEnabled(this)) {\n // This extension only works with the PathLayer\n return;\n }\n\n if (extension.opts.dash) {\n attributeManager.addInstanced({\n instanceDashArrays: {size: 2, accessor: 'getDashArray'},\n instanceDashOffsets: extension.opts.highPrecisionDash\n ? {\n size: 1,\n accessor: 'getPath',\n transform: extension.getDashOffsets.bind(this)\n }\n : {\n size: 1,\n update: attribute => {\n attribute.constant = true;\n attribute.value = [0];\n }\n }\n });\n }\n if (extension.opts.offset) {\n attributeManager.addInstanced({\n instanceOffsets: {size: 1, accessor: 'getOffset'}\n });\n }\n }\n\n updateState(\n this: Layer<PathStyleExtensionProps>,\n params: UpdateParameters<Layer<PathStyleExtensionProps>>,\n extension: this\n ) {\n if (!extension.isEnabled(this)) {\n return;\n }\n\n if (extension.opts.dash) {\n const pathStyleProps: PathStyleProps = {\n dashAlignMode: this.props.dashJustified ? 1 : 0,\n dashGapPickable: Boolean(this.props.dashGapPickable)\n };\n this.setShaderModuleProps({pathStyle: pathStyleProps});\n }\n }\n\n getDashOffsets(this: Layer<PathStyleExtensionProps>, path: number[] | number[][]): number[] {\n const result = [0];\n const positionSize = this.props.positionFormat === 'XY' ? 2 : 3;\n const isNested = Array.isArray(path[0]);\n const geometrySize = isNested ? path.length : path.length / positionSize;\n\n let p;\n let prevP;\n for (let i = 0; i < geometrySize - 1; i++) {\n p = isNested ? path[i] : path.slice(i * positionSize, i * positionSize + positionSize);\n p = this.projectPosition(p);\n\n if (i > 0) {\n result[i] = result[i - 1] + vec3.dist(prevP, p);\n }\n\n prevP = p;\n }\n result[geometrySize - 1] = 0;\n return result;\n }\n}\n", "// deck.gl\n// SPDX-License-Identifier: MIT\n// Copyright (c) vis.gl contributors\n\nexport const dashShaders = {\n inject: {\n 'vs:#decl': `\nin vec2 instanceDashArrays;\nin float instanceDashOffsets;\nout vec2 vDashArray;\nout float vDashOffset;\n`,\n\n 'vs:#main-end': `\nvDashArray = instanceDashArrays;\nvDashOffset = instanceDashOffsets / width.x;\n`,\n\n 'fs:#decl': `\nuniform pathStyleUniforms {\n float dashAlignMode;\n bool dashGapPickable;\n} pathStyle;\n\nin vec2 vDashArray;\nin float vDashOffset;\n`,\n\n // if given position is in the gap part of the dashed line\n // dashArray.x: solid stroke length, relative to width\n // dashArray.y: gap length, relative to width\n // alignMode:\n // 0 - no adjustment\n // o---- ---- ---- ---- o---- -o---- ---- o\n // 1 - stretch to fit, draw half dash at each end for nicer joints\n // o-- ---- ---- ---- --o-- --o-- ---- --o\n 'fs:#main-start': `\n float solidLength = vDashArray.x;\n float gapLength = vDashArray.y;\n float unitLength = solidLength + gapLength;\n\n float offset;\n\n if (unitLength > 0.0) {\n if (pathStyle.dashAlignMode == 0.0) {\n offset = vDashOffset;\n } else {\n unitLength = vPathLength / round(vPathLength / unitLength);\n offset = solidLength / 2.0;\n }\n\n float unitOffset = mod(vPathPosition.y + offset, unitLength);\n\n if (gapLength > 0.0 && unitOffset > solidLength) {\n if (path.capType <= 0.5) {\n if (!(pathStyle.dashGapPickable && bool(picking.isActive))) {\n discard;\n }\n } else {\n