UNPKG

kepler.gl

Version:

kepler.gl is a webgl based application to visualize large scale location data in the browser

129 lines (121 loc) 21.8 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.buildRasterFragmentShader = buildRasterFragmentShader; exports.buildRasterVertexShader = buildRasterVertexShader; exports.ensureRasterHooksRegistered = ensureRasterHooksRegistered; exports.prepareLumaModules = prepareLumaModules; exports.rasterUniforms = void 0; var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray")); var _shadertools = require("@luma.gl/shadertools"); // SPDX-License-Identifier: MIT // Copyright contributors to the kepler.gl project /** * UBO-based shader module for raster layer uniforms. * Replaces the old standalone `uniform float desaturate` etc. */ var rasterUniformBlock = "uniform rasterUniforms {\n vec4 bounds;\n float coordinateConversion;\n float desaturate;\n float opacity;\n vec3 tintColor;\n vec4 transparentColor;\n} raster;\n"; var rasterUniforms = exports.rasterUniforms = { name: 'raster', vs: rasterUniformBlock, fs: rasterUniformBlock, uniformTypes: { bounds: 'vec4<f32>', coordinateConversion: 'f32', desaturate: 'f32', opacity: 'f32', tintColor: 'vec3<f32>', transparentColor: 'vec4<f32>' } }; /** * Register custom DECKGL_CREATE_COLOR and DECKGL_MUTATE_COLOR shader hooks * with the default ShaderAssembler. These hooks are used by the raster layer's * custom shader modules (rgbaImage, combineBands, colormap, etc.). * * In luma.gl 8.x these were registered via ProgramManager; in luma.gl 9.x * we register them with the ShaderAssembler singleton. * * NOTE: We must check the assembler's hook list every time rather than using * a module-level boolean guard, because deck.gl's getShaderAssembler() clears * _hookFunctions when a new Deck instance is created (e.g. during image export). */ function ensureRasterHooksRegistered() { var assembler = _shadertools.ShaderAssembler.getDefaultShaderAssembler(); // @ts-expect-error _hookFunctions is private in ShaderAssembler var existingHooks = assembler._hookFunctions || []; var hookNames = existingHooks.map(function (h) { return typeof h === 'string' ? h : h.hook; }); if (!hookNames.some(function (h) { return h === null || h === void 0 ? void 0 : h.includes('DECKGL_CREATE_COLOR'); })) { assembler.addShaderHook('fs:DECKGL_CREATE_COLOR(inout vec4 image, vec2 coord)'); } if (!hookNames.some(function (h) { return h === null || h === void 0 ? void 0 : h.includes('DECKGL_MUTATE_COLOR'); })) { assembler.addShaderHook('fs:DECKGL_MUTATE_COLOR(inout vec4 image, vec2 coord)'); } } /** * Convert kepler.gl's custom raster shader modules into luma.gl 9 compatible * format. Ensures fs2 (WebGL2) shaders are used and texture2D -> texture. */ function prepareLumaModules(modules) { return modules.map(function (mod) { var fs = mod.fs2 || mod.fs || ''; var result = { name: mod.name, // Replace texture2D with texture for GLSL 300 es fs: fs.replace(/texture2D\(/g, 'texture('), dependencies: mod.dependencies, deprecations: mod.deprecations }; if (mod.vs) { result.vs = mod.vs.replace(/texture2D\(/g, 'texture('); } if (mod.defines) { result.defines = mod.defines; } if (mod.getUniforms) { result.getUniforms = mod.getUniforms; } if (mod.uniforms) { result.uniforms = mod.uniforms; } if (mod.uniformTypes) { result.uniformTypes = mod.uniformTypes; } // Convert inject code, replacing texture2D -> texture if (mod.inject) { result.inject = {}; for (var _i = 0, _Object$entries = Object.entries(mod.inject); _i < _Object$entries.length; _i++) { var _Object$entries$_i = (0, _slicedToArray2["default"])(_Object$entries[_i], 2), hook = _Object$entries$_i[0], code = _Object$entries$_i[1]; var codeStr = typeof code === 'string' ? code : code.injection || ''; result.inject[hook] = codeStr.replace(/texture2D\(/g, 'texture('); } } return result; }); } /** * Build the vertex shader for the raster layer. * References raster.coordinateConversion from the UBO. */ function buildRasterVertexShader() { return "#version 300 es\n#define SHADER_NAME raster-layer-vertex-shader\n\nprecision mediump float;\n\nin vec2 texCoords;\nin vec3 positions;\nin vec3 positions64Low;\n\nout vec2 vTexCoord;\nout vec2 vTexPos;\n\nconst vec3 pickingColor = vec3(1.0, 0.0, 0.0);\n\nvoid main(void) {\n geometry.worldPosition = positions;\n geometry.uv = texCoords;\n geometry.pickingColor = pickingColor;\n\n gl_Position = project_position_to_clipspace(positions, positions64Low, vec3(0.0), geometry.position);\n DECKGL_FILTER_GL_POSITION(gl_Position, geometry);\n\n vTexCoord = texCoords;\n\n if (raster.coordinateConversion < -0.5) {\n vTexPos = geometry.position.xy + project.commonOrigin.xy;\n } else if (raster.coordinateConversion > 0.5) {\n vTexPos = geometry.worldPosition.xy;\n }\n\n vec4 color = vec4(0.0);\n DECKGL_FILTER_COLOR(color, geometry);\n}\n"; } /** * Build the fragment shader for the raster layer. * Uses DECKGL_CREATE_COLOR and DECKGL_MUTATE_COLOR hooks which are now * registered with the ShaderAssembler and populated by module injections. */ function buildRasterFragmentShader() { return "#version 300 es\n#define SHADER_NAME raster-layer-fragment-shader\n\nprecision mediump float;\nprecision mediump int;\nprecision mediump usampler2D;\n\nin vec2 vTexCoord;\nin vec2 vTexPos;\n\nout vec4 fragColor;\n\n/* projection utils */\nconst float TILE_SIZE = 512.0;\nconst float PI = 3.1415926536;\nconst float WORLD_SCALE = TILE_SIZE / PI / 2.0;\n\nvec2 lnglat_to_mercator(vec2 lnglat) {\n float x = lnglat.x;\n float y = clamp(lnglat.y, -89.9, 89.9);\n return vec2(\n radians(x) + PI,\n PI + log(tan(PI * 0.25 + radians(y) * 0.5))\n ) * WORLD_SCALE;\n}\n\nvec2 mercator_to_lnglat(vec2 xy) {\n xy /= WORLD_SCALE;\n return degrees(vec2(\n xy.x - PI,\n atan(exp(xy.y - PI)) * 2.0 - PI * 0.5\n ));\n}\n\nvec3 color_desaturate(vec3 color) {\n float luminance = (color.r + color.g + color.b) * 0.333333333;\n return mix(color, vec3(luminance), raster.desaturate);\n}\n\nvec3 color_tint(vec3 color) {\n return color * raster.tintColor;\n}\n\nvec4 apply_opacity(vec3 color, float alpha) {\n if (raster.transparentColor.a == 0.0) {\n return vec4(color, alpha);\n }\n float blendedAlpha = alpha + raster.transparentColor.a * (1.0 - alpha);\n float highLightRatio = alpha / blendedAlpha;\n vec3 blendedRGB = mix(raster.transparentColor.rgb, color, highLightRatio);\n return vec4(blendedRGB, blendedAlpha);\n}\n\nvec2 getUV(vec2 pos) {\n return vec2(\n (pos.x - raster.bounds[0]) / (raster.bounds[2] - raster.bounds[0]),\n (pos.y - raster.bounds[3]) / (raster.bounds[1] - raster.bounds[3])\n );\n}\n\nvoid main(void) {\n vec2 uv = vTexCoord;\n if (raster.coordinateConversion < -0.5) {\n vec2 lnglat = mercator_to_lnglat(vTexPos);\n uv = getUV(lnglat);\n } else if (raster.coordinateConversion > 0.5) {\n vec2 commonPos = lnglat_to_mercator(vTexPos);\n uv = getUV(commonPos);\n }\n\n vec4 image = vec4(0.0);\n DECKGL_CREATE_COLOR(image, uv);\n\n DECKGL_MUTATE_COLOR(image, uv);\n\n fragColor = apply_opacity(color_tint(color_desaturate(image.rgb)), raster.opacity);\n\n geometry.uv = uv;\n DECKGL_FILTER_COLOR(fragColor, geometry);\n}\n"; } //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJfc2hhZGVydG9vbHMiLCJyZXF1aXJlIiwicmFzdGVyVW5pZm9ybUJsb2NrIiwicmFzdGVyVW5pZm9ybXMiLCJleHBvcnRzIiwibmFtZSIsInZzIiwiZnMiLCJ1bmlmb3JtVHlwZXMiLCJib3VuZHMiLCJjb29yZGluYXRlQ29udmVyc2lvbiIsImRlc2F0dXJhdGUiLCJvcGFjaXR5IiwidGludENvbG9yIiwidHJhbnNwYXJlbnRDb2xvciIsImVuc3VyZVJhc3Rlckhvb2tzUmVnaXN0ZXJlZCIsImFzc2VtYmxlciIsIlNoYWRlckFzc2VtYmxlciIsImdldERlZmF1bHRTaGFkZXJBc3NlbWJsZXIiLCJleGlzdGluZ0hvb2tzIiwiX2hvb2tGdW5jdGlvbnMiLCJob29rTmFtZXMiLCJtYXAiLCJoIiwiaG9vayIsInNvbWUiLCJpbmNsdWRlcyIsImFkZFNoYWRlckhvb2siLCJwcmVwYXJlTHVtYU1vZHVsZXMiLCJtb2R1bGVzIiwibW9kIiwiZnMyIiwicmVzdWx0IiwicmVwbGFjZSIsImRlcGVuZGVuY2llcyIsImRlcHJlY2F0aW9ucyIsImRlZmluZXMiLCJnZXRVbmlmb3JtcyIsInVuaWZvcm1zIiwiaW5qZWN0IiwiX2kiLCJfT2JqZWN0JGVudHJpZXMiLCJPYmplY3QiLCJlbnRyaWVzIiwibGVuZ3RoIiwiX09iamVjdCRlbnRyaWVzJF9pIiwiX3NsaWNlZFRvQXJyYXkyIiwiY29kZSIsImNvZGVTdHIiLCJpbmplY3Rpb24iLCJidWlsZFJhc3RlclZlcnRleFNoYWRlciIsImJ1aWxkUmFzdGVyRnJhZ21lbnRTaGFkZXIiXSwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvcmFzdGVyL3Jhc3Rlci1sYXllci9yYXN0ZXItbGF5ZXItc2hhZGVycy50cyJdLCJzb3VyY2VzQ29udGVudCI6WyIvLyBTUERYLUxpY2Vuc2UtSWRlbnRpZmllcjogTUlUXG4vLyBDb3B5cmlnaHQgY29udHJpYnV0b3JzIHRvIHRoZSBrZXBsZXIuZ2wgcHJvamVjdFxuXG5pbXBvcnQge1NoYWRlckFzc2VtYmxlcn0gZnJvbSAnQGx1bWEuZ2wvc2hhZGVydG9vbHMnO1xuaW1wb3J0IHR5cGUge1NoYWRlck1vZHVsZSBhcyBMdW1hU2hhZGVyTW9kdWxlfSBmcm9tICcuLi93ZWJnbC90eXBlcyc7XG5cbi8qKlxuICogVUJPLWJhc2VkIHNoYWRlciBtb2R1bGUgZm9yIHJhc3RlciBsYXllciB1bmlmb3Jtcy5cbiAqIFJlcGxhY2VzIHRoZSBvbGQgc3RhbmRhbG9uZSBgdW5pZm9ybSBmbG9hdCBkZXNhdHVyYXRlYCBldGMuXG4gKi9cbmNvbnN0IHJhc3RlclVuaWZvcm1CbG9jayA9IGBcXFxudW5pZm9ybSByYXN0ZXJVbmlmb3JtcyB7XG4gIHZlYzQgYm91bmRzO1xuICBmbG9hdCBjb29yZGluYXRlQ29udmVyc2lvbjtcbiAgZmxvYXQgZGVzYXR1cmF0ZTtcbiAgZmxvYXQgb3BhY2l0eTtcbiAgdmVjMyB0aW50Q29sb3I7XG4gIHZlYzQgdHJhbnNwYXJlbnRDb2xvcjtcbn0gcmFzdGVyO1xuYDtcblxuZXhwb3J0IGNvbnN0IHJhc3RlclVuaWZvcm1zID0ge1xuICBuYW1lOiAncmFzdGVyJyxcbiAgdnM6IHJhc3RlclVuaWZvcm1CbG9jayxcbiAgZnM6IHJhc3RlclVuaWZvcm1CbG9jayxcbiAgdW5pZm9ybVR5cGVzOiB7XG4gICAgYm91bmRzOiAndmVjNDxmMzI+JyxcbiAgICBjb29yZGluYXRlQ29udmVyc2lvbjogJ2YzMicsXG4gICAgZGVzYXR1cmF0ZTogJ2YzMicsXG4gICAgb3BhY2l0eTogJ2YzMicsXG4gICAgdGludENvbG9yOiAndmVjMzxmMzI+JyxcbiAgICB0cmFuc3BhcmVudENvbG9yOiAndmVjNDxmMzI+J1xuICB9XG59O1xuXG4vKipcbiAqIFJlZ2lzdGVyIGN1c3RvbSBERUNLR0xfQ1JFQVRFX0NPTE9SIGFuZCBERUNLR0xfTVVUQVRFX0NPTE9SIHNoYWRlciBob29rc1xuICogd2l0aCB0aGUgZGVmYXVsdCBTaGFkZXJBc3NlbWJsZXIuIFRoZXNlIGhvb2tzIGFyZSB1c2VkIGJ5IHRoZSByYXN0ZXIgbGF5ZXInc1xuICogY3VzdG9tIHNoYWRlciBtb2R1bGVzIChyZ2JhSW1hZ2UsIGNvbWJpbmVCYW5kcywgY29sb3JtYXAsIGV0Yy4pLlxuICpcbiAqIEluIGx1bWEuZ2wgOC54IHRoZXNlIHdlcmUgcmVnaXN0ZXJlZCB2aWEgUHJvZ3JhbU1hbmFnZXI7IGluIGx1bWEuZ2wgOS54XG4gKiB3ZSByZWdpc3RlciB0aGVtIHdpdGggdGhlIFNoYWRlckFzc2VtYmxlciBzaW5nbGV0b24uXG4gKlxuICogTk9URTogV2UgbXVzdCBjaGVjayB0aGUgYXNzZW1ibGVyJ3MgaG9vayBsaXN0IGV2ZXJ5IHRpbWUgcmF0aGVyIHRoYW4gdXNpbmdcbiAqIGEgbW9kdWxlLWxldmVsIGJvb2xlYW4gZ3VhcmQsIGJlY2F1c2UgZGVjay5nbCdzIGdldFNoYWRlckFzc2VtYmxlcigpIGNsZWFyc1xuICogX2hvb2tGdW5jdGlvbnMgd2hlbiBhIG5ldyBEZWNrIGluc3RhbmNlIGlzIGNyZWF0ZWQgKGUuZy4gZHVyaW5nIGltYWdlIGV4cG9ydCkuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBlbnN1cmVSYXN0ZXJIb29rc1JlZ2lzdGVyZWQoKTogdm9pZCB7XG4gIGNvbnN0IGFzc2VtYmxlciA9IFNoYWRlckFzc2VtYmxlci5nZXREZWZhdWx0U2hhZGVyQXNzZW1ibGVyKCk7XG4gIC8vIEB0cy1leHBlY3QtZXJyb3IgX2hvb2tGdW5jdGlvbnMgaXMgcHJpdmF0ZSBpbiBTaGFkZXJBc3NlbWJsZXJcbiAgY29uc3QgZXhpc3RpbmdIb29rcyA9IGFzc2VtYmxlci5faG9va0Z1bmN0aW9ucyB8fCBbXTtcbiAgY29uc3QgaG9va05hbWVzID0gZXhpc3RpbmdIb29rcy5tYXAoaCA9PiAodHlwZW9mIGggPT09ICdzdHJpbmcnID8gaCA6IGguaG9vaykpO1xuXG4gIGlmICghaG9va05hbWVzLnNvbWUoaCA9PiBoPy5pbmNsdWRlcygnREVDS0dMX0NSRUFURV9DT0xPUicpKSkge1xuICAgIGFzc2VtYmxlci5hZGRTaGFkZXJIb29rKCdmczpERUNLR0xfQ1JFQVRFX0NPTE9SKGlub3V0IHZlYzQgaW1hZ2UsIHZlYzIgY29vcmQpJyk7XG4gIH1cbiAgaWYgKCFob29rTmFtZXMuc29tZShoID0+IGg/LmluY2x1ZGVzKCdERUNLR0xfTVVUQVRFX0NPTE9SJykpKSB7XG4gICAgYXNzZW1ibGVyLmFkZFNoYWRlckhvb2soJ2ZzOkRFQ0tHTF9NVVRBVEVfQ09MT1IoaW5vdXQgdmVjNCBpbWFnZSwgdmVjMiBjb29yZCknKTtcbiAgfVxufVxuXG4vKipcbiAqIENvbnZlcnQga2VwbGVyLmdsJ3MgY3VzdG9tIHJhc3RlciBzaGFkZXIgbW9kdWxlcyBpbnRvIGx1bWEuZ2wgOSBjb21wYXRpYmxlXG4gKiBmb3JtYXQuIEVuc3VyZXMgZnMyIChXZWJHTDIpIHNoYWRlcnMgYXJlIHVzZWQgYW5kIHRleHR1cmUyRCAtPiB0ZXh0dXJlLlxuICovXG5pbnRlcmZhY2UgTHVtYU1vZHVsZU91dHB1dCB7XG4gIG5hbWU6IHN0cmluZztcbiAgZnM6IHN0cmluZztcbiAgdnM/OiBzdHJpbmc7XG4gIGRlZmluZXM/OiBSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+O1xuICBnZXRVbmlmb3Jtcz86IChvcHRzOiBvYmplY3QpID0+IFJlY29yZDxzdHJpbmcsIHVua25vd24+IHwgbnVsbDtcbiAgdW5pZm9ybXM/OiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPjtcbiAgdW5pZm9ybVR5cGVzPzogUmVjb3JkPHN0cmluZywgc3RyaW5nPjtcbiAgaW5qZWN0PzogUmVjb3JkPHN0cmluZywgc3RyaW5nPjtcbiAgZGVwZW5kZW5jaWVzPzogdW5rbm93bltdO1xuICBkZXByZWNhdGlvbnM/OiB1bmtub3duW107XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBwcmVwYXJlTHVtYU1vZHVsZXMobW9kdWxlczogTHVtYVNoYWRlck1vZHVsZVtdKTogTHVtYU1vZHVsZU91dHB1dFtdIHtcbiAgcmV0dXJuIG1vZHVsZXMubWFwKG1vZCA9PiB7XG4gICAgY29uc3QgZnMgPSBtb2QuZnMyIHx8IG1vZC5mcyB8fCAnJztcbiAgICBjb25zdCByZXN1bHQ6IEx1bWFNb2R1bGVPdXRwdXQgPSB7XG4gICAgICBuYW1lOiBtb2QubmFtZSxcbiAgICAgIC8vIFJlcGxhY2UgdGV4dHVyZTJEIHdpdGggdGV4dHVyZSBmb3IgR0xTTCAzMDAgZXNcbiAgICAgIGZzOiBmcy5yZXBsYWNlKC90ZXh0dXJlMkRcXCgvZywgJ3RleHR1cmUoJyksXG4gICAgICBkZXBlbmRlbmNpZXM6IG1vZC5kZXBlbmRlbmNpZXMsXG4gICAgICBkZXByZWNhdGlvbnM6IG1vZC5kZXByZWNhdGlvbnNcbiAgICB9O1xuXG4gICAgaWYgKG1vZC52cykge1xuICAgICAgcmVzdWx0LnZzID0gbW9kLnZzLnJlcGxhY2UoL3RleHR1cmUyRFxcKC9nLCAndGV4dHVyZSgnKTtcbiAgICB9XG5cbiAgICBpZiAobW9kLmRlZmluZXMpIHtcbiAgICAgIHJlc3VsdC5kZWZpbmVzID0gbW9kLmRlZmluZXM7XG4gICAgfVxuXG4gICAgaWYgKG1vZC5nZXRVbmlmb3Jtcykge1xuICAgICAgcmVzdWx0LmdldFVuaWZvcm1zID0gbW9kLmdldFVuaWZvcm1zO1xuICAgIH1cblxuICAgIGlmIChtb2QudW5pZm9ybXMpIHtcbiAgICAgIHJlc3VsdC51bmlmb3JtcyA9IG1vZC51bmlmb3JtcztcbiAgICB9XG5cbiAgICBpZiAobW9kLnVuaWZvcm1UeXBlcykge1xuICAgICAgcmVzdWx0LnVuaWZvcm1UeXBlcyA9IG1vZC51bmlmb3JtVHlwZXM7XG4gICAgfVxuXG4gICAgLy8gQ29udmVydCBpbmplY3QgY29kZSwgcmVwbGFjaW5nIHRleHR1cmUyRCAtPiB0ZXh0dXJlXG4gICAgaWYgKG1vZC5pbmplY3QpIHtcbiAgICAgIHJlc3VsdC5pbmplY3QgPSB7fTtcbiAgICAgIGZvciAoY29uc3QgW2hvb2ssIGNvZGVdIG9mIE9iamVjdC5lbnRyaWVzKG1vZC5pbmplY3QpKSB7XG4gICAgICAgIGNvbnN0IGNvZGVTdHIgPVxuICAgICAgICAgIHR5cGVvZiBjb2RlID09PSAnc3RyaW5nJyA/IGNvZGUgOiAoY29kZSBhcyB7aW5qZWN0aW9uPzogc3RyaW5nfSkuaW5qZWN0aW9uIHx8ICcnO1xuICAgICAgICByZXN1bHQuaW5qZWN0W2hvb2tdID0gY29kZVN0ci5yZXBsYWNlKC90ZXh0dXJlMkRcXCgvZywgJ3RleHR1cmUoJyk7XG4gICAgICB9XG4gICAgfVxuXG4gICAgcmV0dXJuIHJlc3VsdDtcbiAgfSk7XG59XG5cbi8qKlxuICogQnVpbGQgdGhlIHZlcnRleCBzaGFkZXIgZm9yIHRoZSByYXN0ZXIgbGF5ZXIuXG4gKiBSZWZlcmVuY2VzIHJhc3Rlci5jb29yZGluYXRlQ29udmVyc2lvbiBmcm9tIHRoZSBVQk8uXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBidWlsZFJhc3RlclZlcnRleFNoYWRlcigpOiBzdHJpbmcge1xuICByZXR1cm4gYFxcXG4jdmVyc2lvbiAzMDAgZXNcbiNkZWZpbmUgU0hBREVSX05BTUUgcmFzdGVyLWxheWVyLXZlcnRleC1zaGFkZXJcblxucHJlY2lzaW9uIG1lZGl1bXAgZmxvYXQ7XG5cbmluIHZlYzIgdGV4Q29vcmRzO1xuaW4gdmVjMyBwb3NpdGlvbnM7XG5pbiB2ZWMzIHBvc2l0aW9uczY0TG93O1xuXG5vdXQgdmVjMiB2VGV4Q29vcmQ7XG5vdXQgdmVjMiB2VGV4UG9zO1xuXG5jb25zdCB2ZWMzIHBpY2tpbmdDb2xvciA9IHZlYzMoMS4wLCAwLjAsIDAuMCk7XG5cbnZvaWQgbWFpbih2b2lkKSB7XG4gIGdlb21ldHJ5LndvcmxkUG9zaXRpb24gPSBwb3NpdGlvbnM7XG4gIGdlb21ldHJ5LnV2ID0gdGV4Q29vcmRzO1xuICBnZW9tZXRyeS5waWNraW5nQ29sb3IgPSBwaWNraW5nQ29sb3I7XG5cbiAgZ2xfUG9zaXRpb24gPSBwcm9qZWN0X3Bvc2l0aW9uX3RvX2NsaXBzcGFjZShwb3NpdGlvbnMsIHBvc2l0aW9uczY0TG93LCB2ZWMzKDAuMCksIGdlb21ldHJ5LnBvc2l0aW9uKTtcbiAgREVDS0dMX0ZJTFRFUl9HTF9QT1NJVElPTihnbF9Qb3NpdGlvbiwgZ2VvbWV0cnkpO1xuXG4gIHZUZXhDb29yZCA9IHRleENvb3JkcztcblxuICBpZiAocmFzdGVyLmNvb3JkaW5hdGVDb252ZXJzaW9uIDwgLTAuNSkge1xuICAgIHZUZXhQb3MgPSBnZW9tZXRyeS5wb3NpdGlvbi54eSArIHByb2plY3QuY29tbW9uT3JpZ2luLnh5O1xuICB9IGVsc2UgaWYgKHJhc3Rlci5jb29yZGluYXRlQ29udmVyc2lvbiA+IDAuNSkge1xuICAgIHZUZXhQb3MgPSBnZW9tZXRyeS53b3JsZFBvc2l0aW9uLnh5O1xuICB9XG5cbiAgdmVjNCBjb2xvciA9IHZlYzQoMC4wKTtcbiAgREVDS0dMX0ZJTFRFUl9DT0xPUihjb2xvciwgZ2VvbWV0cnkpO1xufVxuYDtcbn1cblxuLyoqXG4gKiBCdWlsZCB0aGUgZnJhZ21lbnQgc2hhZGVyIGZvciB0aGUgcmFzdGVyIGxheWVyLlxuICogVXNlcyBERUNLR0xfQ1JFQVRFX0NPTE9SIGFuZCBERUNLR0xfTVVUQVRFX0NPTE9SIGhvb2tzIHdoaWNoIGFyZSBub3dcbiAqIHJlZ2lzdGVyZWQgd2l0aCB0aGUgU2hhZGVyQXNzZW1ibGVyIGFuZCBwb3B1bGF0ZWQgYnkgbW9kdWxlIGluamVjdGlvbnMuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBidWlsZFJhc3RlckZyYWdtZW50U2hhZGVyKCk6IHN0cmluZyB7XG4gIHJldHVybiBgXFxcbiN2ZXJzaW9uIDMwMCBlc1xuI2RlZmluZSBTSEFERVJfTkFNRSByYXN0ZXItbGF5ZXItZnJhZ21lbnQtc2hhZGVyXG5cbnByZWNpc2lvbiBtZWRpdW1wIGZsb2F0O1xucHJlY2lzaW9uIG1lZGl1bXAgaW50O1xucHJlY2lzaW9uIG1lZGl1bXAgdXNhbXBsZXIyRDtcblxuaW4gdmVjMiB2VGV4Q29vcmQ7XG5pbiB2ZWMyIHZUZXhQb3M7XG5cbm91dCB2ZWM0IGZyYWdDb2xvcjtcblxuLyogcHJvamVjdGlvbiB1dGlscyAqL1xuY29uc3QgZmxvYXQgVElMRV9TSVpFID0gNTEyLjA7XG5jb25zdCBmbG9hdCBQSSA9IDMuMTQxNTkyNjUzNjtcbmNvbnN0IGZsb2F0IFdPUkxEX1NDQUxFID0gVElMRV9TSVpFIC8gUEkgLyAyLjA7XG5cbnZlYzIgbG5nbGF0X3RvX21lcmNhdG9yKHZlYzIgbG5nbGF0KSB7XG4gIGZsb2F0IHggPSBsbmdsYXQueDtcbiAgZmxvYXQgeSA9IGNsYW1wKGxuZ2xhdC55LCAtODkuOSwgODkuOSk7XG4gIHJldHVybiB2ZWMyKFxuICAgIHJhZGlhbnMoeCkgKyBQSSxcbiAgICBQSSArIGxvZyh0YW4oUEkgKiAwLjI1ICsgcmFkaWFucyh5KSAqIDAuNSkpXG4gICkgKiBXT1JMRF9TQ0FMRTtcbn1cblxudmVjMiBtZXJjYXRvcl90b19sbmdsYXQodmVjMiB4eSkge1xuICB4eSAvPSBXT1JMRF9TQ0FMRTtcbiAgcmV0dXJuIGRlZ3JlZXModmVjMihcbiAgICB4eS54IC0gUEksXG4gICAgYXRhbihleHAoeHkueSAtIFBJKSkgKiAyLjAgLSBQSSAqIDAuNVxuICApKTtcbn1cblxudmVjMyBjb2xvcl9kZXNhdHVyYXRlKHZlYzMgY29sb3IpIHtcbiAgZmxvYXQgbHVtaW5hbmNlID0gKGNvbG9yLnIgKyBjb2xvci5nICsgY29sb3IuYikgKiAwLjMzMzMzMzMzMztcbiAgcmV0dXJuIG1peChjb2xvciwgdmVjMyhsdW1pbmFuY2UpLCByYXN0ZXIuZGVzYXR1cmF0ZSk7XG59XG5cbnZlYzMgY29sb3JfdGludCh2ZWMzIGNvbG9yKSB7XG4gIHJldHVybiBjb2xvciAqIHJhc3Rlci50aW50Q29sb3I7XG59XG5cbnZlYzQgYXBwbHlfb3BhY2l0eSh2ZWMzIGNvbG9yLCBmbG9hdCBhbHBoYSkge1xuICBpZiAocmFzdGVyLnRyYW5zcGFyZW50Q29sb3IuYSA9PSAwLjApIHtcbiAgICByZXR1cm4gdmVjNChjb2xvciwgYWxwaGEpO1xuICB9XG4gIGZsb2F0IGJsZW5kZWRBbHBoYSA9IGFscGhhICsgcmFzdGVyLnRyYW5zcGFyZW50Q29sb3IuYSAqICgxLjAgLSBhbHBoYSk7XG4gIGZsb2F0IGhpZ2hMaWdodFJhdGlvID0gYWxwaGEgLyBibGVuZGVkQWxwaGE7XG4gIHZlYzMgYmxlbmRlZFJHQiA9IG1peChyYXN0ZXIudHJhbnNwYXJlbnRDb2xvci5yZ2IsIGNvbG9yLCBoaWdoTGlnaHRSYXRpbyk7XG4gIHJldHVybiB2ZWM0KGJsZW5kZWRSR0IsIGJsZW5kZWRBbHBoYSk7XG59XG5cbnZlYzIgZ2V0VVYodmVjMiBwb3MpIHtcbiAgcmV0dXJuIHZlYzIoXG4gICAgKHBvcy54IC0gcmFzdGVyLmJvdW5kc1swXSkgLyAocmFzdGVyLmJvdW5kc1syXSAtIHJhc3Rlci5ib3VuZHNbMF0pLFxuICAgIChwb3MueSAtIHJhc3Rlci5ib3VuZHNbM10pIC8gKHJhc3Rlci5ib3VuZHNbMV0gLSByYXN0ZXIuYm91bmRzWzNdKVxuICApO1xufVxuXG52b2lkIG1haW4odm9pZCkge1xuICB2ZWMyIHV2ID0gdlRleENvb3JkO1xuICBpZiAocmFzdGVyLmNvb3JkaW5hdGVDb252ZXJzaW9uIDwgLTAuNSkge1xuICAgIHZlYzIgbG5nbGF0ID0gbWVyY2F0b3JfdG9fbG5nbGF0KHZUZXhQb3MpO1xuICAgIHV2ID0gZ2V0VVYobG5nbGF0KTtcbiAgfSBlbHNlIGlmIChyYXN0ZXIuY29vcmRpbmF0ZUNvbnZlcnNpb24gPiAwLjUpIHtcbiAgICB2ZWMyIGNvbW1vblBvcyA9IGxuZ2xhdF90b19tZXJjYXRvcih2VGV4UG9zKTtcbiAgICB1diA9IGdldFVWKGNvbW1vblBvcyk7XG4gIH1cblxuICB2ZWM0IGltYWdlID0gdmVjNCgwLjApO1xuICBERUNLR0xfQ1JFQVRFX0NPTE9SKGltYWdlLCB1dik7XG5cbiAgREVDS0dMX01VVEFURV9DT0xPUihpbWFnZSwgdXYpO1xuXG4gIGZyYWdDb2xvciA9IGFwcGx5X29wYWNpdHkoY29sb3JfdGludChjb2xvcl9kZXNhdHVyYXRlKGltYWdlLnJnYikpLCByYXN0ZXIub3BhY2l0eSk7XG5cbiAgZ2VvbWV0cnkudXYgPSB1djtcbiAgREVDS0dMX0ZJTFRFUl9DT0xPUihmcmFnQ29sb3IsIGdlb21ldHJ5KTtc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