UNPKG

@deck.gl/extensions

Version:

Plug-and-play functionalities for deck.gl layers

1,661 lines (1,595 loc) 191 kB
(function webpackUniversalModuleDefinition(root, factory) { if (typeof exports === 'object' && typeof module === 'object') module.exports = factory(); else if (typeof define === 'function' && define.amd) define([], factory); else if (typeof exports === 'object') exports['deck'] = factory(); else root['deck'] = factory();})(globalThis, function () { "use strict"; var __exports__ = (() => { var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __commonJS = (cb, mod) => function __require() { return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports; }; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "default"), secondTarget && __copyProps(secondTarget, mod, "default")); var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // external-global-plugin:@deck.gl/core var require_core = __commonJS({ "external-global-plugin:@deck.gl/core"(exports, module) { module.exports = globalThis.deck; } }); // external-global-plugin:@luma.gl/engine var require_engine = __commonJS({ "external-global-plugin:@luma.gl/engine"(exports, module) { module.exports = globalThis.luma; } }); // bundle.ts var bundle_exports = {}; __export(bundle_exports, { BrushingExtension: () => BrushingExtension, ClipExtension: () => ClipExtension, CollisionFilterExtension: () => CollisionFilterExtension, DataFilterExtension: () => DataFilterExtension, FillStyleExtension: () => FillStyleExtension, Fp64Extension: () => Fp64Extension, MaskExtension: () => MaskExtension, PathStyleExtension: () => PathStyleExtension, _TerrainExtension: () => TerrainExtension, project64: () => project64_default }); // ../core/bundle/peer-dependency.ts var peer_dependency_exports = {}; var import_core = __toESM(require_core(), 1); __reExport(peer_dependency_exports, __toESM(require_core(), 1)); if (!import_core.Layer) { throw new Error("@deck.gl/core is not found"); } // bundle.ts __reExport(bundle_exports, peer_dependency_exports); // src/brushing/brushing-extension.ts var import_core3 = __toESM(require_core(), 1); // src/brushing/shader-module.ts var import_core2 = __toESM(require_core(), 1); var uniformBlock = ( /* glsl */ `uniform brushingUniforms { bool enabled; highp int target; vec2 mousePos; float radius; } brushing; ` ); var vertex = ( /* glsl */ ` in vec2 brushingTargets; out float brushing_isVisible; bool brushing_isPointInRange(vec2 position) { if (!brushing.enabled) { return true; } vec2 source_commonspace = project_position(position); vec2 target_commonspace = project_position(brushing.mousePos); float distance = length((target_commonspace - source_commonspace) / project.commonUnitsPerMeter.xy); return distance <= brushing.radius; } bool brushing_arePointsInRange(vec2 sourcePos, vec2 targetPos) { return brushing_isPointInRange(sourcePos) || brushing_isPointInRange(targetPos); } void brushing_setVisible(bool visible) { brushing_isVisible = float(visible); } ` ); var vs = ` ${uniformBlock} ${vertex} `; var fragment = ( /* glsl */ ` in float brushing_isVisible; ` ); var fs = ` ${uniformBlock} ${fragment} `; var TARGET = { source: 0, target: 1, custom: 2, source_target: 3 }; var inject = { "vs:DECKGL_FILTER_GL_POSITION": ( /* glsl */ ` vec2 brushingTarget; vec2 brushingSource; if (brushing.target == 3) { brushingTarget = geometry.worldPositionAlt.xy; brushingSource = geometry.worldPosition.xy; } else if (brushing.target == 0) { brushingTarget = geometry.worldPosition.xy; } else if (brushing.target == 1) { brushingTarget = geometry.worldPositionAlt.xy; } else { brushingTarget = brushingTargets; } bool visible; if (brushing.target == 3) { visible = brushing_arePointsInRange(brushingSource, brushingTarget); } else { visible = brushing_isPointInRange(brushingTarget); } brushing_setVisible(visible); ` ), "fs:DECKGL_FILTER_COLOR": ` if (brushing.enabled && brushing_isVisible < 0.5) { discard; } ` }; var shader_module_default = { name: "brushing", dependencies: [import_core2.project], vs, fs, inject, getUniforms: (opts) => { if (!opts || !("viewport" in opts)) { return {}; } const { brushingEnabled = true, brushingRadius = 1e4, brushingTarget = "source", mousePosition, viewport } = opts; return { enabled: Boolean(brushingEnabled && mousePosition && viewport.containsPixel(mousePosition)), radius: brushingRadius, target: TARGET[brushingTarget] || 0, mousePos: mousePosition ? viewport.unproject([mousePosition.x - viewport.x, mousePosition.y - viewport.y]) : [0, 0] }; }, uniformTypes: { enabled: "i32", target: "i32", mousePos: "vec2<f32>", radius: "f32" } }; // src/brushing/brushing-extension.ts var defaultProps = { getBrushingTarget: { type: "accessor", value: [0, 0] }, brushingTarget: "source", brushingEnabled: true, brushingRadius: 1e4 }; var BrushingExtension = class extends import_core3.LayerExtension { getShaders() { return { modules: [shader_module_default] }; } initializeState(context, extension) { const attributeManager = this.getAttributeManager(); if (attributeManager) { attributeManager.add({ brushingTargets: { size: 2, stepMode: "dynamic", accessor: "getBrushingTarget" } }); } const onMouseMove = () => { this.getCurrentLayer()?.setNeedsRedraw(); }; this.state.onMouseMove = onMouseMove; if (context.deck) { context.deck.eventManager.on({ pointermove: onMouseMove, pointerleave: onMouseMove }); } } finalizeState(context, extension) { if (context.deck) { const onMouseMove = this.state.onMouseMove; context.deck.eventManager.off({ pointermove: onMouseMove, pointerleave: onMouseMove }); } } draw(params, extension) { const { viewport, mousePosition } = params.context; const { brushingEnabled, brushingRadius, brushingTarget } = this.props; const brushingProps = { viewport, mousePosition, brushingEnabled, brushingRadius, brushingTarget }; this.setShaderModuleProps({ brushing: brushingProps }); } }; BrushingExtension.defaultProps = defaultProps; BrushingExtension.extensionName = "BrushingExtension"; // src/data-filter/data-filter-extension.ts var import_core4 = __toESM(require_core(), 1); // src/data-filter/shader-module.ts var uniformBlock2 = ( /* glsl */ `uniform dataFilterUniforms { bool useSoftMargin; bool enabled; bool transformSize; bool transformColor; #ifdef DATAFILTER_TYPE DATAFILTER_TYPE min; DATAFILTER_TYPE softMin; DATAFILTER_TYPE softMax; DATAFILTER_TYPE max; #ifdef DATAFILTER_DOUBLE DATAFILTER_TYPE min64High; DATAFILTER_TYPE max64High; #endif #endif #ifdef DATACATEGORY_TYPE highp uvec4 categoryBitMask; #endif } dataFilter; ` ); var vertex2 = ( /* glsl */ ` #ifdef DATAFILTER_TYPE in DATAFILTER_TYPE filterValues; #ifdef DATAFILTER_DOUBLE in DATAFILTER_TYPE filterValues64Low; #endif #endif #ifdef DATACATEGORY_TYPE in DATACATEGORY_TYPE filterCategoryValues; #endif out float dataFilter_value; float dataFilter_reduceValue(float value) { return value; } float dataFilter_reduceValue(vec2 value) { return min(value.x, value.y); } float dataFilter_reduceValue(vec3 value) { return min(min(value.x, value.y), value.z); } float dataFilter_reduceValue(vec4 value) { return min(min(value.x, value.y), min(value.z, value.w)); } #ifdef DATAFILTER_TYPE void dataFilter_setValue(DATAFILTER_TYPE valueFromMin, DATAFILTER_TYPE valueFromMax) { if (dataFilter.useSoftMargin) { // smoothstep results are undefined if edge0 \u2265 edge1 // Fallback to ignore filterSoftRange if it is truncated by filterRange DATAFILTER_TYPE leftInRange = mix( smoothstep(dataFilter.min, dataFilter.softMin, valueFromMin), step(dataFilter.min, valueFromMin), step(dataFilter.softMin, dataFilter.min) ); DATAFILTER_TYPE rightInRange = mix( 1.0 - smoothstep(dataFilter.softMax, dataFilter.max, valueFromMax), step(valueFromMax, dataFilter.max), step(dataFilter.max, dataFilter.softMax) ); dataFilter_value = dataFilter_reduceValue(leftInRange * rightInRange); } else { dataFilter_value = dataFilter_reduceValue( step(dataFilter.min, valueFromMin) * step(valueFromMax, dataFilter.max) ); } } #endif #ifdef DATACATEGORY_TYPE void dataFilter_setCategoryValue(DATACATEGORY_TYPE category) { #if DATACATEGORY_CHANNELS == 1 // One 128-bit mask uint dataFilter_masks = dataFilter.categoryBitMask[category / 32u]; #elif DATACATEGORY_CHANNELS == 2 // Two 64-bit masks uvec2 dataFilter_masks = uvec2( dataFilter.categoryBitMask[category.x / 32u], dataFilter.categoryBitMask[category.y / 32u + 2u] ); #elif DATACATEGORY_CHANNELS == 3 // Three 32-bit masks uvec3 dataFilter_masks = dataFilter.categoryBitMask.xyz; #else // Four 32-bit masks uvec4 dataFilter_masks = dataFilter.categoryBitMask; #endif // Shift mask and extract relevant bits DATACATEGORY_TYPE dataFilter_bits = DATACATEGORY_TYPE(dataFilter_masks) >> (category & 31u); dataFilter_bits &= 1u; #if DATACATEGORY_CHANNELS == 1 if(dataFilter_bits == 0u) dataFilter_value = 0.0; #else if(any(equal(dataFilter_bits, DATACATEGORY_TYPE(0u)))) dataFilter_value = 0.0; #endif } #endif ` ); var vs2 = ` ${uniformBlock2} ${vertex2} `; var fragment2 = ( /* glsl */ ` in float dataFilter_value; ` ); var fs2 = ` ${uniformBlock2} ${fragment2} `; function getUniforms(opts) { if (!opts || !("extensions" in opts)) { return {}; } const { filterRange = [-1, 1], filterEnabled = true, filterTransformSize = true, filterTransformColor = true, categoryBitMask } = opts; const filterSoftRange = opts.filterSoftRange || filterRange; return { ...Number.isFinite(filterRange[0]) ? { min: filterRange[0], softMin: filterSoftRange[0], softMax: filterSoftRange[1], max: filterRange[1] } : { min: filterRange.map((r) => r[0]), softMin: filterSoftRange.map((r) => r[0]), softMax: filterSoftRange.map((r) => r[1]), max: filterRange.map((r) => r[1]) }, enabled: filterEnabled, useSoftMargin: Boolean(opts.filterSoftRange), transformSize: filterEnabled && filterTransformSize, transformColor: filterEnabled && filterTransformColor, ...categoryBitMask && { categoryBitMask } }; } function getUniforms64(opts) { if (!opts || !("extensions" in opts)) { return {}; } const uniforms = getUniforms(opts); if (Number.isFinite(uniforms.min)) { const min64High = Math.fround(uniforms.min); uniforms.min -= min64High; uniforms.softMin -= min64High; uniforms.min64High = min64High; const max64High = Math.fround(uniforms.max); uniforms.max -= max64High; uniforms.softMax -= max64High; uniforms.max64High = max64High; } else { const min64High = uniforms.min.map(Math.fround); uniforms.min = uniforms.min.map((x, i) => x - min64High[i]); uniforms.softMin = uniforms.softMin.map((x, i) => x - min64High[i]); uniforms.min64High = min64High; const max64High = uniforms.max.map(Math.fround); uniforms.max = uniforms.max.map((x, i) => x - max64High[i]); uniforms.softMax = uniforms.softMax.map((x, i) => x - max64High[i]); uniforms.max64High = max64High; } return uniforms; } var inject2 = { "vs:#main-start": ( /* glsl */ ` dataFilter_value = 1.0; if (dataFilter.enabled) { #ifdef DATAFILTER_TYPE #ifdef DATAFILTER_DOUBLE dataFilter_setValue( filterValues - dataFilter.min64High + filterValues64Low, filterValues - dataFilter.max64High + filterValues64Low ); #else dataFilter_setValue(filterValues, filterValues); #endif #endif #ifdef DATACATEGORY_TYPE dataFilter_setCategoryValue(filterCategoryValues); #endif } ` ), "vs:#main-end": ( /* glsl */ ` if (dataFilter_value == 0.0) { gl_Position = vec4(0.); } ` ), "vs:DECKGL_FILTER_SIZE": ( /* glsl */ ` if (dataFilter.transformSize) { size = size * dataFilter_value; } ` ), "fs:DECKGL_FILTER_COLOR": ( /* glsl */ ` if (dataFilter_value == 0.0) discard; if (dataFilter.transformColor) { color.a *= dataFilter_value; } ` ) }; function uniformTypesFromOptions(opts) { const { categorySize, filterSize, fp64: fp642 } = opts; const uniformTypes = { useSoftMargin: "i32", enabled: "i32", transformSize: "i32", transformColor: "i32" }; if (filterSize) { const uniformFormat = filterSize === 1 ? "f32" : `vec${filterSize}<f32>`; uniformTypes.min = uniformFormat; uniformTypes.softMin = uniformFormat; uniformTypes.softMax = uniformFormat; uniformTypes.max = uniformFormat; if (fp642) { uniformTypes.min64High = uniformFormat; uniformTypes.max64High = uniformFormat; } } if (categorySize) { uniformTypes.categoryBitMask = "vec4<i32>"; } return uniformTypes; } var dataFilter = { name: "dataFilter", vs: vs2, fs: fs2, inject: inject2, getUniforms, uniformTypesFromOptions }; var dataFilter64 = { name: "dataFilter", vs: vs2, fs: fs2, inject: inject2, getUniforms: getUniforms64, uniformTypesFromOptions }; // src/data-filter/aggregator.ts var import_engine = __toESM(require_engine(), 1); var AGGREGATE_VS = `#version 300 es #define SHADER_NAME data-filter-vertex-shader #ifdef FLOAT_TARGET in float filterIndices; in float filterPrevIndices; #else in vec2 filterIndices; in vec2 filterPrevIndices; #endif out vec4 vColor; const float component = 1.0 / 255.0; void main() { #ifdef FLOAT_TARGET dataFilter_value *= float(filterIndices != filterPrevIndices); gl_Position = vec4(0.0, 0.0, 0.0, 1.0); vColor = vec4(0.0, 0.0, 0.0, 1.0); #else // Float texture is not supported: pack result into 4 channels x 256 px x 64px dataFilter_value *= float(filterIndices.x != filterPrevIndices.x); float col = filterIndices.x; float row = filterIndices.y * 4.0; float channel = floor(row); row = fract(row); vColor = component * vec4(bvec4(channel == 0.0, channel == 1.0, channel == 2.0, channel == 3.0)); gl_Position = vec4(col * 2.0 - 1.0, row * 2.0 - 1.0, 0.0, 1.0); #endif gl_PointSize = 1.0; } `; var AGGREGATE_FS = `#version 300 es #define SHADER_NAME data-filter-fragment-shader precision highp float; in vec4 vColor; out vec4 fragColor; void main() { if (dataFilter_value < 0.5) { discard; } fragColor = vColor; } `; var FLOAT_TARGET_FEATURES = [ "float32-renderable-webgl", // ability to render to float texture "texture-blend-float-webgl" // ability to blend when rendering to float texture ]; function supportsFloatTarget(device) { return FLOAT_TARGET_FEATURES.every((feature) => device.features.has(feature)); } function getFramebuffer(device, useFloatTarget) { if (useFloatTarget) { return device.createFramebuffer({ width: 1, height: 1, colorAttachments: [ device.createTexture({ format: "rgba32float", dimension: "2d", width: 1, height: 1 }) ] }); } return device.createFramebuffer({ width: 256, height: 64, colorAttachments: [ device.createTexture({ format: "rgba8unorm", dimension: "2d", width: 256, height: 64 }) ] }); } function getModel(device, bufferLayout, shaderOptions, useFloatTarget) { shaderOptions.defines.NON_INSTANCED_MODEL = 1; if (useFloatTarget) { shaderOptions.defines.FLOAT_TARGET = 1; } return new import_engine.Model(device, { id: "data-filter-aggregation-model", vertexCount: 1, isInstanced: false, topology: "point-list", disableWarnings: true, vs: AGGREGATE_VS, fs: AGGREGATE_FS, bufferLayout, ...shaderOptions }); } var parameters = { blend: true, blendColorSrcFactor: "one", blendColorDstFactor: "one", blendAlphaSrcFactor: "one", blendAlphaDstFactor: "one", blendColorOperation: "add", blendAlphaOperation: "add", depthCompare: "never" }; // src/data-filter/data-filter-extension.ts var defaultProps2 = { getFilterValue: { type: "accessor", value: 0 }, getFilterCategory: { type: "accessor", value: 0 }, onFilteredItemsChange: { type: "function", value: null, optional: true }, filterEnabled: true, filterRange: [-1, 1], filterSoftRange: null, filterCategories: [0], filterTransformSize: true, filterTransformColor: true }; var defaultOptions = { categorySize: 0, filterSize: 1, fp64: false, countItems: false }; var CATEGORY_TYPE_FROM_SIZE = { 1: "uint", 2: "uvec2", 3: "uvec3", 4: "uvec4" }; var DATA_TYPE_FROM_SIZE = { 1: "float", 2: "vec2", 3: "vec3", 4: "vec4" }; var DataFilterExtension = class extends import_core4.LayerExtension { constructor(opts = {}) { super({ ...defaultOptions, ...opts }); } getShaders(extension) { const { categorySize, filterSize, fp64: fp642 } = extension.opts; const defines = {}; if (categorySize) { defines.DATACATEGORY_TYPE = CATEGORY_TYPE_FROM_SIZE[categorySize]; defines.DATACATEGORY_CHANNELS = categorySize; } if (filterSize) { defines.DATAFILTER_TYPE = DATA_TYPE_FROM_SIZE[filterSize]; defines.DATAFILTER_DOUBLE = Boolean(fp642); } const module = fp642 ? dataFilter64 : dataFilter; module.uniformTypes = module.uniformTypesFromOptions(extension.opts); return { modules: [module], defines }; } initializeState(context, extension) { const attributeManager = this.getAttributeManager(); const { categorySize, filterSize, fp64: fp642 } = extension.opts; if (attributeManager) { if (filterSize) { attributeManager.add({ filterValues: { size: filterSize, type: fp642 ? "float64" : "float32", stepMode: "dynamic", accessor: "getFilterValue" } }); } if (categorySize) { attributeManager.add({ filterCategoryValues: { size: categorySize, stepMode: "dynamic", accessor: "getFilterCategory", type: "uint32", transform: categorySize === 1 ? (d) => extension._getCategoryKey.call(this, d, 0) : (d) => d.map((x, i) => extension._getCategoryKey.call(this, x, i)) } }); } } const { device } = this.context; if (attributeManager && extension.opts.countItems) { const useFloatTarget = supportsFloatTarget(device); attributeManager.add({ filterVertexIndices: { size: useFloatTarget ? 1 : 2, vertexOffset: 1, type: "unorm8", accessor: (object, { index }) => { const i = object && object.__source ? object.__source.index : index; return useFloatTarget ? (i + 1) % 255 : [(i + 1) % 255, Math.floor(i / 255) % 255]; }, shaderAttributes: { filterPrevIndices: { vertexOffset: 0 }, filterIndices: { vertexOffset: 1 } } } }); const filterFBO = getFramebuffer(device, useFloatTarget); const filterModel = getModel( device, attributeManager.getBufferLayouts({ isInstanced: false }), extension.getShaders.call(this, extension), useFloatTarget ); this.setState({ filterFBO, filterModel }); } } // eslint-disable-next-line complexity updateState({ props, oldProps, changeFlags }, extension) { const attributeManager = this.getAttributeManager(); const { categorySize } = extension.opts; if (this.state.filterModel) { const filterNeedsUpdate = ( // attributeManager must be defined for filterModel to be set attributeManager.attributes.filterValues?.needsUpdate() || attributeManager.attributes.filterCategoryValues?.needsUpdate() || props.filterEnabled !== oldProps.filterEnabled || props.filterRange !== oldProps.filterRange || props.filterSoftRange !== oldProps.filterSoftRange || props.filterCategories !== oldProps.filterCategories ); if (filterNeedsUpdate) { this.setState({ filterNeedsUpdate }); } } if (attributeManager?.attributes.filterCategoryValues) { const categoryBitMaskNeedsUpdate = attributeManager.attributes.filterCategoryValues.needsUpdate() || !(0, import_core4._deepEqual)(props.filterCategories, oldProps.filterCategories, 2); if (categoryBitMaskNeedsUpdate) { this.setState({ categoryBitMask: null }); } const resetCategories = changeFlags.dataChanged; if (resetCategories) { this.setState({ categoryMap: Array(categorySize).fill(0).map(() => ({})) }); attributeManager.attributes.filterCategoryValues.setNeedsUpdate("categoryMap"); } } } // eslint-disable-next-line max-statements draw(params, extension) { const filterFBO = this.state.filterFBO; const filterModel = this.state.filterModel; const filterNeedsUpdate = this.state.filterNeedsUpdate; if (!this.state.categoryBitMask) { extension._updateCategoryBitMask.call(this, params, extension); } const { onFilteredItemsChange, extensions, filterEnabled, filterRange, filterSoftRange, filterTransformSize, filterTransformColor, filterCategories } = this.props; const dataFilterProps = { extensions, filterEnabled, filterRange, filterSoftRange, filterTransformSize, filterTransformColor, filterCategories }; if (this.state.categoryBitMask) { dataFilterProps.categoryBitMask = this.state.categoryBitMask; } this.setShaderModuleProps({ dataFilter: dataFilterProps }); if (filterNeedsUpdate && onFilteredItemsChange && filterModel) { const attributeManager = this.getAttributeManager(); const { attributes: { filterValues, filterCategoryValues, filterVertexIndices } } = attributeManager; filterModel.setVertexCount(this.getNumInstances()); const attributes = { ...filterValues?.getValue(), ...filterCategoryValues?.getValue(), ...filterVertexIndices?.getValue() }; filterModel.setAttributes(attributes); filterModel.shaderInputs.setProps({ dataFilter: dataFilterProps }); const viewport = [0, 0, filterFBO.width, filterFBO.height]; const renderPass = filterModel.device.beginRenderPass({ id: "data-filter-aggregation", framebuffer: filterFBO, parameters: { viewport }, clearColor: [0, 0, 0, 0] }); filterModel.setParameters(parameters); filterModel.draw(renderPass); renderPass.end(); const color = filterModel.device.readPixelsToArrayWebGL(filterFBO); let count = 0; for (let i = 0; i < color.length; i++) { count += color[i]; } onFilteredItemsChange({ id: this.id, count }); this.state.filterNeedsUpdate = false; } } finalizeState() { const filterFBO = this.state.filterFBO; const filterModel = this.state.filterModel; filterFBO?.destroy(); filterModel?.destroy(); } /** * Updates the bitmask used on the GPU to perform the filter based on the * `filterCategories` prop. The mapping between categories and bit in the bitmask * is performed by `_getCategoryKey()` */ _updateCategoryBitMask(params, extension) { const { categorySize } = extension.opts; if (!categorySize) return; const { filterCategories } = this.props; const categoryBitMask = new Uint32Array([0, 0, 0, 0]); const categoryFilters = categorySize === 1 ? [filterCategories] : filterCategories; const maxCategories = categorySize === 1 ? 128 : categorySize === 2 ? 64 : 32; for (let c = 0; c < categoryFilters.length; c++) { const categoryFilter = categoryFilters[c]; for (const category of categoryFilter) { const key = extension._getCategoryKey.call(this, category, c); if (key < maxCategories) { const channel = c * (maxCategories / 32) + Math.floor(key / 32); categoryBitMask[channel] += Math.pow(2, key % 32); } else { import_core4.log.warn(`Exceeded maximum number of categories (${maxCategories})`)(); } } } this.state.categoryBitMask = categoryBitMask; } /** * Returns an index of bit in the bitmask for a given category. If the category has * not yet been assigned a bit, a new one is assigned. */ _getCategoryKey(category, channel) { const categoryMap = this.state.categoryMap[channel]; if (!(category in categoryMap)) { categoryMap[category] = Object.keys(categoryMap).length; } return categoryMap[category]; } }; DataFilterExtension.defaultProps = defaultProps2; DataFilterExtension.extensionName = "DataFilterExtension"; // src/fp64/fp64-extension.ts var import_core6 = __toESM(require_core(), 1); // ../../node_modules/@math.gl/core/dist/lib/common.js var RADIANS_TO_DEGREES = 1 / Math.PI * 180; var DEGREES_TO_RADIANS = 1 / 180 * Math.PI; var DEFAULT_CONFIG = { EPSILON: 1e-12, debug: false, precision: 4, printTypes: false, printDegrees: false, printRowMajor: true, _cartographicRadians: false }; globalThis.mathgl = globalThis.mathgl || { config: { ...DEFAULT_CONFIG } }; var config = globalThis.mathgl.config; function isArray(value) { return Array.isArray(value) || ArrayBuffer.isView(value) && !(value instanceof DataView); } function equals(a, b, epsilon) { const oldEpsilon = config.EPSILON; if (epsilon) { config.EPSILON = epsilon; } try { if (a === b) { return true; } if (isArray(a) && isArray(b)) { if (a.length !== b.length) { return false; } for (let i = 0; i < a.length; ++i) { if (!equals(a[i], b[i])) { return false; } } return true; } if (a && a.equals) { return a.equals(b); } if (b && b.equals) { return b.equals(a); } if (typeof a === "number" && typeof b === "number") { return Math.abs(a - b) <= config.EPSILON * Math.max(1, Math.abs(a), Math.abs(b)); } return false; } finally { config.EPSILON = oldEpsilon; } } // ../../node_modules/@math.gl/core/dist/gl-matrix/common.js var EPSILON = 1e-6; var ARRAY_TYPE = typeof Float32Array !== "undefined" ? Float32Array : Array; var RANDOM = Math.random; function round(a) { if (a >= 0) return Math.round(a); return a % 0.5 === 0 ? Math.floor(a) : Math.round(a); } var degree = Math.PI / 180; // ../../node_modules/@math.gl/core/dist/gl-matrix/vec3.js var vec3_exports = {}; __export(vec3_exports, { add: () => add, angle: () => angle, bezier: () => bezier, ceil: () => ceil, clone: () => clone, copy: () => copy, create: () => create, cross: () => cross, dist: () => dist, distance: () => distance, div: () => div, divide: () => divide, dot: () => dot, equals: () => equals2, exactEquals: () => exactEquals, floor: () => floor, forEach: () => forEach, fromValues: () => fromValues, hermite: () => hermite, inverse: () => inverse, len: () => len, length: () => length, lerp: () => lerp, max: () => max, min: () => min, mul: () => mul, multiply: () => multiply, negate: () => negate, normalize: () => normalize, random: () => random, rotateX: () => rotateX, rotateY: () => rotateY, rotateZ: () => rotateZ, round: () => round2, scale: () => scale, scaleAndAdd: () => scaleAndAdd, set: () => set, slerp: () => slerp, sqrDist: () => sqrDist, sqrLen: () => sqrLen, squaredDistance: () => squaredDistance, squaredLength: () => squaredLength, str: () => str, sub: () => sub, subtract: () => subtract, transformMat3: () => transformMat3, transformMat4: () => transformMat4, transformQuat: () => transformQuat, zero: () => zero }); function create() { const out = new ARRAY_TYPE(3); if (ARRAY_TYPE != Float32Array) { out[0] = 0; out[1] = 0; out[2] = 0; } return out; } function clone(a) { const out = new ARRAY_TYPE(3); out[0] = a[0]; out[1] = a[1]; out[2] = a[2]; return out; } function length(a) { const x = a[0]; const y = a[1]; const z = a[2]; return Math.sqrt(x * x + y * y + z * z); } function fromValues(x, y, z) { const out = new ARRAY_TYPE(3); out[0] = x; out[1] = y; out[2] = z; return out; } function copy(out, a) { out[0] = a[0]; out[1] = a[1]; out[2] = a[2]; return out; } function set(out, x, y, z) { out[0] = x; out[1] = y; out[2] = z; return out; } function add(out, a, b) { out[0] = a[0] + b[0]; out[1] = a[1] + b[1]; out[2] = a[2] + b[2]; return out; } function subtract(out, a, b) { out[0] = a[0] - b[0]; out[1] = a[1] - b[1]; out[2] = a[2] - b[2]; return out; } function multiply(out, a, b) { out[0] = a[0] * b[0]; out[1] = a[1] * b[1]; out[2] = a[2] * b[2]; return out; } function divide(out, a, b) { out[0] = a[0] / b[0]; out[1] = a[1] / b[1]; out[2] = a[2] / b[2]; return out; } function ceil(out, a) { out[0] = Math.ceil(a[0]); out[1] = Math.ceil(a[1]); out[2] = Math.ceil(a[2]); return out; } function floor(out, a) { out[0] = Math.floor(a[0]); out[1] = Math.floor(a[1]); out[2] = Math.floor(a[2]); return out; } function min(out, a, b) { out[0] = Math.min(a[0], b[0]); out[1] = Math.min(a[1], b[1]); out[2] = Math.min(a[2], b[2]); return out; } function max(out, a, b) { out[0] = Math.max(a[0], b[0]); out[1] = Math.max(a[1], b[1]); out[2] = Math.max(a[2], b[2]); return out; } function round2(out, a) { out[0] = round(a[0]); out[1] = round(a[1]); out[2] = round(a[2]); return out; } function scale(out, a, b) { out[0] = a[0] * b; out[1] = a[1] * b; out[2] = a[2] * b; return out; } function scaleAndAdd(out, a, b, scale2) { out[0] = a[0] + b[0] * scale2; out[1] = a[1] + b[1] * scale2; out[2] = a[2] + b[2] * scale2; return out; } function distance(a, b) { const x = b[0] - a[0]; const y = b[1] - a[1]; const z = b[2] - a[2]; return Math.sqrt(x * x + y * y + z * z); } function squaredDistance(a, b) { const x = b[0] - a[0]; const y = b[1] - a[1]; const z = b[2] - a[2]; return x * x + y * y + z * z; } function squaredLength(a) { const x = a[0]; const y = a[1]; const z = a[2]; return x * x + y * y + z * z; } function negate(out, a) { out[0] = -a[0]; out[1] = -a[1]; out[2] = -a[2]; return out; } function inverse(out, a) { out[0] = 1 / a[0]; out[1] = 1 / a[1]; out[2] = 1 / a[2]; return out; } function normalize(out, a) { const x = a[0]; const y = a[1]; const z = a[2]; let len2 = x * x + y * y + z * z; if (len2 > 0) { len2 = 1 / Math.sqrt(len2); } out[0] = a[0] * len2; out[1] = a[1] * len2; out[2] = a[2] * len2; return out; } function dot(a, b) { return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]; } function cross(out, a, b) { const ax = a[0]; const ay = a[1]; const az = a[2]; const bx = b[0]; const by = b[1]; const bz = b[2]; out[0] = ay * bz - az * by; out[1] = az * bx - ax * bz; out[2] = ax * by - ay * bx; return out; } function lerp(out, a, b, t) { const ax = a[0]; const ay = a[1]; const az = a[2]; out[0] = ax + t * (b[0] - ax); out[1] = ay + t * (b[1] - ay); out[2] = az + t * (b[2] - az); return out; } function slerp(out, a, b, t) { const angle2 = Math.acos(Math.min(Math.max(dot(a, b), -1), 1)); const sinTotal = Math.sin(angle2); const ratioA = Math.sin((1 - t) * angle2) / sinTotal; const ratioB = Math.sin(t * angle2) / sinTotal; out[0] = ratioA * a[0] + ratioB * b[0]; out[1] = ratioA * a[1] + ratioB * b[1]; out[2] = ratioA * a[2] + ratioB * b[2]; return out; } function hermite(out, a, b, c, d, t) { const factorTimes2 = t * t; const factor1 = factorTimes2 * (2 * t - 3) + 1; const factor2 = factorTimes2 * (t - 2) + t; const factor3 = factorTimes2 * (t - 1); const factor4 = factorTimes2 * (3 - 2 * t); out[0] = a[0] * factor1 + b[0] * factor2 + c[0] * factor3 + d[0] * factor4; out[1] = a[1] * factor1 + b[1] * factor2 + c[1] * factor3 + d[1] * factor4; out[2] = a[2] * factor1 + b[2] * factor2 + c[2] * factor3 + d[2] * factor4; return out; } function bezier(out, a, b, c, d, t) { const inverseFactor = 1 - t; const inverseFactorTimesTwo = inverseFactor * inverseFactor; const factorTimes2 = t * t; const factor1 = inverseFactorTimesTwo * inverseFactor; const factor2 = 3 * t * inverseFactorTimesTwo; const factor3 = 3 * factorTimes2 * inverseFactor; const factor4 = factorTimes2 * t; out[0] = a[0] * factor1 + b[0] * factor2 + c[0] * factor3 + d[0] * factor4; out[1] = a[1] * factor1 + b[1] * factor2 + c[1] * factor3 + d[1] * factor4; out[2] = a[2] * factor1 + b[2] * factor2 + c[2] * factor3 + d[2] * factor4; return out; } function random(out, scale2) { scale2 = scale2 === void 0 ? 1 : scale2; const r = RANDOM() * 2 * Math.PI; const z = RANDOM() * 2 - 1; const zScale = Math.sqrt(1 - z * z) * scale2; out[0] = Math.cos(r) * zScale; out[1] = Math.sin(r) * zScale; out[2] = z * scale2; return out; } function transformMat4(out, a, m) { const x = a[0]; const y = a[1]; const z = a[2]; let w = m[3] * x + m[7] * y + m[11] * z + m[15]; w = w || 1; out[0] = (m[0] * x + m[4] * y + m[8] * z + m[12]) / w; out[1] = (m[1] * x + m[5] * y + m[9] * z + m[13]) / w; out[2] = (m[2] * x + m[6] * y + m[10] * z + m[14]) / w; return out; } function transformMat3(out, a, m) { const x = a[0]; const y = a[1]; const z = a[2]; out[0] = x * m[0] + y * m[3] + z * m[6]; out[1] = x * m[1] + y * m[4] + z * m[7]; out[2] = x * m[2] + y * m[5] + z * m[8]; return out; } function transformQuat(out, a, q) { const qx = q[0]; const qy = q[1]; const qz = q[2]; const qw = q[3]; const x = a[0]; const y = a[1]; const z = a[2]; let uvx = qy * z - qz * y; let uvy = qz * x - qx * z; let uvz = qx * y - qy * x; let uuvx = qy * uvz - qz * uvy; let uuvy = qz * uvx - qx * uvz; let uuvz = qx * uvy - qy * uvx; const w2 = qw * 2; uvx *= w2; uvy *= w2; uvz *= w2; uuvx *= 2; uuvy *= 2; uuvz *= 2; out[0] = x + uvx + uuvx; out[1] = y + uvy + uuvy; out[2] = z + uvz + uuvz; return out; } function rotateX(out, a, b, rad) { const p = []; const r = []; p[0] = a[0] - b[0]; p[1] = a[1] - b[1]; p[2] = a[2] - b[2]; r[0] = p[0]; r[1] = p[1] * Math.cos(rad) - p[2] * Math.sin(rad); r[2] = p[1] * Math.sin(rad) + p[2] * Math.cos(rad); out[0] = r[0] + b[0]; out[1] = r[1] + b[1]; out[2] = r[2] + b[2]; return out; } function rotateY(out, a, b, rad) { const p = []; const r = []; p[0] = a[0] - b[0]; p[1] = a[1] - b[1]; p[2] = a[2] - b[2]; r[0] = p[2] * Math.sin(rad) + p[0] * Math.cos(rad); r[1] = p[1]; r[2] = p[2] * Math.cos(rad) - p[0] * Math.sin(rad); out[0] = r[0] + b[0]; out[1] = r[1] + b[1]; out[2] = r[2] + b[2]; return out; } function rotateZ(out, a, b, rad) { const p = []; const r = []; p[0] = a[0] - b[0]; p[1] = a[1] - b[1]; p[2] = a[2] - b[2]; r[0] = p[0] * Math.cos(rad) - p[1] * Math.sin(rad); r[1] = p[0] * Math.sin(rad) + p[1] * Math.cos(rad); r[2] = p[2]; out[0] = r[0] + b[0]; out[1] = r[1] + b[1]; out[2] = r[2] + b[2]; return out; } function angle(a, b) { const ax = a[0]; const ay = a[1]; const az = a[2]; const bx = b[0]; const by = b[1]; const bz = b[2]; const mag = Math.sqrt((ax * ax + ay * ay + az * az) * (bx * bx + by * by + bz * bz)); const cosine = mag && dot(a, b) / mag; return Math.acos(Math.min(Math.max(cosine, -1), 1)); } function zero(out) { out[0] = 0; out[1] = 0; out[2] = 0; return out; } function str(a) { return `vec3(${a[0]}, ${a[1]}, ${a[2]})`; } function exactEquals(a, b) { return a[0] === b[0] && a[1] === b[1] && a[2] === b[2]; } function equals2(a, b) { const a0 = a[0]; const a1 = a[1]; const a2 = a[2]; const b0 = b[0]; const b1 = b[1]; const b2 = b[2]; return Math.abs(a0 - b0) <= EPSILON * Math.max(1, Math.abs(a0), Math.abs(b0)) && Math.abs(a1 - b1) <= EPSILON * Math.max(1, Math.abs(a1), Math.abs(b1)) && Math.abs(a2 - b2) <= EPSILON * Math.max(1, Math.abs(a2), Math.abs(b2)); } var sub = subtract; var mul = multiply; var div = divide; var dist = distance; var sqrDist = squaredDistance; var len = length; var sqrLen = squaredLength; var forEach = function() { const vec = create(); return function(a, stride, offset, count, fn, arg) { let i; let l; if (!stride) { stride = 3; } if (!offset) { offset = 0; } if (count) { l = Math.min(count * stride + offset, a.length); } else { l = a.length; } for (i = offset; i < l; i += stride) { vec[0] = a[i]; vec[1] = a[i + 1]; vec[2] = a[i + 2]; fn(vec, vec, arg); a[i] = vec[0]; a[i + 1] = vec[1]; a[i + 2] = vec[2]; } return a; }; }(); // ../../node_modules/@luma.gl/shadertools/dist/modules/math/fp64/fp64-utils.js function fp64ify(a, out = [], startIndex = 0) { const hiPart = Math.fround(a); const loPart = a - hiPart; out[startIndex] = hiPart; out[startIndex + 1] = loPart; return out; } function fp64LowPart(a) { return a - Math.fround(a); } function fp64ifyMatrix4(matrix) { const matrixFP64 = new Float32Array(32); for (let i = 0; i < 4; ++i) { for (let j = 0; j < 4; ++j) { const index = i * 4 + j; fp64ify(matrix[j * 4 + i], matrixFP64, index * 2); } } return matrixFP64; } // ../../node_modules/@luma.gl/shadertools/dist/modules/math/fp64/fp64-arithmetic-glsl.js var fp64arithmeticShader = ( /* glsl */ ` uniform fp64arithmeticUniforms { uniform float ONE; } fp64; /* About LUMA_FP64_CODE_ELIMINATION_WORKAROUND The purpose of this workaround is to prevent shader compilers from optimizing away necessary arithmetic operations by swapping their sequences or transform the equation to some 'equivalent' form. The method is to multiply an artifical variable, ONE, which will be known to the compiler to be 1 only at runtime. The whole expression is then represented as a polynomial with respective to ONE. In the coefficients of all terms, only one a and one b should appear err = (a + b) * ONE^6 - a * ONE^5 - (a + b) * ONE^4 + a * ONE^3 - b - (a + b) * ONE^2 + a * ONE */ // Divide float number to high and low floats to extend fraction bits vec2 split(float a) { const float SPLIT = 4097.0; float t = a * SPLIT; #if defined(LUMA_FP64_CODE_ELIMINATION_WORKAROUND) float a_hi = t * fp64.ONE - (t - a); float a_lo = a * fp64.ONE - a_hi; #else float a_hi = t - (t - a); float a_lo = a - a_hi; #endif return vec2(a_hi, a_lo); } // Divide float number again when high float uses too many fraction bits vec2 split2(vec2 a) { vec2 b = split(a.x); b.y += a.y; return b; } // Special sum operation when a > b vec2 quickTwoSum(float a, float b) { #if defined(LUMA_FP64_CODE_ELIMINATION_WORKAROUND) float sum = (a + b) * fp64.ONE; float err = b - (sum - a) * fp64.ONE; #else float sum = a + b; float err = b - (sum - a); #endif return vec2(sum, err); } // General sum operation vec2 twoSum(float a, float b) { float s = (a + b); #if defined(LUMA_FP64_CODE_ELIMINATION_WORKAROUND) float v = (s * fp64.ONE - a) * fp64.ONE; float err = (a - (s - v) * fp64.ONE) * fp64.ONE * fp64.ONE * fp64.ONE + (b - v); #else float v = s - a; float err = (a - (s - v)) + (b - v); #endif return vec2(s, err); } vec2 twoSub(float a, float b) { float s = (a - b); #if defined(LUMA_FP64_CODE_ELIMINATION_WORKAROUND) float v = (s * fp64.ONE - a) * fp64.ONE; float err = (a - (s - v) * fp64.ONE) * fp64.ONE * fp64.ONE * fp64.ONE - (b + v); #else float v = s - a; float err = (a - (s - v)) - (b + v); #endif return vec2(s, err); } vec2 twoSqr(float a) { float prod = a * a; vec2 a_fp64 = split(a); #if defined(LUMA_FP64_CODE_ELIMINATION_WORKAROUND) float err = ((a_fp64.x * a_fp64.x - prod) * fp64.ONE + 2.0 * a_fp64.x * a_fp64.y * fp64.ONE * fp64.ONE) + a_fp64.y * a_fp64.y * fp64.ONE * fp64.ONE * fp64.ONE; #else float err = ((a_fp64.x * a_fp64.x - prod) + 2.0 * a_fp64.x * a_fp64.y) + a_fp64.y * a_fp64.y; #endif return vec2(prod, err); } vec2 twoProd(float a, float b) { float prod = a * b; vec2 a_fp64 = split(a); vec2 b_fp64 = split(b); float err = ((a_fp64.x * b_fp64.x - prod) + a_fp64.x * b_fp64.y + a_fp64.y * b_fp64.x) + a_fp64.y * b_fp64.y; return vec2(prod, err); } vec2 sum_fp64(vec2 a, vec2 b) { vec2 s, t; s = twoSum(a.x, b.x); t = twoSum(a.y, b.y); s.y += t.x; s = quickTwoSum(s.x, s.y); s.y += t.y; s = quickTwoSum(s.x, s.y); return s; } vec2 sub_fp64(vec2 a, vec2 b) { vec2 s, t; s = twoSub(a.x, b.x); t = twoSub(a.y, b.y); s.y += t.x; s = quickTwoSum(s.x, s.y); s.y += t.y; s = quickTwoSum(s.x, s.y); return s; } vec2 mul_fp64(vec2 a, vec2 b) { vec2 prod = twoProd(a.x, b.x); // y component is for the error prod.y += a.x * b.y; #if defined(LUMA_FP64_HIGH_BITS_OVERFLOW_WORKAROUND) prod = split2(prod); #endif prod = quickTwoSum(prod.x, prod.y); prod.y += a.y * b.x; #if defined(LUMA_FP64_HIGH_BITS_OVERFLOW_WORKAROUND) prod = split2(prod); #endif prod = quickTwoSum(prod.x, prod.y); return prod; } vec2 div_fp64(vec2 a, vec2 b) { float xn = 1.0 / b.x; #if defined(LUMA_FP64_HIGH_BITS_OVERFLOW_WORKAROUND) vec2 yn = mul_fp64(a, vec2(xn, 0)); #else vec2 yn = a * xn; #endif float diff = (sub_fp64(a, mul_fp64(b, yn))).x; vec2 prod = twoProd(xn, diff); return sum_fp64(yn, prod); } vec2 sqrt_fp64(vec2 a) { if (a.x == 0.0 && a.y == 0.0) return vec2(0.0, 0.0); if (a.x < 0.0) return vec2(0.0 / 0.0, 0.0 / 0.0); float x = 1.0 / sqrt(a.x); float yn = a.x * x; #if defined(LUMA_FP64_CODE_ELIMINATION_WORKAROUND) vec2 yn_sqr = twoSqr(yn) * fp64.ONE; #else vec2 yn_sqr = twoSqr(yn); #endif float diff = sub_fp64(a, yn_sqr).x; vec2 prod = twoProd(x * 0.5, diff); #if defined(LUMA_FP64_HIGH_BITS_OVERFLOW_WORKAROUND) return sum_fp64(split(yn), prod); #else return sum_fp64(vec2(yn, 0.0), prod); #endif } ` ); // ../../node_modules/@luma.gl/shadertools/dist/modules/math/fp64/fp64-functions-glsl.js var fp64functionShader = ( /* glsl */ `const vec2 E_FP64 = vec2(2.7182817459106445e+00, 8.254840366817007e-08); const vec2 LOG2_FP64 = vec2(0.6931471824645996e+00, -1.9046542121259336e-09); const vec2 PI_FP64 = vec2(3.1415927410125732, -8.742278012618954e-8); const vec2 TWO_PI_FP64 = vec2(6.2831854820251465, -1.7484556025237907e-7); const vec2 PI_2_FP64 = vec2(1.5707963705062866, -4.371139006309477e-8); const vec2 PI_4_FP64 = vec2(0.7853981852531433, -2.1855695031547384e-8); const vec2 PI_16_FP64 = vec2(0.19634954631328583, -5.463923757886846e-9); const vec2 PI_16_2_FP64 = vec2(0.39269909262657166, -1.0927847515773692e-8); const vec2 PI_16_3_FP64 = vec2(0.5890486240386963, -1.4906100798128818e-9); const vec2 PI_180_FP64 = vec2(0.01745329238474369, 1.3519960498364902e-10); const vec2 SIN_TABLE_0_FP64 = vec2(0.19509032368659973, -1.6704714833615242e-9); const vec2 SIN_TABLE_1_FP64 = vec2(0.3826834261417389, 6.22335089017767e-9); const vec2 SIN_TABLE_2_FP64 = vec2(0.5555702447891235, -1.1769521357507529e-8); const vec2 SIN_TABLE_3_FP64 = vec2(0.7071067690849304, 1.2101617041793133e-8); const vec2 COS_TABLE_0_FP64 = vec2(0.9807852506637573, 2.9739473106360492e-8); const vec2 COS_TABLE_1_FP64 = vec2(0.9238795042037964, 2.8307490351764386e-8); const vec2 COS_TABLE_2_FP64 = vec2(0.8314695954322815, 1.6870263741530778e-8); const vec2 COS_TABLE_3_FP64 = vec2(0.7071067690849304, 1.2101617152815436e-8); const vec2 INVERSE_FACTORIAL_3_FP64 = vec2(1.666666716337204e-01, -4.967053879312289e-09); // 1/3! const vec2 INVERSE_FACTORIAL_4_FP64 = vec2(4.16666679084301e-02, -1.2417634698280722e-09); // 1/4! const vec2 INVERSE_FACTORIAL_5_FP64 = vec2(8.333333767950535e-03, -4.34617203337595e-10); // 1/5! const vec2 INVERSE_FACTORIAL_6_FP64 = vec2(1.3888889225199819e-03, -3.3631094437103215e-11); // 1/6! const vec2 INVERSE_FACTORIAL_7_FP64 = vec2(1.9841270113829523e-04, -2.725596874933456e-12); // 1/7! const vec2 INVERSE_FACTORIAL_8_FP64 = vec2(2.4801587642286904e-05, -3.406996025904184e-13); // 1/8! const vec2 INVERSE_FACTORIAL_9_FP64 = vec2(2.75573188446287533e-06, 3.7935713937038186e-14); // 1/9! const vec2 INVERSE_FACTORIAL_10_FP64 = vec2(2.755731998149713e-07, -7.575112367869873e-15); // 1/10! float nint(float d) { if (d == floor(d)) return d; return floor(d + 0.5); } vec2 nint_fp64(vec2 a) { float hi = nint(a.x); float lo; vec2 tmp; if (hi == a.x) { lo = nint(a.y); tmp = quickTwoSum(hi, lo); } else { lo = 0.0; if (abs(hi - a.x) == 0.5 && a.y < 0.0) { hi -= 1.0; } tmp = vec2(hi, lo); } return tmp; } /* k_power controls how much range reduction we would like to have Range reduction uses the following method: assume a = k_power * r + m * log(2), k and m being integers. Set k_power = 4 (we can choose other k to trade accuracy with performance. we only need to calculate exp(r) and using exp(a) = 2^m * exp(r)^k_power; */ vec2 exp_fp64(vec2 a) { // We need to make sure these two numbers match // as bit-wise shift is not available in GLSL 1.0 const int k_power = 4; const float k = 16.0; const float inv_k = 1.0 / k; if (a.x <= -88.0) return vec2(0.0, 0.0); if (a.x >= 88.0) return vec2(1.0 / 0.0, 1.0 / 0.0); if (a.x == 0.0 && a.y == 0.0) return vec2(1.0, 0.0); if (a.x == 1.0 && a.y == 0.0) return E_FP64; float m = floor(a.x / LOG2_FP64.x + 0.5); vec2 r = sub_fp64(a, mul_fp64(LOG2_FP64, vec2(m, 0.0))) * inv_k; vec2 s, t, p; p = mul_fp64(r, r); s = sum_fp64(r, p * 0.5); p = mu